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 CHANGED
@@ -152,7 +152,7 @@ class DB:
152
152
  else:
153
153
  return []
154
154
  except Exception as e:
155
- print(f"[VESTA] An error occurred with the db: {e}")
155
+ print(f"[VESTA] An error occurred with the db in getFilters: {e}")
156
156
  self.conn.rollback()
157
157
 
158
158
  def insertDict(self, table, dict, getId=False):
@@ -194,8 +194,15 @@ class DB:
194
194
  self.conn.commit()
195
195
 
196
196
  def resetTable(self, table):
197
- sql_str = """delete from {} cascade;ALTER SEQUENCE {} RESTART WITH 1""".format(table, table + "_id_seq")
198
- self.cur.execute(sql_str)
197
+ # Use SQL identifiers to prevent SQL injection
198
+ self.cur.execute(
199
+ sql.SQL("DELETE FROM {} CASCADE").format(sql.Identifier(table))
200
+ )
201
+ self.cur.execute(
202
+ sql.SQL("ALTER SEQUENCE {} RESTART WITH 1").format(
203
+ sql.Identifier(table + "_id_seq")
204
+ )
205
+ )
199
206
  self.conn.commit()
200
207
 
201
208
  def edit(self, table, id, element, value, selector='id'):
@@ -8,5 +8,4 @@ test:
8
8
  - python -m venv venv
9
9
  - source venv/bin/activate
10
10
  - pip install -r requirements.txt
11
- - pip install git+https://gitlab.com/Louciole/vesta.git/
12
11
  - vesta test
@@ -0,0 +1 @@
1
+ vesta-web
@@ -0,0 +1,11 @@
1
+ let global = {
2
+ "state":{
3
+ },
4
+ "i18n":{
5
+ },
6
+ "settings":{
7
+ },
8
+ "user":{
9
+ },
10
+ }
11
+ export default global
@@ -0,0 +1,295 @@
1
+ import global from "./global.mjs";
2
+ import {xhr} from "./templating.mjs";
3
+
4
+
5
+ /**
6
+ * Opens a menu by its DOM id. If the menu element exists, it is displayed. Otherwise, loads the menu template asynchronously.
7
+ * @param {string} id - The DOM id of the menu to open.
8
+ * @param {boolean} [async=true] - Whether to load the template asynchronously if not found.
9
+ */
10
+ function openMenu(id, async=true){
11
+ const menu = document.getElementById(id)
12
+ if (menu){
13
+ menu.style.display = "flex"
14
+ }else{
15
+ loadTemplate(id.concat(".html"), undefined, id, async)
16
+ }
17
+ }
18
+ window.openMenu = openMenu
19
+
20
+ /**
21
+ * Toggles the display of a menu by its DOM id. If the menu exists, toggles its visibility. Otherwise, loads the menu template.
22
+ * @param {string} id - The DOM id of the menu to toggle.
23
+ * @param {boolean} [async=true] - Whether to load the template asynchronously if not found.
24
+ * @param {HTMLElement} [target=undefined] - Optional target HTML element for template loading.
25
+ */
26
+ function toggleMenu(id, async=true,target=undefined){
27
+ const menu = document.getElementById(id)
28
+ if (menu){
29
+ if (menu.style.display === "flex"){
30
+ menu.style.display = "none"
31
+ return
32
+ }
33
+ menu.style.display = "flex"
34
+ }else{
35
+ loadTemplate(id.concat(".html"), target, id, async)
36
+ }
37
+ }
38
+ window.toggleMenu = toggleMenu
39
+
40
+ /**
41
+ * Navigates to a new view or section by loading a template and updating selection state.
42
+ *
43
+ * @param {string} id - The id of the element to inject the template into.
44
+ * @param {string} target - The base name of the template to load (without .html).
45
+ * @param {Object} [selected=undefined] - Optional selection context {category | event | id} allowing to add/remove the "selected" class.
46
+ * @param {boolean} [async=true] - Whether to load the template asynchronously.
47
+ * @param {Function} [postInsert=undefined] - Optional callback to run after template insertion.
48
+ *
49
+ */
50
+ export function goTo(id, target, selected=undefined, async=true, postInsert=undefined){
51
+ if (postInsert){
52
+ loadTemplate(target.concat(".html"), id, undefined, false)
53
+ postInsert()
54
+ }else{
55
+ loadTemplate(target.concat(".html"), id, undefined, async)
56
+ }
57
+
58
+ if(selected){
59
+ let targetElt;
60
+ if(global.state[selected.category]){
61
+ global.state[selected.category].classList.remove("selected")
62
+ }else if(selected.event){
63
+ selected.event.currentTarget.parentElement.querySelector('.selected').classList.remove("selected")
64
+ }else if(selected.id){
65
+ targetElt = document.getElementById(selected.id)
66
+ targetElt.parentElement.querySelector('.selected').classList.remove("selected")
67
+ }
68
+
69
+ if(selected.event){
70
+ global.state[selected.category] = selected.event.currentTarget
71
+ }else if(selected.id){
72
+ global.state[selected.category] = targetElt
73
+ }
74
+ global.state[selected.category].classList.add("selected")
75
+ }
76
+ }
77
+ window.goTo = goTo
78
+
79
+
80
+ let openingFM = false;
81
+
82
+
83
+ /**
84
+ * Toggles the visibility of a floating menu (FM) by its DOM id.
85
+ * If another FM is open, it will be hidden and the new one will be shown.
86
+ *
87
+ * @param {string} id - The DOM id of the floating menu to toggle.
88
+ */
89
+ function toggleFM(id){
90
+ const FM = document.getElementById(id)
91
+ if(FM !== global.state.activeFM){
92
+ openingFM = true
93
+ if(global.state.activeFM){
94
+ global.state.activeFM.classList.toggle("visible")
95
+ }
96
+ global.state.activeFM = FM
97
+ }
98
+ }
99
+ window.toggleFM = toggleFM
100
+
101
+ /**
102
+ * Closes the currently active floating menu (FM) if one is open.
103
+ */
104
+ export function closeFM(){
105
+ if(global.state.activeFM){
106
+ global.state.activeFM.classList.toggle("visible")
107
+ global.state.activeFM = undefined
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Toggles the display of a modal window at the mouse event position.
113
+ * If another modal is open, it will be hidden and the new one will be shown at the event's coordinates.
114
+ *
115
+ * @param {string} id - The DOM id of the modal to toggle.
116
+ * @param {Event} event - The event triggering the modal (used for positioning).
117
+ */
118
+ function toggleModale(id, event){
119
+ const FM = document.getElementById(id)
120
+ if(FM !== global.state.activeFM){
121
+ openingFM = true
122
+ global.state.modaltarget = event.currentTarget
123
+ if(global.state.activeFM){
124
+ global.state.activeFM.classList.toggle("visible")
125
+ }
126
+ console.log(event.currentTarget,global.state.modaltarget)
127
+ FM.style.top = event.clientY.toString().concat("px")
128
+ FM.style.left = event.clientX.toString().concat("px")
129
+ global.state.activeFM = FM
130
+ if (event.type === "contextmenu"){
131
+ event.preventDefault()
132
+ global.state.activeFM.classList.add("visible")
133
+ }
134
+ }
135
+ }
136
+ window.toggleModale = toggleModale
137
+
138
+ /**
139
+ * Toggles the closed/open state of a group element (e.g., collapsible section).
140
+ */
141
+ function toggleGroup(){
142
+ event.currentTarget.classList.toggle("closed")
143
+ }
144
+ window.toggleGroup = toggleGroup
145
+
146
+ /**
147
+ * Navigates to a specific step in a multi-step menu by translating the menu horizontally.
148
+ *
149
+ * @param {number} step - The step index to navigate to.
150
+ * @param {string} stepsID - The DOM id of the steps container.
151
+ */
152
+ function gotoStep(step,stepsID){
153
+ const menu = document.getElementById(stepsID)
154
+ menu.style.transform=`translateX(${-100*step/menu.childElementCount}%)`
155
+ }
156
+ window.gotoStep = gotoStep
157
+
158
+ /**
159
+ * Removes the 'selected' class from all elements with class 'selected' inside the given container.
160
+ * Used for resetting selection state in multi-step menus.
161
+ *
162
+ * @param {string} id - The DOM id of the container to reset selection in.
163
+ */
164
+ function resetSelected(id){
165
+ const selected = document.getElementById(id).querySelectorAll(".selected")
166
+ for(let el of selected){
167
+ el.classList.remove("selected")
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Closes a menu by hiding it and resetting its state. Can target a specific element or use the event target.
173
+ *
174
+ * @param {string|undefined} cible - CSS selector of the element to close, or undefined to use event target.
175
+ * @param {string} [id='createServerSteps'] - The DOM id of the steps container to reset.
176
+ *
177
+ */
178
+ function closeMenu(cible = undefined,id='createServerSteps'){
179
+ if (global.state.disableClose){
180
+ return
181
+ }
182
+
183
+ if(!cible){
184
+ if(event.target !== event.currentTarget){
185
+ return
186
+ }
187
+ event.target.style.display = "none";
188
+ }else{
189
+ const cibleEl= document.querySelector(cible)
190
+ cibleEl.style.display = "none";
191
+ }
192
+ gotoStep(0,id)
193
+ resetSelected(id)
194
+ }
195
+ window.closeMenu = closeMenu
196
+
197
+ /**
198
+ * Saves the current cursor position in a text input or textarea to global state.
199
+ * Used to paste emojis in a textarea from an emoji-keyboard.
200
+ *
201
+ * @param {Event} event - The input event containing the cursor position.
202
+ */
203
+ function saveCursorPosition(event){
204
+ global.state.previousCursor={start:event.srcElement.selectionStart, end:event.srcElement.selectionEnd}
205
+ }
206
+ window.saveCursorPosition = saveCursorPosition
207
+
208
+ function isMobileDevice() {
209
+ return /Mobi|Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
210
+ }
211
+
212
+ export function initNavigation(){
213
+ window.global = global
214
+ global.state.dom = document.querySelector("body")
215
+
216
+ const { hostname, port } = window.location;
217
+ global.state.location = { host:hostname, port:port, short:port ? hostname + ":" + port : hostname }
218
+
219
+ global.state.isMobile = isMobileDevice()
220
+ if (global.state.isMobile) {
221
+ global.state.dom.classList.add("mobile")
222
+
223
+ const onload = function(){
224
+ console.log(this.responseText)
225
+ }
226
+ xhr("/static/mobileUiManifest.mjs", onload)
227
+ }
228
+
229
+ document.addEventListener('click', function (event) {
230
+ if (global.state.activeFM && !global.state.activeFM.contains(event.target)) {
231
+ global.state.activeFM.classList.toggle("visible")
232
+ if (!openingFM){
233
+ global.state.activeFM = undefined
234
+ }else{
235
+ openingFM=false
236
+ }
237
+ }
238
+ }, false);
239
+
240
+ if (global.state.socket){
241
+ window.addEventListener('unload', () => {
242
+ if (global.state?.socket.readyState !== WebSocket.CLOSED) {
243
+ const message = {"type" : 'unregister', "clientID":global.state.clientID}
244
+ global.state.socket.send(JSON.stringify(message))
245
+ global.state.socket.close()
246
+ }
247
+ });
248
+ }
249
+
250
+ }
251
+
252
+
253
+ function print(...args){
254
+ const green ="#68f66e"
255
+ const blue ="#4284f5"
256
+ const yellow ="#fef972"
257
+ const red ="#ee6966"
258
+ const pink ="#fb7bfa"
259
+ const purple ="#6a76fa"
260
+
261
+ const colors = [green,blue,green,"white",green,blue,"white",yellow,blue,"white",yellow,"white",yellow,red,pink,purple];
262
+ console.log(`%c${args.join(' ')}`, ...colors.map(c => `color: ${c};`));
263
+ // console.log(colors.map(c => `%c${c}`).join(''), ...colors.map(c => `background: ${c};`));
264
+ }
265
+
266
+
267
+ export function printWatermark(name, git){
268
+ print(` %c ${name}\n` +
269
+ "%c⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ %c -----------------------------------\n" +
270
+ "%c⠀⠀⠀⠀⠀⠀⠀⢠⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ %c Credits%c: Lou ! \n" +
271
+ `%c⠀⠀⠀⠀⠀⠀⠀⠸⣷⣦⣀⠀⠀⠀⠀⠀⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀ %c Git%c: ${git}\n` +
272
+ "%c⠀⠀⠀⠀⠀⠀⠀⠀⠙⣿⣿⣿⣦⠀⠠⠾⠿⣿⣷⠀⠀⠀⠀⠀⣠⣤⣄⠀⠀⠀\n" +
273
+ "⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠟⢉⣠⣤⣶⡆⠀⣠⣈⠀⢀⣠⣴⣿⣿⠋⠀⠀⠀⠀ %c Powered by Vesta ! \n" +
274
+ "%c⠀⢀⡀⢀⣀⣀⣠⣤⡄⢀⣀⡘⣿⣿⣿⣷⣼⣿⣿⣷⡄⠹⣿⡿⠁⠀⠀⠀⠀⠀\n" +
275
+ "%c⠀⠀⠻⠿⢿⣿⣿⣿⠁⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣟⣁⠀⠋⠀⠀⠀⠀⠀⠀⠀ \n" +
276
+ "⠀⠀⠀⠀⠀⠀⠈⠻⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⢰⣄⣀⠀⠀⠀⠀⠀\n" +
277
+ "%c⠀⠀ ⠀⠀⠀⠀⣠⡀⠀⣴⣿⣿⣿⣿⣿⣿⣿⡿⢿⡿⠀⣾⣿⣿⣿⣿⣶⡄⠀ \n" +
278
+ "⠀⠀⠀⠀⠀⢀⣾⣿⣷⡀⠻⣿⣿⡿⠻⣿⣿⣿⣿⠀⠀⠈⠉⠉⠉⠀⠀⠀⠀⠀\n" +
279
+ "⠀⠀⠀⠀⣠⣾⡿⠟⠉⠉⠀⢀⡉⠁⠀⠛⠛⢉⣠⣴⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀\n" +
280
+ "%c⠀⠀⠀⠈⠉⠉⠀⠀⠀⠀⠀⢸⣿⣿⡿⠉⠀⠙⠿⣿⣿⣧⡀⠀⠀⠀⠀⠀⠀⠀\n" +
281
+ "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⠁⠀⠀⠀⠀⠀⠙⠿⣷⠀⠀⠀⠀⠀⠀⠀\n" +
282
+ "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠃⠀⠀⠀⠀⠀⠀⠀")
283
+ }
284
+
285
+ export function hideLoadingScreen() {
286
+ const loadingScreen = document.getElementById('loading-screen');
287
+
288
+ // Fade out the loading screen
289
+ loadingScreen.classList.add('fade-out');
290
+
291
+ // Optional: wait until the transition ends before removing or showing content
292
+ loadingScreen.addEventListener('transitionend', () => {
293
+ loadingScreen.style.display = 'none';
294
+ });
295
+ }
@@ -0,0 +1,110 @@
1
+ let templates = {}
2
+
3
+ export function xhr(endpoint,effect,method="GET", async=true, body=undefined){
4
+ let xhr= new XMLHttpRequest();
5
+ xhr.open(method, endpoint, async);
6
+ xhr.onload=effect
7
+ xhr.onerror = function() {
8
+ console.log("request failed")
9
+ };
10
+ if (body) {
11
+ xhr.setRequestHeader("Content-Type", "application/json");
12
+ xhr.send(JSON.stringify(body));
13
+ }else{
14
+ xhr.send();
15
+ }
16
+ return xhr
17
+ }
18
+
19
+
20
+ function loadTemplate(template, target=undefined, flex= undefined, async){
21
+ const effect = function() {
22
+ if (target){
23
+ //maybe not using eval
24
+ document.getElementById(target).innerHTML = eval('`' + this.responseText + '`');
25
+ }else{
26
+ global.state.dom.insertAdjacentHTML('beforeend',eval('`' + this.responseText + '`'))
27
+ }
28
+ if (flex){
29
+ document.getElementById(flex).style.display = "flex"
30
+ }
31
+ };
32
+
33
+ xhr( '/static/templates/'.concat(template), effect,"GET", async)
34
+ }
35
+ window.loadTemplate = loadTemplate
36
+
37
+ function fillWith(template, list){
38
+ // console.log("fillWith",template,list,typeof list)
39
+
40
+ let request
41
+ if(templates[template]){
42
+ request={"responseText":templates[template]}
43
+ }else{
44
+ request = xhr( '/static/templates/'.concat(template,".html"), undefined, "GET", false)
45
+ // console.log("adding to cache",templates, template)
46
+ templates[template] = request.responseText
47
+ }
48
+
49
+ let content = ""
50
+ if (typeof list == 'object'){
51
+ for (let elementId in list){
52
+ const element = list[elementId]
53
+ content += eval('`' + request.responseText + '`')
54
+ }
55
+ }else if (!list){
56
+ console.warn("fillWith called with undefined list")
57
+ return content
58
+ }else{
59
+ for (let element of list){
60
+ content += eval('`' + request.responseText + '`')
61
+ }
62
+ }
63
+
64
+ return content
65
+ }
66
+ window.fillWith = fillWith
67
+
68
+ function Subscribe(element, content, className=undefined, params=undefined){
69
+ //subscribe content to element, content will be reevaluated on element change
70
+ const domElement = document.createElement('div')
71
+ domElement.className = element.replaceAll('.','-').replaceAll('[','🪟').replaceAll(']','🥹')
72
+
73
+ if (className){
74
+ const classList = className.split(" ")
75
+ for (let name of classList){
76
+ domElement.classList.add(name)
77
+ }
78
+ domElement.dataset.defaultClass = domElement.className
79
+ }
80
+
81
+ if (params?.target === "class"){
82
+ domElement.dataset.target = params.target
83
+ domElement.className = domElement.className + " " + content()
84
+ }else if(params?.target === "style"){
85
+ domElement.dataset.target = params.target
86
+ domElement.style = content()
87
+ } else{
88
+ domElement.innerHTML = content()
89
+ }
90
+
91
+
92
+
93
+ if (params?.element){
94
+ domElement.dataset.element = params.element
95
+ }
96
+
97
+ if(params?.repaint){
98
+ domElement.dataset.repaint = params.repaint
99
+ }else{
100
+ domElement.dataset.content = content
101
+ }
102
+ return domElement.outerHTML
103
+ }
104
+ window.Subscribe = Subscribe
105
+
106
+ function getTemplate(name){
107
+ const request = xhr( '/static/templates/'.concat(name,".html"), ()=>{}, "GET", false)
108
+ return eval('`' + request.responseText + '`')
109
+ }
110
+ window.getTemplate = getTemplate
@@ -0,0 +1,173 @@
1
+ function logout() {
2
+ const url = "/logout";
3
+ fetch(url, {
4
+ method: 'POST',
5
+ redirect: 'manual'
6
+ })
7
+ .then(response => {
8
+ const location = response.headers.get('location');
9
+ if (response.status === 302 && location) {
10
+ window.location.href = location;
11
+ } else {
12
+ // fallback: reload page or handle error
13
+ window.location.reload();
14
+ }
15
+ })
16
+ .catch(() => {
17
+ console.log("request failed");
18
+ });
19
+ }
20
+ window.logout = logout
21
+
22
+
23
+
24
+ export function updateElement(selector,value=undefined){
25
+ const subscriptions = document.querySelectorAll(`[class^="${selector.replaceAll('.','-').replaceAll('[','🪟').replaceAll(']','🥹')}"]`)
26
+ for (let sub of subscriptions){
27
+ if(sub.dataset.repaint){
28
+ const fn = eval(sub.dataset.repaint)
29
+ fn()
30
+ }else if(sub.dataset.target === "class"){
31
+
32
+ const content = eval(sub.dataset.content)
33
+ if(sub.dataset.element){
34
+ sub.className = sub.dataset.defaultClass.concat(' ',content(eval(sub.dataset.element)))
35
+ }else{
36
+ sub.className = sub.dataset.defaultClass.concat(' ',content())
37
+ }
38
+ }else if(sub.dataset.target === "style"){
39
+ const content = eval(sub.dataset.content)
40
+ if(sub.dataset.element){
41
+ sub.style = content(eval(sub.dataset.element))
42
+ }else{
43
+ sub.style = content()
44
+ }
45
+ }else{
46
+
47
+ const content = eval(sub.dataset.content)
48
+ if(sub.dataset.element){
49
+ sub.innerHTML = content(eval(sub.dataset.element))
50
+ }else{
51
+ sub.innerHTML = content()
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ export function setElement(element, value){
58
+ // console.log(element,'has been updated to:', value);
59
+ eval(`${element} = value`);
60
+ updateElement(element,value)
61
+ }
62
+
63
+ export function pushElement(element, value){
64
+ // console.log(element,'has been added:', value);
65
+ eval(`${element}.push(value)`);
66
+ updateElement(element,value)
67
+ }
68
+
69
+ export function addElement(element, value){
70
+ // console.log(element,'has been added:', value);
71
+ eval(`${element}[value.id] = value`);
72
+ updateElement(element,value)
73
+ }
74
+
75
+ // this deletes an element from a list BY ID
76
+ export function deleteElement(element, id){
77
+ // console.log(element,'has been removed:', element[id]);
78
+ eval(`${element}.splice(id,1)`);
79
+ updateElement(element)
80
+ }
81
+
82
+ // this deletes an element from a dict BY ID
83
+ export function deleteElementDict(element, id){
84
+ // console.log(element,'has been removed:', element[id]);
85
+ eval(`delete ${element}[id]`);
86
+ updateElement(element)
87
+ }
88
+
89
+ // this deletes an element from a list BY VALUE
90
+ export function deleteVal(element, val){
91
+ // console.log(element,'has been removed:', element[id]);
92
+ eval(`${element} = ${element}.filter(item => item !== val)`);
93
+ updateElement(element)
94
+ }
95
+
96
+ function checkEnter(event, effect){
97
+ if (event.key === "Enter"){
98
+ effect()
99
+ }
100
+ }
101
+ window.checkEnter = checkEnter
102
+
103
+ function getTimeStr(timestamp, options = { locale: "fr-FR" }) {
104
+ const date = new Date(timestamp);
105
+
106
+ const defaultOptions = {
107
+ year: "numeric",
108
+ month: "numeric",
109
+ day: "numeric",
110
+ hour: "numeric",
111
+ minute: "numeric",
112
+ hour12: false,
113
+ };
114
+
115
+ const mergedOptions = { ...defaultOptions, ...options };
116
+ // console.log("TIMESTR",date.toLocaleDateString(undefined, mergedOptions))
117
+ return date.toLocaleDateString(undefined, mergedOptions);
118
+ }
119
+ window.getTimeStr = getTimeStr
120
+
121
+
122
+ function goodbye(){
123
+ if (confirm('Voulez-vous vraiment vous désinscrire et supprimer votre compte de tous les services Carbonlab ? (toutes vos informations seront effacées)')) {
124
+ const url = "/goodbye";
125
+ let request = new XMLHttpRequest();
126
+ request.open('POST', url, true);
127
+ request.onload = function() { // request successful
128
+ console.log("account deleted")
129
+
130
+ if (request.response === 302){
131
+ window.location.href = request.response.headers.get('location');
132
+ }
133
+ };
134
+
135
+ request.onerror = function() {
136
+ console.log("request failed")
137
+ };
138
+
139
+ request.send();
140
+ } else {
141
+ console.log("ouf 😖")
142
+ }
143
+ }
144
+ window.goodbye = goodbye
145
+
146
+ export function difference(arrKeys, dict) {
147
+ const result = [];
148
+ const dictKeys = new Set(Object.keys(dict)); // Convert dict keys to a set for efficient lookup
149
+
150
+ for (const key of arrKeys) {
151
+ if (!dictKeys.has(key.toString())) {
152
+ result.push(key);
153
+ }
154
+ }
155
+ return Array.from(result);
156
+ }
157
+
158
+ export function generateUUID() {
159
+ let d = new Date().getTime();//Timestamp
160
+ let d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now()*1000)) || 0;//Time in microseconds since page-load or 0 if unsupported
161
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
162
+ let r = Math.random() * 16;//random number between 0 and 16
163
+ if(d > 0){//Use timestamp until depleted
164
+ r = (d + r)%16 | 0;
165
+ d = Math.floor(d/16);
166
+ } else {//Use microseconds since page-load if supported
167
+ r = (d2 + r)%16 | 0;
168
+ d2 = Math.floor(d2/16);
169
+ }
170
+ return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
171
+ });
172
+ }
173
+
@@ -0,0 +1,41 @@
1
+ import global from "./global.mjs";
2
+ import {onMessage} from "../ws/onMessage.mjs";
3
+ import {WEBSOCKETS} from "/config";
4
+
5
+
6
+ export function initWebSockets(){
7
+ global.state.websocket = new WebSocket(WEBSOCKETS);
8
+
9
+ global.state.websocket.onopen = function(event) {
10
+ if (!global.user.id) {
11
+ const cookie = document.cookie.split('; ').find(row => row.startsWith('client' + '='));
12
+ if (cookie) {
13
+ global.user['id'] = cookie.split('=')[1];
14
+ } else {
15
+ console.log("ERROR : INVALID SESSION",document);
16
+ }
17
+ }
18
+
19
+
20
+ console.log("Connection opened to Python WebSocket server!");
21
+ const message = {"type" : 'register', "uid": global.user.id};
22
+ global.state.websocket.send(JSON.stringify(message));
23
+ };
24
+
25
+ global.state.websocket.onmessage = onMessage;
26
+
27
+ global.state.websocket.onclose = function(event) {
28
+ console.log(`[WS] Connexion closed - Code: ${event.code}, Reason: ${event.reason}`);
29
+
30
+ if (event.code !== 1000 && event.code !== 1001) {
31
+ console.log("[WS] Trying to reconnect in 3 secondes...");
32
+ setTimeout(() => {
33
+ initWebSockets();
34
+ }, 3000);
35
+ }
36
+ };
37
+
38
+ global.state.websocket.onerror = function(error) {
39
+ console.error("WebSocket error:", error);
40
+ };
41
+ }