Skip to content

Commit 72a0b39

Browse files
authored
Merge pull request #77 from wiseflat/dev/mgarcia/ui-import-export
feat(ui): add import and export functionality
2 parents d6c4558 + 4341cf5 commit 72a0b39

11 files changed

Lines changed: 409 additions & 52 deletions

File tree

ui/controllers/api.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ exports.install = function() {
6161
ROUTE('+API /api/ +variables_remove/{id} --> Variables/remove');
6262
ROUTE('+POST /api/secret --> Variables/secret');
6363

64+
// Settings
65+
ROUTE('+API /api/ -settings --> Settings/list');
66+
ROUTE('+API /api/ -settings_import --> Settings/import');
67+
ROUTE('+API /api/ -settings_export --> Settings/export');
68+
6469
// 3dForceGraph
6570
ROUTE('+API /api/ -graphs --> Graphs/list');
6671

ui/index.js.map

Lines changed: 54 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ui/public/forms/infrastructure.html

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,6 @@
1515
</div>
1616
</div>
1717
<hr class="nmt nmb" />
18-
<div class="padding">
19-
<div>
20-
<div class="m">
21-
<ui-component name="input" path="?.admin_login" config="required:1">@(Admin login)</ui-component>
22-
</div>
23-
<div class="m">
24-
<ui-component name="input" path="?.admin_pass" config="camouflage:1;placeholder:Sensible variable hidden after submission">@(Admin password)</ui-component>
25-
<div class="help exec link" data-exec="?/generate"><i class="ti ti-key mr5"></i>@(Generate password)</div>
26-
</div>
27-
<div class="m">
28-
<ui-component name="input" path="?.admin_ip" config="required:1">@(Admin ip address)</ui-component>
29-
</div>
30-
</div>
31-
</div>
32-
<hr class="nmt nmb" />
3318
<div class="padding">
3419
<div class="grid-2">
3520
<div class="m">
@@ -60,10 +45,6 @@
6045
caller = exports.caller;
6146
};
6247

63-
exports.generate = function() {
64-
exports.set('admin_pass', GUID(10));
65-
};
66-
6748
exports.submit = function(hide) {
6849
var form = exports.form;
6950
exports.tapi('infrastructures_{0} ERROR'.format(form.id ? ('update/' + form.id) : 'create'), form, function() {

ui/public/forms/settings.html

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<ui-component name="box" path="common.form" config="if:~PATH~;width:600;icon:ti ti-cog;title:@(Settings);reload:?/reload;" class="hidden ~PATH~" plugin="~PATH~">
2+
<div class="padding">
3+
<ui-component name="tabs" path="?.tab" class="invisible">
4+
<section data-id="upgrade" title="Upgrade" data-icon="ti ti-bell">
5+
<div class="padding"></div>
6+
</section>
7+
<section data-id="import" title="Import" data-icon="ti ti-cloud-upload">
8+
<div class="padding">
9+
<ui-component name="input" path="?.import_password" config="required:1">@(Set a password to encrypt your import)</ui-component>
10+
</div>
11+
<div class="padding">
12+
<ui-component name="textarea" path="?.import" config="height:200">Import data</ui-component>
13+
</div>
14+
<div class="padding">
15+
<ui-component name="button" config="exec:?/import;validation:true">Import data</ui-component>
16+
</div>
17+
</section>
18+
<section data-id="export" title="Export" data-icon="ti ti-cloud-download">
19+
<div class="padding">
20+
<ui-component name="input" path="?.export_password" config="required:1">@(Set a password to crypt your export)</ui-component>
21+
</div>
22+
<div class="padding">
23+
<ui-component name="checkboxlist" path="?.catalogs1" config="datasource:?.catalogs;type:number">Catalog items</ui-component>
24+
<br />
25+
<ui-component name="checkboxlist" path="?.projects1" config="datasource:?.projects;type:number">Projects</ui-component>
26+
</div>
27+
<div class="padding">
28+
<ui-component name="button" config="exec:?/export;validation:true">Export data</ui-component>
29+
</div>
30+
</section>
31+
</ui-component>
32+
</div>
33+
</ui-component>
34+
35+
<script>
36+
37+
PLUGIN(function(exports) {
38+
39+
exports.reload = function() {
40+
var model = exports.model;
41+
42+
exports.tapi('infrastructures ERROR', function(results){
43+
model.projects = results;
44+
model.catalogs = [{ id: 1, name: 'All items' }];
45+
exports.set(model);
46+
});
47+
};
48+
49+
exports.import = function() {
50+
var model = exports.form;
51+
exports.tapi('settings_import ERROR', { import: model.import, password: model.import_password }, function(response) {
52+
SETTER('notify/success', '@(Done)');
53+
REDIRECT('/ @showloading @hideloading');
54+
});
55+
};
56+
57+
exports.export = function() {
58+
var model = exports.form;
59+
exports.tapi('settings_export ERROR', { projects: model.projects1, password: model.export_password }, function(response) {
60+
SET('formsettingsExport @reset @hideloading', { export: response });
61+
SET('common.form2', 'formsettingsExport');
62+
});
63+
};
64+
});
65+
</script>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<ui-component name="miniform" path="common.form2" config="if:~PATH~;icon:cloud-download;title:@(Export);autofocus:true;reload:?/reload;width:600" class="hidden ~PATH~" plugin="~PATH~">
2+
<div>
3+
<div class="padding">
4+
<ui-component name="textarea" path="?.export" config="height:400">Export</ui-component>
5+
</div>
6+
</div>
7+
</ui-component>
8+
9+
<script>
10+
PLUGIN(function(exports) {
11+
exports.reload = function() {
12+
var model = exports.model;
13+
exports.set(model);
14+
};
15+
});
16+
</script>

ui/schemas/infrastructures.js

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,12 @@ NEWSCHEMA('Infrastructures', function (schema) {
6262

6363
schema.action('create', {
6464
name: 'Create project',
65-
input: '*name:String, *icon:Icon, *color:Color, *description:String, *admin_ip:String, *admin_login:String, *admin_pass:String',
65+
input: '*name:String, *icon:Icon, *color:Color, *description:String',
6666
action: async function ($, model) {
6767
// Input validation – early exit on first failure
6868
const validators = [
6969
{ fn: REGEX_PROJECTS.name, field: 'name' },
70-
{ fn: REGEX_PROJECTS.description, field: 'description' },
71-
{ fn: REGEX_PROJECTS.admin_ip, field: 'admin_ip' },
72-
{ fn: REGEX_PROJECTS.admin_login, field: 'admin_login' },
73-
{ fn: REGEX_PROJECTS.admin_pass, field: 'admin_pass' }
70+
{ fn: REGEX_PROJECTS.description, field: 'description' }
7471
];
7572
for (const v of validators) {
7673
if (v.optional && (model[v.field] === '' || model[v.field] == null)) continue;
@@ -83,7 +80,6 @@ NEWSCHEMA('Infrastructures', function (schema) {
8380
// Populate system fields
8481
model.id = UID();
8582
model.uid = $.user.id;
86-
model.admin_pass = model.admin_pass.sha256(process.env.AUTH_SECRET);
8783
model.dtcreated = new Date();
8884
model.isarchived = false;
8985
model.tfstate = { version: 4 };
@@ -102,7 +98,7 @@ NEWSCHEMA('Infrastructures', function (schema) {
10298
.read('nosql/infrastructures')
10399
.where('uid', $.user.id)
104100
.where('id', id)
105-
.fields('id,name,description,admin_login,admin_ip,icon,color')
101+
.fields('id,name,description,icon,color')
106102
.error('@(Error)')
107103
.promise($);
108104
$.callback(result);
@@ -112,42 +108,21 @@ NEWSCHEMA('Infrastructures', function (schema) {
112108
schema.action('update', {
113109
name: 'Update project',
114110
params: '*id:UID',
115-
input: '*name:String, *icon:Icon, *color:Color, *description:String, *admin_ip:String, *admin_login:String, admin_pass:String',
111+
input: '*name:String, *icon:Icon, *color:Color, *description:String',
116112
action: async function ($, model) {
117113
const { id } = $.params;
118114

119115
// Validate mutable fields
120116
const validators = [
121117
{ fn: REGEX_PROJECTS.name, field: 'name' },
122118
{ fn: REGEX_PROJECTS.description, field: 'description' },
123-
{ fn: REGEX_PROJECTS.admin_ip, field: 'admin_ip' },
124-
{ fn: REGEX_PROJECTS.admin_login, field: 'admin_login' }
125119
];
126120
for (const v of validators) {
127121
if (!FUNC.regex(v.fn, model[v.field])) {
128122
$.invalid(`${v.fn.comment}`);
129123
return;
130124
}
131125
}
132-
133-
// Password handling – only hash when a new password is supplied
134-
if (model.admin_pass) {
135-
if (!FUNC.regex(REGEX_PROJECTS.admin_pass, model.admin_pass)) {
136-
$.invalid(`${REGEX_PROJECTS.admin_pass.comment}`);
137-
return;
138-
}
139-
model.admin_pass = model.admin_pass.sha256(process.env.AUTH_SECRET);
140-
} else {
141-
// Preserve existing hash
142-
const existing = await DATA
143-
.read('nosql/infrastructures')
144-
.where('uid', $.user.id)
145-
.where('id', id)
146-
.fields('admin_pass')
147-
.error('@(Error)')
148-
.promise($);
149-
model.admin_pass = existing.admin_pass;
150-
}
151126

152127
model.dtupdated = new Date();
153128

@@ -202,4 +177,35 @@ NEWSCHEMA('Infrastructures', function (schema) {
202177
$.success();
203178
}
204179
});
180+
181+
schema.action('export', {
182+
name: 'Export all infrastructures',
183+
params: '*ids:String',
184+
action: async function ($) {
185+
const result = await DATA
186+
.list('nosql/infrastructures')
187+
.where('uid', $.user.id)
188+
.in('id', $.params.ids.split(','))
189+
.error('@(Error)')
190+
.promise($);
191+
$.callback(result.items);
192+
}
193+
});
194+
195+
schema.action('import', {
196+
name: 'Import an infrastructure',
197+
params: '*id:UID',
198+
input: '*color:Color, *description:String, *dtcreated:String, *icon:Icon, isarchived:Boolean, *name:String, *tfstate:Json',
199+
action: async function ($, model) {
200+
const { id } = $.params;
201+
model.tfstate = JSON.parse(model.tfstate);
202+
203+
DATA.modify('nosql/infrastructures', model, true).where('id', id).insert(function(doc) {
204+
doc.uid = $.user.id;
205+
doc.id = id;
206+
doc.dtupdated = NOW;
207+
});
208+
$.success();
209+
}
210+
});
205211
});

0 commit comments

Comments
 (0)