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.
@@ -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("&lt;", 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.unquote(query[i][1])
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 = [('Content-Type', 'text/' + type),('Cache-Control', 'no-cache'), ('Server', 'mag v1 Harpie')]
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.
@@ -103,15 +103,16 @@ class Mailing:
103
103
  self.sendMail(mail_confirmation)
104
104
 
105
105
  def sendOrgInvite(self, target, company):
106
- try:
107
- self.template_org_invitation
108
- except:
109
- f = open(self.path + "/mailing/mailInvite.html", "r")
110
- self.template_org_invitation = f.read()
111
- f.close()
112
- self.mail_invitation = MIMEMultipart('alternative')
113
- self.mail_invitation['Subject'] = "🔔 Rejoignez " + company + " 🔔"
114
- self.mail_invitation['From'] = self.name + " <" + self.address + ">"
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 {}, port {}, dbname {});
26
- """).format(
27
- sql.Literal(self.config.get('UNIAUTH', 'DB_HOST')),
28
- sql.Literal(self.config.get('UNIAUTH', 'DB_PORT')),
29
- sql.Literal(self.config.get('UNIAUTH', 'DB_NAME'))
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 '%s', password '%s');
36
- """).format(
37
- sql.Literal(self.config.get('DB', 'DB_USER')),
38
- sql.Literal(self.config.get('DB', 'DB_PASSWORD'))
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
- return True
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 == True:
49
- counter = (counter[0]+1,counter[1]+1)
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.0
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