Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Kastl <[email protected]>
  • Loading branch information
dkastl committed Oct 3, 2024
1 parent 234404b commit 9de2952
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 180 deletions.
41 changes: 41 additions & 0 deletions src/configHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// configHandler.ts

import { getFormValues } from './formHandler';
import { buildProtobuf } from './protobufBuilder';
import { generateQRCode } from './qrCodeGenerator';
import { toUrlSafeBase64 } from './utils';

/**
* Generate a QR code and URL based on the form values.
*/
export async function generateConfig(): Promise<void> {
const formValues = getFormValues();

// Validate channel name length
const byteLength = new TextEncoder().encode(formValues.channelName).length;
if (byteLength > 12) {
alert(`Channel name must be less than or equal to 12 bytes (current byte length: ${byteLength}).`);
return;
}

// Generate the protobuf binary data for the form values
const channelSet = buildProtobuf(formValues);
const binaryData = channelSet.toBinary(); // Binary data from Protobuf

// Convert to URL-safe Base64 string
const base64 = toUrlSafeBase64(binaryData);

// Update the URL hash with the generated configuration
window.location.hash = `#${base64}`;

// Create the Meshtastic URL
const meshtasticUrl = `https://meshtastic.org/e/#${base64}`;
console.log("Generated Meshtastic URL:", meshtasticUrl);

// Update the generated URL in the input field
const urlField = document.getElementById('generatedUrl') as HTMLInputElement;
urlField.value = meshtasticUrl;

// Generate the QR code from the URL
await generateQRCode(meshtasticUrl);
}
11 changes: 4 additions & 7 deletions src/formHandler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// formHandler.ts

import { determinePskType } from './pskHandler';

/**
* Get the values from the form using FormData API.
*/
Expand Down Expand Up @@ -64,13 +66,8 @@ export function populateForm(formValues: any) {
(form.elements.namedItem('ignoreMqtt') as HTMLInputElement).checked = formValues.ignoreMqtt || false;
(form.elements.namedItem('configOkToMqtt') as HTMLInputElement).checked = formValues.configOkToMqtt || false;

// Set the pskType based on the byte length of the psk (psk.length / 2)
let pskType = 'none'; // Default to 'none'
if (formValues.psk && formValues.psk.length === 64) {
pskType = 'aes256'; // AES-256
} else if (formValues.psk && formValues.psk.length === 32) {
pskType = 'aes128'; // AES-128
}
// Set the pskType based on the byte length of the PSK
const pskType = determinePskType(formValues.psk?.length || 0);

// Update the pskType dropdown
(form.elements.namedItem('pskType') as HTMLSelectElement).value = pskType;
Expand Down
43 changes: 43 additions & 0 deletions src/loadConfigurationFromHash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// loadConfigurationFromHash.ts

import { Protobuf } from "@meshtastic/js";
import { populateForm } from './formHandler';
import { determinePskType } from "./pskHandler";
import { fromUrlSafeBase64 } from './utils';

/**
* Load the configuration from the hash and populate the form.
* @param {string} hash - The URL-safe Base64 configuration string.
*/
export function loadConfigurationFromHash(hash: string): void {
try {
const binaryData = fromUrlSafeBase64(hash);
const channelSet = Protobuf.AppOnly.ChannelSet.fromBinary(binaryData);

// Extract the channel settings from the Protobuf message
const channelSettings = channelSet.settings[0];

// Determine PSK type based on the length of the PSK
const pskType = determinePskType(channelSettings.psk.length);

const formValues = {
channelName: channelSettings.name,
pskType: pskType, // Derived from PSK length
psk: new TextDecoder().decode(channelSettings.psk),
uplinkEnabled: channelSettings.uplinkEnabled,
downlinkEnabled: channelSettings.downlinkEnabled,
positionPrecision: channelSettings.moduleSettings?.positionPrecision || 0,
isClientMuted: channelSettings.moduleSettings?.isClientMuted || false,
region: channelSet.loraConfig.region,
modemPreset: channelSet.loraConfig.modemPreset,
hopLimit: channelSet.loraConfig.hopLimit,
ignoreMqtt: channelSet.loraConfig.ignoreMqtt,
configOkToMqtt: channelSet.loraConfig.configOkToMqtt,
};

// Populate the form with these values using formHandler
populateForm(formValues);
} catch (error) {
console.error("Error loading configuration from URL hash:", error);
}
}
156 changes: 5 additions & 151 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
// main.ts

import { getFormValues, populateForm } from './formHandler';
import { generatePSK } from './pskGenerator';
import { buildProtobuf } from './protobufBuilder';
import { generateQRCode } from './qrCodeGenerator';
import { getByteLength, toUrlSafeBase64, fromUrlSafeBase64 } from './utils';
import { Protobuf } from "@meshtastic/js";
import { generateConfig } from './configHandler';
import { handlePSKTypeChange, handleGeneratePSK } from './pskHandler';
import { loadConfigurationFromHash } from './loadConfigurationFromHash';
import { copyUrlToClipboard } from './utils';

/**
* Handle the DOMContentLoaded event.
* Add event listeners to the buttons and form fields.
*/
document.addEventListener('DOMContentLoaded', () => {

// Check if there is a configuration in the URL hash and load it
const urlHash = window.location.hash.substring(1); // Remove the "#" character
if (urlHash) {
Expand All @@ -35,148 +34,3 @@ document.addEventListener('DOMContentLoaded', () => {
// Add click listener for copying the URL
document.getElementById('copyUrlButton')?.addEventListener('click', copyUrlToClipboard);
});

/**
* Load the configuration from the hash and populate the form.
* @param {string} hash - The URL-safe Base64 configuration string.
*/
function loadConfigurationFromHash(hash: string): void {
try {
const binaryData = fromUrlSafeBase64(hash);
const channelSet = Protobuf.AppOnly.ChannelSet.fromBinary(binaryData);

// Extract the channel settings from the Protobuf message
const channelSettings = channelSet.settings[0];

// Determine PSK type based on the length of the PSK
const pskLength = channelSettings.psk.length;
let pskType = 'none';
if (pskLength === 16) {
pskType = 'aes128';
} else if (pskLength === 32) {
pskType = 'aes256';
}

const formValues = {
channelName: channelSettings.name,
pskType: pskType, // Derived from PSK length
psk: new TextDecoder().decode(channelSettings.psk),
uplinkEnabled: channelSettings.uplinkEnabled,
downlinkEnabled: channelSettings.downlinkEnabled,
positionPrecision: channelSettings.moduleSettings?.positionPrecision || 0,
isClientMuted: channelSettings.moduleSettings?.isClientMuted || false,
region: channelSet.loraConfig.region,
modemPreset: channelSet.loraConfig.modemPreset,
hopLimit: channelSet.loraConfig.hopLimit,
ignoreMqtt: channelSet.loraConfig.ignoreMqtt,
configOkToMqtt: channelSet.loraConfig.configOkToMqtt,
};

// Populate the form with these values using formHandler
populateForm(formValues);
} catch (error) {
console.error("Error loading configuration from URL hash:", error);
}
}

/**
* Handle the change event on the PSK type select element.
* Enable or disable the PSK input field based on the selected PSK type.
*/
function handlePSKTypeChange(): void {
const pskType = (document.getElementById('pskType') as HTMLSelectElement).value;
const pskField = document.getElementById('psk') as HTMLInputElement;

if (pskType === 'none') {
pskField.value = '';
pskField.disabled = true;
} else {
pskField.disabled = false;
handleGeneratePSK();
}

// Re-generate config when PSK type changes
generateConfig();
}

/**
* Handle the click event on the "Generate PSK" button.
* Generate a PSK based on the selected PSK type and set the value in the input field.
*/
function handleGeneratePSK(): void {
const pskType = (document.getElementById('pskType') as HTMLSelectElement).value;
const psk = generatePSK(pskType);
(document.getElementById('psk') as HTMLInputElement).value = psk;

// Re-generate config when PSK is generated
generateConfig();
}

/**
* Generate a QR code and URL based on the form values.
*/
async function generateConfig(): Promise<void> {
const formValues = getFormValues();

// Validate channel name length
const byteLength = getByteLength(formValues.channelName);
if (byteLength > 12) {
alert(`Channel name must be less than or equal to 12 bytes (current byte length: ${byteLength}).`);
return;
}

// Generate the protobuf binary data for the form values
const channelSet = buildProtobuf(formValues);
const binaryData = channelSet.toBinary(); // Binary data from Protobuf

// Convert to URL-safe Base64 string
const base64 = toUrlSafeBase64(binaryData);

// Update the URL hash with the generated configuration
window.location.hash = `#${base64}`;

// Create the Meshtastic URL
const meshtasticUrl = `https://meshtastic.org/e/#${base64}`;
console.log("Generated Meshtastic URL:", meshtasticUrl);

// Update the generated URL in the input field
const urlField = document.getElementById('generatedUrl') as HTMLInputElement;
urlField.value = meshtasticUrl;

// Generate the QR code from the URL
await generateQRCode(meshtasticUrl);
}

/**
* Copy the generated URL to the clipboard.
* @returns void
*/
function copyUrlToClipboard(): void {
const urlField = document.getElementById('generatedUrl') as HTMLInputElement;

navigator.clipboard.writeText(urlField.value)
.then(() => {
showCopyNotification();
})
.catch(err => {
console.error('Failed to copy text: ', err);
alert('Failed to copy URL');
});
}

/**
* Show the "URL copied" notification with animation
*/
function showCopyNotification(): void {
const notification = document.getElementById('copyNotification');

if (notification) {
notification.classList.add('show');

// Hide the notification after 2 seconds
setTimeout(() => {
notification.classList.remove('show');
}, 2000);
}
}

21 changes: 0 additions & 21 deletions src/pskGenerator.ts

This file was deleted.

82 changes: 82 additions & 0 deletions src/pskHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// pskHandler.ts

import { generateConfig } from './configHandler';

/**
* Generate a PSK (Pre-Shared Key) for the given PSK type.
* @param pskType
* @returns
*/
export function generatePSK(pskType: string): string {
const pskBytes = new Uint8Array(getPskLengthFromType(pskType));

if (pskBytes.length === 0) {
return ''; // No encryption
}

window.crypto.getRandomValues(pskBytes);
return Array.from(pskBytes).map(b => ('0' + b.toString(16)).slice(-2)).join('');
}

/**
* Handle the change event on the PSK type select element.
* Enable or disable the PSK input field based on the selected PSK type.
*/
export function handlePSKTypeChange(): void {
const pskType = (document.getElementById('pskType') as HTMLSelectElement).value;
const pskField = document.getElementById('psk') as HTMLInputElement;

if (pskType === 'none') {
pskField.value = '';
pskField.disabled = true;
} else {
pskField.disabled = false;
handleGeneratePSK();
}

// Re-generate config when PSK type changes
generateConfig();
}

/**
* Handle the click event on the "Generate PSK" button.
* Generate a PSK based on the selected PSK type and set the value in the input field.
*/
export function handleGeneratePSK(): void {
const pskType = (document.getElementById('pskType') as HTMLSelectElement).value;
const psk = generatePSK(pskType);
(document.getElementById('psk') as HTMLInputElement).value = psk;

// Re-generate config when PSK is generated
generateConfig();
}

/**
* Determine the PSK type based on the PSK length.
* @param pskLength - The length of the PSK string.
* @returns {string} - 'none', 'aes128', or 'aes256'
*/
export function determinePskType(pskLength: number): string {
if (pskLength === 32) { // AES-128 (16 bytes * 2 hex chars per byte)
return 'aes128';
} else if (pskLength === 64) { // AES-256 (32 bytes * 2 hex chars per byte)
return 'aes256';
} else {
return 'none';
}
}

/**
* Get the PSK byte length based on the PSK type.
* @param {string} pskType - 'none', 'aes128', or 'aes256'
* @returns {number} - The expected byte length for the PSK
*/
export function getPskLengthFromType(pskType: string): number {
if (pskType === 'aes128') {
return 16; // AES-128: 16 bytes
} else if (pskType === 'aes256') {
return 32; // AES-256: 32 bytes
} else {
return 0; // No encryption
}
}
Loading

0 comments on commit 9de2952

Please sign in to comment.