mirror of
https://github.com/vitodeploy/vito.git
synced 2025-07-01 22:16:15 +00:00
Add endpoint for reading & updating site deployment script (#562)
* feat(api): Add endpoint for updating site deployment script - Add PUT /api/projects/{project}/servers/{server}/sites/{site}/deployment-script endpoint - Add feature tests for successful and failed updates * added reading deployment script as well * generate docs --------- Co-authored-by: Saeed Vaziry <mr.saeedvaziry@gmail.com>
This commit is contained in:
289
public/api-docs/js/tryitout-4.40.0.js
Normal file
289
public/api-docs/js/tryitout-4.40.0.js
Normal file
@ -0,0 +1,289 @@
|
||||
window.abortControllers = {};
|
||||
|
||||
function cacheAuthValue() {
|
||||
// Whenever the auth header is set for one endpoint, cache it for the others
|
||||
window.lastAuthValue = '';
|
||||
let authInputs = document.querySelectorAll(`.auth-value`)
|
||||
authInputs.forEach(el => {
|
||||
el.addEventListener('input', (event) => {
|
||||
window.lastAuthValue = event.target.value;
|
||||
authInputs.forEach(otherInput => {
|
||||
if (otherInput === el) return;
|
||||
// Don't block the main thread
|
||||
setTimeout(() => {
|
||||
otherInput.value = window.lastAuthValue;
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', cacheAuthValue);
|
||||
|
||||
function getCookie(name) {
|
||||
if (!document.cookie) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const cookies = document.cookie.split(';')
|
||||
.map(c => c.trim())
|
||||
.filter(c => c.startsWith(name + '='));
|
||||
|
||||
if (cookies.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return decodeURIComponent(cookies[0].split('=')[1]);
|
||||
}
|
||||
|
||||
function tryItOut(endpointId) {
|
||||
document.querySelector(`#btn-tryout-${endpointId}`).hidden = true;
|
||||
document.querySelector(`#btn-canceltryout-${endpointId}`).hidden = false;
|
||||
const executeBtn = document.querySelector(`#btn-executetryout-${endpointId}`).hidden = false;
|
||||
executeBtn.disabled = false;
|
||||
|
||||
// Show all input fields
|
||||
document.querySelectorAll(`input[data-endpoint=${endpointId}],label[data-endpoint=${endpointId}]`)
|
||||
.forEach(el => el.style.display = 'block');
|
||||
|
||||
if (document.querySelector(`#form-${endpointId}`).dataset.authed === "1") {
|
||||
const authElement = document.querySelector(`#auth-${endpointId}`);
|
||||
authElement && (authElement.hidden = false);
|
||||
}
|
||||
// Expand all nested fields
|
||||
document.querySelectorAll(`#form-${endpointId} details`)
|
||||
.forEach(el => el.open = true);
|
||||
}
|
||||
|
||||
function cancelTryOut(endpointId) {
|
||||
if (window.abortControllers[endpointId]) {
|
||||
window.abortControllers[endpointId].abort();
|
||||
delete window.abortControllers[endpointId];
|
||||
}
|
||||
|
||||
document.querySelector(`#btn-tryout-${endpointId}`).hidden = false;
|
||||
const executeBtn = document.querySelector(`#btn-executetryout-${endpointId}`);
|
||||
executeBtn.hidden = true;
|
||||
executeBtn.textContent = executeBtn.dataset.initialText;
|
||||
document.querySelector(`#btn-canceltryout-${endpointId}`).hidden = true;
|
||||
// Hide inputs
|
||||
document.querySelectorAll(`input[data-endpoint=${endpointId}],label[data-endpoint=${endpointId}]`)
|
||||
.forEach(el => el.style.display = 'none');
|
||||
document.querySelectorAll(`#form-${endpointId} details`)
|
||||
.forEach(el => el.open = false);
|
||||
const authElement = document.querySelector(`#auth-${endpointId}`);
|
||||
authElement && (authElement.hidden = true);
|
||||
|
||||
document.querySelector('#execution-results-' + endpointId).hidden = true;
|
||||
document.querySelector('#execution-error-' + endpointId).hidden = true;
|
||||
|
||||
// Revert to sample code blocks
|
||||
document.querySelector('#example-requests-' + endpointId).hidden = false;
|
||||
document.querySelector('#example-responses-' + endpointId).hidden = false;
|
||||
}
|
||||
|
||||
function makeAPICall(method, path, body = {}, query = {}, headers = {}, endpointId = null) {
|
||||
console.log({endpointId, path, body, query, headers});
|
||||
|
||||
if (!(body instanceof FormData) && typeof body !== "string") {
|
||||
body = JSON.stringify(body)
|
||||
}
|
||||
|
||||
const url = new URL(window.tryItOutBaseUrl + '/' + path.replace(/^\//, ''));
|
||||
|
||||
// We need this function because if you try to set an array or object directly to a URLSearchParams object,
|
||||
// you'll get [object Object] or the array.toString()
|
||||
function addItemToSearchParamsObject(key, value, searchParams) {
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((v, i) => {
|
||||
// Append {filters: [first, second]} as filters[0]=first&filters[1]second
|
||||
addItemToSearchParamsObject(key + '[' + i + ']', v, searchParams);
|
||||
})
|
||||
} else if (typeof value === 'object' && value !== null) {
|
||||
Object.keys(value).forEach((i) => {
|
||||
// Append {filters: {name: first}} as filters[name]=first
|
||||
addItemToSearchParamsObject(key + '[' + i + ']', value[i], searchParams);
|
||||
});
|
||||
} else {
|
||||
searchParams.append(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(query)
|
||||
.forEach(key => addItemToSearchParamsObject(key, query[key], url.searchParams));
|
||||
|
||||
window.abortControllers[endpointId] = new AbortController();
|
||||
|
||||
return fetch(url, {
|
||||
method,
|
||||
headers,
|
||||
body: method === 'GET' ? undefined : body,
|
||||
signal: window.abortControllers[endpointId].signal,
|
||||
referrer: window.tryItOutBaseUrl,
|
||||
mode: 'cors',
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => Promise.all([response.status, response.statusText, response.text(), response.headers]));
|
||||
}
|
||||
|
||||
function hideCodeSamples(endpointId) {
|
||||
document.querySelector('#example-requests-' + endpointId).hidden = true;
|
||||
document.querySelector('#example-responses-' + endpointId).hidden = true;
|
||||
}
|
||||
|
||||
function handleResponse(endpointId, response, status, headers) {
|
||||
hideCodeSamples(endpointId);
|
||||
|
||||
// Hide error views
|
||||
document.querySelector('#execution-error-' + endpointId).hidden = true;
|
||||
|
||||
const responseContentEl = document.querySelector('#execution-response-content-' + endpointId);
|
||||
|
||||
// Check if the response contains Laravel's dd() default dump output
|
||||
const isLaravelDump = response.includes('Sfdump');
|
||||
|
||||
// If it's a Laravel dd() dump, use innerHTML to render it safely
|
||||
if (isLaravelDump) {
|
||||
responseContentEl.innerHTML = response === '' ? responseContentEl.dataset.emptyResponseText : response;
|
||||
} else {
|
||||
// Otherwise, stick to textContent for regular responses
|
||||
responseContentEl.textContent = response === '' ? responseContentEl.dataset.emptyResponseText : response;
|
||||
}
|
||||
|
||||
// Prettify it if it's JSON
|
||||
let isJson = false;
|
||||
try {
|
||||
const jsonParsed = JSON.parse(response);
|
||||
if (jsonParsed !== null) {
|
||||
isJson = true;
|
||||
response = JSON.stringify(jsonParsed, null, 4);
|
||||
responseContentEl.textContent = response;
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
|
||||
isJson && window.hljs.highlightElement(responseContentEl);
|
||||
const statusEl = document.querySelector('#execution-response-status-' + endpointId);
|
||||
statusEl.textContent = ` (${status})`;
|
||||
document.querySelector('#execution-results-' + endpointId).hidden = false;
|
||||
statusEl.scrollIntoView({behavior: "smooth", block: "center"});
|
||||
}
|
||||
|
||||
function handleError(endpointId, err) {
|
||||
hideCodeSamples(endpointId);
|
||||
// Hide response views
|
||||
document.querySelector('#execution-results-' + endpointId).hidden = true;
|
||||
|
||||
// Show error views
|
||||
let errorMessage = err.message || err;
|
||||
const $errorMessageEl = document.querySelector('#execution-error-message-' + endpointId);
|
||||
$errorMessageEl.textContent = errorMessage + $errorMessageEl.textContent;
|
||||
const errorEl = document.querySelector('#execution-error-' + endpointId);
|
||||
errorEl.hidden = false;
|
||||
errorEl.scrollIntoView({behavior: "smooth", block: "center"});
|
||||
|
||||
}
|
||||
|
||||
async function executeTryOut(endpointId, form) {
|
||||
const executeBtn = document.querySelector(`#btn-executetryout-${endpointId}`);
|
||||
executeBtn.textContent = executeBtn.dataset.loadingText;
|
||||
executeBtn.disabled = true;
|
||||
executeBtn.scrollIntoView({behavior: "smooth", block: "center"});
|
||||
|
||||
let body;
|
||||
let setter;
|
||||
if (form.dataset.hasfiles === "1") {
|
||||
body = new FormData();
|
||||
setter = (name, value) => body.append(name, value);
|
||||
} else if (form.dataset.isarraybody === "1") {
|
||||
body = [];
|
||||
setter = (name, value) => _.set(body, name, value);
|
||||
} else {
|
||||
body = {};
|
||||
setter = (name, value) => _.set(body, name, value);
|
||||
}
|
||||
const bodyParameters = form.querySelectorAll('input[data-component=body]');
|
||||
bodyParameters.forEach(el => {
|
||||
let value = el.value;
|
||||
|
||||
if (el.type === 'number' && typeof value === 'string') {
|
||||
value = parseFloat(value);
|
||||
}
|
||||
|
||||
if (el.type === 'file' && el.files[0]) {
|
||||
setter(el.name, el.files[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (el.type !== 'radio') {
|
||||
if (value === "" && el.required === false) {
|
||||
// Don't include empty optional values in the request
|
||||
return;
|
||||
}
|
||||
setter(el.name, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (el.checked) {
|
||||
value = (value === 'false') ? false : true;
|
||||
setter(el.name, value);
|
||||
}
|
||||
});
|
||||
|
||||
const query = {};
|
||||
const queryParameters = form.querySelectorAll('input[data-component=query]');
|
||||
queryParameters.forEach(el => {
|
||||
if (el.type !== 'radio' || (el.type === 'radio' && el.checked)) {
|
||||
if (el.value === '') {
|
||||
// Don't include empty values in the request
|
||||
return;
|
||||
}
|
||||
|
||||
_.set(query, el.name, el.value);
|
||||
}
|
||||
});
|
||||
|
||||
let path = form.dataset.path;
|
||||
const urlParameters = form.querySelectorAll('input[data-component=url]');
|
||||
urlParameters.forEach(el => (path = path.replace(new RegExp(`\\{${el.name}\\??}`), el.value)));
|
||||
|
||||
const headers = Object.fromEntries(Array.from(form.querySelectorAll('input[data-component=header]'))
|
||||
.map(el => [el.name, el.value]));
|
||||
|
||||
// When using FormData, the browser sets the correct content-type + boundary
|
||||
let method = form.dataset.method;
|
||||
if (body instanceof FormData) {
|
||||
delete headers['Content-Type'];
|
||||
|
||||
// When using FormData with PUT or PATCH, use method spoofing so PHP can access the post body
|
||||
if (['PUT', 'PATCH'].includes(form.dataset.method)) {
|
||||
method = 'POST';
|
||||
setter('_method', form.dataset.method);
|
||||
}
|
||||
}
|
||||
|
||||
let preflightPromise = Promise.resolve();
|
||||
if (window.useCsrf && window.csrfUrl) {
|
||||
preflightPromise = makeAPICall('GET', window.csrfUrl).then(() => {
|
||||
headers['X-XSRF-TOKEN'] = getCookie('XSRF-TOKEN');
|
||||
});
|
||||
}
|
||||
|
||||
return preflightPromise.then(() => makeAPICall(method, path, body, query, headers, endpointId))
|
||||
.then(([responseStatus, statusText, responseContent, responseHeaders]) => {
|
||||
handleResponse(endpointId, responseContent, responseStatus, responseHeaders)
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.name === "AbortError") {
|
||||
console.log("Request cancelled");
|
||||
return;
|
||||
}
|
||||
console.log("Error while making request: ", err);
|
||||
handleError(endpointId, err);
|
||||
})
|
||||
.finally(() => {
|
||||
executeBtn.disabled = false;
|
||||
executeBtn.textContent = executeBtn.dataset.initialText;
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user