Skip to content

Commit

Permalink
feat: Add Ability to Cancel Ongoing Deployments in Dev Center (HeyPut…
Browse files Browse the repository at this point in the history
  • Loading branch information
HatemLamia committed Dec 19, 2024
1 parent 5e076dc commit f27dd4c
Showing 1 changed file with 40 additions and 212 deletions.
252 changes: 40 additions & 212 deletions src/dev-center/js/dev-center.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,8 @@ async function create_app(title, source_path = null, items = null) {

})
.then(async (app) => {
$('.new-app-modal').get(0).close();
window.location.reload();
let app_dir;
// ----------------------------------------------------
// Create app directory in AppData
Expand All @@ -313,6 +315,8 @@ async function create_app(title, source_path = null, items = null) {
maximizeOnStart: false,
background: false,
}).then(async (app) => {
$('.new-app-modal').get(0).close();
window.location.reload();
// refresh app list
puter.apps.list().then(async (resp) => {
apps = resp;
Expand Down Expand Up @@ -555,8 +559,8 @@ function generate_edit_app_section(app) {
</select>
<label for="edit-app-filetype-associations">File Associations</label>
<p style="margin-top: 10px; font-size:13px;">A list of file type specifiers. For example if you include <code>.txt</code> your apps could be opened when a user clicks on a TXT file.</p>
<textarea id="edit-app-filetype-associations" placeholder=".txt .jpg application/json">${JSON.stringify(app.filetype_associations.map(item => ({ "value": item })), null, app.filetype_associations.length)}</textarea>
<p style="margin-top: 10px; font-size:13px;">A comma-separated list of file type specifiers. For example if you include <code>.txt</code>, your apps could be opened when a user clicks on a TXT file.</p>
<textarea id="edit-app-filetype-associations" placeholder=".txt, .jpg, application/json">${app.filetype_associations}</textarea>
<h3 style="font-size: 23px; border-bottom: 1px solid #EEE; margin-top: 50px; margin-bottom: 0px;">Window</h3>
<div>
Expand Down Expand Up @@ -762,63 +766,6 @@ async function edit_app_section(cur_app_name) {
toggleResetButton(); // Ensure Reset button is initially disabled
$('#edit-app').show();

const filetype_association_input = document.querySelector('textarea[id=edit-app-filetype-associations]');
let tagify = new Tagify(filetype_association_input, {
pattern: /\.(?:[a-z0-9]+)|(?:[a-z]+\/(?:[a-z0-9.-]+|\*))/,
delimiters: ", ",
enforceWhitelist: false,
dropdown : {
// show the dropdown immediately on focus (0 character typed)
enabled: 0,
},
whitelist: [
// MIME type patterns
"text/*", "image/*", "audio/*", "video/*", "application/*",

// Documents
".doc", ".docx", ".pdf", ".txt", ".odt", ".rtf", ".tex", ".md", ".pages", ".epub", ".mobi", ".azw", ".azw3", ".djvu", ".xps", ".oxps", ".fb2", ".textile", ".markdown", ".asciidoc", ".rst", ".wpd", ".wps", ".abw", ".zabw",

// Spreadsheets
".xls", ".xlsx", ".csv", ".ods", ".numbers", ".tsv", ".gnumeric", ".xlt", ".xltx", ".xlsm", ".xltm", ".xlam", ".xlsb",

// Presentations
".ppt", ".pptx", ".key", ".odp", ".pps", ".ppsx", ".pptm", ".potx", ".potm", ".ppam",

// Images
".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".tif", ".svg", ".webp", ".ico", ".psd", ".ai", ".eps", ".raw", ".cr2", ".nef", ".orf", ".sr2", ".heic", ".heif", ".avif", ".jxr", ".hdp", ".wdp", ".jng", ".xcf", ".pgm", ".pbm", ".ppm", ".pnm",

// Video
".mp4", ".avi", ".mov", ".wmv", ".mkv", ".flv", ".webm", ".m4v", ".mpeg", ".mpg", ".3gp", ".3g2", ".ogv", ".vob", ".drc", ".gifv", ".mng", ".qt", ".yuv", ".rm", ".rmvb", ".asf", ".amv", ".m2v", ".svi",

// Audio
".mp3", ".wav", ".aac", ".flac", ".ogg", ".m4a", ".wma", ".aiff", ".alac", ".ape", ".au", ".mid", ".midi", ".mka", ".pcm", ".ra", ".ram", ".snd", ".wv", ".opus",

// Code/Development
".js", ".ts", ".html", ".css", ".json", ".xml", ".php", ".py", ".java", ".cpp", ".c", ".cs", ".h", ".hpp", ".hxx", ".rs", ".go", ".rb", ".pl", ".swift", ".kt", ".kts", ".scala", ".coffee", ".sass", ".scss", ".less", ".jsx", ".tsx", ".vue", ".sh", ".bash", ".zsh", ".fish", ".ps1", ".bat", ".cmd", ".sql", ".r", ".dart", ".f", ".f90", ".for", ".lua", ".m", ".mm", ".clj", ".erl", ".ex", ".exs", ".elm", ".hs", ".lhs", ".lisp", ".ml", ".mli", ".nim", ".pl", ".rkt", ".v", ".vhd",

// Archives
".zip", ".rar", ".7z", ".tar", ".gz", ".bz2", ".xz", ".z", ".lz", ".lzma", ".tlz", ".txz", ".tgz", ".tbz2", ".bz", ".br", ".lzo", ".ar", ".cpio", ".shar", ".lrz", ".lz4", ".lz2", ".rz", ".sfark", ".sz", ".zoo",

// Database
".db", ".sql", ".sqlite", ".sqlite3", ".dbf", ".mdb", ".accdb", ".db3", ".s3db", ".dbx",

// Fonts
".ttf", ".otf", ".woff", ".woff2", ".eot", ".pfa", ".pfb", ".sfd",

// CAD and 3D
".dwg", ".dxf", ".stl", ".obj", ".fbx", ".dae", ".3ds", ".blend", ".max", ".ma", ".mb", ".c4d", ".skp", ".usd", ".usda", ".usdc", ".abc",

// Scientific/Technical
".mat", ".fig", ".nb", ".cdf", ".fits", ".fts", ".fit", ".gmsh", ".msh", ".fem", ".neu", ".hdf", ".h5", ".nx", ".unv",

// System
".exe", ".dll", ".so", ".dylib", ".app", ".dmg", ".iso", ".img", ".bin", ".msi", ".apk", ".ipa", ".deb", ".rpm",

// Directory
".directory"
],
})

// --------------------------------------------------------
// Dragster
// --------------------------------------------------------
Expand Down Expand Up @@ -859,7 +806,7 @@ async function edit_app_section(cur_app_name) {
dropped_items = items[0].path;
$('.drop-area').removeClass('drop-area-hover');
$('.drop-area').addClass('drop-area-ready-to-deploy');
drop_area_content = `<p style="margin-bottom:0; font-weight: 500;">index.html</p><p>Ready to deploy 🚀</p><p class="reset-deploy"><span>Cancel</span></p>`;
drop_area_content = `<p style="margin-bottom:0; font-weight: 500;">index.html</p><p>Ready to deploy 🚀</p>`;
$('.drop-area').html(drop_area_content);

// enable deploy button
Expand Down Expand Up @@ -893,7 +840,7 @@ async function edit_app_section(cur_app_name) {
dropped_items = items;
$('.drop-area').removeClass('drop-area-hover');
$('.drop-area').addClass('drop-area-ready-to-deploy');
drop_area_content = `<p style="margin-bottom:0; font-weight: 500;">${items.length} items</p><p>Ready to deploy 🚀</p><p class="reset-deploy"><span>Cancel</span></p>`;
drop_area_content = `<p style="margin-bottom:0; font-weight: 500;">${items.length} items</p><p>Ready to deploy 🚀</p>`;
$('.drop-area').html(drop_area_content);

// enable deploy button
Expand Down Expand Up @@ -934,7 +881,7 @@ async function edit_app_section(cur_app_name) {

$('.drop-area').removeClass('drop-area-hover');
$('.drop-area').addClass('drop-area-ready-to-deploy');
drop_area_content = `<p style="margin-bottom:0; font-weight: 500;">${rootItems}</p><p>Ready to deploy 🚀</p><p class="reset-deploy"><span>Cancel</span></p>`;
drop_area_content = `<p style="margin-bottom:0; font-weight: 500;">${rootItems}</p><p>Ready to deploy 🚀</p>`;
$('.drop-area').html(drop_area_content);

// enable deploy button
Expand Down Expand Up @@ -1008,7 +955,7 @@ async function edit_app_section(cur_app_name) {
rootItems = html_encode(rootItems);
$('.drop-area').removeClass('drop-area-hover');
$('.drop-area').addClass('drop-area-ready-to-deploy');
drop_area_content = `<p style="margin-bottom:0; font-weight: 500;">${rootItems}</p><p>Ready to deploy 🚀</p><p class="reset-deploy"><span>Cancel</span></p>`;
drop_area_content = `<p style="margin-bottom:0; font-weight: 500;">${rootItems}</p><p>Ready to deploy 🚀</p>`;
$('.drop-area').html(drop_area_content);

// enable deploy button
Expand Down Expand Up @@ -1159,32 +1106,6 @@ $(document).on('click', '.edit-app-save-btn', async function (e) {
}
}

// parse filetype_associations

filetype_associations = JSON.parse(filetype_associations);
filetype_associations = filetype_associations.map((type) => {
const fileType = type.value;


if (
!fileType ||
fileType === "." ||
fileType === "/"
) {
error = `<strong>File Association Type</strong> must be valid.`;
return null; // Return null for invalid cases
}
const lower = fileType.toLocaleLowerCase();

if (fileType.includes("/")) {
return lower;
} else if (fileType.includes(".")) {
return "." + lower.split(".")[1];
} else {
return "." + lower;
}
}).filter(Boolean);

// error?
if (error) {
$('#edit-app-error').show();
Expand All @@ -1196,8 +1117,8 @@ $(document).on('click', '.edit-app-save-btn', async function (e) {
// show working spinner
puter.ui.showSpinner();



// parse filetype_associations
filetype_associations = filetype_associations.split(',').map(element => element.trim());
// disable submit button
$('.edit-app-save-btn').prop('disabled', true);

Expand Down Expand Up @@ -1721,130 +1642,14 @@ function sort_apps() {
}
}

/**
* Checks if the items being deployed contain a .git directory
* @param {Array|string} items - Items to check (can be path string or array of items)
* @returns {Promise<boolean>} - True if .git directory is found
*/
async function hasGitDirectory(items) {
// Case 1: Single Puter path
if (typeof items === 'string' && (items.startsWith('/') || items.startsWith('~'))) {
const stat = await puter.fs.stat(items);
if (stat.is_dir) {
const files = await puter.fs.readdir(items);
return files.some(file => file.name === '.git' && file.is_dir);
}
return false;
}

// Case 2: Array of Puter items
if (Array.isArray(items) && items[0]?.uid) {
return items.some(item => item.name === '.git' && item.is_dir);
}

// Case 3: Local items (DataTransferItems)
if (Array.isArray(items)) {
for (let item of items) {
if (item.fullPath?.includes('/.git/') ||
item.path?.includes('/.git/') ||
item.filepath?.includes('/.git/')) {
return true;
}
}
}

return false;
}

/**
* Shows a warning dialog about .git directory deployment
* @returns {Promise<boolean>} - True if the user wants to proceed with deployment
*/
async function showGitWarningDialog() {
try {
// Check if the user has chosen to skip the warning
const skipWarning = await puter.kv.get('skip-git-warning');

// Log retrieved value for debugging
console.log('Retrieved skip-git-warning:', skipWarning);

// If the user opted to skip the warning, proceed without showing it
if (skipWarning === true) {
return true;
}
} catch (error) {
console.error('Error accessing KV store:', error);
// If KV store access fails, fall back to showing the dialog
}

// Create the modal dialog
const modal = document.createElement('div');
modal.innerHTML = `
<div style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; border-radius: 8px; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); z-index: 10000;">
<h3 style="margin-top: 0;">Warning: Git Repository Detected</h3>
<p>A .git directory was found in your deployment files. Deploying .git directories may:</p>
<ul>
<li>Expose sensitive information like commit history and configuration</li>
<li>Significantly increase deployment size</li>
</ul>
<div style="margin-top: 15px; display: flex; align-items: center;">
<input type="checkbox" id="skip-git-warning" style="margin-right: 10px;">
<label for="skip-git-warning" style="margin-top:0;">Don't show this warning again</label>
</div>
<div style="margin-top: 15px; display: flex; justify-content: flex-end;">
<button id="cancel-deployment" style="margin-right: 10px; padding: 10px 15px; background: #f0f0f0; border: none; border-radius: 4px; cursor: pointer;">Cancel</button>
<button id="continue-deployment" style="padding: 10px 15px; background: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer;">Continue Deployment</button>
</div>
</div>
<div style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 9999;"></div>
`;
document.body.appendChild(modal);

return new Promise((resolve) => {
// Handle "Continue Deployment"
document.getElementById('continue-deployment').addEventListener('click', async () => {
try {
const skipChecked = document.getElementById('skip-git-warning')?.checked;
if (skipChecked) {
console.log("Saving 'skip-git-warning' preference as true");
await puter.kv.set('skip-git-warning', true);
}
} catch (error) {
console.error('Error saving user preference to KV store:', error);
} finally {
document.body.removeChild(modal);
resolve(true); // Continue deployment
}
});

// Handle "Cancel Deployment"
document.getElementById('cancel-deployment').addEventListener('click', () => {
document.body.removeChild(modal);
resolve(false); // Cancel deployment
});
});
}

window.deploy = async function (app, items) {
// Check for .git directory before proceeding
try {
if (await hasGitDirectory(items)) {
const shouldProceed = await showGitWarningDialog();
if (!shouldProceed) {
reset_drop_area();
return;
}
}
} catch (err) {
console.error('Error checking for .git directory:', err);
}
let appdata_dir, current_app_dir;

// disable deploy button
$('.deploy-btn').addClass('disabled');

// change drop area text
$('.drop-area').html(deploying_spinner + ' <div>Deploying <span class="deploy-percent">(0%)</span></div>');
$('.drop-area').html(deploying_spinner + ' <div>Deploying <span class="deploy-percent">(0%)</span></div><p class="reset-deploy button button-secondary"><span>Cancel</span></p>');

if (typeof items === 'string' && (items.startsWith('/') || items.startsWith('~'))) {
$('.drop-area').removeClass('drop-area-hover');
Expand Down Expand Up @@ -2215,7 +2020,7 @@ $(document).on('click', '.insta-deploy-existing-app-deploy-btn', function (e) {

$('.drop-area').removeClass('drop-area-hover');
$('.drop-area').addClass('drop-area-ready-to-deploy');
let drop_area_content = `<p style="margin-bottom:0; font-weight: 500;">Ready to deploy 🚀</p><p class="reset-deploy"><span>Cancel</span></p>`;
let drop_area_content = `<p style="margin-bottom:0; font-weight: 500;">Ready to deploy 🚀</p><p class="reset-deploy button button-secondary"><span>Cancel</span></p>`;
$('.drop-area').html(drop_area_content);

// deploy
Expand Down Expand Up @@ -2657,9 +2462,32 @@ function enable_window_settings(){
$('#edit-app-hide-titlebar').prop('disabled', false);
}

$(document).on('click', '.reset-deploy', function (e) {
reset_drop_area();
})
$(document).on('click', '.reset-deploy', async function (e) {
// Display a confirmation dialog to ask the user
const alert_resp = await puter.ui.alert(
'Are you sure you want to cancel the deployment?',
[
{
label: 'Yes, cancel deployment',
value: 'cancel',
type: 'danger', // This can style the button as red/danger
},
{
label: 'No, keep it',
value: 'keep'
}
]
);

if (alert_resp === 'cancel') {
// If the user clicks "Yes, cancel deployment", reset the drop area
reset_drop_area();
} else {
// If the user clicks "No, keep it", do nothing or log it
console.log('Deployment is not canceled.');
}
});


$(document).on('click', '.sidebar-toggle', function (e) {
$('.sidebar').toggleClass('open');
Expand Down

0 comments on commit f27dd4c

Please sign in to comment.