Skip to content

Instantly share code, notes, and snippets.

@erbanku
Created March 21, 2026 04:54
Show Gist options
  • Select an option

  • Save erbanku/1ca4c6674610326aa72b5f55a63985fc to your computer and use it in GitHub Desktop.

Select an option

Save erbanku/1ca4c6674610326aa72b5f55a63985fc to your computer and use it in GitHub Desktop.
(() => {
const LOG = '[Dify Batch Delete]';
const log = (...args) => console.log(LOG, ...args);
const err = (...args) => console.error(LOG, ...args);
const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const getCookie = (name) => {
const match = document.cookie.match(
new RegExp(`(?:^|; )${escapeRegExp(name)}=([^;]*)`)
);
return match ? decodeURIComponent(match[1]) : null;
};
const getApiBase = () => 'https://cloud.dify.ai/console/api';
const getAuthHeaders = () => {
const headers = {};
const csrfNames = ['__Host-csrf_token', 'csrf_token', 'csrf-token', 'x-csrf-token'];
const csrfHit = csrfNames.map(n => [n, getCookie(n)]).find(([, v]) => Boolean(v));
if (csrfHit) {
headers['X-CSRF-Token'] = csrfHit[1];
headers['X-CSRFToken'] = csrfHit[1];
return headers;
}
const token = localStorage.console_token || sessionStorage.console_token || window.console_token;
if (token) headers.Authorization = `Bearer ${token}`;
return headers;
};
const run = async () => {
const apiBase = getApiBase();
const authHeaders = getAuthHeaders();
const limit = 100;
const fetchJson = async (url, options = {}) => {
const response = await fetch(url, {
credentials: 'include',
cache: 'no-store',
headers: authHeaders,
...options
});
if (!response.ok) {
let body = '';
try { body = await response.text(); } catch {}
throw new Error(`${response.status} ${response.statusText}${body ? ` | ${body.slice(0, 300)}` : ''}`);
}
const text = await response.text();
return text ? JSON.parse(text) : {};
};
log('API_BASE =', apiBase);
// Fetch all apps
const apps = [];
let page = 1;
while (true) {
const payload = await fetchJson(
`${apiBase}/apps?page=${page}&limit=${limit}&name=&is_created_by_me=false`
);
const items = payload.data || payload.items || [];
apps.push(...items);
log(`Page ${page}: ${items.length} apps, total ${apps.length}`);
if (!payload.has_more && !payload.has_next || items.length === 0) break;
page++;
}
if (!apps.length) throw new Error('No apps found');
const confirmed = confirm(
`⚠️ WARNING: You are about to permanently delete ${apps.length} app(s).\n\nThis cannot be undone. Are you sure?`
);
if (!confirmed) { log('Cancelled by user.'); return; }
const deleted = [];
const failed = [];
for (let i = 0; i < apps.length; i++) {
const app = apps[i];
try {
await fetchJson(`${apiBase}/apps/${app.id}`, { method: 'DELETE' });
deleted.push(app.name || app.id);
log(`Deleted ${i + 1}/${apps.length}: ${app.name || app.id}`);
} catch (e) {
failed.push({ app: app.name || app.id, error: e.message });
err(`Failed ${i + 1}/${apps.length}: ${app.name || app.id}`, e);
}
}
console.group(`${LOG} Completed`);
console.log('Deleted:', deleted.length);
console.log('Failed:', failed.length);
if (failed.length) console.table(failed);
console.groupEnd();
alert(`Done: ${deleted.length} deleted, ${failed.length} failed.`);
};
run().catch((e) => {
err('Overall failure:', e);
alert(`Delete failed: ${e.message}`);
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment