diff --git a/src/dashboard.py b/src/dashboard.py index 19c038c..052a340 100644 --- a/src/dashboard.py +++ b/src/dashboard.py @@ -77,7 +77,12 @@ class CustomJsonEncoder(DefaultJSONProvider): super().__init__(app) def default(self, o): - if isinstance(o, WireguardConfiguration) or isinstance(o, Peer) or isinstance(o, PeerJob) or isinstance(o, Log) or isinstance(o, DashboardAPIKey): + if (isinstance(o, WireguardConfiguration) + or isinstance(o, Peer) + or isinstance(o, PeerJob) + or isinstance(o, Log) + or isinstance(o, DashboardAPIKey) + or isinstance(o, PeerShareLink)): return o.toJson() return super().default(self, o) @@ -335,19 +340,19 @@ class PeerJobs: return x < y class PeerShareLink: - def __init__(self, ShareID:str, Peer: str, Configuration: str, SharedDate: datetime, ExpireDate: datetime): + def __init__(self, ShareID:str, Configuration: str, Peer: str, ExpireDate: datetime, ShareDate: datetime): self.ShareID = ShareID self.Peer = Peer self.Configuration = Configuration - self.SharedDate = SharedDate - self.ExpireData = ExpireDate + self.ShareDate = ShareDate + self.ExpireDate = ExpireDate def toJson(self): return { - "SharedID": self.ShareID, + "ShareID": self.ShareID, "Peer": self.Peer, "Configuration": self.Configuration, - "ShareDate": self.SharedDate + "ExpireDate": self.ExpireDate } class PeerShareLinks: @@ -363,38 +368,50 @@ class PeerShareLinks: ExpireDate DATETIME, SharedDate DATETIME DEFAULT (datetime('now', 'localtime')) ) - """ % self.Name + """ ) sqldb.commit() - + self.__getSharedLinks() + # print(self.Links) def __getSharedLinks(self): self.Links.clear() allLinks = cursor.execute("SELECT * FROM PeerShareLinks WHERE ExpireDate IS NULL OR ExpireDate > datetime('now', 'localtime')").fetchall() for link in allLinks: - self.Links.append(*link) + self.Links.append(PeerShareLink(*link)) - def getLink(self, Configuration: str, Peer: str): + def getLink(self, Configuration: str, Peer: str) -> list[PeerShareLink]: return list(filter(lambda x : x.Configuration == Configuration and x.Peer == Peer, self.Links)) - def getLink(self, ShareID: str): + def getLinkByID(self, ShareID: str) -> list[PeerShareLink]: return list(filter(lambda x : x.ShareID == ShareID, self.Links)) - def addLink(self, Configuration: str, Peer: str, ExpireDate: datetime = None) -> tuple[bool, message]: + def addLink(self, Configuration: str, Peer: str, ExpireDate: datetime = None) -> tuple[bool, str]: try: newShareID = str(uuid.uuid4()) if len(self.getLink(Configuration, Peer)) > 0: cursor.execute("UPDATE PeerShareLinks SET ExpireDate = datetime('now', 'localtime') WHERE Configuration = ? AND Peer = ?", (Configuration, Peer, )) - cursor.execute("INSERT INTO PeerShareLinks VALUES (?, ?, ?, ?)", (newShareID, Configuration, Peer, ExpireDate, )) + + if ExpireDate is not None: + ExpireDate = datetime.strptime(ExpireDate, '%Y-%m-%d %H:%M:%S') + + cursor.execute("INSERT INTO PeerShareLinks (ShareID, Configuration, Peer, ExpireDate) VALUES (?, ?, ?, ?)", (newShareID, Configuration, Peer, ExpireDate, )) sqldb.commit() self.__getSharedLinks() except Exception as e: return False, str(e) - return True + return True, newShareID - def updateLinkExpireDate(self, ShareID, ExpireDate): - cursor.execute("UPDATE PeerShareLinks SET ExpireDate = datetime('now', 'localtime') WHERE ShareID = ?", (ShareID, )) - sqldb.commit() - self.__getSharedLinks() + def updateLinkExpireDate(self, ShareID, ExpireDate: datetime = None) -> tuple[bool, str]: + try: + if ExpireDate is None: + cursor.execute("UPDATE PeerShareLinks SET ExpireDate = datetime('now', 'localtime') WHERE ShareID = ?", (ShareID, )) + else: + cursor.execute("UPDATE PeerShareLinks SET ExpireDate = ? WHERE ShareID = ?", (ShareID, datetime.strptime(ExpireDate, '%Y-%m-%d %H:%M:%S'), )) + sqldb.commit() + self.__getSharedLinks() + return True + except Exception as e: + return False, str(e) @@ -876,10 +893,13 @@ class Peer: self.remote_endpoint = tableData["remote_endpoint"] self.preshared_key = tableData["preshared_key"] self.jobs: list[PeerJob] = [] + self.ShareLink: list[PeerShareLink] = [] self.getJobs() + self.getShareLink() def toJson(self): self.getJobs() + self.getShareLink() return self.__dict__ def __repr__(self): @@ -978,8 +998,10 @@ PersistentKeepalive = {str(self.keepalive)} def getJobs(self): self.jobs = AllPeerJobs.searchJob(self.configuration.Name, self.id) - # print(AllPeerJobs.searchJob(self.configuration.Name, self.id)) + def getShareLink(self): + self.ShareLink = AllPeerShareLinks.getLink(self.configuration.Name, self.id) + # Regex Match def regex_match(regex, text): pattern = re.compile(regex) @@ -1551,6 +1573,38 @@ def API_restrictPeers(configName: str) -> ResponseObject: return configuration.restrictPeers(peers) return ResponseObject(False, "Configuration does not exist") +@app.route('/api/sharePeer/create', methods=['POST']) +def API_sharePeer_create(): + data: dict[str, str] = request.get_json() + Configuration = data.get('Configuration') + Peer = data.get('Peer') + ExpireDate = data.get('ExpireDate') + if Configuration is None or Peer is None: + return ResponseObject(False, "Please specify configuration and peer") + activeLink = AllPeerShareLinks.getLink(Configuration, Peer) + if len(activeLink) > 0: + return ResponseObject(False, "This peer is already sharing, please stop sharing first.") + status, message = AllPeerShareLinks.addLink(Configuration, Peer, ExpireDate) + if not status: + return ResponseObject(status, message) + return ResponseObject(data=AllPeerShareLinks.getLinkByID(message)) + +@app.route('/api/sharePeer/update', methods=['POST']) +def API_sharePeer_update(): + data: dict[str, str] = request.get_json() + ShareID: str = data.get("ShareID") + ExpireDate: str = data.get("ExpireDate") + + if ShareID is None: + return ResponseObject(False, "Please specify ShareID") + + if len(AllPeerShareLinks.getLinkByID(ShareID)) == 0: + return ResponseObject(False, "ShareID does not exist") + + status, message = AllPeerShareLinks.updateLinkExpireDate(ShareID, ExpireDate) + if not status: + return ResponseObject(status, message) + return ResponseObject(data=AllPeerShareLinks.getLinkByID(ShareID)) @app.route('/api/allowAccessPeers/', methods=['POST']) def API_allowAccessPeers(configName: str) -> ResponseObject: @@ -1949,6 +2003,7 @@ sqldb.row_factory = sqlite3.Row cursor = sqldb.cursor() DashboardConfig = DashboardConfig() +AllPeerShareLinks: PeerShareLinks = PeerShareLinks() AllPeerJobs: PeerJobs = PeerJobs() JobLogger: PeerJobLogger = PeerJobLogger() DashboardLogger: DashboardLogger = DashboardLogger() diff --git a/src/static/app/package-lock.json b/src/static/app/package-lock.json index e28aff8..0a92f2d 100644 --- a/src/static/app/package-lock.json +++ b/src/static/app/package-lock.json @@ -8,6 +8,7 @@ "name": "app", "version": "0.0.0", "dependencies": { + "@vuepic/vue-datepicker": "^9.0.1", "@vueuse/core": "^10.9.0", "@vueuse/shared": "^10.9.0", "animate.css": "^4.1.1", @@ -721,6 +722,20 @@ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.29.tgz", "integrity": "sha512-hQ2gAQcBO/CDpC82DCrinJNgOHI2v+FA7BDW4lMSPeBpQ7sRe2OLHWe5cph1s7D8DUQAwRt18dBDfJJ220APEA==" }, + "node_modules/@vuepic/vue-datepicker": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@vuepic/vue-datepicker/-/vue-datepicker-9.0.1.tgz", + "integrity": "sha512-5sSdwib5cY8cE4Y7SCh+Zemfp+U/m6BDcgaPwd5Vmdv5LAASyV0wugn9sTb6NWX0sIQEdrGDl/RmD9EjcIke3A==", + "dependencies": { + "date-fns": "^3.6.0" + }, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "vue": ">=3.2.0" + } + }, "node_modules/@vueuse/core": { "version": "10.9.0", "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.9.0.tgz", @@ -937,6 +952,15 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/dayjs": { "version": "1.11.12", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.12.tgz", diff --git a/src/static/app/package.json b/src/static/app/package.json index be37e4b..5373acb 100644 --- a/src/static/app/package.json +++ b/src/static/app/package.json @@ -9,6 +9,7 @@ "preview": "vite preview" }, "dependencies": { + "@vuepic/vue-datepicker": "^9.0.1", "@vueuse/core": "^10.9.0", "@vueuse/shared": "^10.9.0", "animate.css": "^4.1.1", diff --git a/src/static/app/src/components/configurationComponents/peer.vue b/src/static/app/src/components/configurationComponents/peer.vue index d92ca21..9d90b8b 100644 --- a/src/static/app/src/components/configurationComponents/peer.vue +++ b/src/static/app/src/components/configurationComponents/peer.vue @@ -86,6 +86,7 @@ export default { @setting="this.$emit('setting')" @jobs="this.$emit('jobs')" @refresh="this.$emit('refresh')" + @share="this.$emit('share')" :Peer="Peer" v-if="this.subMenuOpened" ref="target" diff --git a/src/static/app/src/components/configurationComponents/peerList.vue b/src/static/app/src/components/configurationComponents/peerList.vue index 3a4e4fc..9d116ee 100644 --- a/src/static/app/src/components/configurationComponents/peerList.vue +++ b/src/static/app/src/components/configurationComponents/peerList.vue @@ -40,6 +40,7 @@ import PeerJobs from "@/components/configurationComponents/peerJobs.vue"; import PeerJobsAllModal from "@/components/configurationComponents/peerJobsAllModal.vue"; import PeerJobsLogsModal from "@/components/configurationComponents/peerJobsLogsModal.vue"; import {ref} from "vue"; +import PeerShareLinkModal from "@/components/configurationComponents/peerShareLinkModal.vue"; Chart.register( ArcElement, @@ -70,6 +71,7 @@ Chart.register( export default { name: "peerList", components: { + PeerShareLinkModal, PeerJobsLogsModal, PeerJobsAllModal, PeerJobs, PeerCreate, PeerQRCode, PeerSettings, PeerSearch, Peer, Line, Bar}, setup(){ @@ -131,6 +133,10 @@ export default { }, peerScheduleJobsLogs: { modalOpen: false + }, + peerShare:{ + modalOpen: false, + selectedPeer: undefined } } }, @@ -141,26 +147,21 @@ export default { '$route': { immediate: true, handler(){ - console.log(this.dashboardConfigurationStore.Peers.RefreshInterval) clearInterval(this.dashboardConfigurationStore.Peers.RefreshInterval); - console.log(this.dashboardConfigurationStore.Peers.RefreshInterval) - this.loading = true; let id = this.$route.params.id; this.configurationInfo = []; this.configurationPeers = []; if (id){ this.getPeers(id) - console.log("Changed..") this.setPeerInterval(); } } }, 'dashboardConfigurationStore.Configuration.Server.dashboard_refresh_interval'(){ - console.log("Changed?") clearInterval(this.dashboardConfigurationStore.Peers.RefreshInterval); this.setPeerInterval(); - }, + } }, beforeRouteLeave(){ clearInterval(this.dashboardConfigurationStore.Peers.RefreshInterval); @@ -248,7 +249,6 @@ export default { this.dashboardConfigurationStore.Peers.RefreshInterval = setInterval(() => { this.getPeers() }, parseInt(this.dashboardConfigurationStore.Configuration.Server.dashboard_refresh_interval)) - console.log(this.dashboardConfigurationStore.Peers.RefreshInterval) }, }, computed: { @@ -548,7 +548,7 @@ export default { :key="peer.id" v-for="peer in this.searchPeers"> + + + diff --git a/src/static/app/src/components/configurationComponents/peerSettingsDropdown.vue b/src/static/app/src/components/configurationComponents/peerSettingsDropdown.vue index 2e92229..7d63ece 100644 --- a/src/static/app/src/components/configurationComponents/peerSettingsDropdown.vue +++ b/src/static/app/src/components/configurationComponents/peerSettingsDropdown.vue @@ -123,7 +123,7 @@ export default {
  • - + Share
  • diff --git a/src/static/app/src/components/configurationComponents/peerShareLinkModal.vue b/src/static/app/src/components/configurationComponents/peerShareLinkModal.vue new file mode 100644 index 0000000..8c781b3 --- /dev/null +++ b/src/static/app/src/components/configurationComponents/peerShareLinkModal.vue @@ -0,0 +1,124 @@ + + + + + \ No newline at end of file diff --git a/src/static/app/src/main.js b/src/static/app/src/main.js index 39dca50..a1841d7 100644 --- a/src/static/app/src/main.js +++ b/src/static/app/src/main.js @@ -3,6 +3,7 @@ import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap/dist/js/bootstrap.js' import 'bootstrap-icons/font/bootstrap-icons.css' import 'animate.css/animate.compat.css' +import '@vuepic/vue-datepicker/dist/main.css' import {createApp, markRaw} from 'vue' import { createPinia } from 'pinia' diff --git a/src/static/css/dashboard.css b/src/static/css/dashboard.css index f02e8ab..bba970a 100644 --- a/src/static/css/dashboard.css +++ b/src/static/css/dashboard.css @@ -1,3 +1,8 @@ +*, +.dp__input{ + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important; +} + ::-webkit-scrollbar { display: none; }