Project

General

Profile

Download (6.92 KB) Statistics
| Branch: | Tag: | Revision:

haketilo / html / payload_create.js @ 1c65dd5c

1
/**
2
 * This file is part of Haketilo.
3
 *
4
 * Function: Driving the site payload creation form.
5
 *
6
 * Copyright (C) 2022 Wojtek Kosior
7
 *
8
 * This program is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * As additional permission under GNU GPL version 3 section 7, you
19
 * may distribute forms of that code without the copy of the GNU
20
 * GPL normally required by section 4, provided you include this
21
 * license notice and, in case of non-source distribution, a URL
22
 * through which recipients can access the Corresponding Source.
23
 * If you modify file(s) with this exception, you may extend this
24
 * exception to your version of the file(s), but you are not
25
 * obligated to do so. If you do not wish to do so, delete this
26
 * exception statement from your version.
27
 *
28
 * As a special exception to the GPL, any HTML file which merely
29
 * makes function calls to this code, and for that purpose
30
 * includes it by reference shall be deemed a separate work for
31
 * copyright law purposes. If you modify this code, you may extend
32
 * this exception to your version of the code, but you are not
33
 * obligated to do so. If you do not wish to do so, delete this
34
 * exception statement from your version.
35
 *
36
 * You should have received a copy of the GNU General Public License
37
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
38
 *
39
 * I, Wojtek Kosior, thereby promise not to sue for violation of this file's
40
 * license. Although I request that you do not make use of this code in a
41
 * proprietary program, I am not going to enforce this in court.
42
 */
43

    
44
#IMPORT html/dialog.js
45
#IMPORT common/indexeddb.js AS haketilodb
46

    
47
#FROM html/DOM_helpers.js IMPORT clone_template
48
#FROM common/sha256.js    IMPORT sha256 AS compute_sha256
49
#FROM common/patterns.js  IMPORT validate_normalize_url_pattern, \
50
                                 patterns_doc_url
51

    
52
/* https://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid */
53
/* This is a helper function used by uuidv4(). */
54
function uuid_replace_num(num)
55
{
56
    const randbyte = crypto.getRandomValues(new Uint8Array(1))[0];
57
    return (num ^ randbyte & 15 >> num / 4).toString(16);
58
}
59

    
60
/* Generate a new UUID. */
61
function uuidv4()
62
{
63
    return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, uuid_replace_num);
64
}
65

    
66
function collect_form_data(form_ctx)
67
{
68
    const identifier_nonprepended = form_ctx.identifier.value;
69
    if (!identifier_nonprepended)
70
	throw "The 'identifier' field is required!";
71
    if (!/[-a-zA-Z]+/.test(identifier_nonprepended))
72
	throw "Identifier may only contain digits 0-9, lowercase letters a-z and hyphens '-'!"
73
    const identifier = `local-${identifier_nonprepended}`;
74

    
75
    const long_name = form_ctx.long_name.value || identifier_nonprepended;
76

    
77
    const description = form_ctx.description.value;
78

    
79
    const url_patterns = form_ctx.patterns.value.split("\n").filter(i => i);
80
    if (url_patterns.length === 0)
81
	throw "The 'URL patterns' field is required!";
82

    
83
    const payloads = {};
84

    
85
    for (let pattern of url_patterns) {
86
	pattern = validate_normalize_url_pattern(pattern);
87

    
88
	if (pattern in payloads)
89
	    throw `Pattern '${pattern}' specified multiple times!`;
90

    
91
	payloads[pattern] = {identifier};
92
    }
93

    
94
    const script = form_ctx.script.value;
95
    if (!script)
96
	throw "The 'script' field is required!";
97
    const sha256 = compute_sha256(script);
98

    
99
    const resource = {
100
	source_name: identifier,
101
	source_copyright: [],
102
	type: "resource",
103
	identifier,
104
	long_name,
105
	uuid: uuidv4(),
106
	version: [1],
107
	description,
108
	dependencies: [],
109
	scripts: [{file: "payload.js", sha256}]
110
    };
111

    
112
    const mapping = {
113
	source_name: identifier,
114
	source_copyright: [],
115
	type: "mapping",
116
	identifier,
117
	long_name,
118
	uuid: uuidv4(),
119
	version: [1],
120
	description,
121
	payloads
122
    };
123

    
124
    return {identifier, resource, mapping, files_by_sha256: {[sha256]: script}};
125
}
126

    
127
function clear_form(form_ctx)
128
{
129
    form_ctx.identifier.value = "";
130
    form_ctx.long_name.value = "";
131
    form_ctx.description.value = "";
132
    form_ctx.patterns.value = "https://example.com/***";
133
    form_ctx.script.value = `console.log("Hello, World!");`;
134
}
135

    
136
async function save_payload(saving)
137
{
138
    const db = await haketilodb.get();
139
    const tx_starter = haketilodb.start_items_transaction;
140
    const files = {sha256: saving.files_by_sha256};
141
    const tx_ctx = await tx_starter(["resource", "mapping"], files);
142

    
143
    for (const type of ["resource", "mapping"]) {
144
	if (!saving[`override_${type}`] &&
145
	    (await haketilodb.idb_get(tx_ctx.transaction, type,
146
				      saving.identifier))) {
147
	    saving.ask_override = type;
148
	    return;
149
	}
150
    }
151

    
152
    await haketilodb.save_item(saving.resource, tx_ctx);
153
    await haketilodb.save_item(saving.mapping,  tx_ctx);
154

    
155
    return haketilodb.finalize_transaction(tx_ctx);
156
}
157

    
158
function override_question(saving)
159
{
160
    return saving.ask_override === "resource" ?
161
	`Resource '${saving.identifier}' already exists. Override?` :
162
	`Mapping '${saving.identifier}' already exists. Override?`;
163
}
164

    
165
async function create_clicked(form_ctx)
166
{
167
    if (form_ctx.dialog_ctx.shown)
168
	return;
169

    
170
    try {
171
	var saving = collect_form_data(form_ctx);
172
    } catch(e) {
173
	dialog.error(form_ctx.dialog_ctx, e);
174
	return;
175
    }
176

    
177
    dialog.loader(form_ctx.dialog_ctx, "Saving payload...");
178

    
179
    try {
180
	do {
181
	    if (saving.ask_override) {
182
		const override_prom = dialog.ask(form_ctx.dialog_ctx,
183
						 override_question(saving));
184
		dialog.loader(form_ctx.dialog_ctx, "Saving payload...");
185
		dialog.close(form_ctx.dialog_ctx);
186
		if (!(await override_prom))
187
		    throw "Saving would override existing data.";
188

    
189
		saving[`override_${saving.ask_override}`] = true;
190
		delete saving.ask_override;
191
	    }
192

    
193
	    await save_payload(saving);
194
	} while (saving.ask_override);
195

    
196
	dialog.info(form_ctx.dialog_ctx, "Successfully saved payload!");
197
	clear_form(form_ctx);
198
    } catch(e) {
199
	console.error(e);
200
	dialog.error(form_ctx.dialog_ctx, "Failed to save payload :(");
201
    }
202

    
203
    dialog.close(form_ctx.dialog_ctx);
204
}
205

    
206
function on_show_hide(form_ctx, what_to_show)
207
{
208
    for (const item_id of ["form", "dialog"]) {
209
	const action = item_id === what_to_show ? "remove" : "add";
210
	form_ctx[`${item_id}_container`].classList[action]("hide");
211
    }
212
}
213

    
214
function payload_create_form()
215
{
216
    const form_ctx = clone_template("payload_create");
217

    
218
    form_ctx.dialog_ctx = dialog.make(() => on_show_hide(form_ctx, "dialog"),
219
				      () => on_show_hide(form_ctx, "form"));
220
    form_ctx.dialog_container.prepend(form_ctx.dialog_ctx.main_div);
221

    
222
    form_ctx.patterns_link.href = patterns_doc_url;
223
    form_ctx.create_but.addEventListener("click",
224
					 () => create_clicked(form_ctx));
225

    
226
    return form_ctx;
227
}
228
#EXPORT payload_create_form
(17-17/26)