mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2024-12-27 09:22:19 +08:00
Merge branch 'v4.1-dev' into v4.0.3-fix
This commit is contained in:
commit
c01201b88e
46 changed files with 896 additions and 364 deletions
125
src/dashboard.py
125
src/dashboard.py
|
@ -33,7 +33,8 @@ import threading
|
|||
|
||||
from flask.json.provider import DefaultJSONProvider
|
||||
|
||||
DASHBOARD_VERSION = 'v4.0.3'
|
||||
|
||||
DASHBOARD_VERSION = 'v4.1'
|
||||
CONFIGURATION_PATH = os.getenv('CONFIGURATION_PATH', '.')
|
||||
DB_PATH = os.path.join(CONFIGURATION_PATH, 'db')
|
||||
if not os.path.isdir(DB_PATH):
|
||||
|
@ -398,9 +399,7 @@ class PeerShareLinks:
|
|||
)
|
||||
"""
|
||||
)
|
||||
# sqldb.commit()
|
||||
self.__getSharedLinks()
|
||||
# print(self.Links)
|
||||
def __getSharedLinks(self):
|
||||
self.Links.clear()
|
||||
allLinks = sqlSelect("SELECT * FROM PeerShareLinks WHERE ExpireDate IS NULL OR ExpireDate > datetime('now', 'localtime')").fetchall()
|
||||
|
@ -421,7 +420,6 @@ class PeerShareLinks:
|
|||
if len(self.getLink(Configuration, Peer)) > 0:
|
||||
sqlUpdate("UPDATE PeerShareLinks SET ExpireDate = datetime('now', 'localtime') WHERE Configuration = ? AND Peer = ?", (Configuration, Peer, ))
|
||||
sqlUpdate("INSERT INTO PeerShareLinks (ShareID, Configuration, Peer, ExpireDate) VALUES (?, ?, ?, ?)", (newShareID, Configuration, Peer, ExpireDate, ))
|
||||
# sqldb.commit()
|
||||
self.__getSharedLinks()
|
||||
except Exception as e:
|
||||
return False, str(e)
|
||||
|
@ -429,7 +427,6 @@ class PeerShareLinks:
|
|||
|
||||
def updateLinkExpireDate(self, ShareID, ExpireDate: datetime = None) -> tuple[bool, str]:
|
||||
sqlUpdate("UPDATE PeerShareLinks SET ExpireDate = ? WHERE ShareID = ?;", (ExpireDate, ShareID, ))
|
||||
# sqldb.commit()
|
||||
self.__getSharedLinks()
|
||||
return True, ""
|
||||
|
||||
|
@ -465,11 +462,11 @@ class WireguardConfiguration:
|
|||
|
||||
if name is not None:
|
||||
self.Name = name
|
||||
self.__parser.read_file(open(os.path.join(WG_CONF_PATH, f'{self.Name}.conf')))
|
||||
self.__parser.read_file(open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{self.Name}.conf')))
|
||||
sections = self.__parser.sections()
|
||||
if "Interface" not in sections:
|
||||
raise self.InvalidConfigurationFileException(
|
||||
"[Interface] section not found in " + os.path.join(WG_CONF_PATH, f'{self.Name}.conf'))
|
||||
"[Interface] section not found in " + os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{self.Name}.conf'))
|
||||
interfaceConfig = dict(self.__parser.items("Interface", True))
|
||||
for i in dir(self):
|
||||
if str(i) in interfaceConfig.keys():
|
||||
|
@ -506,7 +503,6 @@ class WireguardConfiguration:
|
|||
|
||||
with open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1],
|
||||
f"{self.Name}.conf"), "w+") as configFile:
|
||||
# print(self.__parser.sections())
|
||||
self.__parser.write(configFile)
|
||||
|
||||
self.Peers: list[Peer] = []
|
||||
|
@ -532,7 +528,6 @@ class WireguardConfiguration:
|
|||
)
|
||||
""" % self.Name
|
||||
)
|
||||
# sqldb.commit()
|
||||
|
||||
if f'{self.Name}_restrict_access' not in existingTables:
|
||||
sqlUpdate(
|
||||
|
@ -548,7 +543,6 @@ class WireguardConfiguration:
|
|||
)
|
||||
""" % self.Name
|
||||
)
|
||||
# sqldb.commit()
|
||||
if f'{self.Name}_transfer' not in existingTables:
|
||||
sqlUpdate(
|
||||
"""
|
||||
|
@ -559,7 +553,6 @@ class WireguardConfiguration:
|
|||
)
|
||||
""" % self.Name
|
||||
)
|
||||
# sqldb.commit()
|
||||
if f'{self.Name}_deleted' not in existingTables:
|
||||
sqlUpdate(
|
||||
"""
|
||||
|
@ -574,9 +567,6 @@ class WireguardConfiguration:
|
|||
)
|
||||
""" % self.Name
|
||||
)
|
||||
# sqldb.commit()
|
||||
|
||||
|
||||
|
||||
def __getPublicKey(self) -> str:
|
||||
return _generatePublicKey(self.PrivateKey)[1]
|
||||
|
@ -592,7 +582,7 @@ class WireguardConfiguration:
|
|||
self.RestrictedPeers.append(Peer(i, self))
|
||||
|
||||
def configurationFileChanged(self) :
|
||||
mt = os.path.getmtime(os.path.join(WG_CONF_PATH, f'{self.Name}.conf'))
|
||||
mt = os.path.getmtime(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{self.Name}.conf'))
|
||||
changed = self.__configFileModifiedTime is None or self.__configFileModifiedTime != mt
|
||||
self.__configFileModifiedTime = mt
|
||||
return changed
|
||||
|
@ -601,7 +591,7 @@ class WireguardConfiguration:
|
|||
|
||||
if self.configurationFileChanged():
|
||||
self.Peers = []
|
||||
with open(os.path.join(WG_CONF_PATH, f'{self.Name}.conf'), 'r') as configFile:
|
||||
with open(os.path.join(DashboardConfig.GetConfig("Server", "wg_conf_path")[1], f'{self.Name}.conf'), 'r') as configFile:
|
||||
p = []
|
||||
pCounter = -1
|
||||
content = configFile.read().split('\n')
|
||||
|
@ -622,7 +612,6 @@ class WireguardConfiguration:
|
|||
|
||||
if regex_match("#Name# = (.*)", i):
|
||||
split = re.split(r'\s*=\s*', i, 1)
|
||||
print(split)
|
||||
if len(split) == 2:
|
||||
p[pCounter]["name"] = split[1]
|
||||
|
||||
|
@ -662,15 +651,14 @@ class WireguardConfiguration:
|
|||
:cumu_data, :mtu, :keepalive, :remote_endpoint, :preshared_key);
|
||||
""" % self.Name
|
||||
, newPeer)
|
||||
# sqldb.commit()
|
||||
self.Peers.append(Peer(newPeer, self))
|
||||
else:
|
||||
sqlUpdate("UPDATE '%s' SET allowed_ip = ? WHERE id = ?" % self.Name,
|
||||
(i.get("AllowedIPs", "N/A"), i['PublicKey'],))
|
||||
# sqldb.commit()
|
||||
self.Peers.append(Peer(checkIfExist, self))
|
||||
except Exception as e:
|
||||
print(f"[WGDashboard] {self.Name} Error: {str(e)}")
|
||||
if __name__ == '__main__':
|
||||
print(f"[WGDashboard] {self.Name} Error: {str(e)}")
|
||||
else:
|
||||
self.Peers.clear()
|
||||
checkIfExist = sqlSelect("SELECT * FROM '%s'" % self.Name).fetchall()
|
||||
|
@ -731,7 +719,6 @@ class WireguardConfiguration:
|
|||
sqlUpdate("UPDATE '%s_restrict_access' SET status = 'stopped' WHERE id = ?" %
|
||||
(self.Name,), (pf.id,))
|
||||
sqlUpdate("DELETE FROM '%s' WHERE id = ?" % self.Name, (pf.id,))
|
||||
# sqldb.commit()
|
||||
numOfRestrictedPeers += 1
|
||||
except Exception as e:
|
||||
numOfFailedToRestrictPeers += 1
|
||||
|
@ -776,7 +763,7 @@ class WireguardConfiguration:
|
|||
def __savePeers(self):
|
||||
for i in self.Peers:
|
||||
d = i.toJson()
|
||||
sqldb.execute(
|
||||
sqlUpdate(
|
||||
'''
|
||||
UPDATE '%s' SET private_key = :private_key,
|
||||
DNS = :DNS, endpoint_allowed_ip = :endpoint_allowed_ip, name = :name,
|
||||
|
@ -787,7 +774,6 @@ class WireguardConfiguration:
|
|||
remote_endpoint = :remote_endpoint, preshared_key = :preshared_key WHERE id = :id
|
||||
''' % self.Name, d
|
||||
)
|
||||
sqldb.commit()
|
||||
|
||||
def __wgSave(self) -> tuple[bool, str] | tuple[bool, None]:
|
||||
try:
|
||||
|
@ -853,7 +839,6 @@ class WireguardConfiguration:
|
|||
self.Name, (cumulative_receive, cumulative_sent,
|
||||
cumulative_sent + cumulative_receive,
|
||||
data_usage[i][0],))
|
||||
sqldb.commit()
|
||||
total_sent = 0
|
||||
total_receive = 0
|
||||
_, p = self.searchPeer(data_usage[i][0])
|
||||
|
@ -862,7 +847,6 @@ class WireguardConfiguration:
|
|||
"UPDATE '%s' SET total_receive = ?, total_sent = ?, total_data = ? WHERE id = ?"
|
||||
% self.Name, (total_receive, total_sent,
|
||||
total_receive + total_sent, data_usage[i][0],))
|
||||
sqldb.commit()
|
||||
except Exception as e:
|
||||
print(f"[WGDashboard] {self.Name} Error: {str(e)} {str(e.__traceback__)}")
|
||||
|
||||
|
@ -877,9 +861,8 @@ class WireguardConfiguration:
|
|||
data_usage = data_usage.decode("UTF-8").split()
|
||||
count = 0
|
||||
for _ in range(int(len(data_usage) / 2)):
|
||||
sqldb.execute("UPDATE '%s' SET endpoint = ? WHERE id = ?" % self.Name
|
||||
sqlUpdate("UPDATE '%s' SET endpoint = ? WHERE id = ?" % self.Name
|
||||
, (data_usage[count + 1], data_usage[count],))
|
||||
# sqldb.commit()
|
||||
count += 2
|
||||
|
||||
def toggleConfiguration(self) -> [bool, str]:
|
||||
|
@ -1020,7 +1003,6 @@ class Peer:
|
|||
(name, private_key, dns_addresses, endpoint_allowed_ip, mtu,
|
||||
keepalive, preshared_key, self.id,)
|
||||
)
|
||||
sqldb.commit()
|
||||
return ResponseObject()
|
||||
except subprocess.CalledProcessError as exc:
|
||||
return ResponseObject(False, exc.output.decode("UTF-8").strip())
|
||||
|
@ -1123,7 +1105,8 @@ class DashboardConfig:
|
|||
"dashboard_refresh_interval": "60000",
|
||||
"dashboard_sort": "status",
|
||||
"dashboard_theme": "dark",
|
||||
"dashboard_api_key": "false"
|
||||
"dashboard_api_key": "false",
|
||||
"dashboard_language": "en"
|
||||
},
|
||||
"Peers": {
|
||||
"peer_global_DNS": "1.1.1.1",
|
||||
|
@ -1156,7 +1139,6 @@ class DashboardConfig:
|
|||
existingTable = sqlSelect("SELECT name FROM sqlite_master WHERE type='table' AND name = 'DashboardAPIKeys'").fetchall()
|
||||
if len(existingTable) == 0:
|
||||
sqlUpdate("CREATE TABLE DashboardAPIKeys (Key VARCHAR NOT NULL PRIMARY KEY, CreatedAt DATETIME NOT NULL DEFAULT (datetime('now', 'localtime')), ExpiredAt VARCHAR)")
|
||||
# sqldb.commit()
|
||||
|
||||
def __getAPIKeys(self) -> list[DashboardAPIKey]:
|
||||
keys = sqlSelect("SELECT * FROM DashboardAPIKeys WHERE ExpiredAt IS NULL OR ExpiredAt > datetime('now', 'localtime') ORDER BY CreatedAt DESC").fetchall()
|
||||
|
@ -1168,12 +1150,11 @@ class DashboardConfig:
|
|||
def createAPIKeys(self, ExpiredAt = None):
|
||||
newKey = secrets.token_urlsafe(32)
|
||||
sqlUpdate('INSERT INTO DashboardAPIKeys (Key, ExpiredAt) VALUES (?, ?)', (newKey, ExpiredAt,))
|
||||
# sqldb.commit()
|
||||
|
||||
self.DashboardAPIKeys = self.__getAPIKeys()
|
||||
|
||||
def deleteAPIKey(self, key):
|
||||
sqlUpdate("UPDATE DashboardAPIKeys SET ExpiredAt = datetime('now', 'localtime') WHERE Key = ?", (key, ))
|
||||
# sqldb.commit()
|
||||
self.DashboardAPIKeys = self.__getAPIKeys()
|
||||
|
||||
|
||||
|
@ -1228,6 +1209,10 @@ class DashboardConfig:
|
|||
else:
|
||||
value = self.generatePassword(value).decode("utf-8")
|
||||
|
||||
if section == "Server" and key == "wg_conf_path":
|
||||
if not os.path.exists(value):
|
||||
return False, "Path does not exist"
|
||||
|
||||
if section not in self.__config:
|
||||
self.__config[section] = {}
|
||||
|
||||
|
@ -1250,7 +1235,7 @@ class DashboardConfig:
|
|||
except Exception as e:
|
||||
return False
|
||||
|
||||
def GetConfig(self, section, key) -> [any, bool]:
|
||||
def GetConfig(self, section, key) -> [bool, any]:
|
||||
if section not in self.__config:
|
||||
return False, None
|
||||
|
||||
|
@ -1296,8 +1281,7 @@ def _regexMatch(regex, text):
|
|||
|
||||
|
||||
def _getConfigurationList():
|
||||
# configurations = {}
|
||||
for i in os.listdir(WG_CONF_PATH):
|
||||
for i in os.listdir(DashboardConfig.GetConfig("Server", "wg_conf_path")[1]):
|
||||
if _regexMatch("^(.{1,}).(conf)$", i):
|
||||
i = i.replace('.conf', '')
|
||||
try:
|
||||
|
@ -1382,7 +1366,10 @@ def _getWireguardConfigurationAvailableIP(configName: str, all: bool = False) ->
|
|||
add = p.allowed_ip.split(',')
|
||||
for i in add:
|
||||
a, c = i.split('/')
|
||||
existedAddress.append(ipaddress.ip_address(a.replace(" ", "")))
|
||||
try:
|
||||
existedAddress.append(ipaddress.ip_address(a.replace(" ", "")))
|
||||
except ValueError as error:
|
||||
print(f"[WGDashboard] Error: {configName} peer {p.id} have invalid ip")
|
||||
|
||||
for p in configuration.getRestrictedPeersList():
|
||||
if len(p.allowed_ip) > 0:
|
||||
|
@ -1415,14 +1402,20 @@ cursor = sqldb.cursor()
|
|||
|
||||
def sqlSelect(statement: str, paramters: tuple = ()) -> sqlite3.Cursor:
|
||||
with sqldb:
|
||||
cursor = sqldb.cursor()
|
||||
return cursor.execute(statement, paramters)
|
||||
try:
|
||||
cursor = sqldb.cursor()
|
||||
return cursor.execute(statement, paramters)
|
||||
except sqlite3.OperationalError as error:
|
||||
print("[WGDashboard] SQLite Error:" + str(error) + " | Statement: " + statement)
|
||||
|
||||
def sqlUpdate(statement: str, paramters: tuple = ()) -> sqlite3.Cursor:
|
||||
with sqldb:
|
||||
cursor = sqldb.cursor()
|
||||
cursor.execute(statement, paramters)
|
||||
sqldb.commit()
|
||||
try:
|
||||
cursor.execute(statement, paramters)
|
||||
sqldb.commit()
|
||||
except sqlite3.OperationalError as error:
|
||||
print("[WGDashboard] SQLite Error:" + str(error))
|
||||
|
||||
DashboardConfig = DashboardConfig()
|
||||
_, APP_PREFIX = DashboardConfig.GetConfig("Server", "app_prefix")
|
||||
|
@ -1481,6 +1474,7 @@ def auth_req():
|
|||
and "getDashboardVersion" not in request.path
|
||||
and "sharePeer/get" not in request.path
|
||||
and "isTotpEnabled" not in request.path
|
||||
and "locale" not in request.path
|
||||
):
|
||||
response = Flask.make_response(app, {
|
||||
"status": False,
|
||||
|
@ -1614,14 +1608,7 @@ def API_getDashboardConfiguration():
|
|||
return ResponseObject(data=DashboardConfig.toJson())
|
||||
|
||||
|
||||
# @app.route(f'{APP_PREFIX}/api/updateDashboardConfiguration', methods=["POST"])
|
||||
# def API_updateDashboardConfiguration():
|
||||
# data = request.get_json()
|
||||
# for section in data['DashboardConfiguration'].keys():
|
||||
# for key in data['DashboardConfiguration'][section].keys():
|
||||
# if not DashboardConfig.SetConfig(section, key, data['DashboardConfiguration'][section][key])[0]:
|
||||
# return ResponseObject(False, "Section or value is invalid.")
|
||||
# return ResponseObject()
|
||||
|
||||
|
||||
|
||||
@app.route(f'{APP_PREFIX}/api/updateDashboardConfigurationItem', methods=["POST"])
|
||||
|
@ -1629,13 +1616,16 @@ def API_updateDashboardConfigurationItem():
|
|||
data = request.get_json()
|
||||
if "section" not in data.keys() or "key" not in data.keys() or "value" not in data.keys():
|
||||
return ResponseObject(False, "Invalid request.")
|
||||
|
||||
valid, msg = DashboardConfig.SetConfig(
|
||||
data["section"], data["key"], data['value'])
|
||||
|
||||
if not valid:
|
||||
return ResponseObject(False, msg)
|
||||
|
||||
if data['section'] == "Server":
|
||||
if data['key'] == 'wg_conf_path':
|
||||
WireguardConfigurations.clear()
|
||||
_getConfigurationList()
|
||||
|
||||
return ResponseObject()
|
||||
|
||||
@app.route(f'{APP_PREFIX}/api/getDashboardAPIKeys', methods=['GET'])
|
||||
|
@ -1804,6 +1794,7 @@ def API_addPeers(configName):
|
|||
mtu = data['mtu']
|
||||
keep_alive = data['keepalive']
|
||||
preshared_key = data['preshared_key']
|
||||
preshared_key_bulkAdd: bool = data['preshared_key_bulkAdd']
|
||||
|
||||
if configName in WireguardConfigurations.keys():
|
||||
config = WireguardConfigurations.get(configName)
|
||||
|
@ -1830,7 +1821,7 @@ def API_addPeers(configName):
|
|||
keyPairs.append({
|
||||
"private_key": newPrivateKey,
|
||||
"id": _generatePublicKey(newPrivateKey)[1],
|
||||
"preshared_key": _generatePrivateKey()[1],
|
||||
"preshared_key": (_generatePrivateKey()[1] if preshared_key_bulkAdd else ""),
|
||||
"allowed_ip": availableIps[1][i],
|
||||
"name": f"BulkPeer #{(i + 1)}_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||||
})
|
||||
|
@ -2132,15 +2123,30 @@ def API_Welcome_Finish():
|
|||
"repeatNewPassword": data["repeatNewPassword"],
|
||||
"currentPassword": "admin"
|
||||
})
|
||||
# updateEnableTotp, updateEnableTotpErr = DashboardConfig.SetConfig("Account", "enable_totp", data["enable_totp"])
|
||||
|
||||
if not updateUsername or not updatePassword:
|
||||
return ResponseObject(False, f"{updateUsernameErr},{updatePasswordErr}".strip(","))
|
||||
|
||||
DashboardConfig.SetConfig("Other", "welcome_session", False)
|
||||
|
||||
return ResponseObject()
|
||||
|
||||
@app.get(f'{APP_PREFIX}/api/locale')
|
||||
def API_Local_CurrentLang():
|
||||
# if param is None or len(param) == 0:
|
||||
# with open(os.path.join("./static/locale/active_languages.json"), "r") as f:
|
||||
# return ResponseObject(data=''.join(f.readlines()))
|
||||
|
||||
_, param = DashboardConfig.GetConfig("Server", "dashboard_language")
|
||||
|
||||
if param == "en":
|
||||
return ResponseObject()
|
||||
|
||||
if os.path.exists(os.path.join(f"./static/locale/{param}.json")):
|
||||
with open(os.path.join(f"./static/locale/{param}.json"), "r") as f:
|
||||
return ResponseObject(data=''.join(f.readlines()))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@app.route(f'{APP_PREFIX}/', methods=['GET'])
|
||||
def index():
|
||||
|
@ -2152,10 +2158,11 @@ def index():
|
|||
|
||||
|
||||
def backGroundThread():
|
||||
with app.app_context():
|
||||
print(f"[WGDashboard] Background Thread #1 Started", flush=True)
|
||||
time.sleep(10)
|
||||
while True:
|
||||
global WireguardConfigurations
|
||||
print(f"[WGDashboard] Background Thread #1 Started", flush=True)
|
||||
time.sleep(10)
|
||||
while True:
|
||||
with app.app_context():
|
||||
for c in WireguardConfigurations.values():
|
||||
if c.getStatus():
|
||||
try:
|
||||
|
@ -2166,7 +2173,7 @@ def backGroundThread():
|
|||
c.getRestrictedPeersList()
|
||||
except Exception as e:
|
||||
print(f"[WGDashboard] Background Thread #1 Error: {str(e)}", flush=True)
|
||||
time.sleep(10)
|
||||
time.sleep(10)
|
||||
|
||||
|
||||
def peerJobScheduleBackgroundThread():
|
||||
|
|
8
src/static/app/dist/assets/index.css
vendored
8
src/static/app/dist/assets/index.css
vendored
File diff suppressed because one or more lines are too long
66
src/static/app/dist/assets/index.js
vendored
66
src/static/app/dist/assets/index.js
vendored
File diff suppressed because one or more lines are too long
12
src/static/app/package-lock.json
generated
12
src/static/app/package-lock.json
generated
|
@ -1,19 +1,19 @@
|
|||
{
|
||||
"name": "app",
|
||||
"version": "4.0.0",
|
||||
"version": "4.0.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "app",
|
||||
"version": "4.0.0",
|
||||
"version": "4.0.2",
|
||||
"dependencies": {
|
||||
"@vuepic/vue-datepicker": "^9.0.1",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"@vueuse/shared": "^10.9.0",
|
||||
"animate.css": "^4.1.1",
|
||||
"bootstrap": "^5.3.2",
|
||||
"bootstrap-icons": "^1.11.2",
|
||||
"bootstrap-icons": "^1.11.3",
|
||||
"cidr-tools": "^7.0.4",
|
||||
"dayjs": "^1.11.12",
|
||||
"electron-builder": "^24.13.3",
|
||||
|
@ -1486,9 +1486,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/bootstrap-icons": {
|
||||
"version": "1.11.2",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.2.tgz",
|
||||
"integrity": "sha512-TgdiPv+IM9tgDb+dsxrnGIyocsk85d2M7T0qIgkvPedZeoZfyeG/j+yiAE4uHCEayKef2RP05ahQ0/e9Sv75Wg==",
|
||||
"version": "1.11.3",
|
||||
"resolved": "https://registry.npmmirror.com/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz",
|
||||
"integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
"@vueuse/shared": "^10.9.0",
|
||||
"animate.css": "^4.1.1",
|
||||
"bootstrap": "^5.3.2",
|
||||
"bootstrap-icons": "^1.11.2",
|
||||
"bootstrap-icons": "^1.11.3",
|
||||
"cidr-tools": "^7.0.4",
|
||||
"dayjs": "^1.11.12",
|
||||
"electron-builder": "^24.13.3",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<script setup>
|
||||
<script setup async>
|
||||
import { RouterView } from 'vue-router'
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import {computed, watch} from "vue";
|
||||
|
@ -21,7 +21,6 @@ const getActiveCrossServer = computed(() => {
|
|||
}
|
||||
return undefined
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -4,17 +4,38 @@ export default {
|
|||
props: {
|
||||
data: Object,
|
||||
saving: Boolean
|
||||
},
|
||||
data(){
|
||||
return{
|
||||
enable: false
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
enable(){
|
||||
if (this.enable){
|
||||
this.data.preshared_key = window.wireguard.generateKeypair().presharedKey
|
||||
}else {
|
||||
this.data.preshared_key = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<label for="peer_preshared_key_textbox" class="form-label">
|
||||
<small class="text-muted">Pre-Shared Key</small>
|
||||
</label>
|
||||
<div class="d-flex align-items-start">
|
||||
<label for="peer_preshared_key_textbox" class="form-label">
|
||||
<small class="text-muted">Pre-Shared Key</small>
|
||||
</label>
|
||||
<div class="form-check form-switch ms-auto">
|
||||
<input class="form-check-input" type="checkbox" role="switch"
|
||||
v-model="this.enable"
|
||||
id="peer_preshared_key_switch">
|
||||
</div>
|
||||
</div>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="this.saving"
|
||||
:disabled="this.saving || !this.enable"
|
||||
v-model="this.data.preshared_key"
|
||||
id="peer_preshared_key_textbox">
|
||||
</div>
|
||||
|
|
|
@ -3,9 +3,10 @@ import { ref } from 'vue'
|
|||
import { onClickOutside } from '@vueuse/core'
|
||||
import "animate.css"
|
||||
import PeerSettingsDropdown from "@/components/configurationComponents/peerSettingsDropdown.vue";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
export default {
|
||||
name: "peer",
|
||||
components: {PeerSettingsDropdown},
|
||||
components: {LocaleText, PeerSettingsDropdown},
|
||||
props: {
|
||||
Peer: Object
|
||||
},
|
||||
|
@ -57,7 +58,7 @@ export default {
|
|||
<div v-else class="border-0 card-header bg-transparent text-warning fw-bold"
|
||||
style="font-size: 0.8rem">
|
||||
<i class="bi-lock-fill me-2"></i>
|
||||
Access Restricted
|
||||
<LocaleText t="Access Restricted"></LocaleText>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body pt-1" style="font-size: 0.9rem">
|
||||
|
@ -65,12 +66,16 @@ export default {
|
|||
{{Peer.name ? Peer.name : 'Untitled Peer'}}
|
||||
</h6>
|
||||
<div class="mb-2">
|
||||
<small class="text-muted">Public Key</small>
|
||||
<small class="text-muted">
|
||||
<LocaleText t="Public Key"></LocaleText>
|
||||
</small>
|
||||
<p class="mb-0"><samp>{{Peer.id}}</samp></p>
|
||||
</div>
|
||||
<div class="d-flex align-items-end">
|
||||
<div>
|
||||
<small class="text-muted">Allowed IP</small>
|
||||
<small class="text-muted">
|
||||
<LocaleText t="Allowed IPs"></LocaleText>
|
||||
</small>
|
||||
<p class="mb-0"><samp>{{Peer.allowed_ip}}</samp></p>
|
||||
</div>
|
||||
<div class="ms-auto px-2 rounded-3 subMenuBtn"
|
||||
|
|
|
@ -33,7 +33,8 @@ export default {
|
|||
endpoint_allowed_ip: this.dashboardStore.Configuration.Peers.peer_endpoint_allowed_ip,
|
||||
keepalive: parseInt(this.dashboardStore.Configuration.Peers.peer_keep_alive),
|
||||
mtu: parseInt(this.dashboardStore.Configuration.Peers.peer_mtu),
|
||||
preshared_key: ""
|
||||
preshared_key: "",
|
||||
preshared_key_bulkAdd: false
|
||||
},
|
||||
availableIp: undefined,
|
||||
availableIpSearchString: "",
|
||||
|
@ -119,16 +120,28 @@ export default {
|
|||
<DnsInput :saving="saving" :data="data"></DnsInput>
|
||||
|
||||
<hr class="mb-0 mt-2">
|
||||
<div class="row">
|
||||
<div class="row gy-3">
|
||||
<div class="col-sm" v-if="!this.data.bulkAdd">
|
||||
<PresharedKeyInput :saving="saving" :data="data" :bulk="this.data.bulkAdd"></PresharedKeyInput>
|
||||
</div>
|
||||
|
||||
<div class="col-sm">
|
||||
<MtuInput :saving="saving" :data="data"></MtuInput>
|
||||
</div>
|
||||
<div class="col-sm">
|
||||
<PersistentKeepAliveInput :saving="saving" :data="data"></PersistentKeepAliveInput>
|
||||
</div>
|
||||
<div class="col-12" v-if="this.data.bulkAdd">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" role="switch"
|
||||
v-model="this.data.preshared_key_bulkAdd"
|
||||
id="bullAdd_PresharedKey_Switch" checked>
|
||||
<label class="form-check-label" for="bullAdd_PresharedKey_Switch">
|
||||
Pre-Share Key
|
||||
{{this.data.preshared_key_bulkAdd ? "Enabled":"Disabled"}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex mt-2">
|
||||
<button class="ms-auto btn btn-dark btn-brand rounded-3 px-3 py-2 shadow"
|
||||
|
|
|
@ -3,6 +3,7 @@ import ScheduleDropdown from "@/components/configurationComponents/peerScheduleJ
|
|||
import SchedulePeerJob from "@/components/configurationComponents/peerScheduleJobsComponents/schedulePeerJob.vue";
|
||||
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||
import {v4} from "uuid";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
export default {
|
||||
name: "peerJobs",
|
||||
setup(){
|
||||
|
@ -13,6 +14,7 @@ export default {
|
|||
selectedPeer: Object
|
||||
},
|
||||
components:{
|
||||
LocaleText,
|
||||
SchedulePeerJob,
|
||||
ScheduleDropdown,
|
||||
},
|
||||
|
@ -50,8 +52,8 @@ export default {
|
|||
<div class="m-auto modal-dialog-centered dashboardModal">
|
||||
<div class="card rounded-3 shadow" style="width: 700px">
|
||||
<div class="card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-2">
|
||||
<h4 class="mb-0 fw-normal">Schedule Jobs
|
||||
<strong></strong>
|
||||
<h4 class="mb-0 fw-normal">
|
||||
<LocaleText t="Schedule Jobs"></LocaleText>
|
||||
</h4>
|
||||
<button type="button" class="btn-close ms-auto" @click="this.$emit('close')"></button>
|
||||
</div>
|
||||
|
@ -60,7 +62,8 @@ export default {
|
|||
<button class="btn bg-primary-subtle border-1 border-primary-subtle text-primary-emphasis rounded-3 shadow"
|
||||
|
||||
@click="this.addJob()">
|
||||
<i class="bi bi-plus-lg me-2"></i> Job
|
||||
<i class="bi bi-plus-lg me-2"></i>
|
||||
<LocaleText t="Job"></LocaleText>
|
||||
</button>
|
||||
</div>
|
||||
<TransitionGroup name="schedulePeerJobTransition" tag="div" class="position-relative">
|
||||
|
@ -76,7 +79,9 @@ export default {
|
|||
style="height: 153px"
|
||||
v-if="this.selectedPeer.jobs.length === 0">
|
||||
<div class="card-body text-muted text-center d-flex">
|
||||
<h6 class="m-auto">This peer does not have any job yet.</h6>
|
||||
<h6 class="m-auto">
|
||||
<LocaleText t="This peer does not have any job yet."></LocaleText>
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import SchedulePeerJob from "@/components/configurationComponents/peerScheduleJobsComponents/schedulePeerJob.vue";
|
||||
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||
import {v4} from "uuid";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
name: "peerJobsAllModal",
|
||||
|
@ -9,7 +10,7 @@ export default {
|
|||
const store = WireguardConfigurationsStore();
|
||||
return {store}
|
||||
},
|
||||
components: {SchedulePeerJob},
|
||||
components: {LocaleText, SchedulePeerJob},
|
||||
props: {
|
||||
configurationPeers: Array[Object]
|
||||
},
|
||||
|
@ -32,7 +33,8 @@ export default {
|
|||
<div class="m-auto modal-dialog-centered dashboardModal">
|
||||
<div class="card rounded-3 shadow" style="width: 700px">
|
||||
<div class="card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-2">
|
||||
<h4 class="mb-0 fw-normal">All Active Jobs
|
||||
<h4 class="mb-0 fw-normal">
|
||||
<LocaleText t="All Active Jobs"></LocaleText>
|
||||
</h4>
|
||||
<button type="button" class="btn-close ms-auto" @click="this.$emit('close')"></button>
|
||||
</div>
|
||||
|
@ -71,7 +73,9 @@ export default {
|
|||
style="height: 153px"
|
||||
v-else>
|
||||
<div class="card-body text-muted text-center d-flex">
|
||||
<h6 class="m-auto">No active job at the moment.</h6>
|
||||
<span class="m-auto">
|
||||
<LocaleText t="No active job at the moment."></LocaleText>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
<script>
|
||||
import dayjs from "dayjs";
|
||||
import {fetchGet} from "@/utilities/fetch.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
export default {
|
||||
name: "peerJobsLogsModal",
|
||||
components: {LocaleText},
|
||||
props: {
|
||||
configurationInfo: Object
|
||||
},
|
||||
|
@ -51,44 +53,56 @@ export default {
|
|||
<div class="m-auto mt-0 modal-dialog-centered dashboardModal" style="width: 100%">
|
||||
<div class="card rounded-3 shadow w-100" >
|
||||
<div class="card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-0">
|
||||
<h4 class="mb-0">Jobs Logs</h4>
|
||||
<h4 class="mb-0">
|
||||
<LocaleText t="Jobs Logs"></LocaleText>
|
||||
</h4>
|
||||
<button type="button" class="btn-close ms-auto" @click="this.$emit('close')"></button>
|
||||
</div>
|
||||
<div class="card-body px-4 pb-4 pt-2">
|
||||
<div v-if="!this.dataLoading">
|
||||
<p>Updated at: {{this.logFetchTime}}</p>
|
||||
<p>
|
||||
<LocaleText t="Updated at"></LocaleText>
|
||||
: {{this.logFetchTime}}</p>
|
||||
<div class="mb-2 d-flex gap-3">
|
||||
<button @click="this.fetchLog()"
|
||||
class="btn btn-sm rounded-3 shadow-sm
|
||||
text-info-emphasis bg-info-subtle border-1 border-info-subtle me-1">
|
||||
<i class="bi bi-arrow-clockwise me-2"></i>
|
||||
Refresh
|
||||
<LocaleText t="Refresh"></LocaleText>
|
||||
</button>
|
||||
<div class="d-flex gap-3 align-items-center">
|
||||
<span class="text-muted">Filter</span>
|
||||
<span class="text-muted">
|
||||
<LocaleText t="Filter"></LocaleText>
|
||||
</span>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" v-model="this.showSuccessJob"
|
||||
id="jobLogsShowSuccessCheck">
|
||||
<label class="form-check-label" for="jobLogsShowSuccessCheck">
|
||||
<span class="badge text-success-emphasis bg-success-subtle">Success</span>
|
||||
<span class="badge text-success-emphasis bg-success-subtle">
|
||||
<LocaleText t="Success"></LocaleText>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" v-model="this.showFailedJob"
|
||||
id="jobLogsShowFailedCheck">
|
||||
<label class="form-check-label" for="jobLogsShowFailedCheck">
|
||||
<span class="badge text-danger-emphasis bg-danger-subtle">Failed</span>
|
||||
<span class="badge text-danger-emphasis bg-danger-subtle">
|
||||
<LocaleText t="Failed"></LocaleText>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-3 align-items-center ms-auto">
|
||||
<span class="text-muted">Display</span>
|
||||
<span class="text-muted">
|
||||
<LocaleText t="Display"></LocaleText>
|
||||
</span>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
v-model="showJobID"
|
||||
id="jobLogsShowJobIDCheck">
|
||||
<label class="form-check-label" for="jobLogsShowJobIDCheck">
|
||||
Job ID
|
||||
<LocaleText t="Job ID"></LocaleText>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
|
@ -96,7 +110,7 @@ export default {
|
|||
v-model="showLogID"
|
||||
id="jobLogsShowLogIDCheck">
|
||||
<label class="form-check-label" for="jobLogsShowLogIDCheck">
|
||||
Log ID
|
||||
<LocaleText t="Log ID"></LocaleText>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
@ -106,11 +120,21 @@ export default {
|
|||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col" v-if="showLogID">Log ID</th>
|
||||
<th scope="col" v-if="showJobID">Job ID</th>
|
||||
<th scope="col">Status</th>
|
||||
<th scope="col">Message</th>
|
||||
<th scope="col">
|
||||
<LocaleText t="Date"></LocaleText>
|
||||
</th>
|
||||
<th scope="col" v-if="showLogID">
|
||||
<LocaleText t="Log ID"></LocaleText>
|
||||
</th>
|
||||
<th scope="col" v-if="showJobID">
|
||||
<LocaleText t="Job ID"></LocaleText>
|
||||
</th>
|
||||
<th scope="col">
|
||||
<LocaleText t="Status"></LocaleText>
|
||||
</th>
|
||||
<th scope="col">
|
||||
<LocaleText t="Message"></LocaleText>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -41,6 +41,7 @@ import PeerJobsAllModal from "@/components/configurationComponents/peerJobsAllMo
|
|||
import PeerJobsLogsModal from "@/components/configurationComponents/peerJobsLogsModal.vue";
|
||||
import {ref} from "vue";
|
||||
import PeerShareLinkModal from "@/components/configurationComponents/peerShareLinkModal.vue";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
Chart.register(
|
||||
ArcElement,
|
||||
|
@ -71,6 +72,7 @@ Chart.register(
|
|||
export default {
|
||||
name: "peerList",
|
||||
components: {
|
||||
LocaleText,
|
||||
PeerShareLinkModal,
|
||||
PeerJobsLogsModal,
|
||||
PeerJobsAllModal, PeerJobs, PeerCreate, PeerQRCode, PeerSettings, PeerSearch, Peer, Line, Bar},
|
||||
|
@ -370,7 +372,11 @@ export default {
|
|||
});
|
||||
|
||||
const result = this.wireguardConfigurationStore.searchString ?
|
||||
fuse.search(this.wireguardConfigurationStore.searchString).map(x => x.item) : this.configurationPeers;
|
||||
this.configurationPeers.filter(x => {
|
||||
return x.name.includes(this.wireguardConfigurationStore.searchString) ||
|
||||
x.id.includes(this.wireguardConfigurationStore.searchString) ||
|
||||
x.allowed_ip.includes(this.wireguardConfigurationStore.searchString)
|
||||
}) : this.configurationPeers;
|
||||
|
||||
if (this.dashboardConfigurationStore.Configuration.Server.dashboard_sort === "restricted"){
|
||||
return result.slice().sort((a, b) => {
|
||||
|
@ -406,7 +412,9 @@ export default {
|
|||
<div v-if="!this.loading" class="container-md">
|
||||
<div class="d-flex align-items-center">
|
||||
<div>
|
||||
<small CLASS="text-muted">CONFIGURATION</small>
|
||||
<small CLASS="text-muted">
|
||||
<LocaleText t="CONFIGURATION"></LocaleText>
|
||||
</small>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<h1 class="mb-0"><samp>{{this.configurationInfo.Name}}</samp></h1>
|
||||
</div>
|
||||
|
@ -414,13 +422,15 @@ export default {
|
|||
<div class="card rounded-3 bg-transparent shadow-sm ms-auto">
|
||||
<div class="card-body py-2 d-flex align-items-center">
|
||||
<div>
|
||||
<p class="mb-0 text-muted"><small>Status</small></p>
|
||||
<p class="mb-0 text-muted"><small>
|
||||
<LocaleText t="Status"></LocaleText>
|
||||
</small></p>
|
||||
<div class="form-check form-switch ms-auto">
|
||||
<label class="form-check-label" style="cursor: pointer" :for="'switch' + this.configurationInfo.id">
|
||||
{{this.configurationToggling ? 'Turning ':''}}
|
||||
{{this.configurationInfo.Status ? "On":"Off"}}
|
||||
<LocaleText t="On" v-if="this.configurationInfo.Status"></LocaleText>
|
||||
<LocaleText t="Off" v-else></LocaleText>
|
||||
<span v-if="this.configurationToggling"
|
||||
class="spinner-border spinner-border-sm" aria-hidden="true"></span>
|
||||
class="spinner-border spinner-border-sm ms-2" aria-hidden="true"></span>
|
||||
</label>
|
||||
<input class="form-check-input"
|
||||
style="cursor: pointer"
|
||||
|
@ -440,7 +450,9 @@ export default {
|
|||
<div class="col-6 col-lg-3">
|
||||
<div class="card rounded-3 bg-transparent shadow-sm">
|
||||
<div class="card-body py-2">
|
||||
<p class="mb-0 text-muted"><small>Address</small></p>
|
||||
<p class="mb-0 text-muted"><small>
|
||||
<LocaleText t="Address"></LocaleText>
|
||||
</small></p>
|
||||
{{this.configurationInfo.Address}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -448,7 +460,9 @@ export default {
|
|||
<div class="col-6 col-lg-3">
|
||||
<div class="card rounded-3 bg-transparent shadow-sm">
|
||||
<div class="card-body py-2">
|
||||
<p class="mb-0 text-muted"><small>Listen Port</small></p>
|
||||
<p class="mb-0 text-muted"><small>
|
||||
<LocaleText t="Listen Port"></LocaleText>
|
||||
</small></p>
|
||||
{{this.configurationInfo.ListenPort}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -456,7 +470,9 @@ export default {
|
|||
<div style="word-break: break-all" class="col-12 col-lg-6">
|
||||
<div class="card rounded-3 bg-transparent shadow-sm">
|
||||
<div class="card-body py-2">
|
||||
<p class="mb-0 text-muted"><small>Public Key</small></p>
|
||||
<p class="mb-0 text-muted"><small>
|
||||
<LocaleText t="Public Key"></LocaleText>
|
||||
</small></p>
|
||||
<samp>{{this.configurationInfo.PublicKey}}</samp>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -467,7 +483,9 @@ export default {
|
|||
<div class="card rounded-3 bg-transparent shadow-sm">
|
||||
<div class="card-body d-flex">
|
||||
<div>
|
||||
<p class="mb-0 text-muted"><small>Connected Peers</small></p>
|
||||
<p class="mb-0 text-muted"><small>
|
||||
<LocaleText t="Connected Peers"></LocaleText>
|
||||
</small></p>
|
||||
<strong class="h4">{{configurationSummary.connectedPeers}}</strong>
|
||||
</div>
|
||||
<i class="bi bi-ethernet ms-auto h2 text-muted"></i>
|
||||
|
@ -478,7 +496,9 @@ export default {
|
|||
<div class="card rounded-3 bg-transparent shadow-sm">
|
||||
<div class="card-body d-flex">
|
||||
<div>
|
||||
<p class="mb-0 text-muted"><small>Total Usage</small></p>
|
||||
<p class="mb-0 text-muted"><small>
|
||||
<LocaleText t="Total Usage"></LocaleText>
|
||||
</small></p>
|
||||
<strong class="h4">{{configurationSummary.totalUsage}} GB</strong>
|
||||
</div>
|
||||
<i class="bi bi-arrow-down-up ms-auto h2 text-muted"></i>
|
||||
|
@ -489,7 +509,9 @@ export default {
|
|||
<div class="card rounded-3 bg-transparent shadow-sm">
|
||||
<div class="card-body d-flex">
|
||||
<div>
|
||||
<p class="mb-0 text-muted"><small>Total Received</small></p>
|
||||
<p class="mb-0 text-muted"><small>
|
||||
<LocaleText t="Total Received"></LocaleText>
|
||||
</small></p>
|
||||
<strong class="h4 text-primary">{{configurationSummary.totalReceive}} GB</strong>
|
||||
</div>
|
||||
<i class="bi bi-arrow-down ms-auto h2 text-muted"></i>
|
||||
|
@ -500,7 +522,9 @@ export default {
|
|||
<div class="card rounded-3 bg-transparent shadow-sm">
|
||||
<div class="card-body d-flex">
|
||||
<div>
|
||||
<p class="mb-0 text-muted"><small>Total Sent</small></p>
|
||||
<p class="mb-0 text-muted"><small>
|
||||
<LocaleText t="Total Sent"></LocaleText>
|
||||
</small></p>
|
||||
<strong class="h4 text-success">{{configurationSummary.totalSent}} GB</strong>
|
||||
</div>
|
||||
<i class="bi bi-arrow-up ms-auto h2 text-muted"></i>
|
||||
|
@ -512,7 +536,9 @@ export default {
|
|||
<div class="col-12 col-lg-6">
|
||||
<div class="card rounded-3 bg-transparent shadow-sm" style="height: 270px">
|
||||
<div class="card-header bg-transparent border-0">
|
||||
<small class="text-muted">Peers Total Data Usage</small></div>
|
||||
<small class="text-muted">
|
||||
<LocaleText t="Peers Data Usage"></LocaleText>
|
||||
</small></div>
|
||||
<div class="card-body pt-1">
|
||||
<Bar
|
||||
:data="individualDataUsage"
|
||||
|
@ -523,7 +549,9 @@ export default {
|
|||
</div>
|
||||
<div class="col-sm col-lg-3">
|
||||
<div class="card rounded-3 bg-transparent shadow-sm" style="height: 270px">
|
||||
<div class="card-header bg-transparent border-0"><small class="text-muted">Real Time Received Data Usage</small></div>
|
||||
<div class="card-header bg-transparent border-0"><small class="text-muted">
|
||||
<LocaleText t="Real Time Received Data Usage"></LocaleText>
|
||||
</small></div>
|
||||
<div class="card-body pt-1">
|
||||
<Line
|
||||
:options="chartOptions"
|
||||
|
@ -535,7 +563,9 @@ export default {
|
|||
</div>
|
||||
<div class="col-sm col-lg-3">
|
||||
<div class="card rounded-3 bg-transparent shadow-sm" style="height: 270px">
|
||||
<div class="card-header bg-transparent border-0"><small class="text-muted">Real Time Sent Data Usage</small></div>
|
||||
<div class="card-header bg-transparent border-0"><small class="text-muted">
|
||||
<LocaleText t="Real Time Sent Data Usage"></LocaleText>
|
||||
</small></div>
|
||||
<div class="card-body pt-1">
|
||||
<Line
|
||||
:options="chartOptions"
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
<script>
|
||||
import QRCode from "qrcode";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
export default {
|
||||
name: "peerQRCode",
|
||||
components: {LocaleText},
|
||||
props: {
|
||||
peerConfigData: String
|
||||
},
|
||||
|
@ -19,7 +21,9 @@ export default {
|
|||
<div class="m-auto modal-dialog-centered dashboardModal justify-content-center">
|
||||
<div class="card rounded-3 shadow">
|
||||
<div class="card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-0">
|
||||
<h4 class="mb-0">QR Code</h4>
|
||||
<h4 class="mb-0">
|
||||
<LocaleText t="QR Code"></LocaleText>
|
||||
</h4>
|
||||
<button type="button" class="btn-close ms-auto" @click="this.$emit('close')"></button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
|
|
@ -5,10 +5,11 @@ import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.
|
|||
import {fetchPost} from "@/utilities/fetch.js";
|
||||
import VueDatePicker from "@vuepic/vue-datepicker";
|
||||
import dayjs from "dayjs";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
name: "schedulePeerJob",
|
||||
components: {VueDatePicker, ScheduleDropdown},
|
||||
components: {LocaleText, VueDatePicker, ScheduleDropdown},
|
||||
props: {
|
||||
dropdowns: Array[Object],
|
||||
pjob: Object,
|
||||
|
@ -110,15 +111,19 @@ export default {
|
|||
<div class="card shadow-sm rounded-3 mb-2" :class="{'border-warning-subtle': this.newJob}">
|
||||
<div class="card-header bg-transparent text-muted border-0">
|
||||
<small class="d-flex" v-if="!this.newJob">
|
||||
<strong class="me-auto">Job ID</strong>
|
||||
<strong class="me-auto">
|
||||
<LocaleText t="Job ID"></LocaleText>
|
||||
</strong>
|
||||
<samp>{{this.job.JobID}}</samp>
|
||||
</small>
|
||||
<small v-else><span class="badge text-bg-warning">Unsaved Job</span></small>
|
||||
<small v-else><span class="badge text-bg-warning">
|
||||
<LocaleText t="Unsaved Job"></LocaleText>
|
||||
</span></small>
|
||||
</div>
|
||||
<div class="card-body pt-1" style="font-family: var(--bs-font-monospace)">
|
||||
<div class="d-flex gap-2 align-items-center mb-2">
|
||||
<samp>
|
||||
if
|
||||
<LocaleText t="if"></LocaleText>
|
||||
</samp>
|
||||
<ScheduleDropdown
|
||||
:edit="edit"
|
||||
|
@ -127,7 +132,7 @@ export default {
|
|||
@update="(value) => {this.job.Field = value}"
|
||||
></ScheduleDropdown>
|
||||
<samp>
|
||||
is
|
||||
<LocaleText t="is"></LocaleText>
|
||||
</samp>
|
||||
<ScheduleDropdown
|
||||
:edit="edit"
|
||||
|
@ -149,12 +154,6 @@ export default {
|
|||
:dark="this.store.Configuration.Server.dashboard_theme === 'dark'"
|
||||
/>
|
||||
|
||||
<!-- <input class="form-control form-control-sm form-control-dark rounded-3 flex-grow-1"-->
|
||||
<!-- :disabled="!edit"-->
|
||||
<!-- type="datetime-local"-->
|
||||
<!-- v-if="this.job.Field === 'date'"-->
|
||||
<!-- v-model="this.job.Value"-->
|
||||
<!-- style="width: auto">-->
|
||||
<input class="form-control form-control-sm form-control-dark rounded-3 flex-grow-1"
|
||||
:disabled="!edit"
|
||||
v-else
|
||||
|
@ -165,7 +164,7 @@ export default {
|
|||
</samp>
|
||||
</div>
|
||||
<div class="px-5 d-flex gap-2 align-items-center">
|
||||
<samp>then</samp>
|
||||
<samp><LocaleText t="then"></LocaleText></samp>
|
||||
<ScheduleDropdown
|
||||
:edit="edit"
|
||||
:options="this.dropdowns.Action"
|
||||
|
@ -178,18 +177,18 @@ export default {
|
|||
<div class="ms-auto d-flex gap-3" v-if="!this.edit">
|
||||
<a role="button"
|
||||
class="ms-auto text-decoration-none"
|
||||
@click="this.edit = true">[E] Edit</a>
|
||||
@click="this.edit = true">[E] <LocaleText t="Edit"></LocaleText></a>
|
||||
<a role="button"
|
||||
@click="this.delete()"
|
||||
class=" text-danger text-decoration-none">[D] Delete</a>
|
||||
class=" text-danger text-decoration-none">[D] <LocaleText t="Delete"></LocaleText></a>
|
||||
</div>
|
||||
<div class="ms-auto d-flex gap-3" v-else>
|
||||
<a role="button"
|
||||
class="text-secondary text-decoration-none"
|
||||
@click="this.reset()">[C] Cancel</a>
|
||||
@click="this.reset()">[C] <LocaleText t="Cancel"></LocaleText></a>
|
||||
<a role="button"
|
||||
class="text-primary ms-auto text-decoration-none"
|
||||
@click="this.save()">[S] Save</a>
|
||||
@click="this.save()">[S] <LocaleText t="Save"></LocaleText></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,10 +2,13 @@
|
|||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import {fetchGet, fetchPost} from "@/utilities/fetch.js";
|
||||
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
import {GetLocale} from "@/utilities/locale.js";
|
||||
|
||||
|
||||
export default {
|
||||
name: "peerSearch",
|
||||
components: {LocaleText},
|
||||
setup(){
|
||||
const store = DashboardConfigurationStore();
|
||||
const wireguardConfigurationStore = WireguardConfigurationsStore()
|
||||
|
@ -18,16 +21,16 @@ export default {
|
|||
data(){
|
||||
return {
|
||||
sort: {
|
||||
status: "Status",
|
||||
name: "Name",
|
||||
allowed_ip: "Allowed IP",
|
||||
restricted: "Restricted"
|
||||
status: GetLocale("Status"),
|
||||
name: GetLocale("Name"),
|
||||
allowed_ip: GetLocale("Allowed IPs"),
|
||||
restricted: GetLocale("Restricted")
|
||||
},
|
||||
interval: {
|
||||
'5000': '5 Seconds',
|
||||
'10000': '10 Seconds',
|
||||
'30000': '30 Seconds',
|
||||
'60000': '1 Minutes'
|
||||
'5000': GetLocale('5 Seconds'),
|
||||
'10000': GetLocale('10 Seconds'),
|
||||
'30000': GetLocale('30 Seconds'),
|
||||
'60000': GetLocale('1 Minutes')
|
||||
},
|
||||
searchString: "",
|
||||
searchStringTimeout: undefined,
|
||||
|
@ -77,8 +80,10 @@ export default {
|
|||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
computed: {
|
||||
searchBarPlaceholder(){
|
||||
return GetLocale("Search Peers...")
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -89,15 +94,18 @@ export default {
|
|||
<RouterLink
|
||||
to="create"
|
||||
class="text-decoration-none btn text-primary-emphasis bg-primary-subtle rounded-3 border-1 border-primary-subtle shadow-sm">
|
||||
<i class="bi bi-plus-lg me-2"></i>Peer
|
||||
<i class="bi bi-plus-lg me-2"></i>
|
||||
<LocaleText t="Peer"></LocaleText>
|
||||
</RouterLink>
|
||||
<button class="btn text-primary-emphasis bg-primary-subtle rounded-3 border-1 border-primary-subtle shadow-sm"
|
||||
@click="this.downloadAllPeer()">
|
||||
<i class="bi bi-download me-2"></i> Download All
|
||||
<i class="bi bi-download me-2"></i>
|
||||
<LocaleText t="Download All"></LocaleText>
|
||||
</button>
|
||||
<div class="flex-grow-1 mt-3 mt-md-0">
|
||||
<div class="mt-3 mt-md-0 flex-grow-1">
|
||||
|
||||
<input class="form-control rounded-3 bg-secondary-subtle border-1 border-secondary-subtle shadow-sm w-100"
|
||||
placeholder="Search..."
|
||||
:placeholder="searchBarPlaceholder"
|
||||
id="searchPeers"
|
||||
@keyup="this.debounce()"
|
||||
v-model="this.searchString">
|
||||
|
@ -105,9 +113,9 @@ export default {
|
|||
<button
|
||||
@click="this.showDisplaySettings = true"
|
||||
class="btn text-secondary-emphasis bg-secondary-subtle rounded-3 border-1 border-secondary-subtle shadow-sm"
|
||||
type="button" aria-expanded="false">
|
||||
type="button" aria-expanded="false">
|
||||
<i class="bi bi-filter-circle me-2"></i>
|
||||
Display
|
||||
<LocaleText t="Display"></LocaleText>
|
||||
</button>
|
||||
<button class="btn text-secondary-emphasis bg-secondary-subtle rounded-3 border-1 border-secondary-subtle shadow-sm"
|
||||
@click="this.showMoreSettings = true"
|
||||
|
@ -122,13 +130,15 @@ export default {
|
|||
<div class="m-auto modal-dialog-centered dashboardModal">
|
||||
<div class="card rounded-3 shadow w-100">
|
||||
<div class="card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-2">
|
||||
<h4 class="mb-0 fw-normal">Display
|
||||
<h4 class="mb-0 fw-normal"><LocaleText t="Display"></LocaleText>
|
||||
</h4>
|
||||
<button type="button" class="btn-close ms-auto" @click="this.showDisplaySettings = false"></button>
|
||||
</div>
|
||||
<div class="card-body px-4 pb-4 d-flex gap-3 flex-column">
|
||||
<div>
|
||||
<p class="text-muted fw-bold mb-2"><small>Sort by</small></p>
|
||||
<p class="text-muted fw-bold mb-2"><small>
|
||||
<LocaleText t="Sort by"></LocaleText>
|
||||
</small></p>
|
||||
<div class="list-group">
|
||||
<a v-for="(value, key) in this.sort" class="list-group-item list-group-item-action d-flex" role="button" @click="this.updateSort(key)">
|
||||
<span class="me-auto">{{value}}</span>
|
||||
|
@ -138,7 +148,9 @@ export default {
|
|||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-muted fw-bold mb-2"><small>Refresh interval</small></p>
|
||||
<p class="text-muted fw-bold mb-2"><small>
|
||||
<LocaleText t="Refresh Interval"></LocaleText>
|
||||
</small></p>
|
||||
<div class="list-group">
|
||||
<a v-for="(value, key) in this.interval"
|
||||
class="list-group-item list-group-item-action d-flex" role="button"
|
||||
|
@ -163,21 +175,24 @@ export default {
|
|||
<div class="m-auto modal-dialog-centered dashboardModal">
|
||||
<div class="card rounded-3 shadow w-100">
|
||||
<div class="card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-2">
|
||||
<h4 class="mb-0 fw-normal">Configuration Settings
|
||||
<h4 class="mb-0 fw-normal">
|
||||
<LocaleText t="Configuration Settings"></LocaleText>
|
||||
</h4>
|
||||
<button type="button" class="btn-close ms-auto" @click="this.showMoreSettings = false"></button>
|
||||
</div>
|
||||
<div class="card-body px-4 pb-4 d-flex gap-3 flex-column">
|
||||
<div>
|
||||
<p class="text-muted fw-bold mb-2"><small>Peer Jobs</small></p>
|
||||
<p class="text-muted fw-bold mb-2"><small>
|
||||
<LocaleText t="Peer Jobs"></LocaleText>
|
||||
</small></p>
|
||||
<div class="list-group">
|
||||
<a class="list-group-item list-group-item-action d-flex" role="button"
|
||||
@click="this.$emit('jobsAll')">
|
||||
Active Jobs
|
||||
<LocaleText t="Active Jobs"></LocaleText>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action d-flex" role="button"
|
||||
@click="this.$emit('jobLogs')">
|
||||
Logs
|
||||
<LocaleText t="Logs"></LocaleText>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<script>
|
||||
import {fetchPost} from "@/utilities/fetch.js";
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
name: "peerSettings",
|
||||
components: {LocaleText},
|
||||
props: {
|
||||
selectedPeer: Object
|
||||
},
|
||||
|
@ -73,18 +75,24 @@ export default {
|
|||
<div class="m-auto modal-dialog-centered dashboardModal">
|
||||
<div class="card rounded-3 shadow flex-grow-1">
|
||||
<div class="card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-2">
|
||||
<h4 class="mb-0">Peer Settings</h4>
|
||||
<h4 class="mb-0">
|
||||
<LocaleText t="Peer Settings"></LocaleText>
|
||||
</h4>
|
||||
<button type="button" class="btn-close ms-auto" @click="this.$emit('close')"></button>
|
||||
</div>
|
||||
<div class="card-body px-4 pb-4" v-if="this.data">
|
||||
<div class="d-flex flex-column gap-2 mb-4">
|
||||
<div class="d-flex align-items-center">
|
||||
<small class="text-muted">Public Key</small>
|
||||
<small class="text-muted">
|
||||
<LocaleText t="Public Key"></LocaleText>
|
||||
</small>
|
||||
<small class="ms-auto"><samp>{{this.data.id}}</samp></small>
|
||||
</div>
|
||||
<div>
|
||||
<label for="peer_name_textbox" class="form-label">
|
||||
<small class="text-muted">Name</small>
|
||||
<small class="text-muted">
|
||||
<LocaleText t="Name"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="this.saving"
|
||||
|
@ -94,7 +102,10 @@ export default {
|
|||
<div>
|
||||
<div class="d-flex position-relative">
|
||||
<label for="peer_private_key_textbox" class="form-label">
|
||||
<small class="text-muted">Private Key <code>(Required for QR Code and Download)</code></small>
|
||||
<small class="text-muted"><LocaleText t="Private Key"></LocaleText>
|
||||
<code>
|
||||
<LocaleText t="(Required for QR Code and Download)"></LocaleText>
|
||||
</code></small>
|
||||
</label>
|
||||
<a role="button" class="ms-auto text-decoration-none toggleShowKey"
|
||||
@click="this.showKey = !this.showKey"
|
||||
|
@ -110,7 +121,11 @@ export default {
|
|||
</div>
|
||||
<div>
|
||||
<label for="peer_allowed_ip_textbox" class="form-label">
|
||||
<small class="text-muted">Allowed IPs <code>(Required)</code></small>
|
||||
<small class="text-muted">
|
||||
<LocaleText t="Allowed IPs"></LocaleText>
|
||||
<code>
|
||||
<LocaleText t="(Required)"></LocaleText>
|
||||
</code></small>
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="this.saving"
|
||||
|
@ -120,7 +135,11 @@ export default {
|
|||
|
||||
<div>
|
||||
<label for="peer_endpoint_allowed_ips" class="form-label">
|
||||
<small class="text-muted">Endpoint Allowed IPs <code>(Required)</code></small>
|
||||
<small class="text-muted">
|
||||
<LocaleText t="Endpoint Allowed IPs"></LocaleText>
|
||||
<code>
|
||||
<LocaleText t="(Required)"></LocaleText>
|
||||
</code></small>
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="this.saving"
|
||||
|
@ -129,7 +148,9 @@ export default {
|
|||
</div>
|
||||
<div>
|
||||
<label for="peer_DNS_textbox" class="form-label">
|
||||
<small class="text-muted">DNS</small>
|
||||
<small class="text-muted">
|
||||
<LocaleText t="DNS"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="this.saving"
|
||||
|
@ -141,7 +162,7 @@ export default {
|
|||
<h2 class="accordion-header">
|
||||
<button class="accordion-button rounded-3 collapsed" type="button"
|
||||
data-bs-toggle="collapse" data-bs-target="#peerSettingsAccordionOptional">
|
||||
Optional Settings
|
||||
<LocaleText t="Optional Settings"></LocaleText>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="peerSettingsAccordionOptional" class="accordion-collapse collapse"
|
||||
|
@ -149,7 +170,8 @@ export default {
|
|||
<div class="accordion-body d-flex flex-column gap-2 mb-2">
|
||||
<div>
|
||||
<label for="peer_preshared_key_textbox" class="form-label">
|
||||
<small class="text-muted">Pre-Shared Key</small>
|
||||
<small class="text-muted">
|
||||
<LocaleText t="Pre-Shared Key"></LocaleText></small>
|
||||
</label>
|
||||
<input type="text" class="form-control form-control-sm rounded-3"
|
||||
:disabled="this.saving"
|
||||
|
@ -157,7 +179,9 @@ export default {
|
|||
id="peer_preshared_key_textbox">
|
||||
</div>
|
||||
<div>
|
||||
<label for="peer_mtu" class="form-label"><small class="text-muted">MTU</small></label>
|
||||
<label for="peer_mtu" class="form-label"><small class="text-muted">
|
||||
<LocaleText t="MTU"></LocaleText>
|
||||
</small></label>
|
||||
<input type="number" class="form-control form-control-sm rounded-3"
|
||||
:disabled="this.saving"
|
||||
v-model="this.data.mtu"
|
||||
|
@ -165,7 +189,9 @@ export default {
|
|||
</div>
|
||||
<div>
|
||||
<label for="peer_keep_alive" class="form-label">
|
||||
<small class="text-muted">Persistent Keepalive</small>
|
||||
<small class="text-muted">
|
||||
<LocaleText t="Persistent Keepalive"></LocaleText>
|
||||
</small>
|
||||
</label>
|
||||
<input type="number" class="form-control form-control-sm rounded-3"
|
||||
:disabled="this.saving"
|
||||
|
@ -178,25 +204,27 @@ export default {
|
|||
</div>
|
||||
<hr>
|
||||
<div class="d-flex gap-2 align-items-center">
|
||||
<strong>Reset Data Usage</strong>
|
||||
<strong>
|
||||
<LocaleText t="Reset Data Usage"></LocaleText>
|
||||
</strong>
|
||||
<div class="d-flex gap-2 ms-auto">
|
||||
<button class="btn bg-primary-subtle text-primary-emphasis rounded-3 flex-grow-1 shadow-sm"
|
||||
@click="this.resetPeerData('total')"
|
||||
>
|
||||
<i class="bi bi-arrow-down-up me-2"></i>
|
||||
Total
|
||||
<LocaleText t="Total"></LocaleText>
|
||||
</button>
|
||||
<button class="btn bg-primary-subtle text-primary-emphasis rounded-3 flex-grow-1 shadow-sm"
|
||||
@click="this.resetPeerData('receive')"
|
||||
>
|
||||
<i class="bi bi-arrow-down me-2"></i>
|
||||
Received
|
||||
<LocaleText t="Received"></LocaleText>
|
||||
</button>
|
||||
<button class="btn bg-primary-subtle text-primary-emphasis rounded-3 flex-grow-1 shadow-sm"
|
||||
@click="this.resetPeerData('sent')"
|
||||
>
|
||||
<i class="bi bi-arrow-up me-2"></i>
|
||||
Sent
|
||||
<LocaleText t="Sent"></LocaleText>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
@ -206,14 +234,16 @@ export default {
|
|||
<button class="btn btn-secondary rounded-3 shadow"
|
||||
@click="this.reset()"
|
||||
:disabled="!this.dataChanged || this.saving">
|
||||
Revert <i class="bi bi-arrow-clockwise ms-2"></i>
|
||||
<LocaleText t="Revert"></LocaleText>
|
||||
<i class="bi bi-arrow-clockwise ms-2"></i>
|
||||
</button>
|
||||
|
||||
<button class="ms-auto btn btn-dark btn-brand rounded-3 px-3 py-2 shadow"
|
||||
:disabled="!this.dataChanged || this.saving"
|
||||
@click="this.savePeer()"
|
||||
>
|
||||
Save Peer<i class="bi bi-save-fill ms-2"></i></button>
|
||||
<LocaleText t="Save Peer"></LocaleText>
|
||||
<i class="bi bi-save-fill ms-2"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<script>
|
||||
import {fetchGet, fetchPost} from "@/utilities/fetch.js";
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
name: "peerSettingsDropdown",
|
||||
components: {LocaleText},
|
||||
setup(){
|
||||
const dashboardStore = DashboardConfigurationStore()
|
||||
return {dashboardStore}
|
||||
|
@ -88,9 +90,8 @@ export default {
|
|||
<template v-if="!this.Peer.private_key">
|
||||
<li>
|
||||
<small class="w-100 dropdown-item text-muted"
|
||||
style="white-space: break-spaces; font-size: 0.7rem"
|
||||
>Download & QR Code is not available due to no <code>private key</code>
|
||||
set for this peer
|
||||
style="white-space: break-spaces; font-size: 0.7rem">
|
||||
<LocaleText t="Download & QR Code is not available due to no private key set for this peer"></LocaleText>
|
||||
</small>
|
||||
</li>
|
||||
|
||||
|
@ -114,7 +115,7 @@ export default {
|
|||
<a class="dropdown-item d-flex" role="button"
|
||||
@click="this.$emit('setting')"
|
||||
>
|
||||
<i class="me-auto bi bi-pen"></i> Edit
|
||||
<i class="me-auto bi bi-pen"></i> <LocaleText t="Peer Settings"></LocaleText>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
@ -4,6 +4,7 @@ import {fetchPost} from "@/utilities/fetch.js";
|
|||
import dayjs from "dayjs";
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import VueDatePicker from '@vuepic/vue-datepicker';
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
|
||||
export default {
|
||||
|
@ -12,6 +13,7 @@ export default {
|
|||
peer: Object
|
||||
},
|
||||
components: {
|
||||
LocaleText,
|
||||
VueDatePicker
|
||||
},
|
||||
data(){
|
||||
|
@ -49,11 +51,9 @@ export default {
|
|||
if (res.status){
|
||||
this.peer.ShareLink = res.data;
|
||||
this.dataCopy = res.data.at(0);
|
||||
this.store.newMessage("Server", "Share link created successfully", "success")
|
||||
}else{
|
||||
this.store.newMessage("Server",
|
||||
"Share link failed to create. Reason: " + res.message, "danger")
|
||||
|
||||
}
|
||||
this.loading = false;
|
||||
})
|
||||
|
@ -108,13 +108,15 @@ export default {
|
|||
<div class="m-auto modal-dialog-centered dashboardModal" style="width: 500px">
|
||||
<div class="card rounded-3 shadow flex-grow-1">
|
||||
<div class="card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4">
|
||||
<h4 class="mb-0">Share Peer</h4>
|
||||
<h4 class="mb-0">
|
||||
<LocaleText t="Share Peer"></LocaleText>
|
||||
</h4>
|
||||
<button type="button" class="btn-close ms-auto" @click="this.$emit('close')"></button>
|
||||
</div>
|
||||
<div class="card-body px-4 pb-4" v-if="this.peer.ShareLink">
|
||||
<div v-if="!this.dataCopy">
|
||||
<h6 class="mb-3 text-muted">
|
||||
Currently the peer is not sharing
|
||||
<LocaleText t="Currently the peer is not sharing"></LocaleText>
|
||||
</h6>
|
||||
<button
|
||||
@click="this.startSharing()"
|
||||
|
@ -123,7 +125,8 @@ export default {
|
|||
<span :class="{'animate__animated animate__flash animate__infinite animate__slower': this.loading}">
|
||||
<i class="bi bi-send-fill me-2" ></i>
|
||||
</span>
|
||||
{{this.loading ? "Sharing...":"Start Sharing"}}
|
||||
<LocaleText t="Sharing..." v-if="this.loading"></LocaleText>
|
||||
<LocaleText t="Start Sharing" v-else></LocaleText>
|
||||
</button>
|
||||
</div>
|
||||
<div v-else>
|
||||
|
@ -137,7 +140,7 @@ export default {
|
|||
<div class="d-flex flex-column gap-2 mb-3">
|
||||
<small>
|
||||
<i class="bi bi-calendar me-2"></i>
|
||||
Expire Date
|
||||
<LocaleText t="Expire At"></LocaleText>
|
||||
</small>
|
||||
<VueDatePicker
|
||||
:is24="true"
|
||||
|
@ -157,7 +160,8 @@ export default {
|
|||
<span :class="{'animate__animated animate__flash animate__infinite animate__slower': this.loading}">
|
||||
<i class="bi bi-send-slash-fill me-2" ></i>
|
||||
</span>
|
||||
{{this.loading ? "Stop Sharing...":"Stop Sharing"}}
|
||||
<LocaleText t="Stop Sharing..." v-if="this.loading"></LocaleText>
|
||||
<LocaleText t="Stop Sharing" v-else></LocaleText>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
import {wgdashboardStore} from "@/stores/wgdashboardStore.js";
|
||||
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||
import ConfigurationCard from "@/components/configurationListComponents/configurationCard.vue";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
name: "configurationList",
|
||||
components: {ConfigurationCard},
|
||||
components: {LocaleText, ConfigurationCard},
|
||||
async setup(){
|
||||
const wireguardConfigurationsStore = WireguardConfigurationsStore();
|
||||
return {wireguardConfigurationsStore}
|
||||
|
@ -36,16 +37,18 @@ export default {
|
|||
<div class="d-flex mb-4 configurationListTitle">
|
||||
<h3 class="text-body d-flex">
|
||||
<i class="bi bi-body-text me-2"></i>
|
||||
<span>WireGuard Configurations</span></h3>
|
||||
<span>
|
||||
<LocaleText t="WireGuard Configurations"></LocaleText>
|
||||
</span></h3>
|
||||
<RouterLink to="/new_configuration" class="btn btn-dark btn-brand rounded-3 px-3 py-2 shadow ms-auto rounded-3">
|
||||
<i class="bi bi-plus-circle-fill me-2"></i>
|
||||
Configuration
|
||||
<LocaleText t="Configuration"></LocaleText>
|
||||
</RouterLink>
|
||||
</div>
|
||||
<Transition name="fade" mode="out-in">
|
||||
<div v-if="this.configurationLoaded">
|
||||
<p class="text-muted" v-if="this.wireguardConfigurationsStore.Configurations.length === 0">
|
||||
You don't have any WireGuard configurations yet. Please check the configuration folder or change it in "Settings". By default the folder is "/etc/wireguard".
|
||||
<LocaleText t="You don't have any WireGuard configurations yet. Please check the configuration folder or change it in Settings. By default the folder is /etc/wireguard."></LocaleText>
|
||||
</p>
|
||||
<div class="d-flex gap-3 flex-column mb-3" v-else>
|
||||
<ConfigurationCard v-for="c in this.wireguardConfigurationsStore.Configurations" :key="c.Name" :c="c"></ConfigurationCard>
|
||||
|
|
|
@ -3,9 +3,11 @@ import {wgdashboardStore} from "@/stores/wgdashboardStore.js";
|
|||
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import {fetchGet} from "@/utilities/fetch.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
name: "navbar",
|
||||
components: {LocaleText},
|
||||
setup(){
|
||||
const wireguardConfigurationsStore = WireguardConfigurationsStore();
|
||||
const dashboardConfigurationStore = DashboardConfigurationStore();
|
||||
|
@ -47,17 +49,19 @@ export default {
|
|||
<RouterLink class="nav-link rounded-3"
|
||||
to="/" exact-active-class="active">
|
||||
<i class="bi bi-house me-2"></i>
|
||||
Home</RouterLink></li>
|
||||
<LocaleText t="Home"></LocaleText>
|
||||
</RouterLink></li>
|
||||
<li class="nav-item">
|
||||
<RouterLink class="nav-link rounded-3" to="/settings"
|
||||
exact-active-class="active">
|
||||
<i class="bi bi-gear me-2"></i>
|
||||
Settings</RouterLink></li>
|
||||
<LocaleText t="Settings"></LocaleText>
|
||||
</RouterLink></li>
|
||||
</ul>
|
||||
<hr class="text-body">
|
||||
<h6 class="sidebar-heading px-3 mt-4 mb-1 text-muted text-center">
|
||||
<i class="bi bi-body-text me-2"></i>
|
||||
Configurations
|
||||
<LocaleText t="WireGuard Configurations"></LocaleText>
|
||||
</h6>
|
||||
<ul class="nav flex-column px-2">
|
||||
<li class="nav-item">
|
||||
|
@ -72,7 +76,7 @@ export default {
|
|||
<hr class="text-body">
|
||||
<h6 class="sidebar-heading px-3 mt-4 mb-1 text-muted text-center">
|
||||
<i class="bi bi-tools me-2"></i>
|
||||
Tools
|
||||
<LocaleText t="Tools"></LocaleText>
|
||||
</h6>
|
||||
<ul class="nav flex-column px-2">
|
||||
<li class="nav-item">
|
||||
|
@ -87,16 +91,18 @@ export default {
|
|||
@click="this.dashboardConfigurationStore.signOut()"
|
||||
role="button" style="font-weight: bold">
|
||||
<i class="bi bi-box-arrow-left me-2"></i>
|
||||
Sign Out</a>
|
||||
<LocaleText t="Sign Out"></LocaleText>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" style="font-size: 0.8rem">
|
||||
<a :href="this.updateUrl" v-if="this.updateAvailable" class="text-decoration-none" target="_blank">
|
||||
<small class="nav-link text-muted rounded-3" >
|
||||
{{ this.updateMessage }}
|
||||
<LocaleText :t="this.updateMessage"></LocaleText>
|
||||
</small>
|
||||
</a>
|
||||
<small class="nav-link text-muted" v-else>
|
||||
{{ this.updateMessage }}
|
||||
<LocaleText :t="this.updateMessage"></LocaleText>
|
||||
({{ dashboardConfigurationStore.Configuration.Server.version}})
|
||||
</small>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import {v4} from "uuid";
|
||||
import {fetchPost} from "@/utilities/fetch.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
name: "accountSettingsInputPassword",
|
||||
components: {LocaleText},
|
||||
props:{
|
||||
targetData: String,
|
||||
warning: false,
|
||||
|
@ -66,6 +68,11 @@ export default {
|
|||
this.invalidFeedback = "Please fill in all required fields."
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
passwordValid(){
|
||||
return Object.values(this.value).find(x => x.length === 0) === undefined && this.value.newPassword === this.value.repeatNewPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -76,7 +83,9 @@ export default {
|
|||
<div class="col-sm">
|
||||
<div class="form-group mb-2">
|
||||
<label :for="'currentPassword_' + this.uuid" class="text-muted mb-1">
|
||||
<strong><small>Current Password</small></strong>
|
||||
<strong><small>
|
||||
<LocaleText t="Current Password"></LocaleText>
|
||||
</small></strong>
|
||||
</label>
|
||||
<input type="password" class="form-control mb-2"
|
||||
:class="{'is-invalid': showInvalidFeedback, 'is-valid': isValid}"
|
||||
|
@ -88,7 +97,9 @@ export default {
|
|||
<div class="col-sm">
|
||||
<div class="form-group mb-2">
|
||||
<label :for="'newPassword_' + this.uuid" class="text-muted mb-1">
|
||||
<strong><small>New Password</small></strong>
|
||||
<strong><small>
|
||||
<LocaleText t="New Password"></LocaleText>
|
||||
</small></strong>
|
||||
</label>
|
||||
<input type="password" class="form-control mb-2"
|
||||
:class="{'is-invalid': showInvalidFeedback, 'is-valid': isValid}"
|
||||
|
@ -100,7 +111,9 @@ export default {
|
|||
<div class="col-sm">
|
||||
<div class="form-group mb-2">
|
||||
<label :for="'repeatNewPassword_' + this.uuid" class="text-muted mb-1">
|
||||
<strong><small>Repeat New Password</small></strong>
|
||||
<strong><small>
|
||||
<LocaleText t="Repeat New Password"></LocaleText>
|
||||
</small></strong>
|
||||
</label>
|
||||
<input type="password" class="form-control mb-2"
|
||||
:class="{'is-invalid': showInvalidFeedback, 'is-valid': isValid}"
|
||||
|
@ -109,8 +122,11 @@ export default {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="ms-auto btn bg-success-subtle text-success-emphasis border-1 border-success-subtle rounded-3 shadow-sm" @click="this.useValidation()">
|
||||
<i class="bi bi-save2-fill me-2"></i>Update Password
|
||||
<button
|
||||
:disabled="!this.passwordValid"
|
||||
class="ms-auto btn bg-success-subtle text-success-emphasis border-1 border-success-subtle rounded-3 shadow-sm" @click="this.useValidation()">
|
||||
<i class="bi bi-save2-fill me-2"></i>
|
||||
<LocaleText t="Update Password"></LocaleText>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import {v4} from "uuid";
|
||||
import {fetchPost} from "@/utilities/fetch.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
name: "accountSettingsInputUsername",
|
||||
components: {LocaleText},
|
||||
props:{
|
||||
targetData: String,
|
||||
title: String,
|
||||
warning: false,
|
||||
warningText: ""
|
||||
},
|
||||
setup(){
|
||||
const store = DashboardConfigurationStore();
|
||||
|
@ -62,7 +62,9 @@ export default {
|
|||
<template>
|
||||
<div class="form-group mb-2">
|
||||
<label :for="this.uuid" class="text-muted mb-1">
|
||||
<strong><small>{{this.title}}</small></strong>
|
||||
<strong><small>
|
||||
<LocaleText :t="this.title"></LocaleText>
|
||||
</small></strong>
|
||||
</label>
|
||||
<input type="text" class="form-control"
|
||||
:class="{'is-invalid': showInvalidFeedback, 'is-valid': isValid}"
|
||||
|
@ -73,11 +75,6 @@ export default {
|
|||
:disabled="this.updating"
|
||||
>
|
||||
<div class="invalid-feedback">{{this.invalidFeedback}}</div>
|
||||
<div class="px-2 py-1 text-warning-emphasis bg-warning-subtle border border-warning-subtle rounded-2 d-inline-block mt-1"
|
||||
v-if="warning"
|
||||
>
|
||||
<small><i class="bi bi-exclamation-triangle-fill me-2"></i><span v-html="warningText"></span></small>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import {v4} from "uuid";
|
||||
import {fetchPost} from "@/utilities/fetch.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
name: "accountSettingsMFA",
|
||||
components: {LocaleText},
|
||||
setup(){
|
||||
const store = DashboardConfigurationStore();
|
||||
const uuid = `input_${v4()}`;
|
||||
|
@ -43,7 +45,9 @@ export default {
|
|||
<template>
|
||||
<div>
|
||||
<div class="d-flex align-items-center">
|
||||
<strong>Multi-Factor Authentication</strong>
|
||||
<strong>
|
||||
<LocaleText t="Multi-Factor Authentication (MFA)"></LocaleText>
|
||||
</strong>
|
||||
<div class="form-check form-switch ms-3">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
v-model="this.status"
|
||||
|
@ -52,7 +56,9 @@ export default {
|
|||
<button class="btn bg-warning-subtle text-warning-emphasis border-1 border-warning-subtle ms-auto rounded-3 shadow-sm"
|
||||
v-if="this.status" @click="this.resetMFA()">
|
||||
<i class="bi bi-shield-lock-fill me-2"></i>
|
||||
{{this.store.Configuration.Account["totp_verified"] ? "Reset" : "Setup" }} MFA
|
||||
<LocaleText t="Reset" v-if='this.store.Configuration.Account["totp_verified"]'></LocaleText>
|
||||
<LocaleText t="Setup" v-else></LocaleText>
|
||||
MFA
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,10 +4,11 @@ import {v4} from "uuid";
|
|||
import {fetchGet, fetchPost} from "@/utilities/fetch.js";
|
||||
import NewDashboardAPIKey from "@/components/settingsComponent/dashboardAPIKeysComponents/newDashboardAPIKey.vue";
|
||||
import DashboardAPIKey from "@/components/settingsComponent/dashboardAPIKeysComponents/dashboardAPIKey.vue";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
name: "dashboardAPIKeys",
|
||||
components: {DashboardAPIKey, NewDashboardAPIKey},
|
||||
components: {LocaleText, DashboardAPIKey, NewDashboardAPIKey},
|
||||
setup(){
|
||||
const store = DashboardConfigurationStore();
|
||||
return {store};
|
||||
|
@ -64,14 +65,17 @@ export default {
|
|||
<template>
|
||||
<div class="card mb-4 shadow rounded-3">
|
||||
<div class="card-header d-flex">
|
||||
API Keys
|
||||
<LocaleText t="API Keys"></LocaleText>
|
||||
<div class="form-check form-switch ms-auto" v-if="!this.store.getActiveCrossServer()">
|
||||
<input class="form-check-input" type="checkbox"
|
||||
v-model="this.value"
|
||||
@change="this.toggleDashboardAPIKeys()"
|
||||
role="switch" id="allowAPIKeysSwitch">
|
||||
<label class="form-check-label" for="allowAPIKeysSwitch">
|
||||
{{this.value ? 'Enabled':'Disabled'}}
|
||||
<LocaleText t="Enabled" v-if="this.value"></LocaleText>
|
||||
<LocaleText t="Disabled" v-else></LocaleText>
|
||||
|
||||
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -80,12 +84,13 @@ export default {
|
|||
@click="this.newDashboardAPIKey = true"
|
||||
v-if="!this.store.getActiveCrossServer()"
|
||||
>
|
||||
<i class="bi bi-key me-2"></i> Create
|
||||
<i class="bi bi-plus-circle-fill me-2"></i>
|
||||
<LocaleText t="API Key"></LocaleText>
|
||||
</button>
|
||||
<div class="card" style="height: 300px" v-if="this.apiKeys.length === 0">
|
||||
<div class="card-body d-flex text-muted">
|
||||
<span class="m-auto">
|
||||
No Dashboard API Key
|
||||
<LocaleText t="No WGDashboard API Key"></LocaleText>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<script>
|
||||
import {fetchPost} from "@/utilities/fetch.js";
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
name: "dashboardAPIKey",
|
||||
components: {LocaleText},
|
||||
props: {
|
||||
apiKey: Object
|
||||
},
|
||||
|
@ -37,12 +39,18 @@ export default {
|
|||
<div class="card rounded-3 shadow-sm">
|
||||
<div class="card-body d-flex gap-3 align-items-center apiKey-card-body" v-if="!this.confirmDelete">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<small class="text-muted">Key</small>
|
||||
<small class="text-muted">
|
||||
<LocaleText t="Key"></LocaleText>
|
||||
</small>
|
||||
<span style="word-break: break-all">{{this.apiKey.Key}}</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2 ms-auto">
|
||||
<small class="text-muted">Expire At</small>
|
||||
{{this.apiKey.ExpiredAt ? this.apiKey.ExpiredAt : 'Never'}}
|
||||
<small class="text-muted">
|
||||
<LocaleText t="Expire At"></LocaleText>
|
||||
</small>
|
||||
|
||||
<LocaleText t="Never Expire" v-if="!this.apiKey.ExpiredAt"></LocaleText>
|
||||
<span>{{ this.apiKey.ExpiredAt }}</span>
|
||||
</div>
|
||||
<a role="button" class="btn btn-sm bg-danger-subtle text-danger-emphasis rounded-3"
|
||||
v-if="!this.store.getActiveCrossServer()"
|
||||
|
@ -52,7 +60,7 @@ export default {
|
|||
</div>
|
||||
<div v-else class="card-body d-flex gap-3 align-items-center justify-content-end"
|
||||
v-if="!this.store.getActiveCrossServer()">
|
||||
Are you sure to delete this API key?
|
||||
<LocaleText t="Are you sure to delete this API key?"></LocaleText>
|
||||
<a role="button" class="btn btn-sm bg-success-subtle text-success-emphasis rounded-3"
|
||||
@click="this.deleteAPIKey()"
|
||||
>
|
||||
|
|
|
@ -3,10 +3,11 @@ import dayjs from "dayjs";
|
|||
import {fetchPost} from "@/utilities/fetch.js";
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import VueDatePicker from "@vuepic/vue-datepicker";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
name: "newDashboardAPIKey",
|
||||
components: {VueDatePicker},
|
||||
components: {LocaleText, VueDatePicker},
|
||||
data(){
|
||||
return{
|
||||
newKeyData:{
|
||||
|
@ -58,11 +59,15 @@ export default {
|
|||
style="background-color: #00000060; backdrop-filter: blur(3px)">
|
||||
<div class="card m-auto rounded-3 mt-5">
|
||||
<div class="card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-0">
|
||||
<h6 class="mb-0">Create API Key</h6>
|
||||
<h6 class="mb-0">
|
||||
<LocaleText t="Create API Key"></LocaleText>
|
||||
</h6>
|
||||
<button type="button" class="btn-close ms-auto" @click="this.$emit('close')"></button>
|
||||
</div>
|
||||
<div class="card-body d-flex gap-2 p-4 flex-column">
|
||||
<small class="text-muted">When should this API Key expire?</small>
|
||||
<small class="text-muted">
|
||||
<LocaleText t="When should this API Key expire?"></LocaleText>
|
||||
</small>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<VueDatePicker
|
||||
:is24="true"
|
||||
|
@ -80,7 +85,8 @@ export default {
|
|||
<input class="form-check-input" type="checkbox"
|
||||
v-model="this.newKeyData.neverExpire" id="neverExpire" :disabled="this.submitting">
|
||||
<label class="form-check-label" for="neverExpire">
|
||||
Never Expire (<i class="bi bi-emoji-grimace-fill"></i> Don't think that's a good idea)
|
||||
<LocaleText t="Never Expire"></LocaleText> (<i class="bi bi-emoji-grimace-fill me-2"></i>
|
||||
<LocaleText t="Don't think that's a good idea"></LocaleText>)
|
||||
</label>
|
||||
</div>
|
||||
<button class="ms-auto btn bg-success-subtle text-success-emphasis border-1 border-success-subtle rounded-3 shadow-sm"
|
||||
|
@ -88,7 +94,8 @@ export default {
|
|||
@click="this.submitNewAPIKey()"
|
||||
>
|
||||
<i class="bi bi-check-lg me-2" v-if="!this.submitting"></i>
|
||||
{{this.submitting ? 'Creating...':'Done'}}
|
||||
<LocaleText t="Creating..." v-if="this.submitting"></LocaleText>
|
||||
<LocaleText t="Create" v-else></LocaleText>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import {v4} from "uuid";
|
||||
import {fetchPost} from "@/utilities/fetch.js";
|
||||
import {WireguardConfigurationsStore} from "@/stores/WireguardConfigurationsStore.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
name: "dashboardSettingsInputWireguardConfigurationPath",
|
||||
components: {LocaleText},
|
||||
props:{
|
||||
targetData: String,
|
||||
title: String,
|
||||
|
@ -13,8 +16,9 @@ export default {
|
|||
},
|
||||
setup(){
|
||||
const store = DashboardConfigurationStore();
|
||||
const WireguardConfigurationStore = WireguardConfigurationsStore()
|
||||
const uuid = `input_${v4()}`;
|
||||
return {store, uuid};
|
||||
return {store, uuid, WireguardConfigurationStore};
|
||||
},
|
||||
data(){
|
||||
return{
|
||||
|
@ -33,6 +37,7 @@ export default {
|
|||
methods:{
|
||||
async useValidation(){
|
||||
if(this.changed){
|
||||
this.updating = true;
|
||||
await fetchPost("/api/updateDashboardConfigurationItem", {
|
||||
section: "Server",
|
||||
key: this.targetData,
|
||||
|
@ -44,6 +49,8 @@ export default {
|
|||
this.store.Configuration.Account[this.targetData] = this.value
|
||||
clearTimeout(this.timeout)
|
||||
this.timeout = setTimeout(() => this.isValid = false, 5000);
|
||||
this.WireguardConfigurationStore.getConfigurations()
|
||||
this.store.newMessage("Server", "WireGuard configuration path saved", "success")
|
||||
}else{
|
||||
this.isValid = false;
|
||||
this.showInvalidFeedback = true;
|
||||
|
@ -59,24 +66,39 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="form-group mb-2">
|
||||
<div class="form-group">
|
||||
<label :for="this.uuid" class="text-muted mb-1">
|
||||
<strong><small>{{this.title}}</small></strong>
|
||||
<strong><small>
|
||||
<LocaleText :t="this.title"></LocaleText>
|
||||
</small></strong>
|
||||
</label>
|
||||
<input type="text" class="form-control"
|
||||
:class="{'is-invalid': this.showInvalidFeedback, 'is-valid': this.isValid}"
|
||||
:id="this.uuid"
|
||||
v-model="this.value"
|
||||
@keydown="this.changed = true"
|
||||
@blur="this.useValidation()"
|
||||
:disabled="this.updating"
|
||||
>
|
||||
<div class="invalid-feedback">{{this.invalidFeedback}}</div>
|
||||
<div class="px-2 py-1 text-warning-emphasis bg-warning-subtle border border-warning-subtle rounded-2 d-inline-block mt-1"
|
||||
<div class="d-flex gap-2 align-items-start">
|
||||
<div class="flex-grow-1">
|
||||
<input type="text" class="form-control rounded-3"
|
||||
:class="{'is-invalid': this.showInvalidFeedback, 'is-valid': this.isValid}"
|
||||
:id="this.uuid"
|
||||
v-model="this.value"
|
||||
@keydown="this.changed = true"
|
||||
:disabled="this.updating"
|
||||
>
|
||||
<div class="invalid-feedback fw-bold">{{this.invalidFeedback}}</div>
|
||||
</div>
|
||||
<button
|
||||
@click="this.useValidation()"
|
||||
:disabled="!this.changed"
|
||||
class="ms-auto btn rounded-3 border-success-subtle bg-success-subtle text-success-emphasis">
|
||||
<i class="bi bi-save2-fill" v-if="!this.updating"></i>
|
||||
<span class="spinner-border spinner-border-sm" v-else></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="px-2 py-1 text-warning-emphasis bg-warning-subtle border border-warning-subtle rounded-2 d-inline-block mt-1 mb-2"
|
||||
v-if="warning"
|
||||
>
|
||||
<small><i class="bi bi-exclamation-triangle-fill me-2"></i><span v-html="warningText"></span></small>
|
||||
<small><i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<LocaleText :t="warningText"></LocaleText>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
<script>
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import {fetchPost} from "@/utilities/fetch.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
name: "dashboardTheme",
|
||||
components: {LocaleText},
|
||||
setup(){
|
||||
const dashboardConfigurationStore = DashboardConfigurationStore();
|
||||
return {dashboardConfigurationStore}
|
||||
|
@ -26,19 +28,21 @@ export default {
|
|||
|
||||
<template>
|
||||
<div class="card mb-4 shadow rounded-3">
|
||||
<p class="card-header">Dashboard Theme</p>
|
||||
<p class="card-header">
|
||||
<LocaleText t="Dashboard Theme"></LocaleText>
|
||||
</p>
|
||||
<div class="card-body d-flex gap-2">
|
||||
<button class="btn bg-primary-subtle text-primary-emphasis flex-grow-1"
|
||||
@click="this.switchTheme('light')"
|
||||
:class="{active: this.dashboardConfigurationStore.Configuration.Server.dashboard_theme === 'light'}">
|
||||
<i class="bi bi-sun-fill"></i>
|
||||
Light
|
||||
<i class="bi bi-sun-fill me-2"></i>
|
||||
<LocaleText t="Light"></LocaleText>
|
||||
</button>
|
||||
<button class="btn bg-primary-subtle text-primary-emphasis flex-grow-1"
|
||||
@click="this.switchTheme('dark')"
|
||||
:class="{active: this.dashboardConfigurationStore.Configuration.Server.dashboard_theme === 'dark'}">
|
||||
<i class="bi bi-moon-fill"></i>
|
||||
Dark
|
||||
<i class="bi bi-moon-fill me-2"></i>
|
||||
<LocaleText t="Dark"></LocaleText>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import {v4} from "uuid";
|
||||
import {fetchPost} from "@/utilities/fetch.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
components: {LocaleText},
|
||||
props:{
|
||||
targetData: String,
|
||||
title: String,
|
||||
|
@ -61,7 +63,9 @@ export default {
|
|||
<template>
|
||||
<div class="form-group mb-2">
|
||||
<label :for="this.uuid" class="text-muted mb-1">
|
||||
<strong><small>{{this.title}}</small></strong>
|
||||
<strong><small>
|
||||
<LocaleText :t="this.title"></LocaleText>
|
||||
</small></strong>
|
||||
</label>
|
||||
<input type="text" class="form-control"
|
||||
:class="{'is-invalid': showInvalidFeedback, 'is-valid': isValid}"
|
||||
|
@ -75,7 +79,9 @@ export default {
|
|||
<div class="px-2 py-1 text-warning-emphasis bg-warning-subtle border border-warning-subtle rounded-2 d-inline-block mt-1"
|
||||
v-if="warning"
|
||||
>
|
||||
<small><i class="bi bi-exclamation-triangle-fill me-2"></i><span v-html="warningText"></span></small>
|
||||
<small><i class="bi bi-exclamation-triangle-fill me-2"></i>
|
||||
<LocaleText :t="warningText"></LocaleText>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
30
src/static/app/src/components/signIn/signInInput.vue
Normal file
30
src/static/app/src/components/signIn/signInInput.vue
Normal file
|
@ -0,0 +1,30 @@
|
|||
<script>
|
||||
import {GetLocale} from "@/utilities/locale.js";
|
||||
|
||||
export default {
|
||||
name: "signInInput",
|
||||
methods: {GetLocale},
|
||||
props: {
|
||||
id: "",
|
||||
data: "",
|
||||
type: "",
|
||||
placeholder: ""
|
||||
},
|
||||
computed: {
|
||||
getLocaleText(){
|
||||
return GetLocale(this.placeholder)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<input :type="type" v-model="this.data[this.id]" class="form-control"
|
||||
:id="this.id" :name="this.id"
|
||||
autocomplete="on"
|
||||
:placeholder="this.getLocaleText" required>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
29
src/static/app/src/components/signIn/signInTOTP.vue
Normal file
29
src/static/app/src/components/signIn/signInTOTP.vue
Normal file
|
@ -0,0 +1,29 @@
|
|||
<script>
|
||||
import {GetLocale} from "@/utilities/locale.js";
|
||||
|
||||
export default {
|
||||
name: "signInTOTP",
|
||||
methods: {GetLocale},
|
||||
props: {
|
||||
data: "",
|
||||
},
|
||||
computed: {
|
||||
getLocaleText(){
|
||||
return GetLocale(this.placeholder)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<input class="form-control totp"
|
||||
required
|
||||
id="totp" maxlength="6" type="text" inputmode="numeric" autocomplete="one-time-code"
|
||||
:placeholder="this.getLocaleText('OTP from your authenticator')"
|
||||
v-model="this.data.totp"
|
||||
>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,5 +1,6 @@
|
|||
<script>
|
||||
import dayjs from "dayjs";
|
||||
import {GetLocale} from "@/utilities/locale.js";
|
||||
|
||||
export default {
|
||||
name: "RemoteServer",
|
||||
|
@ -73,7 +74,7 @@ export default {
|
|||
return `${dayjs().subtract(this.startTime).millisecond()}ms`
|
||||
}else{
|
||||
if (this.refreshing){
|
||||
return `Pinging...`
|
||||
return GetLocale(`Pinging...`)
|
||||
}
|
||||
return this.errorMsg ? this.errorMsg : "N/A"
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script>
|
||||
import RemoteServer from "@/components/signInComponents/RemoteServer.vue";
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
name: "RemoteServerList",
|
||||
|
@ -8,18 +9,21 @@ export default {
|
|||
const store = DashboardConfigurationStore();
|
||||
return {store}
|
||||
},
|
||||
components: {RemoteServer}
|
||||
components: {LocaleText, RemoteServer}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-100 mt-3">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<h5 class="mb-0">Server List</h5>
|
||||
<h5 class="mb-0">
|
||||
<LocaleText t="Server List"></LocaleText>
|
||||
</h5>
|
||||
<button
|
||||
@click="this.store.addCrossServerConfiguration()"
|
||||
class="btn bg-primary-subtle text-primary-emphasis border-1 border-primary-subtle shadow-sm ms-auto">
|
||||
<i class="bi bi-plus-circle-fill me-2"></i>Server
|
||||
<i class="bi bi-plus-circle-fill me-2"></i>
|
||||
<LocaleText t="Server"></LocaleText>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w-100 d-flex gap-3 flex-column p-3 border border-1 border-secondary-subtle rounded-3"
|
||||
|
@ -30,7 +34,10 @@ export default {
|
|||
:key="key"
|
||||
:server="server"></RemoteServer>
|
||||
<h6 class="text-muted m-auto" v-if="Object.keys(this.store.CrossServerConfiguration.ServerList).length === 0">
|
||||
Click<i class="bi bi-plus-circle-fill mx-1"></i>to add your server</h6>
|
||||
<LocaleText t="Click"></LocaleText>
|
||||
<i class="bi bi-plus-circle-fill mx-1"></i>
|
||||
<LocaleText t="to add your server"></LocaleText>
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
23
src/static/app/src/components/text/localeText.vue
Normal file
23
src/static/app/src/components/text/localeText.vue
Normal file
|
@ -0,0 +1,23 @@
|
|||
<script>
|
||||
import {GetLocale} from "@/utilities/locale.js";
|
||||
|
||||
export default {
|
||||
name: "localeText",
|
||||
props: {
|
||||
t: ""
|
||||
},
|
||||
computed: {
|
||||
getLocaleText(){
|
||||
return GetLocale(this.t)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
{{ this.getLocaleText }}
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -10,17 +10,24 @@ import { createPinia } from 'pinia'
|
|||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import {fetchGet} from "@/utilities/fetch.js";
|
||||
|
||||
let Locale;
|
||||
await fetch("/api/locale").then(res => res.json()).then(res => Locale = JSON.parse(res.data))
|
||||
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(router)
|
||||
const pinia = createPinia();
|
||||
|
||||
app.use(router)
|
||||
|
||||
const pinia = createPinia();
|
||||
pinia.use(({ store }) => {
|
||||
store.$router = markRaw(router)
|
||||
})
|
||||
|
||||
app.use(pinia)
|
||||
|
||||
|
||||
const store = DashboardConfigurationStore()
|
||||
window.Locale = Locale;
|
||||
app.mount('#app')
|
|
@ -28,7 +28,6 @@ const checkAuth = async () => {
|
|||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: [
|
||||
|
||||
{
|
||||
name: "Index",
|
||||
path: '/',
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {defineStore} from "pinia";
|
||||
import {fetchGet, fetchPost} from "@/utilities/fetch.js";
|
||||
import {v4} from "uuid";
|
||||
import {GetLocale} from "@/utilities/locale.js";
|
||||
|
||||
export const DashboardConfigurationStore = defineStore('DashboardConfigurationStore', {
|
||||
state: () => ({
|
||||
|
@ -17,7 +18,8 @@ export const DashboardConfigurationStore = defineStore('DashboardConfigurationSt
|
|||
},
|
||||
ActiveServerConfiguration: undefined,
|
||||
IsElectronApp: false,
|
||||
ShowNavBar: false
|
||||
ShowNavBar: false,
|
||||
Locale: undefined
|
||||
}),
|
||||
actions: {
|
||||
initCrossServerConfiguration(){
|
||||
|
@ -57,19 +59,11 @@ export const DashboardConfigurationStore = defineStore('DashboardConfigurationSt
|
|||
this.ActiveServerConfiguration = undefined;
|
||||
localStorage.removeItem('ActiveCrossServerConfiguration')
|
||||
},
|
||||
|
||||
async getConfiguration(){
|
||||
await fetchGet("/api/getDashboardConfiguration", {}, (res) => {
|
||||
if (res.status) this.Configuration = res.data
|
||||
});
|
||||
},
|
||||
// async updateConfiguration(){
|
||||
// await fetchPost("/api/updateDashboardConfiguration", {
|
||||
// DashboardConfiguration: this.Configuration
|
||||
// }, (res) => {
|
||||
// console.log(res)
|
||||
// })
|
||||
// },
|
||||
async signOut(){
|
||||
await fetchGet("/api/signout", {}, (res) => {
|
||||
this.removeActiveCrossServer();
|
||||
|
@ -79,11 +73,25 @@ export const DashboardConfigurationStore = defineStore('DashboardConfigurationSt
|
|||
newMessage(from, content, type){
|
||||
this.Messages.push({
|
||||
id: v4(),
|
||||
from: from,
|
||||
content: content,
|
||||
from: GetLocale(from),
|
||||
content: GetLocale(content),
|
||||
type: type,
|
||||
show: true
|
||||
})
|
||||
},
|
||||
applyLocale(key){
|
||||
if (this.Locale === null)
|
||||
return key
|
||||
|
||||
const reg = Object.keys(this.Locale)
|
||||
const match = reg.filter(x => {
|
||||
return key.match(new RegExp('^' + x + '$', 'g')) !== null
|
||||
})
|
||||
console.log(match)
|
||||
if (match.length === 0 || match.length > 1){
|
||||
return key
|
||||
}
|
||||
return this.Locale[match[0]]
|
||||
}
|
||||
}
|
||||
});
|
|
@ -1,6 +1,7 @@
|
|||
import {defineStore} from "pinia";
|
||||
import {fetchGet} from "@/utilities/fetch.js";
|
||||
import isCidr from "is-cidr";
|
||||
import {GetLocale} from "@/utilities/locale.js";
|
||||
|
||||
export const WireguardConfigurationsStore = defineStore('WireguardConfigurationsStore', {
|
||||
state: () => ({
|
||||
|
@ -11,54 +12,54 @@ export const WireguardConfigurationsStore = defineStore('WireguardConfigurations
|
|||
dropdowns: {
|
||||
Field: [
|
||||
{
|
||||
display: "Total Received",
|
||||
display: GetLocale("Total Received"),
|
||||
value: "total_receive",
|
||||
unit: "GB",
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
display: "Total Sent",
|
||||
display: GetLocale("Total Sent"),
|
||||
value: "total_sent",
|
||||
unit: "GB",
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
display: "Total Data",
|
||||
display: GetLocale("Total Usage"),
|
||||
value: "total_data",
|
||||
unit: "GB",
|
||||
type: 'number'
|
||||
},
|
||||
{
|
||||
display: "Date",
|
||||
display: GetLocale("Date"),
|
||||
value: "date",
|
||||
type: 'date'
|
||||
}
|
||||
],
|
||||
Operator: [
|
||||
// {
|
||||
// display: "equal",
|
||||
// value: "eq"
|
||||
// },
|
||||
// {
|
||||
// display: "not equal",
|
||||
// value: "neq"
|
||||
// },
|
||||
{
|
||||
display: "equal",
|
||||
value: "eq"
|
||||
},
|
||||
{
|
||||
display: "not equal",
|
||||
value: "neq"
|
||||
},
|
||||
{
|
||||
display: "larger than",
|
||||
display: GetLocale("larger than"),
|
||||
value: "lgt"
|
||||
},
|
||||
{
|
||||
display: "less than",
|
||||
value: "lst"
|
||||
},
|
||||
// {
|
||||
// display: "less than",
|
||||
// value: "lst"
|
||||
// },
|
||||
],
|
||||
Action: [
|
||||
{
|
||||
display: "Restrict Peer",
|
||||
display: GetLocale("Restrict Peer"),
|
||||
value: "restrict"
|
||||
},
|
||||
{
|
||||
display: "Delete Peer",
|
||||
display: GetLocale("Delete Peer"),
|
||||
value: "delete"
|
||||
}
|
||||
]
|
||||
|
|
12
src/static/app/src/utilities/locale.js
Normal file
12
src/static/app/src/utilities/locale.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
export const GetLocale = (key) => {
|
||||
if (window.Locale === null)
|
||||
return key
|
||||
const reg = Object.keys(window.Locale)
|
||||
const match = reg.filter(x => {
|
||||
return key.match(new RegExp('^' + x + '$', 'gi')) !== null
|
||||
})
|
||||
if (match.length === 0 || match.length > 1){
|
||||
return key
|
||||
}
|
||||
return key.replace(new RegExp(match[0], 'gi'), window.Locale[match[0]])
|
||||
}
|
|
@ -12,11 +12,13 @@ import DashboardSettingsInputIPAddressAndPort
|
|||
from "@/components/settingsComponent/dashboardSettingsInputIPAddressAndPort.vue";
|
||||
import DashboardAPIKeys from "@/components/settingsComponent/dashboardAPIKeys.vue";
|
||||
import AccountSettingsMFA from "@/components/settingsComponent/accountSettingsMFA.vue";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
|
||||
export default {
|
||||
name: "settings",
|
||||
methods: {ipV46RegexCheck},
|
||||
components: {
|
||||
LocaleText,
|
||||
AccountSettingsMFA,
|
||||
DashboardAPIKeys,
|
||||
DashboardSettingsInputIPAddressAndPort,
|
||||
|
@ -27,24 +29,20 @@ export default {
|
|||
const dashboardConfigurationStore = DashboardConfigurationStore()
|
||||
return {dashboardConfigurationStore}
|
||||
},
|
||||
watch: {
|
||||
// 'dashboardConfigurationStore.Configuration': {
|
||||
// deep: true,
|
||||
// handler(){
|
||||
// this.dashboardConfigurationStore.updateConfiguration();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mt-md-5 mt-3">
|
||||
<div class="container-md">
|
||||
<h3 class="mb-3 text-body">Settings</h3>
|
||||
<h3 class="mb-3 text-body">
|
||||
<LocaleText t="Settings"></LocaleText>
|
||||
</h3>
|
||||
<DashboardTheme></DashboardTheme>
|
||||
<div class="card mb-4 shadow rounded-3">
|
||||
<p class="card-header">Peers Default Settings</p>
|
||||
<p class="card-header">
|
||||
<LocaleText t="Peers Default Settings"></LocaleText>
|
||||
</p>
|
||||
<div class="card-body">
|
||||
<PeersDefaultSettingsInput targetData="peer_global_dns" title="DNS"></PeersDefaultSettingsInput>
|
||||
<PeersDefaultSettingsInput targetData="peer_endpoint_allowed_ip" title="Peer Endpoint Allowed IPs"></PeersDefaultSettingsInput>
|
||||
|
@ -56,19 +54,23 @@ export default {
|
|||
</div>
|
||||
</div>
|
||||
<div class="card mb-4 shadow rounded-3">
|
||||
<p class="card-header">WireGuard Configurations Settings</p>
|
||||
<p class="card-header">
|
||||
<LocaleText t="WireGuard Configurations Settings"></LocaleText>
|
||||
</p>
|
||||
<div class="card-body">
|
||||
<DashboardSettingsInputWireguardConfigurationPath
|
||||
targetData="wg_conf_path"
|
||||
title="Configurations Directory"
|
||||
:warning="true"
|
||||
warning-text="Remember to remove <code>/</code> at the end of your path. e.g <code>/etc/wireguard</code>"
|
||||
warning-text="Remember to remove / at the end of your path. e.g /etc/wireguard"
|
||||
>
|
||||
</DashboardSettingsInputWireguardConfigurationPath>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-4 shadow rounded-3">
|
||||
<p class="card-header">Account Settings</p>
|
||||
<p class="card-header">
|
||||
<LocaleText t="WGDashboard Account Settings"></LocaleText>
|
||||
</p>
|
||||
<div class="card-body d-flex gap-4 flex-column">
|
||||
<AccountSettingsInputUsername targetData="username"
|
||||
title="Username"
|
||||
|
|
|
@ -3,10 +3,14 @@ import {fetchGet, fetchPost} from "../utilities/fetch.js";
|
|||
import {DashboardConfigurationStore} from "@/stores/DashboardConfigurationStore.js";
|
||||
import Message from "@/components/messageCentreComponent/message.vue";
|
||||
import RemoteServerList from "@/components/signInComponents/RemoteServerList.vue";
|
||||
import {GetLocale} from "@/utilities/locale.js";
|
||||
import LocaleText from "@/components/text/localeText.vue";
|
||||
import SignInInput from "@/components/signIn/signInInput.vue";
|
||||
import SignInTOTP from "@/components/signIn/signInTOTP.vue";
|
||||
|
||||
export default {
|
||||
name: "signin",
|
||||
components: {RemoteServerList, Message},
|
||||
components: {SignInTOTP, SignInInput, LocaleText, RemoteServerList, Message},
|
||||
async setup(){
|
||||
const store = DashboardConfigurationStore()
|
||||
let theme = "dark"
|
||||
|
@ -22,6 +26,7 @@ export default {
|
|||
}),
|
||||
fetchGet("/api/getDashboardVersion", {}, (res) => {
|
||||
version = res.data
|
||||
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
@ -30,9 +35,11 @@ export default {
|
|||
},
|
||||
data(){
|
||||
return {
|
||||
username: "",
|
||||
password: "",
|
||||
totp: "",
|
||||
data: {
|
||||
username: "",
|
||||
password: "",
|
||||
totp: "",
|
||||
},
|
||||
loginError: false,
|
||||
loginErrorMessage: "",
|
||||
loading: false
|
||||
|
@ -41,17 +48,17 @@ export default {
|
|||
computed: {
|
||||
getMessages(){
|
||||
return this.store.Messages.filter(x => x.show)
|
||||
},
|
||||
applyLocale(key){
|
||||
return GetLocale(key)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
GetLocale,
|
||||
async auth(){
|
||||
if (this.username && this.password && ((this.totpEnabled && this.totp) || !this.totpEnabled)){
|
||||
if (this.data.username && this.data.password && ((this.totpEnabled && this.data.totp) || !this.totpEnabled)){
|
||||
this.loading = true
|
||||
await fetchPost("/api/authenticate", {
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
totp: this.totp
|
||||
}, (response) => {
|
||||
await fetchPost("/api/authenticate", this.data, (response) => {
|
||||
if (response.status){
|
||||
this.loginError = false;
|
||||
this.$refs["signInBtn"].classList.add("signedIn")
|
||||
|
@ -97,45 +104,38 @@ export default {
|
|||
:data-bs-theme="this.theme">
|
||||
<div class="login-box m-auto" >
|
||||
<div class="m-auto" style="width: 700px;">
|
||||
<h4 class="mb-0 text-body">Welcome to</h4>
|
||||
<h4 class="mb-0 text-body">
|
||||
<LocaleText t="Welcome to"></LocaleText>
|
||||
</h4>
|
||||
<span class="dashboardLogo display-3"><strong>WGDashboard</strong></span>
|
||||
<div class="alert alert-danger mt-2 mb-0" role="alert" v-if="loginError">
|
||||
{{this.loginErrorMessage}}
|
||||
<LocaleText :t="this.loginErrorMessage"></LocaleText>
|
||||
</div>
|
||||
<form @submit="(e) => {e.preventDefault(); this.auth();}"
|
||||
v-if="!this.store.CrossServerConfiguration.Enable">
|
||||
<div class="form-group text-body">
|
||||
<label for="username" class="text-left" style="font-size: 1rem">
|
||||
<i class="bi bi-person-circle"></i></label>
|
||||
<input type="text" v-model="username" class="form-control" id="username" name="username"
|
||||
autocomplete="on"
|
||||
placeholder="Username" required>
|
||||
<SignInInput id="username" :data="this.data"
|
||||
type="text" placeholder="Username"></SignInInput>
|
||||
</div>
|
||||
<div class="form-group text-body">
|
||||
<label for="password" class="text-left" style="font-size: 1rem"><i class="bi bi-key-fill"></i></label>
|
||||
<input type="password"
|
||||
v-model="password" class="form-control" id="password" name="password"
|
||||
autocomplete="on"
|
||||
placeholder="Password" required>
|
||||
<SignInInput id="password" :data="this.data"
|
||||
type="password" placeholder="Password"></SignInInput>
|
||||
</div>
|
||||
<div class="form-group text-body" v-if="totpEnabled">
|
||||
<label for="totp" class="text-left" style="font-size: 1rem"><i class="bi bi-lock-fill"></i></label>
|
||||
<input class="form-control totp"
|
||||
required
|
||||
id="totp" maxlength="6" type="text" inputmode="numeric" autocomplete="one-time-code"
|
||||
placeholder="OTP from your authenticator"
|
||||
v-model="this.totp"
|
||||
>
|
||||
<SignInTOTP :data="this.data"></SignInTOTP>
|
||||
</div>
|
||||
<button class="btn btn-lg btn-dark ms-auto mt-4 w-100 d-flex btn-brand signInBtn" ref="signInBtn">
|
||||
<span v-if="!this.loading" class="d-flex w-100">
|
||||
Sign In<i class="ms-auto bi bi-chevron-right"></i>
|
||||
<LocaleText t="Sign In"></LocaleText>
|
||||
<i class="ms-auto bi bi-chevron-right"></i>
|
||||
</span>
|
||||
<span v-else class="d-flex w-100 align-items-center">
|
||||
Signing In...
|
||||
<span class="spinner-border ms-auto spinner-border-sm" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</span>
|
||||
<LocaleText t="Signing In..."></LocaleText>
|
||||
<span class="spinner-border ms-auto spinner-border-sm" role="status"></span>
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
||||
|
@ -146,7 +146,9 @@ export default {
|
|||
<input
|
||||
v-model="this.store.CrossServerConfiguration.Enable"
|
||||
class="form-check-input" type="checkbox" role="switch" id="flexSwitchCheckChecked">
|
||||
<label class="form-check-label" for="flexSwitchCheckChecked">Access Remote Server</label>
|
||||
<label class="form-check-label" for="flexSwitchCheckChecked">
|
||||
<LocaleText t="Access Remote Server"></LocaleText>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -21,6 +21,7 @@ export default defineConfig(({mode}) => {
|
|||
}
|
||||
},
|
||||
build: {
|
||||
target: "es2022",
|
||||
outDir: '../../../../WGDashboard-Desktop',
|
||||
rollupOptions: {
|
||||
output: {
|
||||
|
@ -50,6 +51,7 @@ export default defineConfig(({mode}) => {
|
|||
host: '0.0.0.0'
|
||||
},
|
||||
build: {
|
||||
target: "es2022",
|
||||
outDir: 'dist',
|
||||
rollupOptions: {
|
||||
output: {
|
||||
|
|
14
src/static/locale/active_languages.json
Normal file
14
src/static/locale/active_languages.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
[
|
||||
{
|
||||
"lang_id": "en",
|
||||
"lang_name": "English"
|
||||
},
|
||||
{
|
||||
"lang_id": "zh-CN",
|
||||
"lang_name": "Chinese (Simplified)"
|
||||
},
|
||||
{
|
||||
"lang_id": "zh-HK",
|
||||
"lang_name": "Chinese (Traditional)"
|
||||
}
|
||||
]
|
10
src/static/locale/languages.json
Normal file
10
src/static/locale/languages.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"WireGuard Configurations": {
|
||||
"zh-CN": "WireGuard 配置",
|
||||
"zh-HK": "WireGuard 配置"
|
||||
},
|
||||
"Configuration": {
|
||||
"zh-CN": "配置",
|
||||
"zh-HK": "配置"
|
||||
}
|
||||
}
|
134
src/static/locale/zh-CN.json
Normal file
134
src/static/locale/zh-CN.json
Normal file
|
@ -0,0 +1,134 @@
|
|||
{
|
||||
"Welcome to": "欢迎来到",
|
||||
"Username": "用户名",
|
||||
"Password": "密码",
|
||||
"OTP from your authenticator": "您双因素身份验证的一次性验证码",
|
||||
"Sign In": "登录",
|
||||
"Signing In\\.\\.\\.": "正在登录...",
|
||||
"Access Remote Server": "访问远程服务器",
|
||||
"Server": "服务器",
|
||||
"Click": "点击",
|
||||
"Pinging...": "尝试连接中...",
|
||||
"to add your server": "添加您的服务器",
|
||||
"Server List": "服务器列表",
|
||||
"Sorry, your username or password is incorrect.": "对不起,您的用户名或密码不正确",
|
||||
"Home": "主页",
|
||||
"Settings": "设定",
|
||||
"Tools": "工具箱",
|
||||
"Sign Out": "退出登录",
|
||||
"Checking for update...": "正在检查是否有新版本...",
|
||||
"You're on the latest version": "已经是最新版本",
|
||||
"WireGuard Configurations": "WireGuard 配置",
|
||||
"You don't have any WireGuard configurations yet. Please check the configuration folder or change it in Settings. By default the folder is /etc/wireguard.": "您还没有任何WireGuard配置。请检查您的配置文件夹或前往设置更改路径。默认文件夹是 /etc/wireguard",
|
||||
"Configuration": "配置",
|
||||
"Configurations": "配置",
|
||||
"Peers Default Settings": "端点默认设置",
|
||||
"Dashboard Theme": "面板主题",
|
||||
"Light": "简约白",
|
||||
"Dark": "简约黑",
|
||||
"This will be changed globally, and will be apply to all peer's QR code and configuration file.": "更改这个设定会应用到所有端点的配置文件和配置二维码",
|
||||
"WireGuard Configurations Settings": "WireGuard 配置设定",
|
||||
"Configurations Directory": "配置文件路径",
|
||||
"Remember to remove / at the end of your path. e.g /etc/wireguard": "请把路径最后的 /(左斜杠)移除,例如:/etc/wireguard",
|
||||
"WGDashboard Account Settings": "WGDashboard 账户设定",
|
||||
"Current Password": "当前密码",
|
||||
"New Password": "新密码",
|
||||
"Repeat New Password": "重复新密码",
|
||||
"Update Password": "更新密码",
|
||||
"Multi-Factor Authentication \\(MFA\\)": "多重身份验证(MFA)",
|
||||
"Reset": "重置",
|
||||
"Setup": "设置",
|
||||
"API Keys": "API 秘钥",
|
||||
"API Key": "API 秘钥",
|
||||
"Key": "秘钥",
|
||||
"Enabled": "已启用",
|
||||
"Disabled": "已停用",
|
||||
"No WGDashboard API Key": "没有 WGDashboard API 秘钥",
|
||||
"Expire At": "过期于",
|
||||
"Are you sure to delete this API key\\?": "确定删除此 API 秘钥?",
|
||||
"Create API Key": "创建 API 秘钥",
|
||||
"When should this API Key expire\\?": "这个 API 秘钥什么时候过期呢?",
|
||||
"Never Expire": "从不过期",
|
||||
"Don't think that's a good idea": "我不觉得这是一个好主意",
|
||||
"Creating\\.\\.\\.": "创建中...",
|
||||
"Create": "创建",
|
||||
"Status": "状态",
|
||||
"On": "已启用",
|
||||
"Off": "已停用",
|
||||
"Address": "网络地址",
|
||||
"Listen Port": "监听端口",
|
||||
"Public Key": "公钥",
|
||||
"Connected Peers": "已连接端点",
|
||||
"Total Usage": "总数据用量",
|
||||
"Total Received": "总接收数据用量",
|
||||
"Total Sent": "总发送数据用量",
|
||||
"Peers Data Usage": "端点的数据用量",
|
||||
"Real Time Received Data Usage": "实时接收数据量",
|
||||
"Real Time Sent Data Usage": "实时发送数据量",
|
||||
"Peer": "端点",
|
||||
"Peer Settings": "端点设定",
|
||||
"Download All": "全部下载",
|
||||
"Search Peers\\.\\.\\.": "搜索端点...",
|
||||
"Display": "显示设置",
|
||||
"Sort By": "排列方式",
|
||||
"Refresh Interval": "刷新间隔",
|
||||
"Name": "名字",
|
||||
"Allowed IPs": "允许的 IP 地址",
|
||||
"Restricted": "已限制端点",
|
||||
"(.*) Seconds": "$1 秒",
|
||||
"(.*) Minutes": "$1 分钟",
|
||||
"Configuration Settings": "配置设定",
|
||||
"Peer Jobs": "端点任务",
|
||||
"Active Jobs": "未运行任务",
|
||||
"All Active Jobs": "所有未运行任务",
|
||||
"Logs": "日志",
|
||||
"Private Key": "秘钥",
|
||||
"\\(Required for QR Code and Download\\)": "(二维码以及下载功能需要填写秘钥)",
|
||||
"\\(Required\\)": "(必填项)",
|
||||
"Endpoint Allowed IPs": "终结点允许的 IP 地址",
|
||||
"DNS": "域名系统(DNS)",
|
||||
"Optional Settings": "可选设定",
|
||||
"Pre-Shared Key": "共享秘钥",
|
||||
"MTU": "最大传输单元",
|
||||
"Persistent Keepalive": "持久保持活动",
|
||||
"Reset Data Usage": "重置数据用量",
|
||||
"Total": "总数据",
|
||||
"Sent": "发送数据",
|
||||
"Received": "接收数据",
|
||||
"Revert": "撤销更改",
|
||||
"Save Peer": "保存端点",
|
||||
"QR Code": "二维码",
|
||||
"Schedule Jobs": "计划任务",
|
||||
"Job": "任务",
|
||||
"Job ID": "任务 ID",
|
||||
"Unsaved Job": "未保存任务",
|
||||
"This peer does not have any job yet\\.": "此端点还没有任何任务",
|
||||
"if": "如果",
|
||||
"is": "是",
|
||||
"then": "那就",
|
||||
"larger than": "大于",
|
||||
"Date": "日期",
|
||||
"Restrict Peer": "限制端点",
|
||||
"Delete Peer": "删除端点",
|
||||
"Edit": "编辑",
|
||||
"Delete": "删除",
|
||||
"Cancel": "取消",
|
||||
"Save": "保存",
|
||||
"No active job at the moment\\.": "没有未运行的任务",
|
||||
"Jobs Logs": "任务日志",
|
||||
"Updated at": "更新于",
|
||||
"Refresh": "刷新",
|
||||
"Filter": "筛选",
|
||||
"Success": "成功",
|
||||
"Failed": "失败",
|
||||
"Log ID": "任务 ID",
|
||||
"Message": "消息",
|
||||
"Share Peer": "分享端点",
|
||||
"Currently the peer is not sharing": "此端点未被共享",
|
||||
"Sharing\\.\\.\\.": "分享中...",
|
||||
"Start Sharing": "开始分享",
|
||||
"Stop Sharing\\.\\.\\.": "停止分享中...",
|
||||
"Stop Sharing": "停止分享",
|
||||
"Access Restricted": "已限制访问",
|
||||
"Download \\& QR Code is not available due to no private key set for this peer": "下载以及二维码功能不可用,需要填写此端点的秘钥"
|
||||
}
|
Loading…
Reference in a new issue