first commit of refactor

This commit is contained in:
KS Jannette
2026-02-27 09:58:18 -05:00
commit 0adfd70853
63 changed files with 10558 additions and 0 deletions

View File

@@ -0,0 +1,113 @@
// API client for address management
import { getAuthHeaders, getAuthHeadersSimple } from './authHeaders';
const API_BASE = '/api';
/**
* Create a new blockchain address to monitor
* @param {Object} data - Address data
* @param {string} data.address - Blockchain address (0x...)
* @param {string} [data.label] - Optional label for the address
* @returns {Promise<Object>} Created address with id and metadata
*/
export async function createAddress(data) {
try {
const headers = await getAuthHeaders();
const response = await fetch(`${API_BASE}/addresses`, {
method: 'POST',
headers: headers,
body: JSON.stringify(data)
});
if (!response.ok) {
// Try to parse error response, but handle if it's not JSON
let errorMessage = 'Failed to create address';
try {
const error = await response.json();
errorMessage = error.message || errorMessage;
} catch {
// If JSON parse fails, use status text
errorMessage = `Server error: ${response.status} ${response.statusText}`;
}
throw new Error(errorMessage);
}
return response.json();
} catch (error) {
// Handle network errors
if (error.message.includes('fetch')) {
throw new Error('Cannot connect to server. Is the backend running?');
}
throw error;
}
}
/**
* Get all tracked addresses
* @returns {Promise<Array>} List of all addresses
*/
export async function getAddresses() {
try {
const headers = await getAuthHeadersSimple();
const response = await fetch(`${API_BASE}/addresses`, {
headers: headers
});
if (!response.ok) {
// Try to parse error response, but handle if it's not JSON
let errorMessage = 'Failed to fetch addresses';
try {
const error = await response.json();
errorMessage = error.message || errorMessage;
} catch {
// If JSON parse fails, use status text
errorMessage = `Server error: ${response.status} ${response.statusText}`;
}
throw new Error(errorMessage);
}
return response.json();
} catch (error) {
// Handle network errors or JSON parse errors
if (error.message.includes('fetch')) {
throw new Error('Cannot connect to server. Is the backend running?');
}
throw error;
}
}
/**
* Delete a tracked address
* @param {number} addressId - Address ID to delete
* @returns {Promise<void>}
*/
export async function deleteAddress(addressId) {
try {
const headers = await getAuthHeadersSimple();
const response = await fetch(`${API_BASE}/addresses/${addressId}`, {
method: 'DELETE',
headers: headers
});
if (!response.ok && response.status !== 204) {
// Try to parse error response, but handle if it's not JSON
let errorMessage = 'Failed to delete address';
try {
const error = await response.json();
errorMessage = error.message || errorMessage;
} catch {
// If JSON parse fails, use status text
errorMessage = `Server error: ${response.status} ${response.statusText}`;
}
throw new Error(errorMessage);
}
} catch (error) {
// Handle network errors
if (error.message.includes('fetch')) {
throw new Error('Cannot connect to server. Is the backend running?');
}
throw error;
}
}

View File

@@ -0,0 +1,36 @@
// API client for alert event history
import { getAuthHeadersSimple } from './authHeaders';
const API_BASE = '/api';
/**
* Get all alert events (history)
* @returns {Promise<Array>} List of alert events, sorted by most recent first
*/
export async function getAlertEvents() {
try {
const headers = await getAuthHeadersSimple();
const response = await fetch(`${API_BASE}/alert-events`, {
headers: headers
});
if (!response.ok) {
let errorMessage = 'Failed to fetch alert events';
try {
const error = await response.json();
errorMessage = error.message || errorMessage;
} catch {
errorMessage = `Server error: ${response.status} ${response.statusText}`;
}
throw new Error(errorMessage);
}
return response.json();
} catch (error) {
if (error.message.includes('fetch')) {
throw new Error('Cannot connect to server. Is the backend running?');
}
throw error;
}
}

150
frontend/src/api/alerts.jsx Normal file
View File

@@ -0,0 +1,150 @@
// API client for alert rule management
import { getAuthHeaders, getAuthHeadersSimple } from './authHeaders';
const API_BASE = '/api';
/**
* Alert types supported by the system
*/
export const ALERT_TYPES = {
INCOMING_TX: 'incoming_tx',
OUTGOING_TX: 'outgoing_tx',
LARGE_TRANSFER: 'large_transfer',
BALANCE_BELOW: 'balance_below'
};
/**
* Create a new alert rule for an address
* @param {number} addressId - Address ID
* @param {Object} data - Alert rule data
* @param {string} data.type - Alert type (one of ALERT_TYPES)
* @param {string} [data.threshold] - Threshold value (required for large_transfer and balance_below)
* @returns {Promise<Object>} Created alert rule
*/
export async function createAlert(addressId, data) {
try {
const headers = await getAuthHeaders();
const response = await fetch(`${API_BASE}/addresses/${addressId}/alerts`, {
method: 'POST',
headers: headers,
body: JSON.stringify(data)
});
if (!response.ok) {
let errorMessage = 'Failed to create alert rule';
try {
const error = await response.json();
errorMessage = error.message || errorMessage;
} catch {
errorMessage = `Server error: ${response.status} ${response.statusText}`;
}
throw new Error(errorMessage);
}
return response.json();
} catch (error) {
if (error.message.includes('fetch')) {
throw new Error('Cannot connect to server. Is the backend running?');
}
throw error;
}
}
/**
* Get all alert rules for an address
* @param {number} addressId - Address ID
* @returns {Promise<Array>} List of alert rules
*/
export async function getAlerts(addressId) {
try {
const headers = await getAuthHeadersSimple();
const response = await fetch(`${API_BASE}/addresses/${addressId}/alerts`, {
headers: headers
});
if (!response.ok) {
let errorMessage = 'Failed to fetch alert rules';
try {
const error = await response.json();
errorMessage = error.message || errorMessage;
} catch {
errorMessage = `Server error: ${response.status} ${response.statusText}`;
}
throw new Error(errorMessage);
}
return response.json();
} catch (error) {
if (error.message.includes('fetch')) {
throw new Error('Cannot connect to server. Is the backend running?');
}
throw error;
}
}
/**
* Enable or disable an alert rule
* @param {number} alertId - Alert rule ID
* @param {boolean} enabled - Whether the alert should be enabled
* @returns {Promise<Object>} Updated alert rule
*/
export async function updateAlertStatus(alertId, enabled) {
try {
const headers = await getAuthHeaders();
const response = await fetch(`${API_BASE}/alerts/${alertId}`, {
method: 'PATCH',
headers: headers,
body: JSON.stringify({ enabled })
});
if (!response.ok) {
let errorMessage = 'Failed to update alert rule';
try {
const error = await response.json();
errorMessage = error.message || errorMessage;
} catch {
errorMessage = `Server error: ${response.status} ${response.statusText}`;
}
throw new Error(errorMessage);
}
return response.json();
} catch (error) {
if (error.message.includes('fetch')) {
throw new Error('Cannot connect to server. Is the backend running?');
}
throw error;
}
}
/**
* Delete an alert rule
* @param {number} alertId - Alert rule ID
* @returns {Promise<void>}
*/
export async function deleteAlert(alertId) {
try {
const headers = await getAuthHeadersSimple();
const response = await fetch(`${API_BASE}/alerts/${alertId}`, {
method: 'DELETE',
headers: headers
});
if (!response.ok && response.status !== 204) {
let errorMessage = 'Failed to delete alert rule';
try {
const error = await response.json();
errorMessage = error.message || errorMessage;
} catch {
errorMessage = `Server error: ${response.status} ${response.statusText}`;
}
throw new Error(errorMessage);
}
} catch (error) {
if (error.message.includes('fetch')) {
throw new Error('Cannot connect to server. Is the backend running?');
}
throw error;
}
}

View File

@@ -0,0 +1,47 @@
/**
* Auth Headers Helper
*
* Provides authentication headers for API calls
* Includes Firebase ID token in Authorization header
*/
import { auth } from '../firebase/config';
/**
* Get headers with authentication token
* @returns {Promise<Object>} Headers object with Authorization
*/
export async function getAuthHeaders() {
const currentUser = auth.currentUser;
if (!currentUser) {
throw new Error('No authenticated user');
}
// Get Firebase ID token
const token = await currentUser.getIdToken();
return {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
};
}
/**
* Get headers for non-JSON requests (e.g., DELETE with no body)
* @returns {Promise<Object>} Headers object with Authorization
*/
export async function getAuthHeadersSimple() {
const currentUser = auth.currentUser;
if (!currentUser) {
throw new Error('No authenticated user');
}
const token = await currentUser.getIdToken();
return {
'Authorization': `Bearer ${token}`
};
}

View File

@@ -0,0 +1,100 @@
// API client for notification configuration
import { getAuthHeaders } from './authHeaders';
const API_BASE = '/api';
/**
* Get notification configuration for current user
* @returns {Promise<Object>} Notification config
*/
export async function getNotificationConfig() {
try {
const headers = await getAuthHeaders();
const response = await fetch(`${API_BASE}/notification-config`, {
headers: headers
});
if (!response.ok) {
let errorMessage = 'Failed to fetch notification config';
try {
const error = await response.json();
errorMessage = error.message || errorMessage;
} catch {
errorMessage = `Server error: ${response.status} ${response.statusText}`;
}
throw new Error(errorMessage);
}
return response.json();
} catch (error) {
if (error.message.includes('fetch')) {
throw new Error('Cannot connect to server. Is the backend running?');
}
throw error;
}
}
/**
* Update notification configuration
* @param {Object} config - Configuration to update
* @param {string} [config.discord_webhook_url] - Discord webhook URL
* @param {string} [config.telegram_chat_id] - Telegram chat ID
* @param {string} [config.email] - Email address
* @param {boolean} [config.notification_enabled] - Enable/disable notifications
* @returns {Promise<Object>} Updated config
*/
export async function updateNotificationConfig(config) {
try {
const headers = await getAuthHeaders();
const response = await fetch(`${API_BASE}/notification-config`, {
method: 'PUT',
headers: headers,
body: JSON.stringify(config)
});
if (!response.ok) {
let errorMessage = 'Failed to update notification config';
try {
const error = await response.json();
errorMessage = error.message || errorMessage;
} catch {
errorMessage = `Server error: ${response.status} ${response.statusText}`;
}
throw new Error(errorMessage);
}
return response.json();
} catch (error) {
if (error.message.includes('fetch')) {
throw new Error('Cannot connect to server. Is the backend running?');
}
throw error;
}
}
/**
* Test a Discord webhook URL
* Sends a test message to verify the webhook works
* @param {string} webhookUrl - Discord webhook URL to test
* @returns {Promise<boolean>} True if test successful
*/
export async function testDiscordWebhook(webhookUrl) {
try {
const payload = {
content: 'Koin Ping test notification - Your Discord webhook is configured correctly!',
};
const response = await fetch(webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
return response.ok;
} catch (error) {
console.error('Discord webhook test failed:', error);
return false;
}
}

View File

@@ -0,0 +1,18 @@
// API client for system status
const API_BASE = '/api';
/**
* Get system status including latest processed block and health
* @returns {Promise<Object>} System status
*/
export async function getSystemStatus() {
const response = await fetch(`${API_BASE}/status`);
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Failed to fetch system status');
}
return response.json();
}