Created
March 21, 2026 04:54
-
-
Save erbanku/1ca4c6674610326aa72b5f55a63985fc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (() => { | |
| 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