extra field in update handler, frontend refactor

This commit is contained in:
tavo 2024-09-20 23:22:23 -06:00
parent b9d1620646
commit 57c7c15ec5
6 changed files with 312 additions and 62 deletions

View file

@ -21,9 +21,12 @@ document.addEventListener("DOMContentLoaded", function () {
const checkoutDialog = document.getElementById("checkoutDialog"); const checkoutDialog = document.getElementById("checkoutDialog");
const editDialog = document.getElementById("editDialog"); const editDialog = document.getElementById("editDialog");
const buyDialog = document.getElementById("buyDialog"); const buyDialog = document.getElementById("buyDialog");
const updateContentDialog = document.getElementById("updateContentDialog");
checkoutDialog.style.display = "none"; checkoutDialog.style.display = "none";
editDialog.style.display = "none"; editDialog.style.display = "none";
buyDialog.style.display = "none";
updateContentDialog.style.display = "none";
loadLanguage('es'); loadLanguage('es');
Promise.all(EditorJSComponents.map(src => loadScript(src))).then(() => { Promise.all(EditorJSComponents.map(src => loadScript(src))).then(() => {
@ -58,7 +61,7 @@ function loadEditorState() {
if (savedData) { if (savedData) {
const parsedData = JSON.parse(savedData); const parsedData = JSON.parse(savedData);
console.log('Loaded parsedData:', parsedData); console.log('Loaded parsedData:', parsedData);
document.getElementById('title').value = parsedData.title || ''; document.getElementById('title').innerText = parsedData.title || '';
document.getElementById('slogan').value = parsedData.slogan || ''; document.getElementById('slogan').value = parsedData.slogan || '';
document.getElementById('banner').src = parsedData.banner || '/static/svg/banner.svg'; document.getElementById('banner').src = parsedData.banner || '/static/svg/banner.svg';
const disclaimers = document.querySelectorAll(".localstorage-exists"); const disclaimers = document.querySelectorAll(".localstorage-exists");
@ -82,7 +85,7 @@ function loadEditorState() {
}) })
.catch(error => console.error('Error loading translation file:', error)); .catch(error => console.error('Error loading translation file:', error));
} else { } else {
document.getElementById('title').value = ''; document.getElementById('title').innerText = '';
document.getElementById('slogan').value = ''; document.getElementById('slogan').value = '';
document.getElementById('banner').src = '/static/svg/banner.svg'; document.getElementById('banner').src = '/static/svg/banner.svg';
document.getElementById("continueEditingModeButton").style.display = "none"; document.getElementById("continueEditingModeButton").style.display = "none";
@ -104,28 +107,73 @@ function initializeEventListeners() {
}); });
document.getElementById("buyButton").addEventListener("click", () => openDialog(checkoutDialog)); document.getElementById("buyButton").addEventListener("click", () => openDialog(checkoutDialog));
document.getElementById("editButton").addEventListener("click", () => openDialog(updateContentDialog));
document.getElementById("closeDialogButton").addEventListener("click", () => closeDialog()); document.getElementById("closeDialogButton").addEventListener("click", () => closeDialog());
// // Editor save // // Editor save
document.getElementById('title').addEventListener('change', saveEditorData); // document.getElementById('title').addEventListener('change', saveEditorData);
document.getElementById('slogan').addEventListener('change', saveEditorData); document.getElementById('slogan').addEventListener('change', saveEditorData);
// // Mode switching // // Mode switching
// document.getElementById('buyModeButton').addEventListener('click', openBuyModeDialog); document.getElementById('buyModeButton').addEventListener('click', openBuyModeDialog);
document.getElementById('buyModeButton').addEventListener('click', buyMode);
document.getElementById('editModeButton').addEventListener('click', openEditModeDialog); document.getElementById('editModeButton').addEventListener('click', openEditModeDialog);
document.getElementById("continueToBuyModeButton").addEventListener('click', async () => {
const directory = sanitizeDirectoryTitle(document.getElementById("buyModeDirectoryInput").value);
const exists = await checkDirectoryExists(directory);
if (exists) {
document.getElementById("checkdir-error-message").style.display = "block";
document.getElementById("checkdir-error-message").innerHTML = `El sitio https://conex.one/${directory} ya existe.`;
} else {
document.getElementById("checkdir-error-message").style.display = "none";
buyMode(directory);
}
});
document.getElementById("continueToEditModeButton").addEventListener('click', () => document.getElementById("continueToEditModeButton").addEventListener('click', () =>
editMode(document.getElementById("editModeDirectoryInput").value) editMode(extractSitePath(document.getElementById("editModeDirectoryInput").value))
); );
document.getElementById('continueEditingModeButton').addEventListener('click', continueMode); document.getElementById('continueEditingModeButton').addEventListener('click', continueMode);
document.getElementById('dashButton').addEventListener('click', dashboardMode); document.getElementById('dashButton').addEventListener('click', dashboardMode);
document.getElementById('uploadBannerBtn').addEventListener('change', handleImageUpload); document.getElementById('uploadBannerBtn').addEventListener('change', handleImageUpload);
const titleElement = document.getElementById('title'); document.getElementById('requestChangesButton').addEventListener('click', async () => {
if (titleElement) { const button = document.getElementById('requestChangesButton');
setupDirectoryInput(titleElement); button.disabled = true;
} button.classList.add('disabled');
await updateSiteRequest();
setTimeout(() => {
button.disabled = false;
button.classList.remove('disabled');
}, 3000);
});
document.getElementById('confirmChangesButton').addEventListener('click', function() {
successElement = document.getElementById('update-success-message');
errorElement = document.getElementById('update-error-message');
const codeInput = document.getElementById('updateContentCodeInput').value;
if (codeInput.length === 6 && !isNaN(codeInput)) {
document.getElementById('updateContentCodeInput').value = '';
errorElement.style.display = "none"
updateSiteConfirm(codeInput);
} else {
successElement.style.display = "none"
errorElement.style.display = "block"
errorElement.innerHTML = "El código es un pin numérico de 6 dígitos.";
console.error('Invalid code. Please enter a 6-digit number.');
}
});
const titleElement = document.getElementById('buyModeDirectoryInput');
titleElement.addEventListener('input', debounce(function() {
const directory = titleElement.value.trim();
if (directory.length > 0) {
validateDirectory(directory);
}
}, 500)); // 500ms debounce
} }
function openDialog(content) { function openDialog(content) {
@ -135,21 +183,31 @@ function openDialog(content) {
floatingButtons.style.display = "none"; floatingButtons.style.display = "none";
} }
function debounce(func, delay) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), delay);
};
}
function closeDialog() { function closeDialog() {
checkoutDialog.style.display = "none"; checkoutDialog.style.display = "none";
editDialog.style.display = "none"; editDialog.style.display = "none";
// buyDialog.style.display = "none"; buyDialog.style.display = "none";
updateContentDialog.style.display = "none";
dialog.style.display = "none"; dialog.style.display = "none";
overlay.style.display = "none"; overlay.style.display = "none";
floatingButtons.style.display = "flex"; floatingButtons.style.display = "flex";
document.getElementById('checkout-error-message').style.display = "none"; document.getElementById('checkout-error-message').style.display = "none";
document.getElementById('update-error-message').style.display = "none";
} }
function saveEditorData() { function saveEditorData() {
const titleValue = document.getElementById('title').value.trim(); const titleValue = document.getElementById('title').innerText.trim();
const dataToSave = { const dataToSave = {
banner: document.getElementById('banner').src || '/static/svg/banner.svg', banner: document.getElementById('banner').src || '/static/svg/banner.svg',
title: document.getElementById('title').value, title: document.getElementById('title').innerText,
slogan: document.getElementById('slogan').value, slogan: document.getElementById('slogan').value,
directory: sanitizeDirectoryTitle(titleValue) directory: sanitizeDirectoryTitle(titleValue)
}; };
@ -162,21 +220,37 @@ function saveEditorData() {
}); });
} }
function setupDirectoryInput(inputElement, debounceTime = 500) { function validateDirectory(directory) {
inputElement.addEventListener('input', () => { successMessageElement = document.getElementById('checkdir-success-message');
clearTimeout(typingTimeout); errorMessageElement = document.getElementById('checkdir-error-message');
successMessageElement.textContent = '';
errorMessageElement.textContent = '';
typingTimeout = setTimeout(() => { if (!validateDirectoryLength(directory)) {
const inputValue = inputElement.value.trim(); successMessageElement.style.display = "none";
errorMessageElement.style.display = "block";
errorMessageElement.textContent = 'Directory name must be between 4 and 35 characters.';
return;
}
if (inputValue.length > 0) { directory = sanitizeDirectoryTitle(directory)
const sanitizedValue = sanitizeDirectoryTitle(inputValue); // Sanitize the input value checkDirectoryExists(directory)
checkDirectory(sanitizedValue); .then(exists => {
if (exists) {
successMessageElement.style.display = "none";
errorMessageElement.style.display = "block";
errorMessageElement.textContent = `El sitio https://conex.one/${directory} ya existe.`;
} else { } else {
hidePopup(); successMessageElement.style.display = "block";
errorMessageElement.style.display = "none";
successMessageElement.textContent = `Se publicará en https://conex.one/${directory}`;
} }
}, debounceTime); })
}); .catch(() => {
successMessageElement.style.display = "none";
errorMessageElement.style.display = "block";
errorMessageElement.textContent = 'Error occurred while checking the directory.';
});
} }
function sanitizeDirectoryTitle(title) { function sanitizeDirectoryTitle(title) {
@ -188,11 +262,6 @@ function sanitizeDirectoryTitle(title) {
.replace(/[^a-z0-9\-]/g, ''); .replace(/[^a-z0-9\-]/g, '');
} }
function checkDirectory(directory) {
if (!validateDirectoryLength(directory)) return;
fetchDirectoryStatus(directory, 'exists', 'available', 'El sitio web ya existe', 'Se publicará en');
}
function validateDirectoryLength(directory) { function validateDirectoryLength(directory) {
if (directory.length < 4 || directory.length > 35) { if (directory.length < 4 || directory.length > 35) {
showPopup('El título debe tener entre 4 y 35 caracteres', 'exists'); showPopup('El título debe tener entre 4 y 35 caracteres', 'exists');
@ -201,14 +270,6 @@ function validateDirectoryLength(directory) {
return true; return true;
} }
function fetchDirectoryStatus(directory, failureStatus, successStatus, failureMessage, successMessage) {
checkDirectoryExists(directory).then(exists => {
const message = exists ? `${failureMessage} conex.one/${directory}` : `${successMessage} conex.one/${directory}`;
const status = exists ? failureStatus : successStatus;
showPopup(message, status);
});
}
function checkDirectoryExists(directory) { function checkDirectoryExists(directory) {
return fetch(`/api/directory/${encodeURIComponent(directory)}`) return fetch(`/api/directory/${encodeURIComponent(directory)}`)
.then(response => response.json()) .then(response => response.json())
@ -331,8 +392,14 @@ function dashboardMode() {
function buyMode() { function buyMode() {
localStorage.removeItem('conex_data'); localStorage.removeItem('conex_data');
const dataToSave = {
title: document.getElementById('buyModeDirectoryInput').value.trim(),
};
localStorage.setItem('conex_data', JSON.stringify(dataToSave));
loadEditorState(); loadEditorState();
closeDialog();
document.getElementById('checkout-success-message').style.display = "none"; document.getElementById('checkout-success-message').style.display = "none";
document.querySelector("#paypal-button-container").style.display = "block"; document.querySelector("#paypal-button-container").style.display = "block";
document.getElementById("buyButton").style.display = "block"; document.getElementById("buyButton").style.display = "block";
@ -380,6 +447,8 @@ function continueBuyMode() {
} }
function openBuyModeDialog() { function openBuyModeDialog() {
document.getElementById("checkdir-error-message").style.display = "none";
document.getElementById("checkdir-success-message").style.display = "none";
overlay.style.display = "block"; overlay.style.display = "block";
dialog.style.display = "block"; dialog.style.display = "block";
buyDialog.style.display = "block"; buyDialog.style.display = "block";
@ -392,13 +461,23 @@ function openEditModeDialog() {
} }
async function editMode(dir) { async function editMode(dir) {
closeDialog(); const errorMessageElement = document.getElementById('edit-error-message');
const success = await fetchAndStoreData(dir); const conexData = JSON.parse(localStorage.getItem('conex_data'));
if (!success) {
console.error("Data could not be loaded, aborting UI changes"); if (conexData?.directory === dir) {
return; console.log("Directory already loaded, skipping fetch.");
} else {
const success = await fetchAndStoreData(dir);
if (!success) {
errorMessageElement.innerHTML = "No se pudo cargar el sitio, asegúrate que estás digitando el enlace correcto.";
errorMessageElement.style.display = "block";
console.error("Data could not be loaded, aborting UI changes");
return;
}
} }
closeDialog();
errorMessageElement.style.display = "none";
document.getElementById("buyButton").style.display = "none"; document.getElementById("buyButton").style.display = "none";
document.getElementById("editButton").style.display = "block"; document.getElementById("editButton").style.display = "block";
const dashboard = document.getElementById('dashboard'); const dashboard = document.getElementById('dashboard');
@ -428,3 +507,77 @@ async function fetchAndStoreData(directoryName) {
return false; return false;
} }
} }
function updateSiteRequest() {
const conexData = JSON.parse(localStorage.getItem('conex_data'));
const directory = conexData?.directory;
successElement = document.getElementById('update-success-message');
errorElement = document.getElementById('update-error-message');
fetch('/api/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ directory: directory })
})
.then(response => {
if (response.status === 200) {
successElement.style.display = "block"
errorElement.style.display = "none"
successElement.innerHTML = "Se envió el código de autenticación de 6 dígitos a su correo electrónico.";
} else {
successElement.style.display = "none"
errorElement.style.display = "block"
errorElement.innerHTML = "Error enviando el código de confirmación a su correo, recuerde que puede solicitar el código solamente una vez cada minuto.";
}
})
}
function updateSiteConfirm(code) {
const conexData = JSON.parse(localStorage.getItem('conex_data'));
const directory = conexData?.directory;
const editorData = conexData?.editor_data;
const slogan = conexData?.slogan;
successElement = document.getElementById('update-success-message');
errorElement = document.getElementById('update-error-message');
if (!directory || !editorData) {
console.error('Directory or editor_data not found in localStorage');
return;
}
fetch('/api/confirm', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
directory: directory,
auth_code: code,
slogan: slogan,
editor_data: editorData
})
})
.then(response => {
if (response.status === 200) {
successElement.style.display = "block"
errorElement.style.display = "none"
successElement.innerHTML = "Se actualizó correctamente la información de su sitio, los cambios deberían verse reflejados en menos de 24 horas.";
} else {
successElement.style.display = "none"
errorElement.style.display = "block"
errorElement.innerHTML = "Error actualizando su sitio, por favor vuelva a intentarlo más tarde.";
}
})
}
function extractSitePath(url) {
if (!url.includes("conex.one")) {
return url;
}
const cleanUrl = url.replace(/^(https?:\/\/)?(www\.)?/, '').replace(/#.*$/, '');
const match = cleanUrl.match(/^conex\.one\/([^\/?#]+)\/?/);
return match ? match[1] : null;
}

View file

@ -10,10 +10,6 @@
<link rel="stylesheet" href="/static/css/style.css"> <link rel="stylesheet" href="/static/css/style.css">
<link rel="stylesheet" href="/static/css/editorjs-dark.css"> <link rel="stylesheet" href="/static/css/editorjs-dark.css">
</head> </head>
<div id="status-popup" class="status-popup">
<span class="close-popup" onclick="hidePopup()">×</span>
<span id="status-message"></span>
</div>
<div id="overlay"></div> <div id="overlay"></div>
<div id="dashboard"> <div id="dashboard">
<div class="wave"></div> <div class="wave"></div>
@ -45,13 +41,21 @@
</div> </div>
<img id="banner" src="/static/svg/banner.svg" class="banner-image"/> <img id="banner" src="/static/svg/banner.svg" class="banner-image"/>
<div class="desc"> <div class="desc">
<input type="text" id="title" maxlength="35" class="input-title" data-translate="sampleTitle" placeholder="" > <h1 id="title" class="input-title"></h1>
<textarea type="text" id="slogan" maxlength="100" class="input-slogan" data-translate="sampleSlogan" placeholder=""></textarea> <textarea type="text" id="slogan" maxlength="100" class="input-slogan" data-translate="sampleSlogan" placeholder=""></textarea>
</div> </div>
</div> </div>
<body> <body>
<div id="editorjs"></div> <div id="editorjs"></div>
</body> </body>
<footer class="footer">
<div class="footer-content">
<ul class="footer-links">
<li><a href="#contact">Contactar con soporte</a></li>
</ul>
<p data-translate="footer"></p>
</div>
</footer>
<div class="floating-buttons-container" id="floatingButtons"> <div class="floating-buttons-container" id="floatingButtons">
<button class="floating-button" id="buyButton"> <button class="floating-button" id="buyButton">
<img src="/static/svg/check.svg"> <img src="/static/svg/check.svg">
@ -86,9 +90,29 @@
<div class="message error-message" id="checkout-error-message"></div> <div class="message error-message" id="checkout-error-message"></div>
<div id="paypal-button-container"></div> <div id="paypal-button-container"></div>
</div> </div>
<div id="buyDialog">
<h2 data-translate="buyDialogHeader"></h2>
<p data-translate="buyDialogParagraph"></p>
<div class="message success-message" id="checkdir-success-message"></div>
<div class="message error-message" id="checkdir-error-message"></div>
<input class="input-dialog" type="text" id="buyModeDirectoryInput" maxlength="35" placeholder="Mi Sitio"><br>
<button id="continueToBuyModeButton" class="right">
<span data-translate="continueToBuyModeButton"></span>
</button>
</div>
<div id="updateContentDialog">
<h2 data-translate="updateContentDialogHeader"></h2>
<p data-translate="updateContentDialogParagraph"></p>
<div class="message success-message" id="update-success-message"></div>
<div class="message error-message" id="update-error-message"></div>
<input data-translate="tempCodePlaceholder" class="input-dialog" type="number" id="updateContentCodeInput" min="0" max="999999" placeholder=""><br>
<button data-translate="requestChangesButton" id="requestChangesButton"></button>
<button data-translate="confirmChangesButton" id="confirmChangesButton"></button>
</div>
<div id="editDialog"> <div id="editDialog">
<h2 data-translate="editDialogHeader"></h2> <h2 data-translate="editDialogHeader"></h2>
<p data-translate="editDialogParagraph"></p> <p data-translate="editDialogParagraph"></p>
<div class="message error-message" id="edit-error-message"></div>
<input class="input-dialog" type="text" id="editModeDirectoryInput" name="title" maxlength="35" placeholder="https://conex.one/mi-sitio"><br> <input class="input-dialog" type="text" id="editModeDirectoryInput" name="title" maxlength="35" placeholder="https://conex.one/mi-sitio"><br>
<button id="continueToEditModeButton" class="right"> <button id="continueToEditModeButton" class="right">
<span data-translate="continueToEditModeButton"></span> <span data-translate="continueToEditModeButton"></span>

View file

@ -6,6 +6,14 @@
"editModeButton": "Editar sitio existente", "editModeButton": "Editar sitio existente",
"editModeButtonDisclaimer": "Limpiará el progreso actual para cargar un sitio existente", "editModeButtonDisclaimer": "Limpiará el progreso actual para cargar un sitio existente",
"continueEditingModeButton": "Continuar editando", "continueEditingModeButton": "Continuar editando",
"buyDialogHeader": "Diseñar un nuevo sitio web",
"buyDialogParagraph": "Ingresa el nombre del sitio y revisa el enlace en el que se publicará una vez adquirido.",
"updateContentDialogHeader": "Actualizar mi sitio",
"updateContentDialogParagraph": "Para actualizar el sitio web, requerimos confirmar que el sitio es suyo. Para esto, enviaremos un correo electrónico a la cuenta de correo con la que compró este sitio web, con un código temporal de 6 dígitos que expirará en 5 minutos luego de ser enviado. Por favor, presione el botón para enviar el código y luego compruebe su identidad digitándolo en la casilla.",
"tempCodePlaceholder": "Código de 6 dígitos enviado a su correo",
"requestChangesButton": "Enviar correo con el pin",
"confirmChangesButton": "Verificar pin y actualizar sitio",
"continueToBuyModeButton": "Empezar a diseñar",
"editDialogHeader": "Editar un sitio existente", "editDialogHeader": "Editar un sitio existente",
"editDialogParagraph": "El sitio ya debe estar registrado en conex.one para poder cargarlo localmente.", "editDialogParagraph": "El sitio ya debe estar registrado en conex.one para poder cargarlo localmente.",
"continueToEditModeButton": "Editar este sitio", "continueToEditModeButton": "Editar este sitio",
@ -18,7 +26,7 @@
"supportEmail": "Correo electrónico: soporte@conex.one", "supportEmail": "Correo electrónico: soporte@conex.one",
"supportPhone": "", "supportPhone": "",
"supportSchedule": "Horario de atención: L-V: 9:00 a.m. - 6:00 p.m.", "supportSchedule": "Horario de atención: L-V: 9:00 a.m. - 6:00 p.m.",
"sampleTitle": "[Título de ejemplo]", "sampleTitle": "Mi Sitio",
"sampleSlogan": "[Slogan o breve descripción]", "sampleSlogan": "[Slogan o breve descripción]",
"sampleHeader": "Acerca de [Empres]", "sampleHeader": "Acerca de [Empres]",
"sampleParagraph": "En [Nombre de Tu Empresa], nos dedicamos a ofrecer [tu servicio/producto] de la más alta calidad con un servicio al cliente excepcional. Nuestro equipo de expertos se asegura de que cada aspecto de tu experiencia sea manejado con profesionalismo y cuidado.", "sampleParagraph": "En [Nombre de Tu Empresa], nos dedicamos a ofrecer [tu servicio/producto] de la más alta calidad con un servicio al cliente excepcional. Nuestro equipo de expertos se asegura de que cada aspecto de tu experiencia sea manejado con profesionalismo y cuidado.",
@ -33,5 +41,6 @@
"sampleTable23": "1000", "sampleTable23": "1000",
"sampleTable31": "Laminado", "sampleTable31": "Laminado",
"sampleTable32": "Breve descripción", "sampleTable32": "Breve descripción",
"sampleTable33": "2000" "sampleTable33": "2000",
"footer": "Copyright (C) 2024 Conex"
} }

View file

@ -262,7 +262,7 @@ a {
#continueEditingModeButton { #continueEditingModeButton {
display: none; display: none;
background-image: linear-gradient(45deg, #4a3b72, #4a3b72); background-image: linear-gradient(45deg, #4a3b72, #8a6cdf);
} }
@keyframes gradient { @keyframes gradient {
@ -352,18 +352,14 @@ a {
#dialog button { #dialog button {
margin: 0.5em 0.5em 0 0; margin: 0.5em 0.5em 0 0;
padding: 1em;
}
#dialog button {
padding: 0.5em; padding: 0.5em;
color: var(--unemph-color); color: var(--unemph-color);
background: var(--background-color); background: var(--background-color);
border: 1px solid var(--hover-border); border: 1px solid var(--hover-border);
border-radius: 10px; border-radius: 10px;
position: absolute; position: absolute;
right: 1em; right: 1.3em;
bottom: 1em; bottom: 1.5em;
} }
#dialog button:hover { #dialog button:hover {
@ -382,6 +378,15 @@ a {
font-size: 0.9em; font-size: 0.9em;
} }
#requestChangesButton,
#confirmChangesButton {
width: 45%;
}
#requestChangesButton {
left: 1.78em;
}
.input-dialog { .input-dialog {
color: var(--color); color: var(--color);
border-width: 0; border-width: 0;
@ -472,3 +477,47 @@ a {
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
.disabled {
background-color: var(--hover-background);
color: var(--unemph-color);
cursor: not-allowed;
pointer-events: none;
}
.footer {
background-color: var(--hover-background);
color: var(--unemph-color);
padding: 0;
text-align: center;
}
.footer-content {
margin: 0 auto;
}
.footer p {
margin: 0.5em;
font-size: 0.9em;
}
.footer-links {
list-style: none;
padding: 0;
margin: 0.5em 0 0;
}
.footer-links li {
display: inline;
margin: 1em 1em;
}
.footer-links a {
font-size: 0.9em;
color: var(--hyper-color);
text-decoration: none;
}
.footer-links a:hover {
text-decoration: underline;
}

View file

@ -119,10 +119,12 @@ func RegisterSitePayment(db *sql.DB, capture Capture, cart ConexData) error {
return nil return nil
} }
func UpdateSite(db *sql.DB, pkey int, editorData json.RawMessage) error { func UpdateSite(db *sql.DB, pkey int, editorData json.RawMessage, slogan string) error {
if _, err := db.Exec(` if _, err := db.Exec(`
UPDATE sites SET raw = $1 WHERE id = $2 UPDATE sites
`, editorData, pkey); err != nil { SET raw = $1, slogan = $2, status = 'diff'
WHERE id = $3
`, editorData, slogan, pkey); err != nil {
return fmt.Errorf("%s: %v", errDBUpdateRaw, err) return fmt.Errorf("%s: %v", errDBUpdateRaw, err)
} }
@ -130,15 +132,27 @@ func UpdateSite(db *sql.DB, pkey int, editorData json.RawMessage) error {
} }
func UpdateSiteAuth(db *sql.DB, folder string, code string) (string, error) { func UpdateSiteAuth(db *sql.DB, folder string, code string) (string, error) {
valid := time.Now().Add(5 * time.Minute) var valid sql.NullTime
if err := db.QueryRow(`
SELECT valid
FROM sites
WHERE folder = $1
`, folder).Scan(&valid); err != nil {
return "", fmt.Errorf("error fetching valid timestamp: %v", err)
}
if valid.Valid && valid.Time.After(time.Now().Add(4*time.Minute)) {
return "", fmt.Errorf("valid timestamp is still active, cannot update")
}
newValid := time.Now().Add(5 * time.Minute)
var email string var email string
if err := db.QueryRow(` if err := db.QueryRow(`
UPDATE sites UPDATE sites
SET auth = $1, valid = $2 SET auth = $1, valid = $2
WHERE folder = $3 WHERE folder = $3
RETURNING email; RETURNING email;
`, code, valid, folder).Scan(&email); err != nil { `, code, newValid, folder).Scan(&email); err != nil {
return "", fmt.Errorf("%s: %v", errDBUpdateSiteAuth, err) return "", fmt.Errorf("%s: %v", errDBUpdateSiteAuth, err)
} }

View file

@ -284,6 +284,7 @@ func ConfirmChangesHandler(db *sql.DB) http.HandlerFunc {
Directory string `json:"directory"` Directory string `json:"directory"`
Code string `json:"auth_code"` Code string `json:"auth_code"`
EditorData json.RawMessage `json:"editor_data"` EditorData json.RawMessage `json:"editor_data"`
Slogan string `json:"slogan"`
} }
if err := json.NewDecoder(r.Body).Decode(&cart); err != nil { if err := json.NewDecoder(r.Body).Decode(&cart); err != nil {
httpErrorAndLog(w, err, errReadBody, errClientNotice) httpErrorAndLog(w, err, errReadBody, errClientNotice)
@ -296,7 +297,7 @@ func ConfirmChangesHandler(db *sql.DB) http.HandlerFunc {
return return
} }
if err := UpdateSite(db, pkey, cart.EditorData); err != nil { if err := UpdateSite(db, pkey, cart.EditorData, cart.Slogan); err != nil {
httpErrorAndLog(w, err, errUpdateSite, errClientNotice) httpErrorAndLog(w, err, errUpdateSite, errClientNotice)
return return
} }