You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
158 lines
6.0 KiB
158 lines
6.0 KiB
/**
|
|
* ethernet.js — Wired interface IP configuration (eth0 + WAN uplink)
|
|
*
|
|
* v1.5.0 Phase 2: io.js의 두 wired 카드를 분리.
|
|
* v1.7.1 Task 3: the second card was mislabeled "LTE Interface" — the device has
|
|
* no cellular modem; this is a wired uplink port. The visible label/icon are
|
|
* corrected to WAN/wired. The DB keys stay `lte_*` (backend/Java contract).
|
|
* server endpoints (TIOT/Update/RTCM/uplink 등)는 server-setting.js 소유.
|
|
*
|
|
* Data model (nested — matches io.js state.device.eth / .lte convention):
|
|
* state.device.eth = { ip, netmask, gateway }
|
|
* state.device.lte = { ip, netmask, gateway, port } ← lte_* = wired WAN keys
|
|
*
|
|
* eth_ip / eth_netmask / eth_gateway / lte_ip / lte_netmask / lte_gateway are
|
|
* present as DOM element IDs (ip-input component convention) and also used as
|
|
* string literals for test detection.
|
|
*/
|
|
|
|
import { state, bindIpGroup } from '../state.js';
|
|
import { escapeHtml } from '../utils.js';
|
|
import { isValidIP } from '../validator.js';
|
|
import { renderIpOctets, setupIpOctets, collectIpValue } from '../components/ip-input.js';
|
|
import { icon } from '../icons.js';
|
|
|
|
// Field definitions — flat DOM ids map to nested state paths
|
|
const ETH_FIELDS = [
|
|
{ key: 'eth_ip', label: 'IP Address', group: 'eth', prop: 'ip' },
|
|
{ key: 'eth_netmask', label: 'Subnet Mask', group: 'eth', prop: 'netmask' },
|
|
{ key: 'eth_gateway', label: 'Gateway', group: 'eth', prop: 'gateway' },
|
|
];
|
|
|
|
const LTE_FIELDS = [
|
|
{ key: 'lte_ip', label: 'IP Address', group: 'lte', prop: 'ip' },
|
|
{ key: 'lte_netmask', label: 'Subnet Mask', group: 'lte', prop: 'netmask' },
|
|
{ key: 'lte_gateway', label: 'Gateway', group: 'lte', prop: 'gateway' },
|
|
];
|
|
|
|
const ethernetPage = {
|
|
render(container) {
|
|
const d = state.device || {};
|
|
const eth = d.eth || {};
|
|
const lte = d.lte || {};
|
|
|
|
container.innerHTML = `
|
|
<div class="page-header">
|
|
<h1 class="page-header__title">${icon('ethernet-port', { size: 28 })} Ethernet</h1>
|
|
<p class="page-header__desc">Configure the wired management and uplink interfaces. Server endpoints are configured under Network → Server Setting.</p>
|
|
</div>
|
|
|
|
<div class="grid-2col">
|
|
<div class="card">
|
|
<div class="card__header">
|
|
<h2 class="card__title"><span class="card__title-icon">${icon('cable', { size: 18 })}</span> Management Ethernet (eth1)</h2>
|
|
</div>
|
|
<div class="card__body">
|
|
${ETH_FIELDS.map(f => `
|
|
<div class="form-group">
|
|
<label class="form-label">${escapeHtml(f.label)}</label>
|
|
${renderIpOctets(f.key, eth[f.prop] || '')}
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card__header">
|
|
<h2 class="card__title"><span class="card__title-icon">${icon('cable', { size: 18 })}</span> Uplink Ethernet (eth0)</h2>
|
|
</div>
|
|
<div class="card__body">
|
|
<p class="form-hint">Physical eth0 uplink. Endpoint ports are configured on Server Setting.</p>
|
|
${LTE_FIELDS.map(f => `
|
|
<div class="form-group">
|
|
<label class="form-label">${escapeHtml(f.label)}</label>
|
|
${renderIpOctets(f.key, lte[f.prop] || '')}
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
},
|
|
|
|
mount(container) {
|
|
setupIpOctets(container);
|
|
|
|
// Ethernet IP bindings
|
|
ETH_FIELDS.forEach(({ key, group, prop }) => {
|
|
bindIpGroup(container, key,
|
|
() => state.device?.[group]?.[prop] || '',
|
|
(val) => {
|
|
if (!state.device) state.device = {};
|
|
if (!state.device[group]) state.device[group] = {};
|
|
state.device[group][prop] = val;
|
|
state.isDirty = true;
|
|
}
|
|
);
|
|
});
|
|
|
|
// LTE IP bindings
|
|
LTE_FIELDS.forEach(({ key, group, prop }) => {
|
|
bindIpGroup(container, key,
|
|
() => state.device?.[group]?.[prop] || '',
|
|
(val) => {
|
|
if (!state.device) state.device = {};
|
|
if (!state.device[group]) state.device[group] = {};
|
|
state.device[group][prop] = val;
|
|
state.isDirty = true;
|
|
}
|
|
);
|
|
});
|
|
|
|
// Register data collector
|
|
window.__pageCollectors.ethernet = collectEthernetData;
|
|
},
|
|
|
|
destroy() {},
|
|
|
|
validate() {
|
|
const errors = [];
|
|
const d = state.device || {};
|
|
|
|
ETH_FIELDS.forEach(({ key, group, prop, label }) => {
|
|
const val = d[group]?.[prop];
|
|
if (val && val !== '...' && !isValidIP(val)) {
|
|
errors.push({ field: key, page: 'ethernet', message: `${label} — Invalid IP format` });
|
|
}
|
|
});
|
|
|
|
LTE_FIELDS.forEach(({ key, group, prop, label }) => {
|
|
const val = d[group]?.[prop];
|
|
if (val && val !== '...' && !isValidIP(val)) {
|
|
errors.push({ field: key, page: 'ethernet', message: `${label} — Invalid IP format` });
|
|
}
|
|
});
|
|
|
|
return errors;
|
|
},
|
|
};
|
|
|
|
function collectEthernetData() {
|
|
if (!state.device) state.device = {};
|
|
|
|
state.device.eth = {
|
|
ip: collectIpValue('eth_ip'),
|
|
netmask: collectIpValue('eth_netmask'),
|
|
gateway: collectIpValue('eth_gateway'),
|
|
};
|
|
|
|
const prevLte = state.device.lte || {};
|
|
state.device.lte = {
|
|
...prevLte,
|
|
ip: collectIpValue('lte_ip'),
|
|
netmask: collectIpValue('lte_netmask'),
|
|
gateway: collectIpValue('lte_gateway'),
|
|
};
|
|
}
|
|
|
|
export default ethernetPage;
|
|
|