Add code
This commit is contained in:
306
client-example.html
Normal file
306
client-example.html
Normal file
@ -0,0 +1,306 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>WebSocket Client Example</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
#messages { border: 1px solid #ccc; padding: 10px; height: 200px; overflow-y: scroll; margin-bottom: 10px; }
|
||||
.message { margin-bottom: 5px; }
|
||||
.server { color: blue; }
|
||||
.client { color: green; }
|
||||
.error { color: red; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>WebSocket Client Example</h1>
|
||||
<div>
|
||||
<label for="wsUrl">WebSocket URL:</label>
|
||||
<input type="text" id="wsUrl" value="ws://localhost:3001">
|
||||
<button id="connectBtn">Connect</button>
|
||||
<button id="disconnectBtn" disabled>Disconnect</button>
|
||||
</div>
|
||||
<div id="messages"></div>
|
||||
<hr>
|
||||
<div>
|
||||
<h3>Client State:</h3>
|
||||
<p>Server-Assigned Client ID: <span id="clientIdSpan">N/A</span></p>
|
||||
<p>Status: <span id="clientStatusSpan">Disconnected</span></p>
|
||||
</div>
|
||||
<hr>
|
||||
<div>
|
||||
<h3>Actions:</h3>
|
||||
<input type="text" id="clientNameInput" placeholder="Your Client Name (for registration)">
|
||||
<button id="registerBtn" disabled>Register Client</button>
|
||||
<br><br>
|
||||
<!-- Authenticate button removed -->
|
||||
<input type="text" id="secretKeyInput" placeholder="Secret Key to Request">
|
||||
<button id="requestSecretBtn" disabled>Request Secret</button>
|
||||
<br><br>
|
||||
<button id="listSecretsBtn" disabled>List Authorized Secrets</button>
|
||||
</div>
|
||||
<hr>
|
||||
<div>
|
||||
<h3>Send Custom JSON Message:</h3>
|
||||
<textarea id="customMessageInput" rows="4" style="width: 90%;" placeholder='{ "type": "YOUR_TYPE", "payload": { ... } }'></textarea>
|
||||
<button id="sendCustomBtn" disabled>Send Custom JSON</button>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
const wsUrlInput = document.getElementById('wsUrl');
|
||||
const connectBtn = document.getElementById('connectBtn');
|
||||
const disconnectBtn = document.getElementById('disconnectBtn');
|
||||
const messagesDiv = document.getElementById('messages');
|
||||
|
||||
const registerBtn = document.getElementById('registerBtn');
|
||||
const clientNameInput = document.getElementById('clientNameInput');
|
||||
// const authenticateBtn = document.getElementById('authenticateBtn'); // Removed
|
||||
// const authTokenInput = document.getElementById('authTokenInput'); // Removed
|
||||
const requestSecretBtn = document.getElementById('requestSecretBtn');
|
||||
const secretKeyInput = document.getElementById('secretKeyInput');
|
||||
const listSecretsBtn = document.getElementById('listSecretsBtn');
|
||||
const customMessageInput = document.getElementById('customMessageInput');
|
||||
const sendCustomBtn = document.getElementById('sendCustomBtn');
|
||||
|
||||
const clientIdSpan = document.getElementById('clientIdSpan');
|
||||
const tempIdSpan = document.getElementById('tempIdSpan');
|
||||
const clientStatusSpan = document.getElementById('clientStatusSpan');
|
||||
|
||||
|
||||
let socket = null;
|
||||
let serverAssignedClientId = null; // Server-assigned client ID (for admin tracking)
|
||||
let clientIsApproved = false; // Track if client is approved by server
|
||||
|
||||
function updateClientStateDisplay() {
|
||||
clientIdSpan.textContent = serverAssignedClientId || 'N/A';
|
||||
}
|
||||
|
||||
function logMessage(message, type = 'info') {
|
||||
const p = document.createElement('p');
|
||||
p.textContent = message;
|
||||
p.className = `message ${type}`;
|
||||
messagesDiv.appendChild(p);
|
||||
messagesDiv.scrollTop = messagesDiv.scrollHeight;
|
||||
}
|
||||
|
||||
connectBtn.addEventListener('click', () => {
|
||||
if (socket) {
|
||||
logMessage('Already connected or connecting.', 'error');
|
||||
return;
|
||||
}
|
||||
const url = wsUrlInput.value;
|
||||
logMessage(`Attempting to connect to ${url}...`);
|
||||
socket = new WebSocket(url);
|
||||
|
||||
socket.onopen = () => {
|
||||
logMessage('Connected to WebSocket server.', 'server');
|
||||
clientStatusSpan.textContent = 'Connected (Not Authenticated)';
|
||||
connectBtn.disabled = true;
|
||||
disconnectBtn.disabled = false;
|
||||
registerBtn.disabled = false;
|
||||
// authenticateBtn.disabled = false; // Removed
|
||||
sendCustomBtn.disabled = false; // Allow sending custom messages once connected
|
||||
clientIsApproved = false; // Reset approval state on new connection
|
||||
};
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
try {
|
||||
const serverMessage = JSON.parse(event.data);
|
||||
logMessage(`Server: ${JSON.stringify(serverMessage, null, 2)} (Code: ${serverMessage.code})`, 'server');
|
||||
|
||||
// Standard response codes
|
||||
const WsResponseCodes = { OK: 2000, REGISTRATION_SUBMITTED: 2001, BAD_REQUEST: 4000, UNAUTHORIZED: 4001, CLIENT_NOT_REGISTERED: 4005, CLIENT_REGISTRATION_EXPIRED: 4006 };
|
||||
|
||||
switch(serverMessage.type) {
|
||||
case 'WELCOME':
|
||||
clientStatusSpan.textContent = 'Connected. Please Register.';
|
||||
break;
|
||||
case 'REGISTRATION_ACK':
|
||||
if (serverMessage.code === WsResponseCodes.REGISTRATION_SUBMITTED) {
|
||||
serverAssignedClientId = serverMessage.payload.clientId;
|
||||
clientStatusSpan.textContent = 'Registration Submitted. Awaiting Admin Approval.';
|
||||
updateClientStateDisplay();
|
||||
logMessage(`Your Server-Assigned Client ID is ${serverAssignedClientId}. You will need admin approval to proceed.`, 'info');
|
||||
} else {
|
||||
clientStatusSpan.textContent = `Registration Failed (Code: ${serverMessage.code})`;
|
||||
logMessage(`Registration Error: ${serverMessage.payload.detail}`, 'error');
|
||||
}
|
||||
break;
|
||||
// 'AUTHENTICATED' and 'AUTH_FAILED' types are removed
|
||||
case 'SECRET_DATA': // Renamed from SECRET_RESPONSE for clarity
|
||||
if (serverMessage.code === WsResponseCodes.OK) {
|
||||
logMessage(`Secret "${serverMessage.payload.secretKey}": ${JSON.stringify(serverMessage.payload.value)}`, 'info');
|
||||
} else {
|
||||
logMessage(`Error fetching secret (Code: ${serverMessage.code}): ${serverMessage.payload.detail}`, 'error');
|
||||
}
|
||||
break;
|
||||
case 'AUTHORIZED_SECRETS_LIST': // Or AVAILABLE_SECRETS_LIST depending on server
|
||||
if (serverMessage.code === WsResponseCodes.OK) {
|
||||
logMessage(`Authorized/Available secrets: ${serverMessage.payload.authorizedSecretKeys || serverMessage.payload.availableSecretKeys.join(', ')}`, 'info');
|
||||
// For this client, assume approval if it gets this list successfully for now
|
||||
// However, STATUS_UPDATE is the definitive source of approval state.
|
||||
// clientIsApproved = true; // Let STATUS_UPDATE handle this
|
||||
// requestSecretBtn.disabled = !clientIsApproved;
|
||||
// listSecretsBtn.disabled = !clientIsApproved;
|
||||
// clientStatusSpan.textContent = 'Approved. Actions enabled.';
|
||||
} else {
|
||||
logMessage(`Error listing secrets (Code: ${serverMessage.code}): ${serverMessage.payload.detail}`, 'error');
|
||||
}
|
||||
break;
|
||||
case 'STATUS_UPDATE':
|
||||
logMessage(`Server Status Update (Code: ${serverMessage.code}): ${serverMessage.payload.detail}`, 'info');
|
||||
clientStatusSpan.textContent = `Status: ${serverMessage.payload.newStatus}. ${serverMessage.payload.detail || ''}`;
|
||||
if (serverMessage.payload.newStatus === 'approved') {
|
||||
clientIsApproved = true;
|
||||
requestSecretBtn.disabled = false;
|
||||
listSecretsBtn.disabled = false;
|
||||
} else {
|
||||
clientIsApproved = false;
|
||||
requestSecretBtn.disabled = true;
|
||||
listSecretsBtn.disabled = true;
|
||||
if (serverMessage.payload.newStatus === 'rejected' && serverAssignedClientId) {
|
||||
// If rejected after being pending, they might need to re-register or be stuck
|
||||
logMessage('Your registration was rejected or your session ended.', 'error');
|
||||
}
|
||||
}
|
||||
break;
|
||||
// UNAUTHORIZED_SECRET_ACCESS type might be consolidated into general ERROR with code 4001
|
||||
case 'ERROR':
|
||||
logMessage(`Server Error (Type: ${serverMessage.type}, Code: ${serverMessage.code}): ${serverMessage.payload.detail}`, 'error');
|
||||
if (serverMessage.code === WsResponseCodes.UNAUTHORIZED ||
|
||||
serverMessage.code === WsResponseCodes.CLIENT_NOT_REGISTERED ||
|
||||
serverMessage.code === WsResponseCodes.CLIENT_REGISTRATION_EXPIRED) {
|
||||
clientIsApproved = false;
|
||||
requestSecretBtn.disabled = true;
|
||||
listSecretsBtn.disabled = true;
|
||||
if (serverMessage.code === WsResponseCodes.CLIENT_REGISTRATION_EXPIRED) {
|
||||
clientStatusSpan.textContent = "Registration Expired. Please re-register.";
|
||||
serverAssignedClientId = null; // Clear client ID as it's no longer valid
|
||||
updateClientStateDisplay();
|
||||
} else if (serverMessage.code === WsResponseCodes.CLIENT_NOT_REGISTERED) {
|
||||
clientStatusSpan.textContent = "Not Registered. Please register.";
|
||||
} else { // General UNAUTHORIZED
|
||||
clientStatusSpan.textContent = "Action Unauthorized or Approval Pending/Rejected.";
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logMessage(`Unknown message type from server: ${serverMessage.type} (Code: ${serverMessage.code})`, 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Error processing server message:", e);
|
||||
logMessage(`Received non-JSON message from server: ${event.data}`, 'error');
|
||||
}
|
||||
};
|
||||
|
||||
socket.onclose = (event) => {
|
||||
logMessage(`Disconnected from WebSocket server. Code: ${event.code}, Reason: ${event.reason || 'N/A'}`, 'error');
|
||||
clientStatusSpan.textContent = 'Disconnected';
|
||||
socket = null;
|
||||
connectBtn.disabled = false;
|
||||
disconnectBtn.disabled = true;
|
||||
registerBtn.disabled = true;
|
||||
// authenticateBtn.disabled = true; // Removed
|
||||
requestSecretBtn.disabled = true;
|
||||
listSecretsBtn.disabled = true;
|
||||
sendCustomBtn.disabled = true;
|
||||
serverAssignedClientId = null;
|
||||
clientIsApproved = false;
|
||||
updateClientStateDisplay();
|
||||
};
|
||||
|
||||
socket.onerror = (error) => {
|
||||
logMessage('WebSocket error. See console for details.', 'error');
|
||||
console.error('WebSocket error:', error);
|
||||
// Note: onclose will usually be called after onerror.
|
||||
};
|
||||
});
|
||||
|
||||
disconnectBtn.addEventListener('click', () => {
|
||||
if (socket) {
|
||||
logMessage('Disconnecting...');
|
||||
socket.close();
|
||||
}
|
||||
});
|
||||
|
||||
// Function to generate a simple UUID for requestId
|
||||
function generateUUID() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
function sendWebSocketMessage(messageObject) {
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
if (!messageObject.requestId) { // Add a requestId if not present
|
||||
messageObject.requestId = generateUUID();
|
||||
}
|
||||
const messageString = JSON.stringify(messageObject);
|
||||
logMessage(`Client (Req ID: ${messageObject.requestId}): ${messageString}`, 'client');
|
||||
socket.send(messageString);
|
||||
} else {
|
||||
logMessage('Not connected. Cannot send message.', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
registerBtn.addEventListener('click', () => {
|
||||
const clientName = clientNameInput.value.trim();
|
||||
if (!clientName) {
|
||||
logMessage('Please enter a client name for registration.', 'error');
|
||||
return;
|
||||
}
|
||||
clientIsApproved = false; // Reset approval state on new registration attempt
|
||||
requestSecretBtn.disabled = true;
|
||||
listSecretsBtn.disabled = true;
|
||||
clientStatusSpan.textContent = "Registering...";
|
||||
sendWebSocketMessage({
|
||||
type: "REGISTER_CLIENT",
|
||||
payload: { clientName: clientName }
|
||||
// Server will respond with REGISTRATION_ACK and its own client ID
|
||||
});
|
||||
});
|
||||
|
||||
// authenticateBtn event listener removed
|
||||
|
||||
requestSecretBtn.addEventListener('click', () => {
|
||||
const secretKey = secretKeyInput.value.trim();
|
||||
if (!secretKey) {
|
||||
logMessage('Please enter a secret key to request.', 'error');
|
||||
return;
|
||||
}
|
||||
if (!clientIsApproved) {
|
||||
logMessage('Client not approved. Cannot request secret.', 'error');
|
||||
return;
|
||||
}
|
||||
sendWebSocketMessage({
|
||||
type: "REQUEST_SECRET",
|
||||
payload: { secretKey: secretKey }
|
||||
});
|
||||
});
|
||||
|
||||
listSecretsBtn.addEventListener('click', () => {
|
||||
if (!clientIsApproved) {
|
||||
logMessage('Client not approved. Cannot list secrets.', 'error');
|
||||
return;
|
||||
}
|
||||
sendWebSocketMessage({
|
||||
type: "LIST_AUTHORIZED_SECRETS"
|
||||
// Server will respond with AUTHORIZED_SECRETS_LIST or AVAILABLE_SECRETS_LIST
|
||||
});
|
||||
});
|
||||
|
||||
sendCustomBtn.addEventListener('click', () => {
|
||||
try {
|
||||
const customJson = JSON.parse(customMessageInput.value);
|
||||
sendWebSocketMessage(customJson);
|
||||
} catch (e) {
|
||||
logMessage('Invalid JSON in custom message input.', 'error');
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user