summaryrefslogtreecommitdiffstats
path: root/static
diff options
context:
space:
mode:
Diffstat (limited to 'static')
-rw-r--r--static/css/styles.css120
-rw-r--r--static/js/app.js135
2 files changed, 255 insertions, 0 deletions
diff --git a/static/css/styles.css b/static/css/styles.css
new file mode 100644
index 0000000..9c26cab
--- /dev/null
+++ b/static/css/styles.css
@@ -0,0 +1,120 @@
+:root {
+ --light-white: #FFFFFF;
+ --dark-white: #EFFFFF;
+ --light-black: #181818;
+ --dark-black: #000000;
+
+ --light-green: #27AE60;
+ --dark-green: #27AE60;
+ --light-yellow: #F1C40F;
+ --dark-yellow: #F1C40F;
+ --light-red: #E74C3C;
+ --dark-red: #E74C3C;
+}
+
+html {
+ background-color: var(--light-black);
+}
+
+@media (prefers-color-scheme: light) {
+ html {
+ background-color: var(--light-white);
+ }
+ .message.loading,
+ .message.warning {
+ border-color: var(--light-yellow);
+ }
+ .message.success {
+ border-color: var(--light-green);
+ }
+ .message.error {
+ border-color: var(--light-red);
+ }
+ .connection.loading {
+ border-color: var(--light-yellow);
+ }
+
+ .connection.active {
+ border-color: var(--light-green);
+ }
+}
+
+body {
+ font-family: "PT Sans", "Kohinoor Bangla", sans-serif;
+ max-width: 1024px;
+ min-width: 375px;
+ margin: 0 auto;
+ float: none;
+ font-size: 18px;
+}
+
+header {
+ text-align: center;
+ font-style: normal;
+ .header__title {
+ font-size: 32px;
+ margin-bottom: -16px;
+ margin-top: 36px;
+ clear: right;
+ }
+}
+
+main {
+ margin: 32px;
+}
+
+.status {
+ min-height: 160px;
+}
+
+.status__header,
+.connections__header {
+}
+
+.message {
+ font-family: monospace;
+ font-size: 14px;
+ line-height: 1.5;
+ border-style: solid;
+ border-width: 1px;
+ border-left-width: 14px;
+ padding: 14px;
+ white-space: pre-line;
+}
+
+.message.loading,
+.message.warning {
+ border-color: var(--dark-yellow);
+}
+.message.success {
+ border-color: var(--dark-green);
+}
+.message.error {
+ border-color: var(--dark-red);
+}
+
+.connections__container {
+ width: 100%;
+}
+.connection {
+ font-family: monospace;
+ font-size: 14px;
+ cursor: pointer;
+ border: 1px solid;
+ padding: 1rem;
+ margin-top: 1rem;
+ margin-bottom: 1rem;
+}
+.connection.loading {
+ border-left-width: 14px;
+ border-color: var(--dark-yellow);
+}
+
+.connection.active {
+ border-left-width: 14px;
+ border-color: var(--dark-green);
+}
+
+footer {
+ margin: 32px;
+}
diff --git a/static/js/app.js b/static/js/app.js
new file mode 100644
index 0000000..db8ed8a
--- /dev/null
+++ b/static/js/app.js
@@ -0,0 +1,135 @@
+// Application state and configuration
+const App = {
+ apiBase: "/api",
+ elements: {
+ connectionList: document.getElementById('connections__container'),
+ statusArea: document.getElementById('status__container'),
+ messageArea: document.getElementById('notifications__container')
+ }
+};
+
+// Utility functions
+const Utils = {
+ // Show message to user
+ renderError(element, message) {
+ Utils.renderMessage(element, message, "error");
+ },
+ renderWarning(element, message) {
+ Utils.renderMessage(element, message, "warning");
+ },
+ renderSuccess(element, message) {
+ Utils.renderMessage(element, message, "success");
+ },
+ renderMessage(element, message, type = '') {
+ element.innerHTML = `
+ <div class="message ${type}">${message}</div>
+ `;
+ },
+ // Make API calls with proper error handling
+ async apiCall(endpoint, options = {}) {
+ try {
+ const response = await fetch(`${App.apiBase}${endpoint}`, {
+ headers: {
+ 'Content-Type': 'application/json',
+ ...options.headers
+ },
+ ...options
+ });
+
+ const data = await response.json();
+
+ if (!response.ok) {
+ throw new Error(data.error || `HTTP ${response.status}`);
+ }
+
+ if (!data.success) {
+ throw new Error(data.error || 'API request failed');
+ }
+
+ return data.data;
+ } catch (error) {
+ console.error('API call failed:', error);
+ throw error;
+ }
+ }
+};
+
+// Connection management
+const ConnectionManager = {
+ // Load and display all connections
+ async loadConnections() {
+ try {
+ const connections = await Utils.apiCall('/connections');
+ this.renderConnections(connections);
+ } catch (error) {
+ Utils.renderError(App.elements.connectionList,
+ `Failed to load connections: ${error.message}`);
+ }
+ },
+
+ // Render connections to the DOM
+ renderConnections(connections) {
+ if (!connections || connections.length === 0) {
+ Utils.renderWarning(App.elements.connectionList,
+ "No WireGuard connections found in /etc/wireguard/");
+ return;
+ }
+
+ const html = connections.map(conn => `
+ <div class="connection ${conn.active ? 'active' : ''}"
+ data-connection="${conn.name}"
+ onclick="ConnectionManager.toggleConnection('${conn.name}')">
+ <div class="connection__name ${conn.active ? 'active' : ''}">${conn.name}</div>
+ </div>
+ `).join('');
+ App.elements.connectionList.innerHTML = html;
+ },
+
+ // Toggle connection state
+ async toggleConnection(name) {
+ const connection = document.querySelector(`[data-connection="${name}"]`);
+
+ try {
+ connection.disabled = true;
+ connection.textContent = 'Processing...';
+ connection.className = "connection loading"
+
+ await Utils.apiCall('/connections/toggle', {
+ method: 'POST',
+ body: JSON.stringify({ name })
+ });
+ await this.loadConnections(); // Refresh the list
+ await StatusManager.loadStatus(); // Refresh the status
+ Utils.renderSuccess(App.elements.messageArea, `No Errors Found.`);
+ } catch (error) {
+ Utils.renderError(App.elements.messageArea, `Failed to toggle ${name}: ${error.message}`);
+ connection.disabled = false;
+ connection.textContent = name;
+ connection.className = "connection"
+ }
+ }
+};
+
+// Status management
+const StatusManager = {
+ // Load and display WireGuard status
+ async loadStatus() {
+ try {
+ const statusData = await Utils.apiCall('/status');
+ const statusText = statusData.status;
+ if (statusText) {
+ Utils.renderSuccess(App.elements.statusArea, statusText);
+ } else {
+ Utils.renderWarning(App.elements.statusArea, "No active connections.");
+ }
+ } catch (error) {
+ Utils.renderError(App.elements.statusArea, error.message);
+ }
+ },
+};
+
+// Initialize the application
+document.addEventListener('DOMContentLoaded', () => {
+ StatusManager.loadStatus();
+ ConnectionManager.loadConnections();
+});