vesta-web 1.1.0__py3-none-any.whl → 1.1.2__py3-none-any.whl
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.
- vesta/db/db_service.py +10 -3
- vesta/emptyProject/.gitlab-ci.yml +0 -1
- vesta/emptyProject/requirements.txt +1 -0
- vesta/emptyProject/static/framework/global.mjs +11 -0
- vesta/emptyProject/static/framework/navigation.mjs +295 -0
- vesta/emptyProject/static/framework/templating.mjs +110 -0
- vesta/emptyProject/static/framework/vesta.mjs +173 -0
- vesta/emptyProject/static/framework/websockets.mjs +41 -0
- vesta/emptyProject/static/markdown/markdown.mjs +463 -0
- vesta/emptyProject/static/markdown/utils.mjs +21 -0
- vesta/http/baseServer.py +1 -1
- vesta/http/response.py +15 -5
- vesta/mailing/mailing_service.py +10 -9
- vesta/scripts/initDB.py +11 -9
- vesta/scripts/testsRun.py +6 -6
- {vesta_web-1.1.0.dist-info → vesta_web-1.1.2.dist-info}/METADATA +1 -1
- {vesta_web-1.1.0.dist-info → vesta_web-1.1.2.dist-info}/RECORD +21 -14
- vesta_web-1.1.2.dist-info/entry_points.txt +2 -0
- vesta_web-1.1.0.dist-info/entry_points.txt +0 -2
- /vesta/scripts/{vesta.py → cli.py} +0 -0
- {vesta_web-1.1.0.dist-info → vesta_web-1.1.2.dist-info}/WHEEL +0 -0
- {vesta_web-1.1.0.dist-info → vesta_web-1.1.2.dist-info}/licenses/LICENSE.md +0 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
// This in absolutely no spec, it comes exclusively from my deranged mind
|
|
2
|
+
|
|
3
|
+
export class Markdown {
|
|
4
|
+
constructor() {
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
HTML_equiv = {
|
|
8
|
+
"#":"<h${props.level}>${content}</h${props.level}>",
|
|
9
|
+
"text":"${content}",
|
|
10
|
+
"start li":"<li>${content}</li>",
|
|
11
|
+
"*":"<i>${content}</i>",
|
|
12
|
+
"**":"<b>${content}</b>",
|
|
13
|
+
">":"<div class='answer'>${content}</div>",
|
|
14
|
+
"'''":"<div class='code-wrapper'><code>${content}</code><div class='circle grey' onclick='copyCode(event)'></div></div>",
|
|
15
|
+
"~~":"<div class='crossed'>${content}</div>",
|
|
16
|
+
"||":"<div class='spoiler' onclick='showSpoiler(event)'>${content}</div>",
|
|
17
|
+
"link":"<a href='${props.link}' target='_blank'>${content}</a>",
|
|
18
|
+
"color":"<div class='color' style='color: ${props.color}'>${content}</div>",
|
|
19
|
+
"endline":"\n",
|
|
20
|
+
"newline":"",
|
|
21
|
+
")":")",
|
|
22
|
+
"'":"'",
|
|
23
|
+
"(":"(",
|
|
24
|
+
"/>":"/>",
|
|
25
|
+
"]":"]",
|
|
26
|
+
"/":"${content}/"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
tokenize(str) {
|
|
30
|
+
const tokenList = []
|
|
31
|
+
let currentToken = new Token("newline")
|
|
32
|
+
let commitEndline = false
|
|
33
|
+
|
|
34
|
+
let char_id=0
|
|
35
|
+
while (char_id < str.length ){
|
|
36
|
+
|
|
37
|
+
//look for url
|
|
38
|
+
if(str.slice(char_id,char_id+6) === "https:" || str.slice(char_id,char_id+5) === "http:" ){
|
|
39
|
+
if (currentToken.content !== ""){
|
|
40
|
+
tokenList.push(currentToken)
|
|
41
|
+
currentToken = new Token("link")
|
|
42
|
+
}else{
|
|
43
|
+
currentToken.type="link"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let look_id = 5
|
|
47
|
+
let nextToken
|
|
48
|
+
while (char_id+look_id<str.length){
|
|
49
|
+
if(str[char_id+look_id]=== "\n"){
|
|
50
|
+
nextToken = new Token("newline")
|
|
51
|
+
look_id++
|
|
52
|
+
break
|
|
53
|
+
}
|
|
54
|
+
if([" ",")","]"].includes(str[char_id+look_id])){
|
|
55
|
+
nextToken = new Token("text")
|
|
56
|
+
break
|
|
57
|
+
}
|
|
58
|
+
look_id++
|
|
59
|
+
}
|
|
60
|
+
currentToken.content = str.slice(char_id,char_id+look_id)
|
|
61
|
+
currentToken.props.link = currentToken.content
|
|
62
|
+
tokenList.push(currentToken)
|
|
63
|
+
if(char_id+look_id>=str.length){
|
|
64
|
+
return tokenList;
|
|
65
|
+
}
|
|
66
|
+
char_id += look_id
|
|
67
|
+
currentToken = nextToken
|
|
68
|
+
}else{
|
|
69
|
+
const char = str[char_id]
|
|
70
|
+
switch (currentToken.type){
|
|
71
|
+
case "text":
|
|
72
|
+
switch (char){
|
|
73
|
+
case '*':
|
|
74
|
+
if (currentToken.content !== ""){
|
|
75
|
+
tokenList.push(currentToken)
|
|
76
|
+
}
|
|
77
|
+
currentToken = new Token("*")
|
|
78
|
+
currentToken.props.level = 1
|
|
79
|
+
break
|
|
80
|
+
case '[':
|
|
81
|
+
case ']':
|
|
82
|
+
case '(':
|
|
83
|
+
case ')':
|
|
84
|
+
if (currentToken.content !== ""){
|
|
85
|
+
tokenList.push(currentToken)
|
|
86
|
+
}
|
|
87
|
+
tokenList.push(new Token(char))
|
|
88
|
+
currentToken = new Token("text")
|
|
89
|
+
break
|
|
90
|
+
case '|':
|
|
91
|
+
case '~':
|
|
92
|
+
case "'":
|
|
93
|
+
case '/':
|
|
94
|
+
case '&':
|
|
95
|
+
case '<':
|
|
96
|
+
currentToken.type = char
|
|
97
|
+
break
|
|
98
|
+
case '\n':
|
|
99
|
+
if (commitEndline){
|
|
100
|
+
commitEndline = false
|
|
101
|
+
tokenList.push(currentToken)
|
|
102
|
+
tokenList.push(new Token("endline"))
|
|
103
|
+
currentToken = new Token("newline")
|
|
104
|
+
}else{
|
|
105
|
+
currentToken.content = currentToken.content.concat(char)
|
|
106
|
+
currentToken.props.consuming="text"
|
|
107
|
+
currentToken.type="newline"
|
|
108
|
+
}
|
|
109
|
+
break
|
|
110
|
+
default:
|
|
111
|
+
currentToken.content = currentToken.content.concat(char)
|
|
112
|
+
}
|
|
113
|
+
break
|
|
114
|
+
case "#":
|
|
115
|
+
switch (char){
|
|
116
|
+
case '#':
|
|
117
|
+
currentToken.props.level = currentToken.props.level<3 ? currentToken.props.level+1 : 3
|
|
118
|
+
break
|
|
119
|
+
case '\n':
|
|
120
|
+
tokenList.push(currentToken)
|
|
121
|
+
currentToken = new Token("newline")
|
|
122
|
+
break
|
|
123
|
+
default:
|
|
124
|
+
currentToken.content = currentToken.content.concat(char)
|
|
125
|
+
}
|
|
126
|
+
break
|
|
127
|
+
case "newline":
|
|
128
|
+
switch (char){
|
|
129
|
+
case '#':
|
|
130
|
+
if (currentToken.content.trim() !== ""){
|
|
131
|
+
currentToken.type=currentToken.props.consuming
|
|
132
|
+
tokenList.push(currentToken)
|
|
133
|
+
}
|
|
134
|
+
currentToken = new Token("#")
|
|
135
|
+
currentToken.props["level"] = 1
|
|
136
|
+
break
|
|
137
|
+
case '-':
|
|
138
|
+
if (currentToken.content.trim() !== ""){
|
|
139
|
+
currentToken.type=currentToken.props.consuming
|
|
140
|
+
tokenList.push(currentToken)
|
|
141
|
+
}
|
|
142
|
+
currentToken = new Token("text")
|
|
143
|
+
tokenList.push(new Token("start li"))
|
|
144
|
+
commitEndline = true
|
|
145
|
+
break
|
|
146
|
+
case '>':
|
|
147
|
+
if (currentToken.content.trim() !== ""){
|
|
148
|
+
currentToken.type=currentToken.props.consuming
|
|
149
|
+
tokenList.push(currentToken)
|
|
150
|
+
}
|
|
151
|
+
currentToken = new Token("text")
|
|
152
|
+
tokenList.push(new Token(">"))
|
|
153
|
+
commitEndline = true
|
|
154
|
+
break
|
|
155
|
+
case '*':
|
|
156
|
+
currentToken = new Token("*")
|
|
157
|
+
currentToken.props.level = 1
|
|
158
|
+
break
|
|
159
|
+
case '|':
|
|
160
|
+
case '~':
|
|
161
|
+
case "'":
|
|
162
|
+
case '/':
|
|
163
|
+
case '&':
|
|
164
|
+
case '<':
|
|
165
|
+
currentToken.type = char
|
|
166
|
+
break
|
|
167
|
+
case '[':
|
|
168
|
+
case ']':
|
|
169
|
+
case '(':
|
|
170
|
+
case ')':
|
|
171
|
+
tokenList.push(new Token(char))
|
|
172
|
+
currentToken = new Token("text")
|
|
173
|
+
break
|
|
174
|
+
case ' ':
|
|
175
|
+
currentToken.content = currentToken.content.concat(char)
|
|
176
|
+
break
|
|
177
|
+
case '\n':
|
|
178
|
+
currentToken.type = "text"
|
|
179
|
+
tokenList.push(currentToken)
|
|
180
|
+
tokenList.push(new Token("endline"))
|
|
181
|
+
currentToken = new Token("newline")
|
|
182
|
+
break
|
|
183
|
+
default:
|
|
184
|
+
currentToken.type="text"
|
|
185
|
+
currentToken.content = currentToken.content.concat(char)
|
|
186
|
+
}
|
|
187
|
+
break
|
|
188
|
+
case "*":
|
|
189
|
+
switch (char){
|
|
190
|
+
case "*":
|
|
191
|
+
currentToken.props.level = currentToken.props.level<3 ? currentToken.props.level+1 : 3
|
|
192
|
+
break
|
|
193
|
+
case '\n':
|
|
194
|
+
tokenList.push(currentToken)
|
|
195
|
+
currentToken = new Token("newline")
|
|
196
|
+
currentToken.content = "\n"
|
|
197
|
+
if (commitEndline) {
|
|
198
|
+
commitEndline = false
|
|
199
|
+
}
|
|
200
|
+
break
|
|
201
|
+
case '|':
|
|
202
|
+
case '~':
|
|
203
|
+
case '/':
|
|
204
|
+
tokenList.push(currentToken)
|
|
205
|
+
currentToken = new Token(char)
|
|
206
|
+
break
|
|
207
|
+
default:
|
|
208
|
+
tokenList.push(currentToken)
|
|
209
|
+
currentToken = new Token("text")
|
|
210
|
+
currentToken.content = currentToken.content.concat(char)
|
|
211
|
+
}
|
|
212
|
+
break
|
|
213
|
+
case '|':
|
|
214
|
+
switch (char){
|
|
215
|
+
case "|":
|
|
216
|
+
if (currentToken.content !== ""){
|
|
217
|
+
currentToken.type = "text"
|
|
218
|
+
tokenList.push(currentToken)
|
|
219
|
+
}
|
|
220
|
+
currentToken = new Token("||")
|
|
221
|
+
tokenList.push(currentToken)
|
|
222
|
+
currentToken = new Token("text")
|
|
223
|
+
break
|
|
224
|
+
default:
|
|
225
|
+
currentToken.type="text"
|
|
226
|
+
currentToken.content="|".concat(char)
|
|
227
|
+
}
|
|
228
|
+
break
|
|
229
|
+
case '~':
|
|
230
|
+
switch (char){
|
|
231
|
+
case "~":
|
|
232
|
+
if (currentToken.content !== ""){
|
|
233
|
+
currentToken.type = "text"
|
|
234
|
+
tokenList.push(currentToken)
|
|
235
|
+
}
|
|
236
|
+
currentToken = new Token("~~")
|
|
237
|
+
tokenList.push(currentToken)
|
|
238
|
+
currentToken = new Token("text")
|
|
239
|
+
break
|
|
240
|
+
default:
|
|
241
|
+
currentToken.type="text"
|
|
242
|
+
currentToken.content="~".concat(char)
|
|
243
|
+
}
|
|
244
|
+
break
|
|
245
|
+
case "'":
|
|
246
|
+
switch (char){
|
|
247
|
+
case "'":
|
|
248
|
+
if(!currentToken.props.level || currentToken.props.level<2){
|
|
249
|
+
currentToken.props.level = currentToken.props.level ? currentToken.props.level+1 : 2
|
|
250
|
+
}else{
|
|
251
|
+
currentToken = new Token("'''")
|
|
252
|
+
currentToken.props.level=0
|
|
253
|
+
}
|
|
254
|
+
break
|
|
255
|
+
default:
|
|
256
|
+
currentToken.type = "text"
|
|
257
|
+
currentToken.content = currentToken.content.concat("'",char)
|
|
258
|
+
}
|
|
259
|
+
break
|
|
260
|
+
case "'''":
|
|
261
|
+
switch (char){
|
|
262
|
+
case "'":
|
|
263
|
+
if(currentToken.props.level<2){
|
|
264
|
+
currentToken.props.level += 1
|
|
265
|
+
}else{
|
|
266
|
+
tokenList.push(currentToken)
|
|
267
|
+
currentToken = new Token("text")
|
|
268
|
+
}
|
|
269
|
+
break
|
|
270
|
+
default:
|
|
271
|
+
if(currentToken.props.level !==0){
|
|
272
|
+
currentToken.content = currentToken.content.concat("'".repeat(currentToken.props.level))
|
|
273
|
+
currentToken.props.level = 0
|
|
274
|
+
}
|
|
275
|
+
currentToken.content = currentToken.content.concat(char)
|
|
276
|
+
}
|
|
277
|
+
break
|
|
278
|
+
case "&":
|
|
279
|
+
if(str.slice(char_id, char_id+3) === "lt;"){
|
|
280
|
+
if (currentToken.content !== ""){
|
|
281
|
+
currentToken.type = "text"
|
|
282
|
+
tokenList.push(currentToken)
|
|
283
|
+
}
|
|
284
|
+
char_id+=2
|
|
285
|
+
currentToken = new Token("<")
|
|
286
|
+
}else{
|
|
287
|
+
currentToken.content = currentToken.content.concat(char)
|
|
288
|
+
currentToken.type = "text"
|
|
289
|
+
}
|
|
290
|
+
break
|
|
291
|
+
case "<":
|
|
292
|
+
switch (char){
|
|
293
|
+
case "$":
|
|
294
|
+
if (currentToken.content !== ""){
|
|
295
|
+
currentToken.type = "text"
|
|
296
|
+
tokenList.push(currentToken)
|
|
297
|
+
}
|
|
298
|
+
currentToken = new Token("color")
|
|
299
|
+
currentToken.props.color=""
|
|
300
|
+
break
|
|
301
|
+
default:
|
|
302
|
+
currentToken.type = "text"
|
|
303
|
+
currentToken.content = currentToken.content.concat("<", char)
|
|
304
|
+
}
|
|
305
|
+
break
|
|
306
|
+
case "color":
|
|
307
|
+
switch (char){
|
|
308
|
+
case " ":
|
|
309
|
+
tokenList.push(currentToken)
|
|
310
|
+
currentToken = new Token("text")
|
|
311
|
+
break
|
|
312
|
+
case "\n":
|
|
313
|
+
tokenList.push(currentToken)
|
|
314
|
+
currentToken = new Token("newline")
|
|
315
|
+
break
|
|
316
|
+
default:
|
|
317
|
+
currentToken.props.color = currentToken.props.color.concat(char)
|
|
318
|
+
}
|
|
319
|
+
break
|
|
320
|
+
case "/":
|
|
321
|
+
switch (char){
|
|
322
|
+
case '>':
|
|
323
|
+
if (currentToken.content !== ""){
|
|
324
|
+
currentToken.type = "text"
|
|
325
|
+
tokenList.push(currentToken)
|
|
326
|
+
}
|
|
327
|
+
currentToken = new Token("/>")
|
|
328
|
+
tokenList.push(currentToken)
|
|
329
|
+
currentToken = new Token("text")
|
|
330
|
+
break
|
|
331
|
+
default:
|
|
332
|
+
currentToken.type = "text"
|
|
333
|
+
currentToken.content = currentToken.content.concat("/", char)
|
|
334
|
+
}
|
|
335
|
+
break
|
|
336
|
+
}
|
|
337
|
+
char_id += 1
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
tokenList.push(currentToken)
|
|
341
|
+
return tokenList;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
render(tokens) {
|
|
345
|
+
let result = ""
|
|
346
|
+
for(let token_id = 0; token_id < tokens.length; token_id+=1){
|
|
347
|
+
const render = this.renderToken(tokens[token_id], token_id, tokens)
|
|
348
|
+
token_id = render[1]
|
|
349
|
+
result = result.concat(render[0])
|
|
350
|
+
}
|
|
351
|
+
return result;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
renderToken(token, token_id, tokens){
|
|
355
|
+
// console.log("render token",token,token_id,tokens)
|
|
356
|
+
const old_id = token_id
|
|
357
|
+
let content = ""
|
|
358
|
+
switch(token.type) {
|
|
359
|
+
case ">":
|
|
360
|
+
case "start li":
|
|
361
|
+
while (token_id + 1 < tokens.length && tokens[token_id + 1].type !== "endline") {
|
|
362
|
+
token_id += 1
|
|
363
|
+
const render = this.renderToken(tokens[token_id], token_id, tokens)
|
|
364
|
+
token_id = render[1]
|
|
365
|
+
content = content.concat(render[0])
|
|
366
|
+
}
|
|
367
|
+
token.content = content
|
|
368
|
+
return [fillTemplate(this.HTML_equiv[token.type], token), token_id+1]
|
|
369
|
+
case "*":
|
|
370
|
+
// this is suboptimal because we're looking for same size closing
|
|
371
|
+
// smth like *** a ** b * will not work as intended
|
|
372
|
+
while (token_id + 1 < tokens.length && tokens[token_id + 1].type !== "endline") {
|
|
373
|
+
token_id += 1
|
|
374
|
+
if(tokens[token_id].type === "*" && tokens[token_id].props.level === token.props.level){
|
|
375
|
+
token.content = content
|
|
376
|
+
if (token.props.level % 2) {
|
|
377
|
+
if (token.props.level === 3) {
|
|
378
|
+
token.content = fillTemplate(this.HTML_equiv["**"], token)
|
|
379
|
+
}
|
|
380
|
+
return [fillTemplate(this.HTML_equiv["*"], token), token_id]
|
|
381
|
+
} else {
|
|
382
|
+
return [fillTemplate(this.HTML_equiv["**"], token), token_id]
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const render = this.renderToken(tokens[token_id], token_id, tokens)
|
|
386
|
+
token_id = render[1]
|
|
387
|
+
content = content.concat(render[0])
|
|
388
|
+
}
|
|
389
|
+
token.content = "*".repeat(token.props.level)
|
|
390
|
+
// should be optimized, because we just drop a part of the render here
|
|
391
|
+
return [fillTemplate(this.HTML_equiv["text"], token), old_id]
|
|
392
|
+
case "~~":
|
|
393
|
+
case "||":
|
|
394
|
+
while (token_id + 1 < tokens.length) {
|
|
395
|
+
token_id += 1
|
|
396
|
+
if(tokens[token_id].type === token.type){
|
|
397
|
+
token.content = content
|
|
398
|
+
return [fillTemplate(this.HTML_equiv[token.type], token), token_id]
|
|
399
|
+
}
|
|
400
|
+
const render = this.renderToken(tokens[token_id], token_id, tokens)
|
|
401
|
+
token_id = render[1]
|
|
402
|
+
content = content.concat(render[0])
|
|
403
|
+
}
|
|
404
|
+
token.content = token.type
|
|
405
|
+
return [fillTemplate(this.HTML_equiv["text"], token), old_id]
|
|
406
|
+
case "[":
|
|
407
|
+
while (token_id + 1 < tokens.length) {
|
|
408
|
+
token_id += 1
|
|
409
|
+
if(tokens[token_id].type === "\n"){
|
|
410
|
+
break
|
|
411
|
+
}
|
|
412
|
+
if(tokens[token_id].type === "]"){
|
|
413
|
+
if(tokens[token_id+1].type === "(" && tokens[token_id+2].type === "link" && tokens[token_id+3].type === ")"){
|
|
414
|
+
token.content = content
|
|
415
|
+
token.props.link = tokens[token_id+2].content
|
|
416
|
+
return [fillTemplate(this.HTML_equiv["link"], token), token_id+3]
|
|
417
|
+
}else{
|
|
418
|
+
break
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
const render = this.renderToken(tokens[token_id], token_id, tokens)
|
|
422
|
+
token_id = render[1]
|
|
423
|
+
content = content.concat(render[0])
|
|
424
|
+
}
|
|
425
|
+
token.content = token.type
|
|
426
|
+
return [fillTemplate(this.HTML_equiv["text"], token), old_id]
|
|
427
|
+
case "color":
|
|
428
|
+
while (token_id + 1 < tokens.length) {
|
|
429
|
+
token_id += 1
|
|
430
|
+
if(tokens[token_id].type === "/>"){
|
|
431
|
+
token.content = content
|
|
432
|
+
return [fillTemplate(this.HTML_equiv[token.type], token), token_id]
|
|
433
|
+
}
|
|
434
|
+
const render = this.renderToken(tokens[token_id], token_id, tokens)
|
|
435
|
+
token_id = render[1]
|
|
436
|
+
content = content.concat(render[0])
|
|
437
|
+
}
|
|
438
|
+
token.type="text"
|
|
439
|
+
token.content = "<$"+token.props?.color
|
|
440
|
+
return [fillTemplate(this.HTML_equiv[token.type], token), token_id]
|
|
441
|
+
default:
|
|
442
|
+
return [fillTemplate(this.HTML_equiv[token.type], token), token_id]
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
class Token{
|
|
448
|
+
constructor(type) {
|
|
449
|
+
this.type = type
|
|
450
|
+
this.content=""
|
|
451
|
+
this.props={}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const fillTemplate = (template, vars = {}) => {
|
|
456
|
+
const handler = new Function('vars', [
|
|
457
|
+
'const tagged = ( ' + Object.keys(vars).join(', ') + ' ) =>',
|
|
458
|
+
'`' + template + '`',
|
|
459
|
+
'return tagged(...Object.values(vars))'
|
|
460
|
+
].join('\n'));
|
|
461
|
+
const res = handler(vars)
|
|
462
|
+
return res;
|
|
463
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {Markdown} from "./markdown.mjs";
|
|
2
|
+
|
|
3
|
+
export function MDToHTML(text){
|
|
4
|
+
const engine = new Markdown()
|
|
5
|
+
const tokens = engine.tokenize(text)
|
|
6
|
+
console.log("text",text,"tokens",tokens)
|
|
7
|
+
return engine.render(tokens)
|
|
8
|
+
}
|
|
9
|
+
window.MDToHTML = MDToHTML
|
|
10
|
+
|
|
11
|
+
export function showSpoiler(event){
|
|
12
|
+
event.currentTarget.classList.toggle("clicked")
|
|
13
|
+
}
|
|
14
|
+
window.showSpoiler = showSpoiler
|
|
15
|
+
|
|
16
|
+
export function copyCode(event){
|
|
17
|
+
navigator.clipboard.writeText(event.currentTarget.parentElement.firstElementChild.innerHTML).then(
|
|
18
|
+
event.currentTarget.style.background = "var(--green)"
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
window.copyCode = copyCode
|
vesta/http/baseServer.py
CHANGED
|
@@ -116,7 +116,7 @@ class BaseServer:
|
|
|
116
116
|
query = re.split(RE_URL, environ['QUERY_STRING'])
|
|
117
117
|
for i in range(0, len(query)):
|
|
118
118
|
query[i] = re.split(RE_PARAM, query[i])
|
|
119
|
-
args[query[i][0]] = urllib.parse.
|
|
119
|
+
args[query[i][0]] = urllib.parse.unquote_plus(query[i][1], encoding='utf-8')
|
|
120
120
|
if content_type[0] == "multipart/form-data":
|
|
121
121
|
length = int(environ.get('CONTENT_LENGTH'))
|
|
122
122
|
body = environ['wsgi.input'].read(length)
|
vesta/http/response.py
CHANGED
|
@@ -7,7 +7,17 @@ class Response:
|
|
|
7
7
|
def __init__(self, start_response, code=200, type="html"):
|
|
8
8
|
self.cookies = {}
|
|
9
9
|
self.type = type
|
|
10
|
-
self.headers = [
|
|
10
|
+
self.headers = [
|
|
11
|
+
('Content-Type', 'text/' + type + '; charset=utf-8'),
|
|
12
|
+
('Cache-Control', 'no-cache'),
|
|
13
|
+
('Server', 'Vesta v1 Harpie'),
|
|
14
|
+
# Security headers
|
|
15
|
+
('X-Content-Type-Options', 'nosniff'),
|
|
16
|
+
('X-Frame-Options', 'DENY'),
|
|
17
|
+
('X-XSS-Protection', '1; mode=block'),
|
|
18
|
+
('Referrer-Policy', 'strict-origin-when-cross-origin'),
|
|
19
|
+
('Permissions-Policy', 'geolocation=(), microphone=(), camera=()')
|
|
20
|
+
]
|
|
11
21
|
self.code = code
|
|
12
22
|
self.start_response = start_response
|
|
13
23
|
self.content = ""
|
|
@@ -16,21 +26,21 @@ class Response:
|
|
|
16
26
|
if self.code != 200 and self.code != 302:
|
|
17
27
|
if self.code in self.ERROR_PAGES.keys():
|
|
18
28
|
self.type = "html"
|
|
19
|
-
self.headers = [('Content-Type', 'text/html')]
|
|
29
|
+
self.headers = [('Content-Type', 'text/html; charset=utf-8')]
|
|
20
30
|
file = open(self.ERROR_PAGES[self.code])
|
|
21
31
|
self.content = file.read()
|
|
22
32
|
file.close()
|
|
23
33
|
else:
|
|
24
34
|
self.type = "plain"
|
|
25
|
-
self.headers = [('Content-Type', 'text/plain')]
|
|
35
|
+
self.headers = [('Content-Type', 'text/plain; charset=utf-8')]
|
|
26
36
|
self.start_response(self.CODES.get(self.code, "500 UNEXPECTED"), self.headers)
|
|
27
37
|
|
|
28
38
|
def encode(self):
|
|
29
39
|
# print("[INFO] encoding response : ", self.content)
|
|
30
40
|
|
|
31
41
|
if self.type == "plain":
|
|
32
|
-
return (self.CODES[self.code] + " " + self.content).encode()
|
|
33
|
-
return str(self.content).encode()
|
|
42
|
+
return (self.CODES[self.code] + " " + self.content).encode('utf-8')
|
|
43
|
+
return str(self.content).encode('utf-8')
|
|
34
44
|
|
|
35
45
|
def set_cookie(self, name, value, exp=None, samesite=None, secure=False, httponly=False):
|
|
36
46
|
"""Set a response cookie for the client.
|
vesta/mailing/mailing_service.py
CHANGED
|
@@ -103,15 +103,16 @@ class Mailing:
|
|
|
103
103
|
self.sendMail(mail_confirmation)
|
|
104
104
|
|
|
105
105
|
def sendOrgInvite(self, target, company):
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
106
|
+
if not hasattr(self, 'template_org_invitation'):
|
|
107
|
+
try:
|
|
108
|
+
with open(self.path + "/mailing/mailInvite.html", "r") as f:
|
|
109
|
+
self.template_org_invitation = f.read()
|
|
110
|
+
self.mail_invitation = MIMEMultipart('alternative')
|
|
111
|
+
self.mail_invitation['Subject'] = "🔔 Rejoignez " + company + " 🔔"
|
|
112
|
+
self.mail_invitation['From'] = self.name + " <" + self.address + ">"
|
|
113
|
+
except (FileNotFoundError, IOError) as e:
|
|
114
|
+
print(f"[Vesta - mails] Error loading org invitation template: {e}")
|
|
115
|
+
raise
|
|
115
116
|
|
|
116
117
|
self.mail_invitation['Message-ID'] = email.utils.make_msgid(domain='carbonlab.dev')
|
|
117
118
|
self.mail_invitation['Date'] = email.utils.formatdate()
|
vesta/scripts/initDB.py
CHANGED
|
@@ -22,20 +22,22 @@ class DBInitializer(Server):
|
|
|
22
22
|
sql.SQL("""
|
|
23
23
|
CREATE SERVER if not exists uniauth
|
|
24
24
|
FOREIGN DATA WRAPPER postgres_fdw
|
|
25
|
-
OPTIONS (host
|
|
26
|
-
""")
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
OPTIONS (host %s, port %s, dbname %s);
|
|
26
|
+
"""),
|
|
27
|
+
(
|
|
28
|
+
self.config.get('UNIAUTH', 'DB_HOST'),
|
|
29
|
+
self.config.get('UNIAUTH', 'DB_PORT'),
|
|
30
|
+
self.config.get('UNIAUTH', 'DB_NAME')
|
|
30
31
|
))
|
|
31
32
|
|
|
32
33
|
self.db.cur.execute(
|
|
33
34
|
sql.SQL("""
|
|
34
35
|
CREATE USER MAPPING if not exists FOR CURRENT_USER SERVER uniauth
|
|
35
|
-
OPTIONS (user
|
|
36
|
-
""")
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
OPTIONS (user %s, password %s);
|
|
37
|
+
"""),
|
|
38
|
+
(
|
|
39
|
+
self.config.get('DB', 'DB_USER'),
|
|
40
|
+
self.config.get('DB', 'DB_PASSWORD')
|
|
39
41
|
))
|
|
40
42
|
self.db.cur.execute(
|
|
41
43
|
"""
|
vesta/scripts/testsRun.py
CHANGED
|
@@ -20,14 +20,16 @@ def run_file(file_path):
|
|
|
20
20
|
# Look for a 'run' function within the module (assuming tests are run from this function)
|
|
21
21
|
if hasattr(test_module, 'run'):
|
|
22
22
|
try:
|
|
23
|
+
count = (0,0)
|
|
23
24
|
result = test_module.run()
|
|
24
25
|
for res in result:
|
|
25
26
|
if res[1] == False:
|
|
27
|
+
count = (count[0],count[1]+1)
|
|
26
28
|
print(Fore.RED +f"FAILED: '{res[0]}' @{readable_path}")
|
|
27
|
-
return False
|
|
28
29
|
else:
|
|
30
|
+
count = (count[0]+1,count[1]+1)
|
|
29
31
|
print(Fore.GREEN +f"PASSED: '{res[0]}' @{readable_path}")
|
|
30
|
-
|
|
32
|
+
return count
|
|
31
33
|
except Exception as e:
|
|
32
34
|
print(f"Error running test file '{readable_path}': {e}")
|
|
33
35
|
else:
|
|
@@ -45,10 +47,8 @@ def run_folder(folder):
|
|
|
45
47
|
if file.endswith('.py'):
|
|
46
48
|
file_path = os.path.join(root, file)
|
|
47
49
|
res = run_file(file_path)
|
|
48
|
-
if res
|
|
49
|
-
counter = (counter[0]+
|
|
50
|
-
elif res == False:
|
|
51
|
-
counter = (counter[0],counter[1]+1)
|
|
50
|
+
if res:
|
|
51
|
+
counter = (counter[0] + res[0], counter[1] + res[1])
|
|
52
52
|
|
|
53
53
|
return counter
|
|
54
54
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vesta-web
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.2
|
|
4
4
|
Summary: An extensive web framework adding every feature needed for Carbonlab
|
|
5
5
|
Project-URL: Homepage, https://gitlab.com/Louciole/vesta
|
|
6
6
|
Project-URL: Issues, https://gitlab.com/Louciole/vesta/-/issues
|