]>
git.proxmox.com Git - proxmox-backup.git/blob - www/DataStoreContent.js
1 Ext
.define('pbs-data-store-snapshots', {
2 extend
: 'Ext.data.Model',
9 dateFormat
: 'timestamp'
13 { name
: 'size', type
: 'int' },
17 calculate: function(data
) {
20 data
.files
.forEach(file
=> {
21 if (file
.filename
=== 'index.json.blob') return; // is never encrypted
28 if (encrypted
=== 0) {
30 } else if (encrypted
< files
) {
40 Ext
.define('PBS.DataStoreContent', {
41 extend
: 'Ext.tree.Panel',
42 alias
: 'widget.pbsDataStoreContent',
46 title
: gettext('Content'),
49 xclass
: 'Ext.app.ViewController',
51 init: function(view
) {
52 if (!view
.datastore
) {
53 throw "no datastore specified";
56 this.store
= Ext
.create('Ext.data.Store', {
57 model
: 'pbs-data-store-snapshots',
58 groupField
: 'backup-group',
60 this.store
.on('load', this.onLoad
, this);
62 view
.getStore().setSorters([
67 Proxmox
.Utils
.monStoreErrors(view
, this.store
);
68 this.reload(); // initial load
72 let view
= this.getView();
74 if (!view
.store
|| !this.store
) {
75 console
.warn('cannot reload, no store(s)');
79 let url
= `/api2/json/admin/datastore/${view.datastore}/snapshots`;
82 timeout
: 300*1000, // 5 minutes, we should make that api call faster
89 getRecordGroups: function(records
) {
92 for (const item
of records
) {
93 var btype
= item
.data
["backup-type"];
94 let group
= btype
+ "/" + item
.data
["backup-id"];
96 if (groups
[group
] !== undefined) {
101 if (btype
=== 'vm') {
103 } else if (btype
=== 'ct') {
105 } else if (btype
=== 'host') {
108 console
.warn(`got unknown backup-type '${btype}'`);
109 continue; // FIXME: auto render? what do?
115 iconCls
: "fa " + cls
,
117 backup_type
: item
.data
["backup-type"],
118 backup_id
: item
.data
["backup-id"],
126 onLoad: function(store
, records
, success
, operation
) {
127 let view
= this.getView();
130 Proxmox
.Utils
.setErrorMask(view
, Proxmox
.Utils
.getResponseErrorMessage(operation
.getError()));
134 let groups
= this.getRecordGroups(records
);
136 for (const item
of records
) {
137 let group
= item
.data
["backup-type"] + "/" + item
.data
["backup-id"];
138 let children
= groups
[group
].children
;
140 let data
= item
.data
;
142 data
.text
= group
+ '/' + PBS
.Utils
.render_datetime_utc(data
["backup-time"]);
144 data
.cls
= 'no-leaf-icons';
150 for (const [_key
, group
] of Object
.entries(groups
)) {
153 for (const item
of group
.children
) {
154 if (item
.encrypted
> 0) {
157 if (item
["backup-time"] > last_backup
) {
158 last_backup
= item
["backup-time"];
159 group
["backup-time"] = last_backup
;
160 group
.files
= item
.files
;
161 group
.size
= item
.size
;
162 group
.owner
= item
.owner
;
166 if (encrypted
=== 0) {
168 } else if (encrypted
< group
.children
.length
) {
173 group
.count
= group
.children
.length
;
174 children
.push(group
);
181 Proxmox
.Utils
.setErrorMask(view
, false);
184 onPrune: function() {
185 var view
= this.getView();
187 let rec
= view
.selModel
.getSelection()[0];
188 if (!(rec
&& rec
.data
)) return;
190 if (data
.leaf
) return;
192 if (!view
.datastore
) return;
194 let win
= Ext
.create('PBS.DataStorePrune', {
195 datastore
: view
.datastore
,
196 backup_type
: data
.backup_type
,
197 backup_id
: data
.backup_id
,
199 win
.on('destroy', this.reload
, this);
203 onVerify: function() {
204 var view
= this.getView();
206 if (!view
.datastore
) return;
208 let rec
= view
.selModel
.getSelection()[0];
209 if (!(rec
&& rec
.data
)) return;
216 "backup-type": data
["backup-type"],
217 "backup-id": data
["backup-id"],
218 "backup-time": (data
['backup-time'].getTime()/1000).toFixed(0),
222 "backup-type": data
.backup_type
,
223 "backup-id": data
.backup_id
,
227 Proxmox
.Utils
.API2Request({
229 url
: `/admin/datastore/${view.datastore}/verify`,
231 failure: function(response
) {
232 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
234 success: function(response
, options
) {
235 Ext
.create('Proxmox.window.TaskViewer', {
236 upid
: response
.result
.data
,
242 onForget: function() {
243 var view
= this.getView();
245 let rec
= view
.selModel
.getSelection()[0];
246 if (!(rec
&& rec
.data
)) return;
248 if (!data
.leaf
) return;
250 if (!view
.datastore
) return;
254 Proxmox
.Utils
.API2Request({
256 "backup-type": data
["backup-type"],
257 "backup-id": data
["backup-id"],
258 "backup-time": (data
['backup-time'].getTime()/1000).toFixed(0),
260 url
: `/admin/datastore/${view.datastore}/snapshots`,
263 failure: function(response
, opts
) {
264 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
266 callback
: this.reload
.bind(this),
270 openBackupFileDownloader: function() {
272 let view
= me
.getView();
274 let rec
= view
.selModel
.getSelection()[0];
275 if (!(rec
&& rec
.data
)) return;
278 Ext
.create('PBS.window.BackupFileDownloader', {
279 baseurl
: `/api2/json/admin/datastore/${view.datastore}`,
281 'backup-id': data
['backup-id'],
282 'backup-type': data
['backup-type'],
283 'backup-time': (data
['backup-time'].getTime()/1000).toFixed(0),
289 openPxarBrowser: function() {
291 let view
= me
.getView();
293 let rec
= view
.selModel
.getSelection()[0];
294 if (!(rec
&& rec
.data
)) return;
297 let encrypted
= false;
298 data
.files
.forEach(file
=> {
299 if (file
.filename
=== 'catalog.pcat1.didx' && file
.encrypted
) {
306 gettext('Cannot open Catalog'),
307 gettext('Only unencrypted Backups can be opened on the server. Please use the client with the decryption key instead.'),
312 let id
= data
['backup-id'];
313 let time
= data
['backup-time'];
314 let type
= data
['backup-type'];
315 let timetext
= PBS
.Utils
.render_datetime_utc(data
["backup-time"]);
317 Ext
.create('PBS.window.FileBrowser', {
318 title
: `${type}/${id}/${timetext}`,
319 datastore
: view
.datastore
,
321 'backup-time': (time
.getTime()/1000).toFixed(0),
330 header
: gettext("Backup Group"),
336 header
: gettext('Backup Time'),
338 dataIndex
: 'backup-time',
339 format
: 'Y-m-d H:i:s',
343 header
: gettext("Size"),
346 renderer
: Proxmox
.Utils
.format_size
,
349 xtype
: 'numbercolumn',
351 header
: gettext("Count"),
356 header
: gettext("Owner"),
361 header
: gettext('Encrypted'),
362 dataIndex
: 'encrypted',
363 renderer: function(value
) {
365 case 0: return Proxmox
.Utils
.noText
;
366 case 1: return gettext('Mixed');
367 case 2: return Proxmox
.Utils
.yesText
;
368 default: Proxmox
.Utils
.unknownText
;
373 header
: gettext("Files"),
376 renderer: function(files
) {
377 return files
.map((file
) => {
380 if (file
.encrypted
) {
381 icon
= '<i class="fa fa-lock"></i> ';
384 size
= ` (${Proxmox.Utils.format_size(file.size)})`;
386 return `${icon}${file.filename}${size}`;
395 text
: gettext('Reload'),
396 iconCls
: 'fa fa-refresh',
400 xtype
: 'proxmoxButton',
401 text
: gettext('Verify'),
403 parentXType
: 'pbsDataStoreContent',
404 enableFn: function(record
) { return !!record
.data
; },
408 xtype
: 'proxmoxButton',
409 text
: gettext('Prune'),
411 parentXType
: 'pbsDataStoreContent',
412 enableFn: function(record
) { return !record
.data
.leaf
; },
416 xtype
: 'proxmoxButton',
417 text
: gettext('Forget'),
419 parentXType
: 'pbsDataStoreContent',
421 confirmMsg: function(record
) {
423 let name
= record
.data
.text
;
424 return Ext
.String
.format(gettext('Are you sure you want to remove snapshot {0}'), `'${name}'`);
426 enableFn: function(record
) {
427 return !!record
.data
.leaf
;
431 xtype
: 'proxmoxButton',
432 text
: gettext('Download Files'),
434 parentXType
: 'pbsDataStoreContent',
435 handler
: 'openBackupFileDownloader',
436 enableFn: function(record
) {
437 return !!record
.data
.leaf
;
441 xtype
: "proxmoxButton",
442 text
: gettext('PXAR File Browser'),
444 handler
: 'openPxarBrowser',
445 parentXType
: 'pbsDataStoreContent',
446 enableFn: function(record
) {
447 return !!record
.data
.leaf
&& record
.data
.files
.some(el
=> el
.filename
.endsWith('pxar.didx'));