diff --git a/LICENSE.txt b/LICENSE.txt index ebe1307..74ca367 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2020 Christoph Haas +Copyright (c) 2020-2023 Christoph Haas Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README-RASPBERRYPI.md b/README-RASPBERRYPI.md deleted file mode 100644 index 9e5f2c2..0000000 --- a/README-RASPBERRYPI.md +++ /dev/null @@ -1,51 +0,0 @@ -# WireGuard Portal on Raspberry Pi - -This readme only contains a detailed explanation of how to set up the WireGuard Portal service on a raspberry pi (>= 3). - -## Setup - -You can either download prebuild binaries from the [release page](https://github.com/h44z/wg-portal/releases) or use Docker images for ARM. -If you want to build the binary yourself, use the following building instructions. - -### Building -This section describes how to build the WireGuard Portal code. -To compile the final binary, use the Makefile provided in the repository. -As WireGuard Portal is written in Go, **golang >= 1.16** must be installed prior to building. -If you want to cross compile ARM binaries from AMD64 systems, install *arm-linux-gnueabi-gcc* (armv7) or *aarch64-linux-gnu-gcc* (arm64). - -``` -# for 64 bit OS -make build-arm64 - -# for 32 bit OS -make build-arm -``` - -The compiled binary and all necessary assets will be located in the dist folder. - -### Service setup - - - Copy the contents from the dist folder (or from the downloaded zip file) to `/opt/wg-portal`. You can choose a different path as well, but make sure to update the systemd service file accordingly. - - Update the provided systemd `wg-portal.service` file: - - Make sure that the binary matches the system architecture. - - There are three pre-build binaries available: wg-portal-**amd64**, wg-portal-**arm64** and wg-portal-**arm**. - - For a raspberry pi use the arm binary if you are using armv7l architecture. If armv8 is used, the arm64 version should work. - - Make sure that the paths to the binary and the working directory are set correctly (defaults to /opt/wg-portal/wg-portal-amd64): - - ConditionPathExists - - WorkingDirectory - - ExecStart - - EnvironmentFile - - Update environment variables in the `wg-portal.env` file to fit your needs - - Make sure that the binary application file is executable - - `sudo chmod +x /opt/wg-portal/wg-portal-*` - - Link the system service file to the correct folder: - - `sudo ln -s /opt/wg-portal/wg-portal.service /etc/systemd/system/wg-portal.service` - - Reload the systemctl daemon: - - `sudo systemctl daemon-reload` - -### Manage the service -Once the service has been setup, you can simply manage the service using `systemctl`: - - Enable on startup: `systemctl enable wg-portal.service` - - Start: `systemctl start wg-portal.service` - - Stop: `systemctl stop wg-portal.service` - - Status: `systemctl status wg-portal.service` \ No newline at end of file diff --git a/README.md b/README.md index 01b3fc3..f4a9f15 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,7 @@ The portal uses the WireGuard [wgctrl](https://github.com/WireGuard/wgctrl-go) l interfaces. This allows for seamless activation or deactivation of new users, without disturbing existing VPN connections. -The configuration portal currently supports using SQLite and MySQL as a user source for authentication and profile data. -It also supports LDAP (Active Directory or OpenLDAP) as authentication provider. +The configuration portal supports using a database (SQLite, MySQL, MsSQL or Postgres), OAuth or LDAP (Active Directory or OpenLDAP) as a user source for authentication and profile data. ## Features * Self-hosted and web based @@ -22,221 +21,117 @@ It also supports LDAP (Active Directory or OpenLDAP) as authentication provider. * QR-Code for convenient mobile client configuration * Sent email to client with QR-code and client config * Enable / Disable clients seamlessly - * Generation of `wgX.conf` after any modification + * Generation of `wgX.conf` if required * IPv6 ready - * User authentication (SQLite/MySQL and LDAP) + * User authentication (database, OAuth or LDAP) * Dockerized - * Responsive template + * Responsive web UI written in Vue.JS * One single binary * Can be used with existing WireGuard setups * Support for multiple WireGuard interfaces - * REST API for management and client deployment * Peer Expiry Feature + * REST API for management and client deployment (coming soon) ![Screenshot](screenshot.png) -## Setup -Make sure that your host system has at least one WireGuard interface (for example wg0) available. -If you did not start up a WireGuard interface yet, take a look at [wg-quick](https://manpages.debian.org/unstable/wireguard-tools/wg-quick.8.en.html) in order to get started. - -### Docker -The easiest way to run WireGuard Portal is to use the Docker image provided. - -HINT: the *latest* tag always refers to the master branch and might contain unstable or incompatible code! - -Docker Compose snippet with some sample configuration values: -``` -version: '3.6' -services: - wg-portal: - image: h44z/wg-portal:latest - container_name: wg-portal - restart: unless-stopped - cap_add: - - NET_ADMIN - network_mode: "host" - volumes: - - /etc/wireguard:/etc/wireguard - - ./data:/app/data - ports: - - '8123:8123' - environment: - # WireGuard Settings - - WG_DEVICES=wg0 - - WG_DEFAULT_DEVICE=wg0 - - WG_CONFIG_PATH=/etc/wireguard - # Core Settings - - EXTERNAL_URL=https://vpn.company.com - - WEBSITE_TITLE=WireGuard VPN - - COMPANY_NAME=Your Company Name - - ADMIN_USER=admin@domain.com - - ADMIN_PASS=supersecret - # Mail Settings - - MAIL_FROM=WireGuard VPN - - EMAIL_HOST=10.10.10.10 - - EMAIL_PORT=25 - # LDAP Settings - - LDAP_ENABLED=true - - LDAP_URL=ldap://srv-ad01.company.local:389 - - LDAP_BASEDN=DC=COMPANY,DC=LOCAL - - LDAP_USER=ldap_wireguard@company.local - - LDAP_PASSWORD=supersecretldappassword - - LDAP_ADMIN_GROUP=CN=WireGuardAdmins,OU=Users,DC=COMPANY,DC=LOCAL -``` -Please note that mapping ```/etc/wireguard``` to ```/etc/wireguard``` inside the docker, will erase your host's current configuration. -If needed, please make sure to back up your files from ```/etc/wireguard```. -For a full list of configuration options take a look at the source file [internal/server/configuration.go](internal/server/configuration.go#L58). - -### Standalone -For a standalone application, use the Makefile provided in the repository to build the application. Go version 1.16 or higher has to be installed to build WireGuard Portal. - -```shell -# show all possible make commands -make - -# build wg-portal for current system architecture -make build -``` - -The compiled binary will be located in the dist folder. -A detailed description for using this software with a raspberry pi can be found in the [README-RASPBERRYPI.md](README-RASPBERRYPI.md). - -To build the Docker image, Docker (> 20.x) with buildx is required. If you want to build cross-platform images, you need to install qemu. -On arch linux for example install: `docker-buildx qemu-user-static qemu-user-static-binfmt`. - -Once the Docker setup is completed, create a new buildx builder: -```shell -docker buildx create --name wgportalbuilder --platform linux/arm/v7,linux/arm64,linux/amd64 -docker buildx use wgportalbuilder -docker buildx inspect --bootstrap -``` -Now you can compile the Docker image: -```shell -# multi platform build, can only be exported to tar archives -docker buildx build --platform linux/arm/v7,linux/arm64,linux/amd64 --output type=local,dest=docker_images \ - --build-arg BUILD_IDENTIFIER=dev --build-arg BUILD_VERSION=0.1 -t h44z/wg-portal . - - -# image for current platform only (same as docker build) -docker buildx build --load \ - --build-arg BUILD_IDENTIFIER=dev --build-arg BUILD_VERSION=0.1 -t h44z/wg-portal . -``` - ## Configuration -You can configure WireGuard Portal using either environment variables or a yaml configuration file. +You can configure WireGuard Portal using a yaml configuration file. The filepath of the yaml configuration file defaults to **config.yml** in the working directory of the executable. -It is possible to override the configuration filepath using the environment variable **CONFIG_FILE**. -For example: `CONFIG_FILE=/home/test/config.yml ./wg-portal-amd64`. +It is possible to override the configuration filepath using the environment variable **WG_PORTAL_CONFIG**. +For example: `WG_PORTAL_CONFIG=/home/test/config.yml ./wg-portal-amd64`. ### Configuration Options The following configuration options are available: -| environment | yaml | yaml_parent | default_value | description | -|----------------------------|-------------------------|-------------|-----------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------| -| LISTENING_ADDRESS | listeningAddress | core | :8123 | The address on which the web server is listening. Optional IP address and port, e.g.: 127.0.0.1:8080. | -| EXTERNAL_URL | externalUrl | core | http://localhost:8123 | The external URL where the web server is reachable. This link is used in emails that are created by the WireGuard Portal. | -| WEBSITE_TITLE | title | core | WireGuard VPN | The website title. | -| COMPANY_NAME | company | core | WireGuard Portal | The company name (for branding). | -| MAIL_FROM | mailFrom | core | WireGuard VPN | The email address from which emails are sent. | -| LOGO_URL | logoUrl | core | /img/header-logo.png | The logo displayed in the page's header. | -| ADMIN_USER | adminUser | core | admin@wgportal.local | The administrator user. Must be a valid email address. | -| ADMIN_PASS | adminPass | core | wgportal | The administrator password. If unchanged, a random password will be set on first startup. | -| EDITABLE_KEYS | editableKeys | core | true | Allow to edit key-pairs in the UI. | -| CREATE_DEFAULT_PEER | createDefaultPeer | core | false | If an LDAP user logs in for the first time, a new WireGuard peer will be created on the WG_DEFAULT_DEVICE if this option is enabled. | -| SELF_PROVISIONING | selfProvisioning | core | false | Allow registered users to automatically create peers via the RESTful API. | -| WG_EXPORTER_FRIENDLY_NAMES | wgExporterFriendlyNames | core | false | Enable integration with [prometheus_wireguard_exporter friendly name](https://github.com/MindFlavor/prometheus_wireguard_exporter#friendly-tags). | -| LDAP_ENABLED | ldapEnabled | core | false | Enable or disable the LDAP backend. | -| SESSION_SECRET | sessionSecret | core | secret | Use a custom secret to encrypt session data. | -| BACKGROUND_TASK_INTERVAL | backgroundTaskInterval | core | 900 | The interval (in seconds) for the background tasks (like peer expiry check). | -| EXPIRY_REENABLE | expiryReEnable | core | false | Reactivate expired peers if the expiration date is in the future. | -| DATABASE_TYPE | typ | database | sqlite | Either mysql or sqlite. | -| DATABASE_HOST | host | database | | The mysql server address. | -| DATABASE_PORT | port | database | | The mysql server port. | -| DATABASE_NAME | database | database | data/wg_portal.db | For sqlite database: the database file-path, otherwise the database name. | -| DATABASE_USERNAME | user | database | | The mysql user. | -| DATABASE_PASSWORD | password | database | | The mysql password. | -| EMAIL_HOST | host | email | 127.0.0.1 | The email server address. | -| EMAIL_PORT | port | email | 25 | The email server port. | -| EMAIL_TLS | tls | email | false | Use STARTTLS. DEPRECATED: use EMAIL_ENCRYPTION instead. | -| EMAIL_ENCRYPTION | encryption | email | none | Either none, tls or starttls. | -| EMAIL_CERT_VALIDATION | certcheck | email | false | Validate the email server certificate. | -| EMAIL_USERNAME | user | email | | An optional username for SMTP authentication. | -| EMAIL_PASSWORD | pass | email | | An optional password for SMTP authentication. | -| EMAIL_AUTHTYPE | auth | email | plain | Either plain, login or crammd5. If username and password are empty, this value is ignored. | -| WG_DEVICES | devices | wg | wg0 | A comma separated list of WireGuard devices. | -| WG_DEFAULT_DEVICE | defaultDevice | wg | wg0 | This device is used for auto-created peers (if CREATE_DEFAULT_PEER is enabled). | -| WG_CONFIG_PATH | configDirectory | wg | /etc/wireguard | If set, interface configuration updates will be written to this path, filename: .conf. | -| MANAGE_IPS | manageIPAddresses | wg | true | Handle IP address setup of interface, only available on linux. | -| USER_MANAGE_PEERS | userManagePeers | wg | false | Logged in user can create or update peers (partially). | -| LDAP_URL | url | ldap | ldap://srv-ad01.company.local:389 | The LDAP server url. | -| LDAP_STARTTLS | startTLS | ldap | true | Use STARTTLS. | -| LDAP_CERT_VALIDATION | certcheck | ldap | false | Validate the LDAP server certificate. | -| LDAP_BASEDN | dn | ldap | DC=COMPANY,DC=LOCAL | The base DN for searching users. | -| LDAP_USER | user | ldap | company\\\\ldap_wireguard | The bind user. | -| LDAP_PASSWORD | pass | ldap | SuperSecret | The bind password. | -| LDAP_LOGIN_FILTER | loginFilter | ldap | (&(objectClass=organizationalPerson)(mail={{login_identifier}})(!userAccountControl:1.2.840.113556.1.4.803:=2)) | {{login_identifier}} will be replaced with the login email address. | -| LDAP_SYNC_FILTER | syncFilter | ldap | (&(objectClass=organizationalPerson)(!userAccountControl:1.2.840.113556.1.4.803:=2)(mail=*)) | The filter string for the LDAP synchronization service. Users matching this filter will be synchronized with the WireGuard Portal database. | -| LDAP_SYNC_GROUP_FILTER | syncGroupFilter | ldap | | The filter string for the LDAP groups, for example: (objectClass=group). The groups are used to recursively check for admin group member ship of users. | -| LDAP_ADMIN_GROUP | adminGroup | ldap | CN=WireGuardAdmins,OU=_O_IT,DC=COMPANY,DC=LOCAL | Users in this group are marked as administrators. | -| LDAP_ATTR_EMAIL | attrEmail | ldap | mail | User email attribute. | -| LDAP_ATTR_FIRSTNAME | attrFirstname | ldap | givenName | User firstname attribute. | -| LDAP_ATTR_LASTNAME | attrLastname | ldap | sn | User lastname attribute. | -| LDAP_ATTR_PHONE | attrPhone | ldap | telephoneNumber | User phone number attribute. | -| LDAP_ATTR_GROUPS | attrGroups | ldap | memberOf | User groups attribute. | -| LDAP_CERT_CONN | ldapCertConn | ldap | false | Allow connection with certificate against LDAP server without user/password | -| LDAPTLS_CERT | ldapTlsCert | ldap | | The LDAP cert's path | -| LDAPTLS_KEY | ldapTlsKey | ldap | | The LDAP key's path | -| LOG_LEVEL | | | debug | Specify log level, one of: trace, debug, info, off. | -| LOG_JSON | | | false | Format log output as JSON. | -| LOG_COLOR | | | true | Colorize log output. | -| CONFIG_FILE | | | config.yml | The config file path. | - -### Sample yaml configuration -config.yml: -```yaml -core: - listeningAddress: :8123 - externalUrl: https://wg-test.test.com - adminUser: test@test.com - adminPass: test - editableKeys: true - createDefaultPeer: false - ldapEnabled: true - mailFrom: WireGuard VPN -ldap: - url: ldap://10.10.10.10:389 - dn: DC=test,DC=test - startTLS: false - user: wireguard@test.test - pass: test - adminGroup: CN=WireGuardAdmins,CN=Users,DC=test,DC=test -database: - typ: sqlite - database: data/wg_portal.db -email: - host: smtp.gmail.com - port: 587 - tls: true - user: test@gmail.com - pass: topsecret -wg: - devices: - - wg0 - - wg1 - defaultDevice: wg0 - configDirectory: /etc/wireguard - manageIPAddresses: true -``` - -### RESTful API -WireGuard Portal offers a RESTful API to interact with. -The API is documented using OpenAPI 2.0, the Swagger UI can be found -under the URL `http:///swagger/index.html?displayOperationId=true`. - -The [API's unittesting](tests/test_API.py) may serve as an example how to make use of the API with python3 & pyswagger. +| configuration key | parent key | default_value | description | +|---------------------------|------------|--------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------| +| admin_user | core | admin@wgportal.local | The administrator user. This user will be created as default admin if it does not yet exist. | +| admin_password | core | wgportal | The administrator password. If unchanged, a random password will be set on first startup. | +| editable_keys | core | true | Allow to edit key-pairs in the UI. | +| create_default_peer | core | false | If an LDAP user logs in for the first time, a new WireGuard peer will be created on the WG_DEFAULT_DEVICE if this option is enabled. | +| self_provisioning_allowed | core | false | Allow registered users to automatically create peers via their profile page. | +| import_existing | core | true | Import existing WireGuard interfaces and peers into WireGuard Portal. | +| restore_state | core | true | Restore the WireGuard interface state after WireGuard Portal has started. | +| log_level | advanced | warn | The loglevel, can be one of: trace, debug, info, warn, error. | +| log_pretty | advanced | false | Uses pretty, colorized log messages. | +| log_json | advanced | false | Logs in JSON format. | +| ldap_sync_interval | advanced | 15m | | +| start_listen_port | advanced | 51820 | | +| start_cidr_v4 | advanced | 10.11.12.0/24 | | +| start_cidr_v6 | advanced | fdfd:d3ad:c0de:1234::0/64 | | +| use_ip_v6 | advanced | true | | +| config_storage_path | advanced | | | +| expiry_check_interval | advanced | 15m | | +| use_ping_checks | statistics | true | | +| ping_check_workers | statistics | 10 | | +| ping_unprivileged | statistics | false | | +| ping_check_interval | statistics | 1m | | +| data_collection_interval | statistics | 10m | | +| collect_interface_data | statistics | true | | +| collect_peer_data | statistics | true | | +| collect_audit_data | statistics | true | | +| host | mail | 127.0.0.1 | | +| port | mail | 25 | | +| encryption | mail | none | | +| cert_validation | mail | false | | +| username | mail | | | +| password | mail | | | +| auth_type | mail | plain | | +| from | mail | Wireguard Portal | | +| link_only | mail | false | | +| callback_url_prefix | auth | /api/v0 | | +| oidc | auth | Empty Array - no providers configured | | +| oauth | auth | Empty Array - no providers configured | | +| ldap | auth | Empty Array - no providers configured | | +| provider_name | auth/oidc | | | +| display_name | auth/oidc | | | +| base_url | auth/oidc | | | +| client_id | auth/oidc | | | +| client_secret | auth/oidc | | | +| extra_scopes | auth/oidc | | | +| field_map | auth/oidc | | | +| registration_enabled | auth/oidc | | | +| provider_name | auth/oidc | | | +| display_name | auth/oauth | | | +| base_url | auth/oauth | | | +| client_id | auth/oauth | | | +| client_secret | auth/oauth | | | +| auth_url | auth/oauth | | | +| token_url | auth/oauth | | | +| redirect_url | auth/oauth | | | +| user_info_url | auth/oauth | | | +| scopes | auth/oauth | | | +| field_map | auth/oauth | | | +| registration_enabled | auth/oauth | | | +| url | auth/ldap | | | +| start_tls | auth/ldap | | | +| cert_validation | auth/ldap | | | +| tls_certificate_path | auth/ldap | | | +| tls_key_path | auth/ldap | | | +| base_dn | auth/ldap | | | +| bind_user | auth/ldap | | | +| bind_pass | auth/ldap | | | +| field_map | auth/ldap | | | +| login_filter | auth/ldap | | | +| admin_group | auth/ldap | | | +| synchronize | auth/ldap | | | +| disable_missing | auth/ldap | | | +| sync_filter | auth/ldap | | | +| registration_enabled | auth/ldap | | | +| debug | database | false | | +| slow_query_threshold | database | | | +| type | database | sqlite | | +| dsn | database | sqlite.db | | +| request_logging | web | false | | +| external_url | web | http://localhost:8888 | | +| listening_address | web | :8888 | | +| session_identifier | web | wgPortalSession | | +| session_secret | web | very_secret | | +| csrf_secret | web | extremely_secret | | +| site_title | web | WireGuard Portal | | +| site_company_name | web | WireGuard Portal | | ## What is out of scope - * Creating or removing WireGuard (wgX) interfaces. * Generation or application of any `iptables` or `nftables` rules. * Setting up or changing IP-addresses of the WireGuard interface on operating systems other than linux. * Importing private keys of an existing WireGuard setup. @@ -244,13 +139,9 @@ The [API's unittesting](tests/test_API.py) may serve as an example how to make u ## Application stack * [Gin, HTTP web framework written in Go](https://github.com/gin-gonic/gin) - * [go-template, data-driven templates for generating textual output](https://golang.org/pkg/text/template/) * [Bootstrap, for the HTML templates](https://getbootstrap.com/) - * [JQuery, for some nice JavaScript effects ;)](https://jquery.com/) + * [Vue.JS, for the frontend](hhttps://vuejs.org/) ## License * MIT License. [MIT](LICENSE.txt) or https://opensource.org/licenses/MIT - - -This project was inspired by [wg-gen-web](https://github.com/vx3r/wg-gen-web). diff --git a/cmd/wg-portal/main.go b/cmd/wg-portal/main.go index ccfd5f3..97aaef0 100644 --- a/cmd/wg-portal/main.go +++ b/cmd/wg-portal/main.go @@ -4,6 +4,7 @@ import ( "context" "github.com/h44z/wg-portal/internal/app/api/core" handlersV0 "github.com/h44z/wg-portal/internal/app/api/v0/handlers" + "github.com/h44z/wg-portal/internal/app/audit" "github.com/h44z/wg-portal/internal/app/auth" "github.com/h44z/wg-portal/internal/app/configfile" "github.com/h44z/wg-portal/internal/app/mail" @@ -31,6 +32,8 @@ func main() { internal.AssertNoError(err) setupLogging(cfg) + cfg.LogStartupValues() + rawDb, err := adapters.NewDatabase(cfg.Database) internal.AssertNoError(err) @@ -73,6 +76,10 @@ func main() { mailManager, err := mail.NewMailManager(cfg, mailer, cfgFileManager, database, database) internal.AssertNoError(err) + auditRecorder, err := audit.NewAuditRecorder(cfg, eventBus, database) + internal.AssertNoError(err) + auditRecorder.StartBackgroundJobs(ctx) + backend, err := app.New(cfg, eventBus, authenticator, userManager, wireGuardManager, statisticsCollector, cfgFileManager, mailManager) internal.AssertNoError(err) @@ -111,4 +118,16 @@ func setupLogging(cfg *config.Config) { default: logrus.SetLevel(logrus.WarnLevel) } + + switch { + case cfg.Advanced.LogJson: + logrus.SetFormatter(&logrus.JSONFormatter{ + PrettyPrint: cfg.Advanced.LogPretty, + }) + case cfg.Advanced.LogPretty: + logrus.SetFormatter(&logrus.TextFormatter{ + ForceColors: true, + DisableColors: false, + }) + } } diff --git a/frontend/index.html b/frontend/index.html index 7765708..b667f8d 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -9,6 +9,9 @@ diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 8db0b35..9b51d7e 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,12 +1,14 @@ \ No newline at end of file diff --git a/frontend/src/components/InterfaceEditModal.vue b/frontend/src/components/InterfaceEditModal.vue index bfb192f..ee7c9ba 100644 --- a/frontend/src/components/InterfaceEditModal.vue +++ b/frontend/src/components/InterfaceEditModal.vue @@ -238,8 +238,8 @@ async function save() { } catch (e) { console.log(e) notify({ - title: "Backend Connection Failure", - text: "Failed to save interface!", + title: "Failed to save interface!", + text: e.toString(), type: 'error', }) } @@ -263,8 +263,8 @@ async function applyPeerDefaults() { } catch (e) { console.log(e) notify({ - title: "Backend Connection Failure", - text: "Failed to apply peer defaults!", + title: "Failed to apply peer defaults!", + text: e.toString(), type: 'error', }) } @@ -277,8 +277,8 @@ async function del() { } catch (e) { console.log(e) notify({ - title: "Backend Connection Failure", - text: "Failed to delete interface!", + title: "Failed to delete interface!", + text: e.toString(), type: 'error', }) } @@ -508,23 +508,5 @@ async function del() { diff --git a/frontend/src/components/PeerEditModal.vue b/frontend/src/components/PeerEditModal.vue index 2dfb7f9..52d09d7 100644 --- a/frontend/src/components/PeerEditModal.vue +++ b/frontend/src/components/PeerEditModal.vue @@ -169,6 +169,13 @@ watch(() => formData.value.IgnoreGlobalSettings, async (newValue, oldValue) => { } ) +watch(() => formData.value.Disabled, async (newValue, oldValue) => { + if (oldValue && !newValue && formData.value.ExpiresAt) { + formData.value.ExpiresAt = "" // reset expiry date + } + } +) + function close() { formData.value = freshPeer() emit('close') @@ -257,8 +264,8 @@ async function save() { } catch (e) { console.log(e) notify({ - title: "Backend Connection Failure", - text: "Failed to save peer!", + title: "Failed to save peer!", + text: e.toString(), type: 'error', }) } @@ -271,8 +278,8 @@ async function del() { } catch (e) { console.log(e) notify({ - title: "Backend Connection Failure", - text: "Failed to delete peer!", + title: "Failed to delete peer!", + text: e.toString(), type: 'error', }) } diff --git a/frontend/src/components/PeerMultiCreateModal.vue b/frontend/src/components/PeerMultiCreateModal.vue index c1d4e91..6075a0f 100644 --- a/frontend/src/components/PeerMultiCreateModal.vue +++ b/frontend/src/components/PeerMultiCreateModal.vue @@ -2,7 +2,7 @@ import Modal from "./Modal.vue"; import {peerStore} from "@/stores/peers"; import {interfaceStore} from "@/stores/interfaces"; -import {computed, ref, watch} from "vue"; +import {computed, ref} from "vue"; import { useI18n } from 'vue-i18n'; import { notify } from "@kyvg/vue3-notification"; import Vue3TagsInput from "vue3-tags-input"; @@ -63,8 +63,8 @@ async function save() { } catch (e) { console.log(e) notify({ - title: "Backend Connection Failure", - text: "Failed to create peers!", + title: "Failed to create peers!", + text: e.toString(), type: 'error', }) } diff --git a/frontend/src/components/PeerViewModal.vue b/frontend/src/components/PeerViewModal.vue index 4e88189..a6f1fe5 100644 --- a/frontend/src/components/PeerViewModal.vue +++ b/frontend/src/components/PeerViewModal.vue @@ -7,9 +7,11 @@ import {useI18n} from "vue-i18n"; import {freshInterface, freshPeer, freshStats} from '@/helpers/models'; import Prism from "vue-prism-component"; import {notify} from "@kyvg/vue3-notification"; +import {settingsStore} from "@/stores/settings"; const { t } = useI18n() +const settings = settingsStore() const peers = peerStore() const interfaces = interfaceStore() @@ -101,10 +103,10 @@ function download() { } function email() { - peers.MailPeerConfig(false, [selectedPeer.value.Identifier]).catch(error => { + peers.MailPeerConfig(settings.Setting("MailLinkOnly"), [selectedPeer.value.Identifier]).catch(e => { notify({ - title: "Peer email failure", - text: "Failed to send mail with peer configuration!", + title: "Failed to send mail with peer configuration!", + text: e.toString(), type: 'error', }) }) diff --git a/frontend/src/components/UserViewModal.vue b/frontend/src/components/UserViewModal.vue index 861ce7b..8a808d3 100644 --- a/frontend/src/components/UserViewModal.vue +++ b/frontend/src/components/UserViewModal.vue @@ -40,7 +40,6 @@ const userPeers = computed(() => { watch(() => props.visible, async (newValue, oldValue) => { if (oldValue === false && newValue === true) { // if modal is shown - console.log(selectedUser.value) await users.LoadUserPeers(selectedUser.value.Identifier) } } diff --git a/frontend/src/helpers/fetch-wrapper.js b/frontend/src/helpers/fetch-wrapper.js index 38715ca..8ac930a 100644 --- a/frontend/src/helpers/fetch-wrapper.js +++ b/frontend/src/helpers/fetch-wrapper.js @@ -1,5 +1,5 @@ -import { authStore } from '../stores/auth'; -import { securityStore } from '../stores/security'; +import { authStore } from '@/stores/auth'; +import { securityStore } from '@/stores/security'; export const fetchWrapper = { url: apiUrl(), diff --git a/frontend/src/main.js b/frontend/src/main.js index c14385a..300a8fb 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -4,7 +4,6 @@ import { createPinia } from "pinia"; import App from "./App.vue"; import router from "./router"; -import { createI18n } from "vue-i18n"; import i18n from "./lang"; import Notifications from '@kyvg/vue3-notification' diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index d567b93..09ef969 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -3,7 +3,7 @@ import HomeView from '../views/HomeView.vue' import LoginView from '../views/LoginView.vue' import InterfaceView from '../views/InterfaceView.vue' -import {authStore} from '../stores/auth.js' +import {authStore} from '@/stores/auth' import {notify} from "@kyvg/vue3-notification"; const router = createRouter({ diff --git a/frontend/src/stores/auth.js b/frontend/src/stores/auth.js index 594c3e5..58b3759 100644 --- a/frontend/src/stores/auth.js +++ b/frontend/src/stores/auth.js @@ -1,7 +1,7 @@ import { defineStore } from 'pinia' import { notify } from "@kyvg/vue3-notification"; -import { apiWrapper } from '../helpers/fetch-wrapper.js' +import { apiWrapper } from '@/helpers/fetch-wrapper' import router from '../router' export const authStore = defineStore({ diff --git a/frontend/src/stores/peers.js b/frontend/src/stores/peers.js index 398991e..df68116 100644 --- a/frontend/src/stores/peers.js +++ b/frontend/src/stores/peers.js @@ -1,5 +1,5 @@ import { defineStore } from 'pinia' -import {apiWrapper} from "../helpers/fetch-wrapper"; +import {apiWrapper} from "@/helpers/fetch-wrapper"; import {notify} from "@kyvg/vue3-notification"; import {interfaceStore} from "./interfaces"; import {freshPeer, freshStats} from '@/helpers/models'; diff --git a/frontend/src/stores/security.js b/frontend/src/stores/security.js index 24001c0..1d81a38 100644 --- a/frontend/src/stores/security.js +++ b/frontend/src/stores/security.js @@ -1,7 +1,7 @@ import { defineStore } from 'pinia' import { notify } from "@kyvg/vue3-notification"; -import { apiWrapper } from '../helpers/fetch-wrapper.js' +import { apiWrapper } from '@/helpers/fetch-wrapper' export const securityStore = defineStore({ id: 'security', diff --git a/frontend/src/stores/settings.js b/frontend/src/stores/settings.js new file mode 100644 index 0000000..6a404d8 --- /dev/null +++ b/frontend/src/stores/settings.js @@ -0,0 +1,36 @@ +import { defineStore } from 'pinia' + +import { notify } from "@kyvg/vue3-notification"; +import { apiWrapper } from '@/helpers/fetch-wrapper' + +const baseUrl = `/config` + +export const settingsStore = defineStore({ + id: 'settings', + state: () => ({ + settings: {}, + }), + getters: { + Setting: (state) => { + return (key) => (key in state.settings) ? state.settings[key] : undefined + } + }, + actions: { + setSettings(settings) { + this.settings = settings + }, + // LoadSecurityProperties always returns a fulfilled promise, even if the request failed. + async LoadSettings() { + await apiWrapper.get(`${baseUrl}/settings`) + .then(data => this.setSettings(data)) + .catch(error => { + this.setSettings({}); + console.log("Failed to load settings: ", error); + notify({ + title: "Backend Connection Failure", + text: "Failed to load settings!", + }); + }) + } + } +}); \ No newline at end of file diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue index c1b4579..f3d6eef 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -1,5 +1,5 @@ - - + +