@tachybase/module-backup 0.23.58 → 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.js +1 -1
- package/dist/externalVersion.js +6 -6
- package/dist/locale/en-US.json +1 -0
- package/dist/locale/zh-CN.json +1 -0
- package/dist/node_modules/@hapi/topo/package.json +1 -1
- package/dist/node_modules/archiver/package.json +1 -1
- package/dist/node_modules/mkdirp/package.json +1 -1
- package/dist/node_modules/semver/package.json +1 -1
- package/dist/node_modules/yauzl/package.json +1 -1
- package/dist/server/dumper.d.ts +16 -8
- package/dist/server/dumper.js +39 -44
- package/dist/server/resourcers/backup-files.js +36 -21
- package/dist/server/server.d.ts +3 -1
- package/dist/server/server.js +4 -3
- package/package.json +9 -9
package/dist/client/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(p,r){typeof exports=="object"&&typeof module!="undefined"?r(exports,require("@tachybase/client"),require("react/jsx-runtime"),require("react"),require("@tachybase/components"),require("@ant-design/icons"),require("antd"),require("react-i18next")):typeof define=="function"&&define.amd?define(["exports","@tachybase/client","react/jsx-runtime","react","@tachybase/components","@ant-design/icons","antd","react-i18next"],r):(p=typeof globalThis!="undefined"?globalThis:p||self,r(p["@tachybase/module-backup"]={},p["@tachybase/client"],p.jsxRuntime,p.react,p["@tachybase/components"],p["@ant-design/icons"],p.antd,p["react-i18next"]))})(this,function(p,r,e,y,L,x,l,P){"use strict";var Z=Object.defineProperty,R=Object.defineProperties;var ee=Object.getOwnPropertyDescriptors;var $=Object.getOwnPropertySymbols;var te=Object.prototype.hasOwnProperty,oe=Object.prototype.propertyIsEnumerable;var W=(p,r,e)=>r in p?Z(p,r,{enumerable:!0,configurable:!0,writable:!0,value:e}):p[r]=e,q=(p,r)=>{for(var e in r||(r={}))te.call(r,e)&&W(p,e,r[e]);if($)for(var e of $(r))oe.call(r,e)&&W(p,e,r[e]);return p},E=(p,r)=>R(p,ee(r));var T=(p,r,e)=>new Promise((y,L)=>{var x=m=>{try{P(e.next(m))}catch(A){L(A)}},l=m=>{try{P(e.throw(m))}catch(A){L(A)}},P=m=>m.done?y(m.value):Promise.resolve(m.value).then(x,l);P((e=e.apply(p,r)).next())});var m=typeof globalThis!="undefined"?globalThis:typeof window!="undefined"?window:typeof global!="undefined"?global:typeof self!="undefined"?self:{},A={exports:{}};(function(a,g){(function(i,c){c()})(m,function(){function i(t,s){return typeof s=="undefined"?s={autoBom:!1}:typeof s!="object"&&(console.warn("Deprecated: Expected third argument to be a object"),s={autoBom:!s}),s.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(t.type)?new Blob(["\uFEFF",t],{type:t.type}):t}function c(t,s,k){var n=new XMLHttpRequest;n.open("GET",t),n.responseType="blob",n.onload=function(){v(n.response,s,k)},n.onerror=function(){console.error("could not download file")},n.send()}function u(t){var s=new XMLHttpRequest;s.open("HEAD",t,!1);try{s.send()}catch(k){}return 200<=s.status&&299>=s.status}function h(t){try{t.dispatchEvent(new MouseEvent("click"))}catch(k){var s=document.createEvent("MouseEvents");s.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),t.dispatchEvent(s)}}var f=typeof window=="object"&&window.window===window?window:typeof self=="object"&&self.self===self?self:typeof m=="object"&&m.global===m?m:void 0,w=f.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),v=f.saveAs||(typeof window!="object"||window!==f?function(){}:"download"in HTMLAnchorElement.prototype&&!w?function(t,s,k){var n=f.URL||f.webkitURL,d=document.createElement("a");s=s||t.name||"download",d.download=s,d.rel="noopener",typeof t=="string"?(d.href=t,d.origin===location.origin?h(d):u(d.href)?c(t,s,k):h(d,d.target="_blank")):(d.href=n.createObjectURL(t),setTimeout(function(){n.revokeObjectURL(d.href)},4e4),setTimeout(function(){h(d)},0))}:"msSaveOrOpenBlob"in navigator?function(t,s,k){if(s=s||t.name||"download",typeof t!="string")navigator.msSaveOrOpenBlob(i(t,k),s);else if(u(t))c(t,s,k);else{var n=document.createElement("a");n.href=t,n.target="_blank",setTimeout(function(){h(n)})}}:function(t,s,k,n){if(n=n||open("","_blank"),n&&(n.document.title=n.document.body.innerText="downloading..."),typeof t=="string")return c(t,s,k);var d=t.type==="application/octet-stream",C=/constructor/i.test(f.HTMLElement)||f.safari,o=/CriOS\/[\d]+/.test(navigator.userAgent);if((o||d&&C||w)&&typeof FileReader!="undefined"){var b=new FileReader;b.onloadend=function(){var S=b.result;S=o?S:S.replace(/^data:[^;]*;/,"data:attachment/file;"),n?n.location.href=S:location=S,n=null},b.readAsDataURL(t)}else{var B=f.URL||f.webkitURL,O=B.createObjectURL(t);n?n.location=O:location.href=O,n=null,setTimeout(function(){B.revokeObjectURL(O)},4e4)}});f.saveAs=v.saveAs=v,a.exports=v})})(A);var K=A.exports;const I="backup";function F(){return P.useTranslation(I,{nsMode:"fallback"})}const{Dragger:X}=l.Upload;function J(a){const g=c=>{var u;(u=a.onChange)==null||u.call(a,c)},i=r.useAPIClient();return E(q({},a),{customRequest({action:c,data:u,file:h,filename:f,headers:w,onError:v,onProgress:t,onSuccess:s,withCredentials:k}){const n=new FormData;return u&&Object.keys(u).forEach(d=>{n.append(d,u[d])}),n.append(f,h),i.axios.post(c,n,{withCredentials:k,headers:w,onUploadProgress:({total:d,loaded:C})=>{t({percent:Math.round(C/d*100).toFixed(2)},h)}}).then(({data:d})=>{s(d,h)}).catch(v).finally(()=>{}),{abort(){console.log("upload progress is aborted.")}}},onChange:g})}const U=a=>{const{collectionsData:g}=a,{t:i}=F(),[c,u]=y.useState(!1),[h,f]=y.useState(g);y.useEffect(()=>{f(g)},[g]);const w=r.useAPIClient(),v=r.useCompile(),t=y.useMemo(()=>w.resource("backupFiles"),[w]),s=()=>T(this,null,function*(){if(a.isBackup){const o=yield t.dumpableCollections();f(o==null?void 0:o.data),u(!0)}u(!0)}),k=()=>{u(!1)},n=()=>{u(!1)},d=[{title:i("Collection"),dataIndex:"collection",key:"collection",render:(o,b)=>{const B=v(b.title);return b.name===B?B:e.jsxs("div",{children:[b.name," ",e.jsxs("span",{style:{color:"rgba(0, 0, 0, 0.3)",fontSize:"0.9em"},children:["(",v(b.title),")"]})]})}},{title:i("Origin"),dataIndex:"origin",key:"origin",width:"50%"}],C=Object.keys(h||{}).map(o=>({key:o,label:i(`${o}.title`),children:e.jsxs(e.Fragment,{children:[e.jsx(l.Alert,{style:{marginBottom:16},message:i(`${o}.description`)}),e.jsx(l.Table,{pagination:{pageSize:100},bordered:!0,size:"small",dataSource:h[o],columns:d,scroll:{y:400}})]})}));return e.jsxs(e.Fragment,{children:[e.jsx("a",{onClick:s,children:i("Learn more")}),e.jsx(l.Modal,{title:i("Backup instructions"),width:"80vw",open:c,footer:null,onOk:k,onCancel:n,children:e.jsx(l.Tabs,{defaultActiveKey:"required",items:C})})]})},G=({ButtonComponent:a=l.Button,title:g,upload:i=!1,fileData:c})=>{const{t:u}=F(),[h,f]=y.useState(["required"]),[w,v]=y.useState(!1),[t,s]=y.useState(null),[k,n]=y.useState(!1),d=r.useAPIClient(),C=y.useMemo(()=>d.resource("backupFiles"),[d]),[o,b]=y.useState([]);y.useEffect(()=>{b(Object.keys((t==null?void 0:t.dumpableCollectionsGroupByGroup)||[]).map(M=>({value:M,label:u(`${M}.title`),disabled:["required","skipped"].includes(M)})))},[t]);const B=()=>T(this,null,function*(){var M,z,H;if(v(!0),!i){n(!0);const{data:D}=yield C.get({filterByTk:c.name});b(Object.keys(((z=(M=D==null?void 0:D.data)==null?void 0:M.meta)==null?void 0:z.dumpableCollectionsGroupByGroup)||[]).map(j=>({value:j,label:u(`${j}.title`),disabled:["required","skipped"].includes(j)}))),s((H=D==null?void 0:D.data)==null?void 0:H.meta),n(!1)}}),O=()=>{C.restore({values:{dataTypes:h,filterByTk:c==null?void 0:c.name,key:t==null?void 0:t.key}}),v(!1)},S=()=>{v(!1),s(null),f(["required"])};return e.jsxs(e.Fragment,{children:[e.jsx(a,{onClick:B,children:g}),e.jsx(l.Modal,{title:u("Restore"),width:800,footer:i&&!t?null:void 0,open:w,onOk:O,onCancel:S,children:e.jsxs(l.Spin,{spinning:k,children:[i&&!t&&e.jsx(V,{setRestoreData:s}),(!i||t)&&[e.jsxs("strong",{style:{fontWeight:600,display:"block",margin:"16px 0 8px"},children:[u("Select the data to be restored")," (",e.jsx(U,{collectionsData:t==null?void 0:t.dumpableCollectionsGroupByGroup}),"):"]},"info"),e.jsx("div",{style:{lineHeight:2,marginBottom:8},children:e.jsx(L.FormItem,{children:e.jsx(r.Checkbox.Group,{options:o,style:{flexDirection:"column"},value:h,onChange:M=>f(M)})})},"dataType")]]})})]})},Q=({ButtonComponent:a=l.Button,refresh:g})=>{const{t:i}=F(),[c,u]=y.useState(!1),[h,f]=y.useState(["required"]),w=r.useAPIClient(),{notification:v}=l.App.useApp(),[t,s]=y.useState([]),k=()=>T(this,null,function*(){const{data:C}=yield w.resource("backupFiles").dumpableCollections();s(Object.keys(C||[]).map(o=>({value:o,label:i(`${o}.title`),disabled:["required","skipped"].includes(o)}))),u(!0)}),n=C=>{w.request({url:"backupFiles:create",method:"post",data:{dataTypes:h,method:C}}).finally(()=>{v.destroy("backup")}),v.info({key:"backup",message:e.jsxs("span",{children:[i("Processing...")," ",e.jsx(l.Spin,{indicator:e.jsx(x.LoadingOutlined,{style:{fontSize:24},spin:!0})})]}),duration:0}),u(!1),f(["required"]),setTimeout(()=>{g()},500)},d=()=>{u(!1),f(["required"])};return e.jsxs(e.Fragment,{children:[e.jsx(a,{icon:e.jsx(x.PlusOutlined,{}),type:"primary",onClick:k,children:i("New backup")}),e.jsxs(l.Modal,{title:i("New backup"),width:800,open:c,onCancel:d,footer:[e.jsxs(l.Row,{gutter:16,justify:"end",align:"middle",children:[e.jsx(l.Col,{children:e.jsx(l.Button,{onClick:d,children:i("Cancel")},"cancel")}),e.jsx(l.Col,{children:e.jsx(l.Dropdown.Button,{type:"primary",onClick:()=>n("priority"),overlay:e.jsxs(l.Menu,{children:[e.jsx(l.Menu.Item,{onClick:()=>n("main"),children:i("Self backup")},"main"),e.jsx(l.Menu.Item,{onClick:()=>n("worker"),children:i("Worker backup")},"worker")]}),children:i("Backup")},"submit")})]})],children:[e.jsxs("strong",{style:{fontWeight:600,display:"block",margin:"16px 0 8px"},children:[i("Select the data to be backed up")," (",e.jsx(U,{isBackup:!0}),"):"]}),e.jsx("div",{style:{lineHeight:2,marginBottom:8},children:e.jsx(r.Checkbox.Group,{options:t,style:{flexDirection:"column"},onChange:C=>f(C),value:h})})]})]})},V=a=>{const{t:g}=F(),i={multiple:!1,action:"/backupFiles:upload",onChange(c){var h,f,w;c.fileList.length>1&&c.fileList.splice(0,c.fileList.length-1);const{status:u}=c.file;u==="done"?(l.message.success(`${c.file.name} `+g("file uploaded successfully")),a.setRestoreData(E(q({},(f=(h=c.file.response)==null?void 0:h.data)==null?void 0:f.meta),{key:(w=c.file.response)==null?void 0:w.data.key}))):u==="error"&&l.message.error(`${c.file.name} `+g("file upload failed"))},onDrop(c){console.log("Dropped files",c.dataTransfer.files)}};return e.jsxs(X,E(q({},J(i)),{children:[e.jsx("p",{className:"ant-upload-drag-icon",children:e.jsx(x.InboxOutlined,{})}),e.jsxs("p",{className:"ant-upload-text",children:[" ",g("Click or drag file to this area to upload")]})]}))},Y=()=>{const{t:a}=F(),g=r.useAPIClient(),[i,c]=y.useState([]),[u,h]=y.useState(!1),[f,w]=y.useState(!1),{modal:v,notification:t}=l.App.useApp(),s=y.useMemo(()=>g.resource("backupFiles"),[g]),k=y.useCallback(()=>T(this,null,function*(){yield n()}),[]);r.useNoticeSub("backup",o=>{(t[o.level]||t.info)({key:"backup",message:o.msg}),k()}),y.useEffect(()=>{n()},[]);const n=()=>T(this,null,function*(){h(!0);const{data:o}=yield s.list();c(o.data),h(!1)}),d=o=>T(this,null,function*(){w(o.name);const b=yield g.request({url:"backupFiles:download",method:"get",params:{filterByTk:o.name},responseType:"blob",onDownloadProgress:O=>{const S=Math.round(O.loaded*100/O.total);S>=100?t.success({key:"downloadBackup",message:e.jsx("span",{children:a("Downloaded success!")}),duration:1}):t.info({key:"downloadBackup",message:e.jsxs("span",{children:[a("Downloading ")+S+"%"," ",e.jsx(l.Spin,{indicator:e.jsx(x.LoadingOutlined,{style:{fontSize:24},spin:!0})})]}),duration:0})}});w(!1);const B=new Blob([b.data]);K.saveAs(B,o.name)}),C=o=>{v.confirm({title:a("Delete record",{ns:"core"}),content:a("Are you sure you want to delete it?",{ns:"core"}),onOk:()=>T(this,null,function*(){yield s.destroy({filterByTk:o.name}),yield n(),l.message.success(a("Deleted successfully"))})})};return e.jsx("div",{children:e.jsxs(l.Card,{bordered:!1,children:[e.jsxs(l.Space,{style:{float:"right",marginBottom:16},children:[e.jsx(l.Button,{onClick:k,icon:e.jsx(x.ReloadOutlined,{}),children:a("Refresh")}),e.jsx(G,{upload:!0,title:e.jsxs(e.Fragment,{children:[e.jsx(x.UploadOutlined,{})," ",a("Restore backup from local")]})}),e.jsx(Q,{refresh:k})]}),e.jsx(l.Table,{dataSource:i,loading:u,columns:[{title:a("Backup file"),dataIndex:"name",width:400,onCell:o=>o.inProgress?{colSpan:4}:{},render:(o,b)=>b.inProgress?e.jsxs("div",{style:{color:"rgba(0, 0, 0, 0.88)"},children:[o,"(",a("Backing up"),"...)"]}):e.jsx("div",{children:o})},{title:a("File size"),dataIndex:"fileSize",onCell:o=>o.inProgress?{colSpan:0}:{}},{title:a("Created at",{ns:"core"}),dataIndex:"createdAt",onCell:o=>o.inProgress?{colSpan:0}:{},render:o=>e.jsx(r.DatePicker.ReadPretty,{value:o,showTime:!0})},{title:a("Actions",{ns:"core"}),dataIndex:"actions",onCell:o=>o.inProgress?{colSpan:0}:{},render:(o,b)=>e.jsxs(l.Space,{split:e.jsx(l.Divider,{type:"vertical"}),children:[e.jsx(G,{ButtonComponent:"a",title:a("Restore"),fileData:b}),e.jsx("a",{type:"link",onClick:()=>d(b),children:a("Download")}),e.jsx("a",{onClick:()=>C(b),children:a("Delete")})]})}]})]})})},_=function(a){return e.jsx(r.SchemaComponentOptions,{children:a.children})};_.displayName="DuplicatorProvider";class N extends r.Plugin{load(){return T(this,null,function*(){this.app.use(_),this.app.systemSettingsManager.add("system-services."+I,{title:this.t("Backup & Restore"),icon:"CloudServerOutlined",Component:Y,aclSnippet:"pm.backup.restore"})})}}p.PluginBackupRestoreClient=N,p.default=N,Object.defineProperties(p,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
|
1
|
+
(function(p,r){typeof exports=="object"&&typeof module!="undefined"?r(exports,require("@tachybase/client"),require("react/jsx-runtime"),require("react"),require("@tachybase/components"),require("@ant-design/icons"),require("antd"),require("react-i18next")):typeof define=="function"&&define.amd?define(["exports","@tachybase/client","react/jsx-runtime","react","@tachybase/components","@ant-design/icons","antd","react-i18next"],r):(p=typeof globalThis!="undefined"?globalThis:p||self,r(p["@tachybase/module-backup"]={},p["@tachybase/client"],p.jsxRuntime,p.react,p["@tachybase/components"],p["@ant-design/icons"],p.antd,p["react-i18next"]))})(this,function(p,r,e,k,L,x,l,P){"use strict";var Z=Object.defineProperty,R=Object.defineProperties;var ee=Object.getOwnPropertyDescriptors;var $=Object.getOwnPropertySymbols;var te=Object.prototype.hasOwnProperty,oe=Object.prototype.propertyIsEnumerable;var W=(p,r,e)=>r in p?Z(p,r,{enumerable:!0,configurable:!0,writable:!0,value:e}):p[r]=e,E=(p,r)=>{for(var e in r||(r={}))te.call(r,e)&&W(p,e,r[e]);if($)for(var e of $(r))oe.call(r,e)&&W(p,e,r[e]);return p},q=(p,r)=>R(p,ee(r));var T=(p,r,e)=>new Promise((k,L)=>{var x=m=>{try{P(e.next(m))}catch(A){L(A)}},l=m=>{try{P(e.throw(m))}catch(A){L(A)}},P=m=>m.done?k(m.value):Promise.resolve(m.value).then(x,l);P((e=e.apply(p,r)).next())});var m=typeof globalThis!="undefined"?globalThis:typeof window!="undefined"?window:typeof global!="undefined"?global:typeof self!="undefined"?self:{},A={exports:{}};(function(a,b){(function(i,c){c()})(m,function(){function i(t,s){return typeof s=="undefined"?s={autoBom:!1}:typeof s!="object"&&(console.warn("Deprecated: Expected third argument to be a object"),s={autoBom:!s}),s.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(t.type)?new Blob(["\uFEFF",t],{type:t.type}):t}function c(t,s,g){var n=new XMLHttpRequest;n.open("GET",t),n.responseType="blob",n.onload=function(){v(n.response,s,g)},n.onerror=function(){console.error("could not download file")},n.send()}function u(t){var s=new XMLHttpRequest;s.open("HEAD",t,!1);try{s.send()}catch(g){}return 200<=s.status&&299>=s.status}function h(t){try{t.dispatchEvent(new MouseEvent("click"))}catch(g){var s=document.createEvent("MouseEvents");s.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),t.dispatchEvent(s)}}var f=typeof window=="object"&&window.window===window?window:typeof self=="object"&&self.self===self?self:typeof m=="object"&&m.global===m?m:void 0,w=f.navigator&&/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),v=f.saveAs||(typeof window!="object"||window!==f?function(){}:"download"in HTMLAnchorElement.prototype&&!w?function(t,s,g){var n=f.URL||f.webkitURL,d=document.createElement("a");s=s||t.name||"download",d.download=s,d.rel="noopener",typeof t=="string"?(d.href=t,d.origin===location.origin?h(d):u(d.href)?c(t,s,g):h(d,d.target="_blank")):(d.href=n.createObjectURL(t),setTimeout(function(){n.revokeObjectURL(d.href)},4e4),setTimeout(function(){h(d)},0))}:"msSaveOrOpenBlob"in navigator?function(t,s,g){if(s=s||t.name||"download",typeof t!="string")navigator.msSaveOrOpenBlob(i(t,g),s);else if(u(t))c(t,s,g);else{var n=document.createElement("a");n.href=t,n.target="_blank",setTimeout(function(){h(n)})}}:function(t,s,g,n){if(n=n||open("","_blank"),n&&(n.document.title=n.document.body.innerText="downloading..."),typeof t=="string")return c(t,s,g);var d=t.type==="application/octet-stream",C=/constructor/i.test(f.HTMLElement)||f.safari,o=/CriOS\/[\d]+/.test(navigator.userAgent);if((o||d&&C||w)&&typeof FileReader!="undefined"){var y=new FileReader;y.onloadend=function(){var S=y.result;S=o?S:S.replace(/^data:[^;]*;/,"data:attachment/file;"),n?n.location.href=S:location=S,n=null},y.readAsDataURL(t)}else{var B=f.URL||f.webkitURL,O=B.createObjectURL(t);n?n.location=O:location.href=O,n=null,setTimeout(function(){B.revokeObjectURL(O)},4e4)}});f.saveAs=v.saveAs=v,a.exports=v})})(A);var K=A.exports;const I="backup";function F(){return P.useTranslation(I,{nsMode:"fallback"})}const{Dragger:X}=l.Upload;function J(a){const b=c=>{var u;(u=a.onChange)==null||u.call(a,c)},i=r.useAPIClient();return q(E({},a),{customRequest({action:c,data:u,file:h,filename:f,headers:w,onError:v,onProgress:t,onSuccess:s,withCredentials:g}){const n=new FormData;return u&&Object.keys(u).forEach(d=>{n.append(d,u[d])}),n.append(f,h),i.axios.post(c,n,{withCredentials:g,headers:w,onUploadProgress:({total:d,loaded:C})=>{t({percent:Math.round(C/d*100).toFixed(2)},h)}}).then(({data:d})=>{s(d,h)}).catch(v).finally(()=>{}),{abort(){console.log("upload progress is aborted.")}}},onChange:b})}const U=a=>{const{collectionsData:b}=a,{t:i}=F(),[c,u]=k.useState(!1),[h,f]=k.useState(b);k.useEffect(()=>{f(b)},[b]);const w=r.useAPIClient(),v=r.useCompile(),t=k.useMemo(()=>w.resource("backupFiles"),[w]),s=()=>T(this,null,function*(){if(a.isBackup){const o=yield t.dumpableCollections();f(o==null?void 0:o.data),u(!0)}u(!0)}),g=()=>{u(!1)},n=()=>{u(!1)},d=[{title:i("Collection"),dataIndex:"collection",key:"collection",render:(o,y)=>{const B=v(y.title);return y.name===B?B:e.jsxs("div",{children:[y.name," ",e.jsxs("span",{style:{color:"rgba(0, 0, 0, 0.3)",fontSize:"0.9em"},children:["(",v(y.title),")"]})]})}},{title:i("Origin"),dataIndex:"origin",key:"origin",width:"50%"}],C=Object.keys(h||{}).map(o=>({key:o,label:i(`${o}.title`),children:e.jsxs(e.Fragment,{children:[e.jsx(l.Alert,{style:{marginBottom:16},message:i(`${o}.description`)}),e.jsx(l.Table,{pagination:{pageSize:100},bordered:!0,size:"small",dataSource:h[o],columns:d,scroll:{y:400}})]})}));return e.jsxs(e.Fragment,{children:[e.jsx("a",{onClick:s,children:i("Learn more")}),e.jsx(l.Modal,{title:i("Backup instructions"),width:"80vw",open:c,footer:null,onOk:g,onCancel:n,children:e.jsx(l.Tabs,{defaultActiveKey:"required",items:C})})]})},G=({ButtonComponent:a=l.Button,title:b,upload:i=!1,fileData:c})=>{const{t:u}=F(),[h,f]=k.useState(["required"]),[w,v]=k.useState(!1),[t,s]=k.useState(null),[g,n]=k.useState(!1),d=r.useAPIClient(),C=k.useMemo(()=>d.resource("backupFiles"),[d]),[o,y]=k.useState([]);k.useEffect(()=>{y(Object.keys((t==null?void 0:t.dumpableCollectionsGroupByGroup)||[]).map(M=>({value:M,label:u(`${M}.title`),disabled:["required","skipped"].includes(M)})))},[t]);const B=()=>T(this,null,function*(){var M,z,H;if(v(!0),!i){n(!0);const{data:D}=yield C.get({filterByTk:c.name});y(Object.keys(((z=(M=D==null?void 0:D.data)==null?void 0:M.meta)==null?void 0:z.dumpableCollectionsGroupByGroup)||[]).map(j=>({value:j,label:u(`${j}.title`),disabled:["required","skipped"].includes(j)}))),s((H=D==null?void 0:D.data)==null?void 0:H.meta),n(!1)}}),O=()=>{C.restore({values:{dataTypes:h,filterByTk:c==null?void 0:c.name,key:t==null?void 0:t.key}}),v(!1)},S=()=>{v(!1),s(null),f(["required"])};return e.jsxs(e.Fragment,{children:[e.jsx(a,{onClick:B,children:b}),e.jsx(l.Modal,{title:u("Restore"),width:800,footer:i&&!t?null:void 0,open:w,onOk:O,onCancel:S,children:e.jsxs(l.Spin,{spinning:g,children:[i&&!t&&e.jsx(V,{setRestoreData:s}),(!i||t)&&[e.jsxs("strong",{style:{fontWeight:600,display:"block",margin:"16px 0 8px"},children:[u("Select the data to be restored")," (",e.jsx(U,{collectionsData:t==null?void 0:t.dumpableCollectionsGroupByGroup}),"):"]},"info"),e.jsx("div",{style:{lineHeight:2,marginBottom:8},children:e.jsx(L.FormItem,{children:e.jsx(r.Checkbox.Group,{options:o,style:{flexDirection:"column"},value:h,onChange:M=>f(M)})})},"dataType")]]})})]})},Q=({ButtonComponent:a=l.Button,refresh:b})=>{const{t:i}=F(),[c,u]=k.useState(!1),[h,f]=k.useState(["required"]),w=r.useAPIClient(),{notification:v}=l.App.useApp(),[t,s]=k.useState([]),g=()=>T(this,null,function*(){const{data:C}=yield w.resource("backupFiles").dumpableCollections();s(Object.keys(C||[]).map(o=>({value:o,label:i(`${o}.title`),disabled:["required","skipped"].includes(o)}))),u(!0)}),n=C=>{w.request({url:"backupFiles:create",method:"post",data:{dataTypes:h,method:C}}),v.info({key:"backup",message:e.jsxs("span",{children:[i("Processing...")," ",e.jsx(l.Spin,{indicator:e.jsx(x.LoadingOutlined,{style:{fontSize:24},spin:!0})})]}),duration:0}),u(!1),f(["required"]),setTimeout(()=>{b()},500)},d=()=>{u(!1),f(["required"])};return e.jsxs(e.Fragment,{children:[e.jsx(a,{icon:e.jsx(x.PlusOutlined,{}),type:"primary",onClick:g,children:i("New backup")}),e.jsxs(l.Modal,{title:i("New backup"),width:800,open:c,onCancel:d,footer:[e.jsxs(l.Row,{gutter:16,justify:"end",align:"middle",children:[e.jsx(l.Col,{children:e.jsx(l.Button,{onClick:d,children:i("Cancel")},"cancel")}),e.jsx(l.Col,{children:e.jsx(l.Dropdown.Button,{type:"primary",onClick:()=>n("priority"),overlay:e.jsxs(l.Menu,{children:[e.jsx(l.Menu.Item,{onClick:()=>n("main"),children:i("Self backup")},"main"),e.jsx(l.Menu.Item,{onClick:()=>n("worker"),children:i("Worker backup")},"worker")]}),children:i("Backup")},"submit")})]})],children:[e.jsxs("strong",{style:{fontWeight:600,display:"block",margin:"16px 0 8px"},children:[i("Select the data to be backed up")," (",e.jsx(U,{isBackup:!0}),"):"]}),e.jsx("div",{style:{lineHeight:2,marginBottom:8},children:e.jsx(r.Checkbox.Group,{options:t,style:{flexDirection:"column"},onChange:C=>f(C),value:h})})]})]})},V=a=>{const{t:b}=F(),i={multiple:!1,action:"/backupFiles:upload",onChange(c){var h,f,w;c.fileList.length>1&&c.fileList.splice(0,c.fileList.length-1);const{status:u}=c.file;u==="done"?(l.message.success(`${c.file.name} `+b("file uploaded successfully")),a.setRestoreData(q(E({},(f=(h=c.file.response)==null?void 0:h.data)==null?void 0:f.meta),{key:(w=c.file.response)==null?void 0:w.data.key}))):u==="error"&&l.message.error(`${c.file.name} `+b("file upload failed"))},onDrop(c){console.log("Dropped files",c.dataTransfer.files)}};return e.jsxs(X,q(E({},J(i)),{children:[e.jsx("p",{className:"ant-upload-drag-icon",children:e.jsx(x.InboxOutlined,{})}),e.jsxs("p",{className:"ant-upload-text",children:[" ",b("Click or drag file to this area to upload")]})]}))},Y=()=>{const{t:a}=F(),b=r.useAPIClient(),[i,c]=k.useState([]),[u,h]=k.useState(!1),[f,w]=k.useState(!1),{modal:v,notification:t}=l.App.useApp(),s=k.useMemo(()=>b.resource("backupFiles"),[b]),g=k.useCallback(()=>T(this,null,function*(){yield n()}),[]);r.useNoticeSub("backup",o=>{(t[o.level]||t.info)({key:"backup",message:o.msg}),g()}),k.useEffect(()=>{n()},[]);const n=()=>T(this,null,function*(){h(!0);const{data:o}=yield s.list();c(o.data),h(!1)}),d=o=>T(this,null,function*(){w(o.name);const y=yield b.request({url:"backupFiles:download",method:"get",params:{filterByTk:o.name},responseType:"blob",onDownloadProgress:O=>{const S=Math.round(O.loaded*100/O.total);S>=100?t.success({key:"downloadBackup",message:e.jsx("span",{children:a("Downloaded success!")}),duration:1}):t.info({key:"downloadBackup",message:e.jsxs("span",{children:[a("Downloading ")+S+"%"," ",e.jsx(l.Spin,{indicator:e.jsx(x.LoadingOutlined,{style:{fontSize:24},spin:!0})})]}),duration:0})}});w(!1);const B=new Blob([y.data]);K.saveAs(B,o.name)}),C=o=>{v.confirm({title:a("Delete record",{ns:"core"}),content:a("Are you sure you want to delete it?",{ns:"core"}),onOk:()=>T(this,null,function*(){yield s.destroy({filterByTk:o.name}),yield n(),l.message.success(a("Deleted successfully"))})})};return e.jsx("div",{children:e.jsxs(l.Card,{bordered:!1,children:[e.jsxs(l.Space,{style:{float:"right",marginBottom:16},children:[e.jsx(l.Button,{onClick:g,icon:e.jsx(x.ReloadOutlined,{}),children:a("Refresh")}),e.jsx(G,{upload:!0,title:e.jsxs(e.Fragment,{children:[e.jsx(x.UploadOutlined,{})," ",a("Restore backup from local")]})}),e.jsx(Q,{refresh:g})]}),e.jsx(l.Table,{dataSource:i,loading:u,columns:[{title:a("Backup file"),dataIndex:"name",width:400,onCell:o=>o.inProgress?{colSpan:4}:{},render:(o,y)=>y.inProgress?e.jsxs("div",{style:{color:"rgba(0, 0, 0, 0.88)"},children:[o,"(",a("Backing up"),"...)"]}):y.status==="error"?e.jsxs("div",{style:{color:"red"},children:[o,"(",a("Error"),")"]}):e.jsx("div",{children:o})},{title:a("File size"),dataIndex:"fileSize",onCell:o=>o.inProgress?{colSpan:0}:{}},{title:a("Created at",{ns:"core"}),dataIndex:"createdAt",onCell:o=>o.inProgress?{colSpan:0}:{},render:o=>e.jsx(r.DatePicker.ReadPretty,{value:o,showTime:!0})},{title:a("Actions",{ns:"core"}),dataIndex:"actions",onCell:o=>o.inProgress?{colSpan:0}:{},render:(o,y)=>e.jsxs(l.Space,{split:e.jsx(l.Divider,{type:"vertical"}),children:[y.status!=="error"&&e.jsx(G,{ButtonComponent:"a",title:a("Restore"),fileData:y}),y.status!=="error"&&e.jsx("a",{type:"link",onClick:()=>d(y),children:a("Download")}),e.jsx("a",{onClick:()=>C(y),children:a("Delete")})]})}]})]})})},_=function(a){return e.jsx(r.SchemaComponentOptions,{children:a.children})};_.displayName="DuplicatorProvider";class N extends r.Plugin{load(){return T(this,null,function*(){this.app.use(_),this.app.systemSettingsManager.add("system-services."+I,{title:this.t("Backup & Restore"),icon:"CloudServerOutlined",Component:Y,aclSnippet:"pm.backup.restore"})})}}p.PluginBackupRestoreClient=N,p.default=N,Object.defineProperties(p,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
package/dist/externalVersion.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
"react": "18.3.1",
|
|
3
|
-
"@tachybase/client": "0.
|
|
4
|
-
"@tachybase/components": "0.
|
|
3
|
+
"@tachybase/client": "1.0.18",
|
|
4
|
+
"@tachybase/components": "1.0.18",
|
|
5
5
|
"@ant-design/icons": "5.5.2",
|
|
6
6
|
"antd": "5.22.5",
|
|
7
|
-
"@tachybase/server": "0.
|
|
8
|
-
"@tachybase/utils": "0.
|
|
9
|
-
"@tachybase/database": "0.
|
|
7
|
+
"@tachybase/server": "1.0.18",
|
|
8
|
+
"@tachybase/utils": "1.0.18",
|
|
9
|
+
"@tachybase/database": "1.0.18",
|
|
10
10
|
"dayjs": "1.11.13",
|
|
11
11
|
"lodash": "4.17.21",
|
|
12
12
|
"react-i18next": "15.2.0",
|
|
13
|
-
"@tachybase/actions": "0.
|
|
13
|
+
"@tachybase/actions": "1.0.18"
|
|
14
14
|
};
|
package/dist/locale/en-US.json
CHANGED
package/dist/locale/zh-CN.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@hapi/topo","description":"Topological sorting with grouping support","version":"6.0.2","repository":"git://github.com/hapijs/topo","main":"lib/index.js","types":"lib/index.d.ts","files":["lib"],"keywords":["topological","sort","toposort","topsort"],"eslintConfig":{"extends":["plugin:@hapi/module"]},"dependencies":{"@hapi/hoek":"^11.0.2"},"devDependencies":{"@hapi/code":"^9.0.3","@hapi/eslint-plugin":"*","@hapi/lab":"^25.1.2","@types/node":"^17.0.31","typescript":"~4.6.4"},"scripts":{"test":"lab -a @hapi/code -t 100 -L -Y","test-cov-html":"lab -a @hapi/code -t 100 -L -r html -o coverage.html"},"license":"BSD-3-Clause","_lastModified":"2025-
|
|
1
|
+
{"name":"@hapi/topo","description":"Topological sorting with grouping support","version":"6.0.2","repository":"git://github.com/hapijs/topo","main":"lib/index.js","types":"lib/index.d.ts","files":["lib"],"keywords":["topological","sort","toposort","topsort"],"eslintConfig":{"extends":["plugin:@hapi/module"]},"dependencies":{"@hapi/hoek":"^11.0.2"},"devDependencies":{"@hapi/code":"^9.0.3","@hapi/eslint-plugin":"*","@hapi/lab":"^25.1.2","@types/node":"^17.0.31","typescript":"~4.6.4"},"scripts":{"test":"lab -a @hapi/code -t 100 -L -Y","test-cov-html":"lab -a @hapi/code -t 100 -L -r html -o coverage.html"},"license":"BSD-3-Clause","_lastModified":"2025-04-18T20:33:17.453Z"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"archiver","version":"5.3.2","description":"a streaming interface for archive generation","homepage":"https://github.com/archiverjs/node-archiver","author":{"name":"Chris Talkington","url":"http://christalkington.com/"},"repository":{"type":"git","url":"https://github.com/archiverjs/node-archiver.git"},"bugs":{"url":"https://github.com/archiverjs/node-archiver/issues"},"license":"MIT","main":"index.js","files":["index.js","lib"],"engines":{"node":">= 10"},"scripts":{"test":"mocha --reporter dot","bench":"node benchmark/simple/pack-zip.js"},"dependencies":{"archiver-utils":"^2.1.0","async":"^3.2.4","buffer-crc32":"^0.2.1","readable-stream":"^3.6.0","readdir-glob":"^1.1.2","tar-stream":"^2.2.0","zip-stream":"^4.1.0"},"devDependencies":{"archiver-jsdoc-theme":"^1.1.3","chai":"^4.3.7","jsdoc":"^3.6.4","mkdirp":"^2.1.5","mocha":"^9.0.2","rimraf":"^4.3.1","stream-bench":"^0.1.2","tar":"^6.1.13","yauzl":"^2.9.0"},"keywords":["archive","archiver","stream","zip","tar"],"publishConfig":{"registry":"https://registry.npmjs.org/"},"_lastModified":"2025-
|
|
1
|
+
{"name":"archiver","version":"5.3.2","description":"a streaming interface for archive generation","homepage":"https://github.com/archiverjs/node-archiver","author":{"name":"Chris Talkington","url":"http://christalkington.com/"},"repository":{"type":"git","url":"https://github.com/archiverjs/node-archiver.git"},"bugs":{"url":"https://github.com/archiverjs/node-archiver/issues"},"license":"MIT","main":"index.js","files":["index.js","lib"],"engines":{"node":">= 10"},"scripts":{"test":"mocha --reporter dot","bench":"node benchmark/simple/pack-zip.js"},"dependencies":{"archiver-utils":"^2.1.0","async":"^3.2.4","buffer-crc32":"^0.2.1","readable-stream":"^3.6.0","readdir-glob":"^1.1.2","tar-stream":"^2.2.0","zip-stream":"^4.1.0"},"devDependencies":{"archiver-jsdoc-theme":"^1.1.3","chai":"^4.3.7","jsdoc":"^3.6.4","mkdirp":"^2.1.5","mocha":"^9.0.2","rimraf":"^4.3.1","stream-bench":"^0.1.2","tar":"^6.1.13","yauzl":"^2.9.0"},"keywords":["archive","archiver","stream","zip","tar"],"publishConfig":{"registry":"https://registry.npmjs.org/"},"_lastModified":"2025-04-18T20:33:17.269Z"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"mkdirp","description":"Recursively mkdir, like `mkdir -p`","version":"1.0.4","main":"index.js","keywords":["mkdir","directory","make dir","make","dir","recursive","native"],"repository":{"type":"git","url":"https://github.com/isaacs/node-mkdirp.git"},"scripts":{"test":"tap","snap":"tap","preversion":"npm test","postversion":"npm publish","postpublish":"git push origin --follow-tags"},"tap":{"check-coverage":true,"coverage-map":"map.js"},"devDependencies":{"require-inject":"^1.4.4","tap":"^14.10.7"},"bin":"bin/cmd.js","license":"MIT","engines":{"node":">=10"},"files":["bin","lib","index.js"],"_lastModified":"2025-
|
|
1
|
+
{"name":"mkdirp","description":"Recursively mkdir, like `mkdir -p`","version":"1.0.4","main":"index.js","keywords":["mkdir","directory","make dir","make","dir","recursive","native"],"repository":{"type":"git","url":"https://github.com/isaacs/node-mkdirp.git"},"scripts":{"test":"tap","snap":"tap","preversion":"npm test","postversion":"npm publish","postpublish":"git push origin --follow-tags"},"tap":{"check-coverage":true,"coverage-map":"map.js"},"devDependencies":{"require-inject":"^1.4.4","tap":"^14.10.7"},"bin":"bin/cmd.js","license":"MIT","engines":{"node":">=10"},"files":["bin","lib","index.js"],"_lastModified":"2025-04-18T20:33:17.357Z"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"semver","version":"7.6.3","description":"The semantic version parser used by npm.","main":"index.js","scripts":{"test":"tap","snap":"tap","lint":"eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"","postlint":"template-oss-check","lintfix":"npm run lint -- --fix","posttest":"npm run lint","template-oss-apply":"template-oss-apply --force"},"devDependencies":{"@npmcli/eslint-config":"^4.0.0","@npmcli/template-oss":"4.22.0","benchmark":"^2.1.4","tap":"^16.0.0"},"license":"ISC","repository":{"type":"git","url":"git+https://github.com/npm/node-semver.git"},"bin":{"semver":"bin/semver.js"},"files":["bin/","lib/","classes/","functions/","internal/","ranges/","index.js","preload.js","range.bnf"],"tap":{"timeout":30,"coverage-map":"map.js","nyc-arg":["--exclude","tap-snapshots/**"]},"engines":{"node":">=10"},"author":"GitHub Inc.","templateOSS":{"//@npmcli/template-oss":"This file is partially managed by @npmcli/template-oss. Edits may be overwritten.","version":"4.22.0","engines":">=10","distPaths":["classes/","functions/","internal/","ranges/","index.js","preload.js","range.bnf"],"allowPaths":["/classes/","/functions/","/internal/","/ranges/","/index.js","/preload.js","/range.bnf","/benchmarks"],"publish":"true"},"_lastModified":"2025-
|
|
1
|
+
{"name":"semver","version":"7.6.3","description":"The semantic version parser used by npm.","main":"index.js","scripts":{"test":"tap","snap":"tap","lint":"eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"","postlint":"template-oss-check","lintfix":"npm run lint -- --fix","posttest":"npm run lint","template-oss-apply":"template-oss-apply --force"},"devDependencies":{"@npmcli/eslint-config":"^4.0.0","@npmcli/template-oss":"4.22.0","benchmark":"^2.1.4","tap":"^16.0.0"},"license":"ISC","repository":{"type":"git","url":"git+https://github.com/npm/node-semver.git"},"bin":{"semver":"bin/semver.js"},"files":["bin/","lib/","classes/","functions/","internal/","ranges/","index.js","preload.js","range.bnf"],"tap":{"timeout":30,"coverage-map":"map.js","nyc-arg":["--exclude","tap-snapshots/**"]},"engines":{"node":">=10"},"author":"GitHub Inc.","templateOSS":{"//@npmcli/template-oss":"This file is partially managed by @npmcli/template-oss. Edits may be overwritten.","version":"4.22.0","engines":">=10","distPaths":["classes/","functions/","internal/","ranges/","index.js","preload.js","range.bnf"],"allowPaths":["/classes/","/functions/","/internal/","/ranges/","/index.js","/preload.js","/range.bnf","/benchmarks"],"publish":"true"},"_lastModified":"2025-04-18T20:33:17.681Z"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"yauzl","version":"3.2.0","description":"yet another unzip library for node","engines":{"node":">=12"},"main":"index.js","scripts":{"test":"node test/test.js"},"repository":{"type":"git","url":"git+https://github.com/thejoshwolfe/yauzl.git"},"keywords":["unzip","zip","stream","archive","file"],"author":"Josh Wolfe <thejoshwolfe@gmail.com>","license":"MIT","bugs":{"url":"https://github.com/thejoshwolfe/yauzl/issues"},"homepage":"https://github.com/thejoshwolfe/yauzl","dependencies":{"buffer-crc32":"~0.2.3","pend":"~1.2.0"},"devDependencies":{"bl":"^6.0.11"},"files":["fd-slicer.js","index.js"],"_lastModified":"2025-
|
|
1
|
+
{"name":"yauzl","version":"3.2.0","description":"yet another unzip library for node","engines":{"node":">=12"},"main":"index.js","scripts":{"test":"node test/test.js"},"repository":{"type":"git","url":"git+https://github.com/thejoshwolfe/yauzl.git"},"keywords":["unzip","zip","stream","archive","file"],"author":"Josh Wolfe <thejoshwolfe@gmail.com>","license":"MIT","bugs":{"url":"https://github.com/thejoshwolfe/yauzl/issues"},"homepage":"https://github.com/thejoshwolfe/yauzl","dependencies":{"buffer-crc32":"~0.2.3","pend":"~1.2.0"},"devDependencies":{"bl":"^6.0.11"},"files":["fd-slicer.js","index.js"],"_lastModified":"2025-04-18T20:33:17.841Z"}
|
package/dist/server/dumper.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { AppMigrator } from './app-migrator';
|
|
|
3
3
|
type DumpOptions = {
|
|
4
4
|
groups: Set<DumpRulesGroupType>;
|
|
5
5
|
fileName?: string;
|
|
6
|
+
appName?: string;
|
|
6
7
|
};
|
|
7
8
|
type BackUpStatusOk = {
|
|
8
9
|
name: string;
|
|
@@ -15,6 +16,11 @@ type BackUpStatusDoing = {
|
|
|
15
16
|
inProgress: true;
|
|
16
17
|
status: 'in_progress';
|
|
17
18
|
};
|
|
19
|
+
type BackUpStatusError = {
|
|
20
|
+
name: string;
|
|
21
|
+
createdAt: Date;
|
|
22
|
+
status: 'error';
|
|
23
|
+
};
|
|
18
24
|
export declare class Dumper extends AppMigrator {
|
|
19
25
|
static dumpTasks: Map<string, Promise<any>>;
|
|
20
26
|
direction: "dump";
|
|
@@ -25,7 +31,7 @@ export declare class Dumper extends AppMigrator {
|
|
|
25
31
|
};
|
|
26
32
|
};
|
|
27
33
|
static getTaskPromise(taskId: string): Promise<any> | undefined;
|
|
28
|
-
static getFileStatus(filePath: string): Promise<BackUpStatusOk | BackUpStatusDoing>;
|
|
34
|
+
static getFileStatus(filePath: string): Promise<BackUpStatusOk | BackUpStatusDoing | BackUpStatusError>;
|
|
29
35
|
static generateFileName(): string;
|
|
30
36
|
writeSQLContent(key: string, data: {
|
|
31
37
|
sql: string | string[];
|
|
@@ -40,16 +46,18 @@ export declare class Dumper extends AppMigrator {
|
|
|
40
46
|
collectionsGroupByDataTypes(): Promise<{
|
|
41
47
|
[k: string]: any[];
|
|
42
48
|
}>;
|
|
43
|
-
backUpStorageDir(): string;
|
|
49
|
+
backUpStorageDir(appName?: string): string;
|
|
44
50
|
allBackUpFilePaths(options?: {
|
|
45
51
|
includeInProgress?: boolean;
|
|
46
52
|
dir?: string;
|
|
53
|
+
appName?: string;
|
|
47
54
|
}): Promise<string[]>;
|
|
48
|
-
backUpFilePath(fileName: string): string;
|
|
49
|
-
lockFilePath(fileName: string): string;
|
|
50
|
-
writeLockFile(fileName: string): Promise<void>;
|
|
51
|
-
cleanLockFile(fileName: string): Promise<void>;
|
|
52
|
-
|
|
55
|
+
backUpFilePath(fileName: string, appName?: string): string;
|
|
56
|
+
lockFilePath(fileName: string, appName?: string): string;
|
|
57
|
+
writeLockFile(fileName: string, appName?: string): Promise<void>;
|
|
58
|
+
cleanLockFile(fileName: string, appName: string): Promise<void>;
|
|
59
|
+
getLockFile(appName: string): Promise<string>;
|
|
60
|
+
runDumpTask(options: DumpOptions): Promise<void>;
|
|
53
61
|
dumpableCollectionsGroupByGroup(): Promise<{
|
|
54
62
|
[x: string]: Pick<any, string>[];
|
|
55
63
|
}>;
|
|
@@ -63,7 +71,7 @@ export declare class Dumper extends AppMigrator {
|
|
|
63
71
|
dumpCollection(options: {
|
|
64
72
|
name: string;
|
|
65
73
|
}): Promise<void>;
|
|
66
|
-
packDumpedDir(fileName: string): Promise<{
|
|
74
|
+
packDumpedDir(fileName: string, appName?: string): Promise<{
|
|
67
75
|
filePath: string;
|
|
68
76
|
dirname: string;
|
|
69
77
|
}>;
|
package/dist/server/dumper.js
CHANGED
|
@@ -36,7 +36,6 @@ var import_path = __toESM(require("path"));
|
|
|
36
36
|
var process = __toESM(require("process"));
|
|
37
37
|
var import_stream = __toESM(require("stream"));
|
|
38
38
|
var import_util = __toESM(require("util"));
|
|
39
|
-
var import_worker_threads = require("worker_threads");
|
|
40
39
|
var import_database = require("@tachybase/database");
|
|
41
40
|
var import_archiver = __toESM(require("archiver"));
|
|
42
41
|
var import_dayjs = __toESM(require("dayjs"));
|
|
@@ -60,11 +59,19 @@ const _Dumper = class _Dumper extends import_app_migrator.AppMigrator {
|
|
|
60
59
|
const fileName = import_path.default.basename(filePath);
|
|
61
60
|
return import_fs.default.promises.stat(lockFile).then((lockFileStat) => {
|
|
62
61
|
if (lockFileStat.isFile()) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
if (lockFileStat.ctime.getTime() < Date.now() - 2 * 60 * 60 * 1e3) {
|
|
63
|
+
return {
|
|
64
|
+
name: fileName,
|
|
65
|
+
createdAt: lockFileStat.ctime,
|
|
66
|
+
status: "error"
|
|
67
|
+
};
|
|
68
|
+
} else {
|
|
69
|
+
return {
|
|
70
|
+
name: fileName,
|
|
71
|
+
inProgress: true,
|
|
72
|
+
status: "in_progress"
|
|
73
|
+
};
|
|
74
|
+
}
|
|
68
75
|
} else {
|
|
69
76
|
throw new Error("Lock file is not a file");
|
|
70
77
|
}
|
|
@@ -134,11 +141,14 @@ const _Dumper = class _Dumper extends import_app_migrator.AppMigrator {
|
|
|
134
141
|
const grouped = import_lodash.default.groupBy(await this.dumpableCollections(), "group");
|
|
135
142
|
return Object.fromEntries(Object.entries(grouped).map(([key, value]) => [key, value.map((item) => item.name)]));
|
|
136
143
|
}
|
|
137
|
-
backUpStorageDir() {
|
|
144
|
+
backUpStorageDir(appName) {
|
|
145
|
+
if (appName && appName !== "main") {
|
|
146
|
+
return import_path.default.resolve(process.cwd(), "storage", "backups", appName);
|
|
147
|
+
}
|
|
138
148
|
return import_path.default.resolve(process.cwd(), "storage", "backups");
|
|
139
149
|
}
|
|
140
150
|
async allBackUpFilePaths(options) {
|
|
141
|
-
const dirname = (options == null ? void 0 : options.dir) || this.backUpStorageDir();
|
|
151
|
+
const dirname = (options == null ? void 0 : options.dir) || this.backUpStorageDir(options == null ? void 0 : options.appName);
|
|
142
152
|
const includeInProgress = options == null ? void 0 : options.includeInProgress;
|
|
143
153
|
try {
|
|
144
154
|
const files = await import_promises.default.readdir(dirname);
|
|
@@ -165,52 +175,37 @@ const _Dumper = class _Dumper extends import_app_migrator.AppMigrator {
|
|
|
165
175
|
return [];
|
|
166
176
|
}
|
|
167
177
|
}
|
|
168
|
-
backUpFilePath(fileName) {
|
|
169
|
-
const dirname = this.backUpStorageDir();
|
|
178
|
+
backUpFilePath(fileName, appName) {
|
|
179
|
+
const dirname = this.backUpStorageDir(appName);
|
|
170
180
|
return import_path.default.resolve(dirname, fileName);
|
|
171
181
|
}
|
|
172
|
-
lockFilePath(fileName) {
|
|
182
|
+
lockFilePath(fileName, appName) {
|
|
173
183
|
const lockFile = fileName + ".lock";
|
|
174
|
-
const dirname = this.backUpStorageDir();
|
|
184
|
+
const dirname = this.backUpStorageDir(appName);
|
|
175
185
|
return import_path.default.resolve(dirname, lockFile);
|
|
176
186
|
}
|
|
177
|
-
async writeLockFile(fileName) {
|
|
178
|
-
const dirname = this.backUpStorageDir();
|
|
187
|
+
async writeLockFile(fileName, appName) {
|
|
188
|
+
const dirname = this.backUpStorageDir(appName);
|
|
179
189
|
await (0, import_mkdirp.default)(dirname);
|
|
180
|
-
const filePath = this.lockFilePath(fileName);
|
|
190
|
+
const filePath = this.lockFilePath(fileName, appName);
|
|
181
191
|
await import_promises.default.writeFile(filePath, "lock", "utf8");
|
|
182
192
|
}
|
|
183
|
-
async cleanLockFile(fileName) {
|
|
184
|
-
const filePath = this.lockFilePath(fileName);
|
|
193
|
+
async cleanLockFile(fileName, appName) {
|
|
194
|
+
const filePath = this.lockFilePath(fileName, appName);
|
|
185
195
|
await import_promises.default.unlink(filePath);
|
|
186
196
|
}
|
|
187
|
-
async
|
|
197
|
+
async getLockFile(appName) {
|
|
188
198
|
const backupFileName = _Dumper.generateFileName();
|
|
189
|
-
await this.writeLockFile(backupFileName);
|
|
190
|
-
if (import_worker_threads.isMainThread) {
|
|
191
|
-
const promise = this.dump({
|
|
192
|
-
groups: options.groups,
|
|
193
|
-
fileName: backupFileName
|
|
194
|
-
}).finally(() => {
|
|
195
|
-
this.cleanLockFile(backupFileName);
|
|
196
|
-
_Dumper.dumpTasks.delete(backupFileName);
|
|
197
|
-
this.app.noticeManager.notify("backup", { msg: "Done" });
|
|
198
|
-
});
|
|
199
|
-
_Dumper.dumpTasks.set(backupFileName, promise);
|
|
200
|
-
} else {
|
|
201
|
-
try {
|
|
202
|
-
await this.dump({
|
|
203
|
-
groups: options.groups,
|
|
204
|
-
fileName: backupFileName
|
|
205
|
-
});
|
|
206
|
-
} catch (err) {
|
|
207
|
-
throw err;
|
|
208
|
-
} finally {
|
|
209
|
-
this.cleanLockFile(backupFileName);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
199
|
+
await this.writeLockFile(backupFileName, appName);
|
|
212
200
|
return backupFileName;
|
|
213
201
|
}
|
|
202
|
+
async runDumpTask(options) {
|
|
203
|
+
await this.dump({
|
|
204
|
+
groups: options.groups,
|
|
205
|
+
fileName: options.fileName,
|
|
206
|
+
appName: options.appName
|
|
207
|
+
});
|
|
208
|
+
}
|
|
214
209
|
async dumpableCollectionsGroupByGroup() {
|
|
215
210
|
return (0, import_lodash.default)(await this.dumpableCollections()).map((c) => import_lodash.default.pick(c, ["name", "group", "origin", "title", "isView", "inherits"])).groupBy("group").mapValues((items) => import_lodash.default.sortBy(items, (item) => item.name)).value();
|
|
216
211
|
}
|
|
@@ -235,7 +230,7 @@ const _Dumper = class _Dumper extends import_app_migrator.AppMigrator {
|
|
|
235
230
|
});
|
|
236
231
|
await this.dumpDb(options);
|
|
237
232
|
const backupFileName = options.fileName || _Dumper.generateFileName();
|
|
238
|
-
const filePath = await this.packDumpedDir(backupFileName);
|
|
233
|
+
const filePath = await this.packDumpedDir(backupFileName, options.appName);
|
|
239
234
|
await this.clearWorkDir();
|
|
240
235
|
return filePath;
|
|
241
236
|
}
|
|
@@ -380,8 +375,8 @@ const _Dumper = class _Dumper extends import_app_migrator.AppMigrator {
|
|
|
380
375
|
}
|
|
381
376
|
await import_promises.default.writeFile(import_path.default.resolve(collectionDataDir, "meta"), JSON.stringify(meta), "utf8");
|
|
382
377
|
}
|
|
383
|
-
async packDumpedDir(fileName) {
|
|
384
|
-
const dirname = this.backUpStorageDir();
|
|
378
|
+
async packDumpedDir(fileName, appName) {
|
|
379
|
+
const dirname = this.backUpStorageDir(appName);
|
|
385
380
|
await (0, import_mkdirp.default)(dirname);
|
|
386
381
|
const filePath = import_path.default.resolve(dirname, fileName);
|
|
387
382
|
const output = import_fs.default.createWriteStream(filePath);
|
|
@@ -60,7 +60,8 @@ var backup_files_default = {
|
|
|
60
60
|
const { page = import_actions.DEFAULT_PAGE, pageSize = import_actions.DEFAULT_PER_PAGE } = ctx.action.params;
|
|
61
61
|
const dumper = new import_dumper.Dumper(ctx.app);
|
|
62
62
|
const backupFiles = await dumper.allBackUpFilePaths({
|
|
63
|
-
includeInProgress: true
|
|
63
|
+
includeInProgress: true,
|
|
64
|
+
appName: ctx.app.name
|
|
64
65
|
});
|
|
65
66
|
const count = backupFiles.length;
|
|
66
67
|
const rows = await Promise.all(
|
|
@@ -80,7 +81,7 @@ var backup_files_default = {
|
|
|
80
81
|
async get(ctx, next) {
|
|
81
82
|
const { filterByTk } = ctx.action.params;
|
|
82
83
|
const dumper = new import_dumper.Dumper(ctx.app);
|
|
83
|
-
const filePath = dumper.backUpFilePath(filterByTk);
|
|
84
|
+
const filePath = dumper.backUpFilePath(filterByTk, ctx.app.name);
|
|
84
85
|
async function sendError(message, status = 404) {
|
|
85
86
|
ctx.body = { status: "error", message };
|
|
86
87
|
ctx.status = status;
|
|
@@ -114,31 +115,45 @@ var backup_files_default = {
|
|
|
114
115
|
async create(ctx, next) {
|
|
115
116
|
var _a, _b;
|
|
116
117
|
const data = ctx.request.body;
|
|
117
|
-
let taskId;
|
|
118
118
|
const app = ctx.app;
|
|
119
119
|
if (data.method === "worker" && !((_a = app.worker) == null ? void 0 : _a.available)) {
|
|
120
120
|
ctx.throw(500, ctx.t("No worker thread", { ns: "worker-thread" }));
|
|
121
121
|
return next();
|
|
122
122
|
}
|
|
123
123
|
let useWorker = data.method === "worker" || data.method === "priority" && ((_b = app.worker) == null ? void 0 : _b.available);
|
|
124
|
+
const dumper = new import_dumper.Dumper(ctx.app);
|
|
125
|
+
const taskId = await dumper.getLockFile(ctx.app.name);
|
|
124
126
|
if (useWorker) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
127
|
+
app.worker.callPluginMethod({
|
|
128
|
+
plugin: import_server2.default,
|
|
129
|
+
method: "workerCreateBackUp",
|
|
130
|
+
params: {
|
|
131
|
+
dataTypes: data.dataTypes,
|
|
132
|
+
appName: ctx.app.name,
|
|
133
|
+
filename: taskId
|
|
134
|
+
},
|
|
135
|
+
// 目前限制方法并发为1
|
|
136
|
+
concurrency: 1
|
|
137
|
+
}).then((res) => {
|
|
138
|
+
app.noticeManager.notify("backup", { level: "info", msg: ctx.t("Done", { ns: "backup" }) });
|
|
139
|
+
}).catch((error) => {
|
|
140
|
+
app.noticeManager.notify("backup", { level: "error", msg: error.message });
|
|
141
|
+
}).finally(() => {
|
|
142
|
+
dumper.cleanLockFile(taskId, ctx.app.name);
|
|
143
|
+
});
|
|
139
144
|
} else {
|
|
140
145
|
const plugin = app.pm.get(import_server2.default);
|
|
141
|
-
|
|
146
|
+
plugin.workerCreateBackUp({
|
|
147
|
+
dataTypes: data.dataTypes,
|
|
148
|
+
appName: ctx.app.name,
|
|
149
|
+
filename: taskId
|
|
150
|
+
}).then((res) => {
|
|
151
|
+
app.noticeManager.notify("backup", { level: "info", msg: ctx.t("Done", { ns: "backup" }) });
|
|
152
|
+
}).catch((error) => {
|
|
153
|
+
app.noticeManager.notify("backup", { level: "error", msg: error.message });
|
|
154
|
+
}).finally(() => {
|
|
155
|
+
dumper.cleanLockFile(taskId, ctx.app.name);
|
|
156
|
+
});
|
|
142
157
|
}
|
|
143
158
|
ctx.body = {
|
|
144
159
|
key: taskId
|
|
@@ -153,7 +168,7 @@ var backup_files_default = {
|
|
|
153
168
|
async download(ctx, next) {
|
|
154
169
|
const { filterByTk } = ctx.action.params;
|
|
155
170
|
const dumper = new import_dumper.Dumper(ctx.app);
|
|
156
|
-
const filePath = dumper.backUpFilePath(filterByTk);
|
|
171
|
+
const filePath = dumper.backUpFilePath(filterByTk, ctx.app.name);
|
|
157
172
|
const fileState = await import_dumper.Dumper.getFileStatus(filePath);
|
|
158
173
|
if (fileState.status !== "ok") {
|
|
159
174
|
throw new Error(`Backup file ${filterByTk} not found`);
|
|
@@ -174,7 +189,7 @@ var backup_files_default = {
|
|
|
174
189
|
}
|
|
175
190
|
if (filterByTk) {
|
|
176
191
|
const dumper = new import_dumper.Dumper(ctx.app);
|
|
177
|
-
return dumper.backUpFilePath(filterByTk);
|
|
192
|
+
return dumper.backUpFilePath(filterByTk, ctx.app.name);
|
|
178
193
|
}
|
|
179
194
|
})();
|
|
180
195
|
if (!filePath) {
|
|
@@ -190,7 +205,7 @@ var backup_files_default = {
|
|
|
190
205
|
async destroy(ctx, next) {
|
|
191
206
|
const { filterByTk } = ctx.action.params;
|
|
192
207
|
const dumper = new import_dumper.Dumper(ctx.app);
|
|
193
|
-
const filePath = dumper.backUpFilePath(filterByTk);
|
|
208
|
+
const filePath = dumper.backUpFilePath(filterByTk, ctx.app.name);
|
|
194
209
|
await import_promises.default.unlink(filePath);
|
|
195
210
|
ctx.body = {
|
|
196
211
|
status: "ok"
|
package/dist/server/server.d.ts
CHANGED
package/dist/server/server.js
CHANGED
|
@@ -44,9 +44,10 @@ class PluginBackupRestoreServer extends import_server.Plugin {
|
|
|
44
44
|
this.app.resourcer.define(import_backup_files.default);
|
|
45
45
|
}
|
|
46
46
|
async workerCreateBackUp(data) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
await new import_dumper.Dumper(this.app).runDumpTask({
|
|
48
|
+
groups: new Set(data.dataTypes),
|
|
49
|
+
appName: data.appName,
|
|
50
|
+
fileName: data.filename
|
|
50
51
|
});
|
|
51
52
|
}
|
|
52
53
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tachybase/module-backup",
|
|
3
3
|
"displayName": "App backup & restore",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "1.0.18",
|
|
5
5
|
"description": "Backup and restore applications for scenarios such as application replication, migration, and upgrades.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"System management"
|
|
@@ -30,16 +30,16 @@
|
|
|
30
30
|
"semver": "^7.6.3",
|
|
31
31
|
"tar": "^6.2.1",
|
|
32
32
|
"yauzl": "^3.2.0",
|
|
33
|
-
"@tachybase/components": "0.
|
|
34
|
-
"@tachybase/module-worker-thread": "0.
|
|
33
|
+
"@tachybase/components": "1.0.18",
|
|
34
|
+
"@tachybase/module-worker-thread": "1.0.18"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@tachybase/actions": "0.
|
|
38
|
-
"@tachybase/client": "0.
|
|
39
|
-
"@tachybase/
|
|
40
|
-
"@tachybase/
|
|
41
|
-
"@tachybase/
|
|
42
|
-
"@tachybase/
|
|
37
|
+
"@tachybase/actions": "1.0.18",
|
|
38
|
+
"@tachybase/client": "1.0.18",
|
|
39
|
+
"@tachybase/database": "1.0.18",
|
|
40
|
+
"@tachybase/test": "1.0.18",
|
|
41
|
+
"@tachybase/utils": "1.0.18",
|
|
42
|
+
"@tachybase/server": "1.0.18"
|
|
43
43
|
},
|
|
44
44
|
"description.zh-CN": "备份和还原应用,可用于应用的复制、迁移、升级等场景。",
|
|
45
45
|
"displayName.zh-CN": "应用的备份与还原",
|