diff --git a/public/scripts/api.js b/public/scripts/api.js index 6074991..a1b560a 100644 --- a/public/scripts/api.js +++ b/public/scripts/api.js @@ -12,11 +12,33 @@ function apiGet(url) { }); } +function apiPost(url, data) { + return fetch(`${baseUrl}/${url}`, { + method: 'POST', + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }) + .then(res => { + if (!res.ok) { + // do something here + throw new Error('Not ok'); + } + return res.json(); + }); +} + function getRecords(zone) { return apiGet(`zones/${zone}/records`); } +function createRecords(zone, record) { + return apiPost(`zones/${zone}/records`, record); +} + export { getRecords, + createRecords, }; diff --git a/public/scripts/records.js b/public/scripts/records.js index 3e65d50..aab7604 100644 --- a/public/scripts/records.js +++ b/public/scripts/records.js @@ -1,29 +1,84 @@ import { html, render, useState, useEffect } from './vendor/preact/standalone.js'; -import { getRecords } from './api.js'; +import { getRecords, createRecords } from './api.js'; const rdataInputProperties = { - Address: {label: 'adresse :', type: 'text'}, - Serial: {label: 'serial :', type: 'number'}, - Minimum: {label: 'minimum :', type: 'number'}, - Retry: {label: 'nouvelle tentative :', type: 'number'}, - Refresh: {label: 'actualisation :', type: 'number'}, - MaintainerName: {label: 'contact :', type: 'text'}, - MasterServerName: {label: 'serveur primaire :', type: 'text'}, - Expire: {label: 'expiration :', type: 'number'}, - Target: {label: 'cible :', type: 'text'}, + Address: {label: 'Adresse', type: 'text'}, + Serial: {label: 'Numéro de série', type: 'number'}, + Minimum: {label: 'Minimum', type: 'number'}, + Retry: {label: 'Nouvelle tentative', type: 'number'}, + Refresh: {label: 'Actualisation', type: 'number'}, + MaintainerName: {label: 'Contact', type: 'text'}, + MasterServerName: {label: 'Serveur primaire', type: 'text'}, + Expire: {label: 'Expiration', type: 'number'}, + Target: {label: 'Cible', type: 'text'}, }; -const recordTypes = { - 'A': 'address', - 'AAAA': 'address', - 'SRV': 'service', - 'CNAME': 'alias', - 'NS': 'name_server', - 'SOA': 'soa', +const realRecordDataConfig = { + 'A': { + friendlyType: 'address', + fields: ['Address'], + }, + 'AAAA': { + friendlyType: 'address', + fields: ['Address'], + }, + 'CNAME': { + friendlyType: 'alias', + fields: ['Target'], + }, + 'SRV': { + friendlyType: 'service', + fields: [ /* TODO */ ], + }, + 'NS': { + friendlyType: 'name_server', + fields: ['Target'], + }, + 'SOA': { + friendlyType: 'soa', + fields: ['MasterServerName', 'MaintainerName', 'Refresh', 'Retry', 'Expire', 'Minimum', 'Serial'], + }, }; +function defaultBuildData(realRecordType) { + const defaultFields = realRecordDataConfig[realRecordType].fields.map(field => [field, null]); + return (fields) => { + return {...defaultFields, ...fields, Type: realRecordType}; + } +} + +function defaultRecordToField(realRecord) { + const type = realRecord.Type; + return realRecordDataConfig[type].fields.map(field => [field, realRecord[field]]); +} + + +const friendlyRecordDataConfig = { + 'address': { + realRecordToFields: defaultRecordToField, + fields: realRecordDataConfig['AAAA'].fields, + buildData: buildAddressRecord, + }, + 'alias': { + realRecordToFields: defaultRecordToField, + fields: realRecordDataConfig['CNAME'].fields, + buildData: defaultBuildData('CNAME'), + }, + 'name_server': { + realRecordToFields: defaultRecordToField, + fields: realRecordDataConfig['CNAME'].fields, + buildData: defaultBuildData('NS'), + }, + 'soa': { + realRecordToFields: defaultRecordToField, + fields: realRecordDataConfig['SOA'].fields, + buildData: defaultBuildData('SOA'), + }, +}; + + const recordTypeNames = { 'address': 'Adresse IP', 'service': 'Service', @@ -32,18 +87,20 @@ const recordTypeNames = { 'soa': 'SOA', } +/* Type to use for SRV to derive name without port / service */ function getNameForRecord(name, type) { return name; } -function getTypeForRecord(name, type) { - return recordTypes[type]; +/* Name to use with _spf for example */ +function getFriendlyTypeForRecord(name, type) { + return realRecordDataConfig[type].friendlyType; } function processRecords(records) { return records.reduce((acc, record) => { let name = getNameForRecord(record.Name, record.Type); - let type = getTypeForRecord(record.Name, record.Type); + let type = getFriendlyTypeForRecord(record.Name, record.Type); if (!(name in acc)) { acc[name] = {}; } @@ -54,26 +111,16 @@ function processRecords(records) { return acc; }, {}); } -const recordsKeys = { - 'address': (record) => { return [[ 'Address', record.Address ]] }, - 'service': (record) => { /* TODO */ }, - 'alias': (record) => { return [[ 'Target', record.Target ]] }, - 'name_server': (record) => { return [[ 'Target', record.Target ]] }, - 'soa': (record) => { - return [ - [ 'MasterServerName', record.MasterServerName ], - [ 'MaintainerName', record.MaintainerName ], - [ 'Refresh', record.Refresh ], - [ 'Retry', record.Retry ], - [ 'Expire', record.Expire ], - [ 'Minimum', record.Minimum ], - [ 'Serial', record.Serial ], - ] - }, + +function buildAddressRecord({ Address = ''}) { + return { + Type: Address.indexOf(':') > -1 ? 'AAAA' : 'A', + Address + } } function FriendlyRecord({type, record}) { - let keys = recordsKeys[type](record); + let keys = friendlyRecordDataConfig[type].realRecordToFields(record); if (keys.length == 1) { return html`${keys[0][1]}`; } else { @@ -122,7 +169,6 @@ function RecordListFriendly({ zone }) { ${Object.entries(records).map( ([name, recordSets]) => { return html` - <${RecordsByName} name=${name} recordSets=${recordSets}/> `; } @@ -130,46 +176,96 @@ function RecordListFriendly({ zone }) { `; } -function NewRecordFormFriendly({ zone }) { +function NewRecordFormFriendly({ zone, enabled, id, onCancel }) { + const [recordType, setRecordType] = useState(Object.keys(recordTypeNames)[0]); + const [recordData, setRecordData] = useState({}); + const [realRecordData, setRealRecordData] = useState({}); + const [realType, setRealType] = useState(''); + const [domain, setDomain] = useState(''); + const [ttl, setTTL] = useState(3600); + + const setRecordDataFactory = (field) => { + return (e) => { + const newData = {...recordData}; + newData[field] = e.target.value; + const newRealRecordData = friendlyRecordDataConfig[recordType].buildData(newData) + setRecordData(newData); + setRealRecordData(newRealRecordData); + setRealType(newRealRecordData.Type); + } + } + + const createNewRecord = (e) => { + e.preventDefault(); + const newRecords = [{...realRecordData, Class: 'IN', TTL: ttl, Name: `${domain}.${zone}`}]; + console.log(newRecords) + createRecords(zone, newRecords); + } + + // TODO: Reset valeurs champs quand changement de type => bound la valeur de l'input au state + // TODO: Dans le cas où un domain est dans le RDATA mettre le domaine absolue dans la preview + // TODO: Déplacer preview dans son component, faire une vue en "diff" et l'appeler "prévisualisation des changements" + // TODO: Validation des données client et serveur + return html` -