PageSourceSearch

https://podster.fm/build/assets/DatabaseToolsView-6784921b.js

js podster.fm collected 2026-09-24 08:52:22 UTC 15,727 bytes, 6 lines download raw bytes

1import{K as E,f as g,C as G,s as N,e as F,w as f,o as r,a as S,u as p,Z as L,b as t,c as o,j as v,q as W,z as M,d,t as l,F as h,g as y,A as w,h as j,O}from"./app-f96b9a38.js";import{u as P}from"./useTabState-7a595df1.js";import U from"./Layout-6142b859.js";import{_ as T}from"./Modal-046c4c5e.js";import{S as Z}from"./search-1bd09d24.js";import{W as H}from"./Sidebar-1f9c9aee.js";import{A as I}from"./archive-24f1f5ec.js";import"./useAuthStore-bd281680.js";import"./isObject-a1be2107.js";import"./MainAuthUserMenu-64843d4c.js";import"./FavoriteIcon-d21876e3.js";import"./_plugin-vue_export-helper-c27b6911.js";import"./SettingsIcon-17514e01.js";import"./menu-6e48567b.js";import"./dom-cadfdd7e.js";import"./focus-management-dccaee5f.js";import"./use-outside-click-f54ccb8d.js";import"./use-resolve-button-type-e81962cc.js";import"./ChevronLeftIcon-ff15d7a3.js";import"./CabinetMainNavMobile-cc0ad8f4.js";import"./vue-composable.esm-bundler-11aa148c.js";import"./MainNavMobileMenu-8fcb8fd4.js";import"./useDarkMode-eb5341a5.js";import"./RecommendationIcon-375aa968.js";import"./StatsIcon-2edff3fb.js";import"./CloseIcon-82975fa5.js";import"./KnowledgebaseIcon-e039e069.js";import"./EpisodeIcon-bc94c554.js";import"./ReportBugButton-3cf539b2.js";import"./FormTextarea-aff24f0e.js";import"./FormInputLabel-5489c904.js";import"./v4-a960c1f4.js";import"./FormTextInput-85d75d75.js";import"./FormPrimaryButton-0c835555.js";import"./LoadingIcon-9ac274f8.js";import"./transition-d5d1ed5a.js";import"./hidden-e39bbb26.js";import"./micro-task-89dcd6af.js";import"./description-9a269291.js";import"./createLucideIcon-23d060b2.js";import"./chevron-down-7de84f01.js";import"./sparkles-2d5fa576.js";import"./switch-97e49124.js";const Q=t("title",null,"Инструменты БД",-1),X=t("meta",{name:"robots",content:"noindex"},null,-1),Y={class:"mb-6 flex flex-wrap gap-2 border-b border-neutral-200 dark:border-neutral-700"},tt=["onClick"],et={class:"mb-10"},at=t("h5",{class:"mb-3 text-lg font-semibold text-brand-300 dark:text-neutral-100"},"Сводка",-1),st={class:"rounded-lg bg-white p-5 text-sm text-brand-300 shadow-sm dark:bg-neutral-700 dark:text-neutral-100"},lt={class:"mb-2"},rt={class:"mb-3"},ot={class:"rounded bg-neutral-200 px-1 py-0.5 text-xs dark:bg-neutral-600"},nt={class:"mb-3"},dt=t("div",{class:"mb-1 text-xs font-semibold uppercase tracking-wide text-neutral-500 dark:text-neutral-400"}," Что чистится ",-1),it={class:"list-disc pl-5"},ut=t("div",{class:"mb-1 text-xs font-semibold uppercase tracking-wide text-rose-600 dar
1k:text-rose-400"}," НЕ трогать ",-1),ct={class:"list-disc pl-5"},pt={class:"rounded bg-neutral-200 px-1 dark:bg-neutral-600"},bt={class:"mb-10"},xt=t("h5",{class:"mb-3 text-lg font-semibold text-brand-300 dark:text-neutral-100"},"Действия",-1),mt={class:"flex flex-wrap items-center gap-3"},_t=["disabled"],ht=["disabled"],kt=["disabled"],gt={key:0,class:"text-sm text-neutral-500 dark:text-neutral-400"},ft=t("p",{class:"mt-3 max-w-3xl text-xs text-neutral-500 dark:text-neutral-400"}," Превью показывает количество строк, которые удалятся, без изменений в БД. Кнопка «выполнить» проводит реальное удаление: orphan-media (вместе с файлами через Spatie), старые failed_jobs и завершённые job_batches. Прочее — только через консоль. ",-1),vt={key:0},yt=t("h5",{class:"mb-3 text-lg font-semibold text-brand-300 dark:text-neutral-100"},"Результат запуска",-1),wt={class:"mb-2 text-xs text-neutral-500 dark:text-neutral-400"},jt={class:"rounded bg-neutral-200 px-1 dark:bg-neutral-600"},Ct={class:"overflow-x-auto rounded-lg bg-neutral-900 p-4 text-xs leading-relaxed text-neutral-100"},$t=t("p",{class:"text-sm text-brand-300 dark:text-neutral-100"}," Точно выполнить очистку? Удалятся orphan-media (вместе с файлами Ð
1½Ð° диске), старые failed_jobs и завершённые job_batches. ",-1),St=t("p",{class:"mt-2 text-xs text-neutral-500 dark:text-neutral-400"},[d(" Перед удалением создаётся JSON-бэкап в "),t("code",null,"storage/app/db-backups/"),d(". Откатить можно кнопкой «Откатить» ниже или командой "),t("code",null,"php artisan db:cleanup-rollback {timestamp}"),d(". ")],-1),Bt={class:"mt-6 flex justify-end gap-2"},Nt=["disabled"],Ft={class:"text-sm text-brand-300 dark:text-neutral-100"},Ot={class:"mt-3 space-y-1 text-sm text-brand-300 dark:text-neutral-100"},Tt=t("p",{class:"mt-3 text-xs text-neutral-500 dark:text-neutral-400"}," Строки будут добавлены обратно в БД (с теми же id). Уже существующие записи пропускаются. Файлы media восстанавливаются из бэкапа, если были сохранены. ",-1),At={class:"mt-6 flex justify-end gap-2"},Dt=["disabled"],Vt={class:"mt-10"},zt=t("h5",{class:"mb-3 text-lg font-semibold text-brand-300 dark:text-neutral-100"},"Прошлые очистки",-1),Rt={key:0,class:"rounded-lg border border-dashed border-neutral-300 bg-white p-5 text-sm text-neutral-500 dark:border-neutral-600 dar
1k:bg-neutral-700 dark:text-neutral-300"},Jt={key:1,class:"overflow-hidden rounded-lg border border-neutral-200 bg-white dark:border-neutral-600 dark:bg-neutral-700"},Kt={class:"w-full text-sm"},qt=t("thead",{class:"bg-neutral-100 text-left text-xs uppercase tracking-wide text-neutral-500 dark:bg-neutral-800 dark:text-neutral-400"},[t("tr",null,[t("th",{class:"px-4 py-2"},"Когда"),t("th",{class:"px-4 py-2"},"Метка"),t("th",{class:"px-4 py-2 text-right"},"Строк"),t("th",{class:"px-4 py-2 text-right"},"media · failed_jobs · job_batches"),t("th",{class:"px-4 py-2 text-right"},"Размер бэкапа"),t("th",{class:"px-4 py-2"},"Статус"),t("th",{class:"px-4 py-2 text-right"},"Действия")])],-1),Et={class:"divide-y divide-neutral-200 text-brand-300 dark:divide-neutral-600 dark:text-neutral-100"},Gt={class:"px-4 py-2 whitespace-nowrap"},Lt={class:"px-4 py-2 whitespace-nowrap font-mono text-xs"},Wt={class:"px-4 py-2 text-right"},Mt={class:"px-4 py-2 text-right text-xs text-neutral-500 dark:text-neutral-300"},Pt={class:"px-4 py-2 text-right text-xs"},Ut={class:"px-4 py-2"},Zt={key:0,class:"inline-block rounded bg-amber-100 px-2 py-0.5 text-xs font-semibold text-amber-700 dark:bg-amber-900 dark:text-amber-200"},Ht={key:1,class:"inline-block rounded bg-neutral-200 px-2 py-0.5 text-xs text-neutral-600 dar
1k:bg-neutral-600 dark:text-neutral-200"},It={key:2,class:"inline-block rounded bg-emerald-100 px-2 py-0.5 text-xs font-semibold text-emerald-700 dark:bg-emerald-900 dark:text-emerald-200"},Qt={class:"px-4 py-2 text-right"},Xt=["disabled","onClick"],Yt={key:1,class:"text-xs text-neutral-400"},te=["disabled"],ee=t("section",{class:"mt-12 rounded-lg border border-neutral-300 bg-white p-5 text-sm text-brand-300 dark:border-neutral-600 dark:bg-neutral-700 dark:text-neutral-100"},[t("h5",{class:"mb-2 text-base font-semibold"},"Как пользоваться"),t("ol",{class:"list-decimal space-y-1 pl-5"},[t("li",null,"«Найти orphans» — read-only отчёт. Запускайте сколько угодно, ничего не меняет."),t("li",null,"«Очистка — превью» — считает, что было бы удалено по текущим cutoff'ам (failed_jobs > 90д, job_batches > 30д, orphan-media). Не трогает БД."),t("li",null,[d("«Очистка — выполнить» — делает удаление через ту же команду с "),t("code",null,"--execute --force"),d(". Перед удалением пишет JSON-бэкап в "),t("code",null,"storage/app/db-backups/"),d(".")]),t("li",null,"«Откатить» — re-insert строк из бэкапа. Существующие записи (по PK) пропускаются. Файлы media восстанавливаются, если есть в бэкапе.")]),t("h5",{class:"mb-2 mt-4 text-base font-semibold"},"Из консоли"),t("pre",{class:"overflow-x-auto rounded bg-neutral-900 p-3 text-xs text-neutral-100"},`php artisan db:find-orphans
2php artisan db:cleanup --dry-run
3php artisan db:cleanup --execute                       # запросит подтверждение
4php artisan db:cleanup --execute --force               # без подтверждения
5php artisan db:cleanup-rollback 20260506-150500        # откат конкретного бэкапа
6php artisan db:cleanup-rollback 20260506-150500 --force`)],-1),Pe={__name:"DatabaseToolsView",props:{summary:{type:Object,required:!0},backups:{type:Array,default:()=>[]}},setup(b){const A=b,x=P(["audit","cleanup","backups"],"audit"),D=[{value:"audit",label:"Аудит",icon:Z},{value:"cleanup",label:"Очистка",icon:H},{value:"backups",label:"Бэкапы",icon:I}],V=a=>{const s="inline-flex items-center gap-1.5 rounded-t-md px-4 py-2 text-sm font-medium transition-colors";return a?`${s} border-b-2 border-brand-300 text-brand-300 dark:border-neutral-100 dark:text-neutral-100`:`${s} text-neutral-500 hover:text-brand-300 dark:text-neutral-400 dark:hover:text-neutral-100`},z=E(),n=g(!1),m=g(!1),i=g(null),u=g(null);G(()=>{var a;return(a=z.props.flash)==null?void 0:a.dbToolsOutput},a=>{a&&(u.value=a)},{immediate:!0});const R=N(()=>u.value?u.value.json?JSON.stringify(u.value.json,null,2):u.value.raw||"":""),B=N(()=>A.backups.find(a=>!a.rolled_back&&a.backup_exists)??null);function C(a){if(n.value)return;n.value=!0;const s={"find-orphans":"/admin/database-tools/find-orphans","cleanup-dry-run":"/admin/database-tools/cleanup-dry-run","cleanup-execute":"/admin/database-tools/cleanup-execute"}[a];O.post(s,{},{preserveScroll:!0,onFinish:()=>{n.value=!1}})}function J(){m.value=!1,C("cleanup-execute")}function K(){const a=i.value;i.value=null,!(!a||n.value)&&(n.value=!0,O.post(`/admin/database-tools/rollback/${a.timestamp}`,{},{preserveScroll:!0,onFinish:()=>{n.value=!1}}))}function $(a){if(!a)return"—";const s=new Date(a);return Number.isNaN(s.getTime())?a:s.toLocaleString("ru-RU",{dateStyle:"short",timeStyle:"short"})}function q(a){return typeof a!="number"||!isFinite(a)?"—":a<1024?a+" B":a<1024*1024?(a/1024).toFixed(1)+" KB":a<1024*1024*1024?(a/1024/1024).toFixed(1)+" MB":(a/1024/1024/1024).toFixed(2)+" GB"}return(a,s)=>(r(),F(U,{heading:"Инструменты БД"},{default:f(()=>[S(p(L),null,{default:f(()=>[Q,X]),_:1}),t("div",Y,[(r(),o(h,null,v(D,e=>t("button",{key:e.value,type:"button",class:W(V(p(x)===e.value)),onClick:c=>x.value=e.value},[(r(),F(M(e.icon),{class:"h-4 w-4","stroke-width":2})),d(" "+l(e.label),1)],10,tt)),64))]),y(t("section",et,[at,t("div",st,[t("p",lt,[d(" Всего таблиц: "),t("b",null,l(b.summary.tables_total),1),d(" · общий размер: "),t("b",null,l(b.summary.data_size_gb)+" GB",1)]),t("p",rt,[d(" Полный отчёт: "),t("code",ot,l(b.summary.docs_path),1)]),t("div",nt,[dt,t("ul",it,[(r(!0),o(h,null,v(b.summary.cleanup_targets,(e,c)=>(r(),o("li",{key:c},l(e),1))),128))])]),t("div",null,[ut,t("ul",ct,[(r(!0),o(h,null,v(b.summary.protected,(e,c)=>(r(),o("li",{key:c},[t("code",pt,l(c),1),d(" — "+l(e),1)]))),128))])])])],512),[[w,p(x)==="audit"]]),y(t("section",bt,[xt,t("div",mt,[t("button",{type:"button",disabled:n.value,class:"rounded-lg bg-brand-300 px-5 py-2 text-sm text-white transition disabled:cursor-not-allowed disabled:opacity-60 dark:bg-neutral-600 dar
6k:text-neutral-100",onClick:s[0]||(s[0]=e=>C("find-orphans"))}," Найти orphans (read-only) ",8,_t),t("button",{type:"button",disabled:n.value,class:"rounded-lg bg-brand-300 px-5 py-2 text-sm text-white transition disabled:cursor-not-allowed disabled:opacity-60 dark:bg-neutral-600 dark:text-neutral-100",onClick:s[1]||(s[1]=e=>C("cleanup-dry-run"))}," Очистка — превью (dry-run) ",8,ht),t("button",{type:"button",disabled:n.value,class:"rounded-lg bg-rose-600 px-5 py-2 text-sm text-white transition hover:bg-rose-700 disabled:cursor-not-allowed disabled:opacity-60",onClick:s[2]||(s[2]=e=>m.value=!0)}," Очистка — выполнить ",8,kt),n.value?(r(),o("span",gt,"Выполняется…")):j("",!0)]),ft],512),[[w,p(x)==="cleanup"]]),u.value?y((r(),o("section",vt,[yt,t("div",wt,[t("code",jt,"php artisan "+l(u.value.command)+l(u.value.args.length?" "+u.value.args.join(" "):""),1),d(" · exit="+l(u.value.exit_code)+" · "+l(u.value.elapsed_ms)+" ms · "+l(u.value.ran_at),1)]),t("pre",Ct,l(p(R)),1)],512)),[[w,p(x)==="cleanup"]]):j("",!0),S(T,{"show-initial":m.value,title:"Подтверждение",onClose:s[5]||(s[5]=e=>m.value=!1)},{default:f(()=>[$t,St,t("div",Bt,[t("button",{type:"button",class:"rounded-lg bg-neutral-200 px-4 py-2 text-sm text-brand-300 dark:bg-neutral-600 dar
6k:text-neutral-100",onClick:s[3]||(s[3]=e=>m.value=!1)}," Отмена "),t("button",{type:"button",disabled:n.value,class:"rounded-lg bg-rose-600 px-4 py-2 text-sm text-white disabled:opacity-60",onClick:s[4]||(s[4]=e=>J())}," Да, выполнить ",8,Nt)])]),_:1},8,["show-initial"]),S(T,{"show-initial":i.value!==null,title:"Откатить очистку",onClose:s[8]||(s[8]=e=>i.value=null)},{default:f(()=>{var e,c,_;return[i.value?(r(),o(h,{key:0},[t("p",Ft,[d(" Откатить очистку от "),t("b",null,l($(i.value.ran_at)),1),d("? ")]),t("ul",Ot,[t("li",null,"media: "+l(((e=i.value.counts)==null?void 0:e.media)??0),1),t("li",null,"failed_jobs: "+l(((c=i.value.counts)==null?void 0:c.failed_jobs)??0),1),t("li",null,"job_batches: "+l(((_=i.value.counts)==null?void 0:_.job_batches)??0),1)]),Tt,t("div",At,[t("button",{type:"button",class:"rounded-lg bg-neutral-200 px-4 py-2 text-sm text-brand-300 dark:bg-neutral-600 dark:text-neutral-100",onClick:s[6]||(s[6]=k=>i.value=null)}," Отмена "),t("button",{type:"button",disabled:n.value,class:"rounded-lg bg-emerald-600 px-4 py-2 text-sm text-white disabled:opacity-60",onClick:s[7]||(s[7]=k=>K())}," Да, откатить ",8,Dt)])],64)):j("",!0)]}),_:1},8,["show-initial"]),y(t("section",Vt,[zt,b.backups.length?(r(),o("div",Jt,[t("table",Kt,[qt,t("tbody",Et,[(r(!0),o(h,null,v(b.backups,e=>{var c,_,k;return r(),o("tr",{key:e.timestamp},[t("td",Gt,l($(e.ran_at)),1),t("td",Lt,l(e.timestamp),1),t("td",Wt,l(e.total),1),t("td",Mt,l(((c=e.counts)==null?void 0:c.media)??0)+" · "+l(((_=e.counts)==null?void 0:_.failed_jobs)??0)+" · "+l(((k=e.counts)==null?void 0:k.job_batches)??0),1),t("td",Pt,l(q(e.backup_size)),1),t("td",Ut,[e.rolled_back?(r(),o("span",Zt," Откатили "+l($(e.rolled_back.rolled_back_at)),1)):e.backup_exists?(r(),o("span",It," Можно откатить ")):(r(),o("span",Ht," Файл бэкапа удалён "))]),t("td",Qt,[!e.rolled_back&&e.backup_exists?(r(),o("button",{key:0,type:"button",disabled:n.value,class:"rounded-lg bg-emerald-600 px-3 py-1 text-xs text-white disabled:cursor-not-allowed disabled:opacity-60 hover:bg-emerald-700",onClick:ae=>i.value=e}," Откатить ",8,Xt)):(r(),o("span",Yt,"—"))])])}),128))])])])):(r(),o("div",Rt," Пока ни одной очистки не запускалось — бэкапов нет. ")),p(B)?(r(),o("button",{key:2,type:"button",disabled:n.value,class:"mt-3 rounded-lg bg-emerald-600 px-5 py-2 text-sm text-white disabled:cursor-not-allowed disabled:opacity-60 hover:bg-emerald-700",onClick:s[9]||(s[9]=e=>i.value=p(B))}," Откатить последнюю очистку ",8,te)):j("",!0)],512),[[w,p(x)==="backups"]]),ee]),_:1}))}};export{Pe as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.