feat: deploy autonomous French web edition for Joy
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
.git
|
||||||
|
.github
|
||||||
|
.godot
|
||||||
|
dist
|
||||||
|
addons/godot-sqlite/bin
|
||||||
|
*.log
|
||||||
|
.DS_Store
|
||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
# syntax=docker/dockerfile:1.7
|
||||||
|
|
||||||
|
ARG GODOT_VERSION=4.6.3
|
||||||
|
|
||||||
|
FROM ubuntu:24.04 AS web-builder
|
||||||
|
ARG GODOT_VERSION
|
||||||
|
ARG SQLITE_VERSION=4.8
|
||||||
|
ARG TARGETARCH
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates curl libfontconfig1 unzip \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN case "$TARGETARCH" in \
|
||||||
|
amd64) GODOT_ARCH="x86_64" ;; \
|
||||||
|
arm64) GODOT_ARCH="arm64" ;; \
|
||||||
|
*) echo "Unsupported build architecture: $TARGETARCH" >&2; exit 1 ;; \
|
||||||
|
esac \
|
||||||
|
&& curl -fsSL "https://github.com/godotengine/godot/releases/download/${GODOT_VERSION}-stable/Godot_v${GODOT_VERSION}-stable_linux.${GODOT_ARCH}.zip" -o /tmp/godot.zip \
|
||||||
|
&& unzip -q /tmp/godot.zip -d /usr/local/bin \
|
||||||
|
&& mv "/usr/local/bin/Godot_v${GODOT_VERSION}-stable_linux.${GODOT_ARCH}" /usr/local/bin/godot \
|
||||||
|
&& chmod +x /usr/local/bin/godot \
|
||||||
|
&& mkdir -p "/root/.local/share/godot/export_templates/${GODOT_VERSION}.stable" \
|
||||||
|
&& curl -fsSL "https://github.com/godotengine/godot/releases/download/${GODOT_VERSION}-stable/Godot_v${GODOT_VERSION}-stable_export_templates.tpz" -o /tmp/templates.tpz \
|
||||||
|
&& unzip -q /tmp/templates.tpz -d /tmp/godot-templates \
|
||||||
|
&& mv /tmp/godot-templates/templates/* "/root/.local/share/godot/export_templates/${GODOT_VERSION}.stable/" \
|
||||||
|
&& rm -rf /tmp/godot.zip /tmp/templates.tpz /tmp/godot-templates
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN mkdir -p addons/godot-sqlite/bin \
|
||||||
|
&& curl -fsSL "https://github.com/2shady4u/godot-sqlite/releases/download/v${SQLITE_VERSION}/addons.zip" -o /tmp/godot-sqlite.zip \
|
||||||
|
&& unzip -j /tmp/godot-sqlite.zip \
|
||||||
|
'addons/godot-sqlite/bin/libgdsqlite.web.template_release.wasm32.wasm' \
|
||||||
|
-d addons/godot-sqlite/bin \
|
||||||
|
&& rm /tmp/godot-sqlite.zip \
|
||||||
|
&& find assets -type f -name '*.import' -exec sed -i 's|^compress/mode=2$|compress/mode=0|' {} +
|
||||||
|
|
||||||
|
RUN mkdir -p dist \
|
||||||
|
&& godot --headless --path /src --import \
|
||||||
|
&& find .godot/imported -name 'title.png-*.ctex' ! -name '*.etc2.ctex' ! -name '*.s3tc.ctex' -type f -size +0c | grep -q . \
|
||||||
|
&& find .godot/imported -name 'kalulu_sprite_sheet.png-*.ctex' ! -name '*.etc2.ctex' ! -name '*.s3tc.ctex' -type f -size +0c | grep -q .
|
||||||
|
|
||||||
|
RUN godot --headless --path /src --export-release "Web Test" dist/index.html \
|
||||||
|
&& mv dist/index.pck dist/index-3.0.1-fr-joy.pck \
|
||||||
|
&& sed -i 's|"index.pck":|"index-3.0.1-fr-joy.pck":|; s|"focusCanvas":true,|"focusCanvas":true,"mainPack":"index-3.0.1-fr-joy.pck",|' dist/index.html \
|
||||||
|
&& test -s dist/index.html \
|
||||||
|
&& test -s dist/index.wasm \
|
||||||
|
&& test -s dist/index-3.0.1-fr-joy.pck
|
||||||
|
|
||||||
|
FROM alpine:3.22 AS french-content
|
||||||
|
ARG FRENCH_PACK_VERSION=v20260615-9e5d0d4
|
||||||
|
RUN apk add --no-cache ca-certificates curl \
|
||||||
|
&& mkdir -p /content \
|
||||||
|
&& curl -fsSL "https://github.com/Excello-Recherche-Education/Kalulu-Languages/releases/download/${FRENCH_PACK_VERSION}/fr_FR.zip" -o /content/fr_FR.zip \
|
||||||
|
&& test -s /content/fr_FR.zip
|
||||||
|
|
||||||
|
FROM nginx:1.29.1-alpine AS runtime
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
COPY --from=web-builder /src/dist/ /usr/share/nginx/html/
|
||||||
|
COPY --from=french-content /content/fr_FR.zip /usr/share/nginx/html/content/fr_FR.zip
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||||
|
CMD wget -q -O /dev/null http://127.0.0.1:8080/healthz || exit 1
|
||||||
+18
-1
@@ -2,6 +2,23 @@
|
|||||||
|
|
||||||
# 📖 Kalulu — Apprends à lire intelligemment
|
# 📖 Kalulu — Apprends à lire intelligemment
|
||||||
|
|
||||||
|
## Version Web Docker autonome — Joy
|
||||||
|
|
||||||
|
Cette variante déploie Kalulu en français sur `https://kalulu.agendx.ch` avec un profil local **Joy**, 5 ans, niveau débutant (Grande Section). Elle n’utilise pas l’API officielle de Kalulu : le pack `fr_FR` est servi par la même image et la progression reste dans le stockage IndexedDB du navigateur.
|
||||||
|
|
||||||
|
Prérequis : Docker avec BuildKit, un réseau Docker externe `traefik_proxy` et un Traefik fournissant HTTPS.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker network inspect traefik_proxy >/dev/null 2>&1 || docker network create traefik_proxy
|
||||||
|
docker compose build kalulu
|
||||||
|
docker compose up -d kalulu
|
||||||
|
./scripts/verify-live.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Le premier lancement charge environ 34 Mo d’application puis télécharge 66 Mo de contenu pédagogique depuis la même origine. Le pack est ensuite extrait et conservé localement ; cette première installation peut prendre quelques minutes. Effacer les données du site dans le navigateur réinitialise le profil et la progression.
|
||||||
|
|
||||||
|
L’image construit l’export avec Godot 4.6.3, les modèles d’export officiels et Godot-SQLite 4.8 (la version compilée pour Godot 4.6.3). Nginx sert WebAssembly avec les en-têtes COOP/COEP requis pour les threads.
|
||||||
|
|
||||||
## 🌍 Langues disponibles
|
## 🌍 Langues disponibles
|
||||||
|
|
||||||
- 🇬🇧 [English](README.md)
|
- 🇬🇧 [English](README.md)
|
||||||
@@ -126,4 +143,4 @@ Oui ! Des versions pour Windows, Mac et Linux sont disponibles dans la section [
|
|||||||
|
|
||||||
### 🌐 Kalulu est-il disponible directement sur le web ?
|
### 🌐 Kalulu est-il disponible directement sur le web ?
|
||||||
|
|
||||||
Nous y travaillons, restez à l’écoute !
|
Oui, cette variante autonome est disponible sur [kalulu.agendx.ch](https://kalulu.agendx.ch).
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
services:
|
||||||
|
kalulu:
|
||||||
|
image: kalulu-web:3.0.1-fr-joy
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: kalulu-web
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- traefik_proxy
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.docker.network=traefik_proxy
|
||||||
|
- traefik.http.routers.kalulu.rule=Host(`kalulu.agendx.ch`)
|
||||||
|
- traefik.http.routers.kalulu.entrypoints=websecure
|
||||||
|
- traefik.http.routers.kalulu.tls=true
|
||||||
|
- traefik.http.routers.kalulu.tls.certresolver=myresolver
|
||||||
|
- traefik.http.routers.kalulu.middlewares=crowdsec-bouncer@docker
|
||||||
|
- traefik.http.services.kalulu.loadbalancer.server.port=8080
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik_proxy:
|
||||||
|
external: true
|
||||||
+3
-3
@@ -1360,7 +1360,7 @@ custom_features=""
|
|||||||
export_filter="all_resources"
|
export_filter="all_resources"
|
||||||
include_filter=""
|
include_filter=""
|
||||||
exclude_filter=""
|
exclude_filter=""
|
||||||
export_path="../Export/web/Kalulu.html"
|
export_path="dist/index.html"
|
||||||
patches=PackedStringArray()
|
patches=PackedStringArray()
|
||||||
patch_delta_encoding=false
|
patch_delta_encoding=false
|
||||||
patch_delta_compression_level_zstd=19
|
patch_delta_compression_level_zstd=19
|
||||||
@@ -1378,9 +1378,9 @@ script_export_mode=2
|
|||||||
|
|
||||||
custom_template/debug=""
|
custom_template/debug=""
|
||||||
custom_template/release=""
|
custom_template/release=""
|
||||||
variant/extensions_support=false
|
variant/extensions_support=true
|
||||||
variant/thread_support=true
|
variant/thread_support=true
|
||||||
vram_texture_compression/for_desktop=true
|
vram_texture_compression/for_desktop=false
|
||||||
vram_texture_compression/for_mobile=false
|
vram_texture_compression/for_mobile=false
|
||||||
html/export_icon=true
|
html/export_icon=true
|
||||||
html/custom_html_shell=""
|
html/custom_html_shell=""
|
||||||
|
|||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
server_name _;
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||||
|
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||||
|
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
add_header Referrer-Policy "no-referrer" always;
|
||||||
|
|
||||||
|
default_type application/octet-stream;
|
||||||
|
types {
|
||||||
|
text/html html;
|
||||||
|
text/css css;
|
||||||
|
application/javascript js;
|
||||||
|
application/wasm wasm;
|
||||||
|
application/json json;
|
||||||
|
image/png png;
|
||||||
|
image/svg+xml svg;
|
||||||
|
image/x-icon ico;
|
||||||
|
application/zip zip;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /healthz {
|
||||||
|
access_log off;
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /index.html {
|
||||||
|
add_header Cache-Control "no-cache" always;
|
||||||
|
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||||
|
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||||
|
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /content/ {
|
||||||
|
add_header Cache-Control "public, max-age=31536000, immutable" always;
|
||||||
|
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||||
|
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||||
|
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* \.(wasm|pck|js)$ {
|
||||||
|
add_header Cache-Control "public, max-age=31536000, immutable" always;
|
||||||
|
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||||
|
add_header Cross-Origin-Embedder-Policy "require-corp" always;
|
||||||
|
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ =404;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ boot_splash/bg_color=Color(0.141176, 0.141176, 0.141176, 1)
|
|||||||
config/icon="res://assets/kalulu_icon.png"
|
config/icon="res://assets/kalulu_icon.png"
|
||||||
custom/unlock_everything=false
|
custom/unlock_everything=false
|
||||||
custom/unlock_everything.debug=false
|
custom/unlock_everything.debug=false
|
||||||
|
custom/local_web_mode=true
|
||||||
|
|
||||||
[audio]
|
[audio]
|
||||||
|
|
||||||
|
|||||||
Executable
+14
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
BASE_URL="${1:-https://kalulu.agendx.ch}"
|
||||||
|
HEADERS="$(mktemp)"
|
||||||
|
trap 'rm -f "$HEADERS"' EXIT
|
||||||
|
|
||||||
|
curl -fsS -D "$HEADERS" -o /dev/null "$BASE_URL/"
|
||||||
|
grep -qi '^cross-origin-opener-policy: same-origin' "$HEADERS"
|
||||||
|
grep -qi '^cross-origin-embedder-policy: require-corp' "$HEADERS"
|
||||||
|
curl -fsS -o /dev/null "$BASE_URL/index.wasm"
|
||||||
|
curl -fsS -r 0-1023 -o /dev/null "$BASE_URL/content/fr_FR.zip"
|
||||||
|
|
||||||
|
printf 'Kalulu Web OK: %s\n' "$BASE_URL"
|
||||||
@@ -15,6 +15,12 @@ var teacher_settings: TeacherSettings
|
|||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
await get_tree().process_frame
|
await get_tree().process_frame
|
||||||
|
if (ServerManager as ServerManagerClass).is_local_web_mode():
|
||||||
|
UserDataManager.ensure_joy_local_profile()
|
||||||
|
UserDataManager.set_language("fr_FR", true)
|
||||||
|
UserDataManager.save_all()
|
||||||
|
go_to_package_download()
|
||||||
|
return
|
||||||
device_language = UserDataManager.get_device_settings().language
|
device_language = UserDataManager.get_device_settings().language
|
||||||
Log.trace("LanguageCheck: Starting with device language %s" % device_language)
|
Log.trace("LanguageCheck: Starting with device language %s" % device_language)
|
||||||
# Check the teacher settings, if we are logged in (this scene should not be accessible otherwise)
|
# Check the teacher settings, if we are logged in (this scene should not be accessible otherwise)
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ const MAIN_MENU_SCENE_PATH: String = "res://sources/menus/main/main_menu.tscn"
|
|||||||
const DEVICE_SELECTION_SCENE_PATH: String = "res://sources/menus/device_selection/device_selection.tscn"
|
const DEVICE_SELECTION_SCENE_PATH: String = "res://sources/menus/device_selection/device_selection.tscn"
|
||||||
const LOGIN_SCENE_PATH: String = "res://sources/menus/login/login.tscn"
|
const LOGIN_SCENE_PATH: String = "res://sources/menus/login/login.tscn"
|
||||||
const USER_LANGUAGE_RESOURCES_PATH: String = "user://language_resources"
|
const USER_LANGUAGE_RESOURCES_PATH: String = "user://language_resources"
|
||||||
|
const LOCAL_WEB_PACK_PATH: String = "/content/fr_FR.zip"
|
||||||
|
const LOCAL_WEB_PACK_VERSION: Dictionary = {"year": 2026, "month": 6, "day": 15}
|
||||||
|
const GARDENS_SCENE_PATH: String = "res://sources/gardens/gardens.tscn"
|
||||||
# Translation key shown in the error popup for each DownloadError value
|
# Translation key shown in the error popup for each DownloadError value
|
||||||
const ERROR_MESSAGES: Array[String] = [
|
const ERROR_MESSAGES: Array[String] = [
|
||||||
"DISCONNECTED_ERROR",
|
"DISCONNECTED_ERROR",
|
||||||
@@ -63,6 +66,12 @@ func _ready() -> void:
|
|||||||
Log.trace("PackageDownloader: Using language from teacherSettings: %s" % language)
|
Log.trace("PackageDownloader: Using language from teacherSettings: %s" % language)
|
||||||
current_language_path = USER_LANGUAGE_RESOURCES_PATH.path_join(language)
|
current_language_path = USER_LANGUAGE_RESOURCES_PATH.path_join(language)
|
||||||
current_language_version = UserDataManager.get_device_settings().language_versions.get(language, {})
|
current_language_version = UserDataManager.get_device_settings().language_versions.get(language, {})
|
||||||
|
|
||||||
|
if (ServerManager as ServerManagerClass).is_local_web_mode():
|
||||||
|
server_language_version = LOCAL_WEB_PACK_VERSION
|
||||||
|
var origin: String = str(JavaScriptBridge.eval("window.location.origin"))
|
||||||
|
_start_pack_if_needed(origin + LOCAL_WEB_PACK_PATH)
|
||||||
|
return
|
||||||
|
|
||||||
Log.trace("PackageDownloader: Checking internet access")
|
Log.trace("PackageDownloader: Checking internet access")
|
||||||
if not await ServerManager.check_internet_access():
|
if not await ServerManager.check_internet_access():
|
||||||
@@ -97,6 +106,10 @@ func _ready() -> void:
|
|||||||
_show_error(DownloadError.DOWNLOAD_FAILED)
|
_show_error(DownloadError.DOWNLOAD_FAILED)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
_start_pack_if_needed(res.body.url as String)
|
||||||
|
|
||||||
|
|
||||||
|
func _start_pack_if_needed(pack_url: String) -> void:
|
||||||
# If the language pack is not already downloaded or an update is needed
|
# If the language pack is not already downloaded or an update is needed
|
||||||
if not DirAccess.dir_exists_absolute(current_language_path) or current_language_version != server_language_version:
|
if not DirAccess.dir_exists_absolute(current_language_path) or current_language_version != server_language_version:
|
||||||
Log.trace("PackageDownloader: A new version of the language pack has been detected.\n Current version = " + str(current_language_version) + "\n Server version = " + str(server_language_version))
|
Log.trace("PackageDownloader: A new version of the language pack has been detected.\n Current version = " + str(current_language_version) + "\n Server version = " + str(server_language_version))
|
||||||
@@ -116,8 +129,8 @@ func _ready() -> void:
|
|||||||
# still run offline if the download fails; it is only removed during
|
# still run offline if the download fails; it is only removed during
|
||||||
# extraction, once the new pack has been fully downloaded.
|
# extraction, once the new pack has been fully downloaded.
|
||||||
http_request.set_download_file(USER_LANGUAGE_RESOURCES_PATH.path_join(language + ".zip"))
|
http_request.set_download_file(USER_LANGUAGE_RESOURCES_PATH.path_join(language + ".zip"))
|
||||||
Log.trace("PackageDownloader: Downloading pack from %s" % res.body.url)
|
Log.trace("PackageDownloader: Downloading pack from %s" % pack_url)
|
||||||
var request_error: Error = http_request.request(res.body.url as String)
|
var request_error: Error = http_request.request(pack_url)
|
||||||
if request_error != OK:
|
if request_error != OK:
|
||||||
Log.error("PackageDownloader: Cannot start language pack download: %s" % error_string(request_error))
|
Log.error("PackageDownloader: Cannot start language pack download: %s" % error_string(request_error))
|
||||||
_show_error(DownloadError.DOWNLOAD_FAILED)
|
_show_error(DownloadError.DOWNLOAD_FAILED)
|
||||||
@@ -255,6 +268,14 @@ func _go_to_next_scene() -> void:
|
|||||||
|
|
||||||
if not server_language_version.is_empty():
|
if not server_language_version.is_empty():
|
||||||
UserDataManager.set_language_version(language, server_language_version)
|
UserDataManager.set_language_version(language, server_language_version)
|
||||||
|
|
||||||
|
if (ServerManager as ServerManagerClass).is_local_web_mode():
|
||||||
|
if not UserDataManager.login_student("123"):
|
||||||
|
Log.error("PackageDownloader: Could not open Joy local profile")
|
||||||
|
_show_error(DownloadError.DISCONNECTED)
|
||||||
|
return
|
||||||
|
SceneLoader.change_scene(GARDENS_SCENE_PATH)
|
||||||
|
return
|
||||||
|
|
||||||
# Check if we have a valid device id
|
# Check if we have a valid device id
|
||||||
if not UserDataManager.get_device_settings().device_id:
|
if not UserDataManager.get_device_settings().device_id:
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ var dev_last_click_time: float = 0.0
|
|||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
UserDataManager.ensure_joy_local_profile()
|
||||||
Log.info("MainMenu: Loaded successfuly")
|
Log.info("MainMenu: Loaded successfuly")
|
||||||
version_label.text = Utils.get_application_version_with_code()
|
version_label.text = Utils.get_application_version_with_code()
|
||||||
teacher_label.text = UserDataManager.get_device_settings().teacher
|
teacher_label.text = UserDataManager.get_device_settings().teacher
|
||||||
@@ -33,6 +34,9 @@ func _ready() -> void:
|
|||||||
func _on_main_button_pressed() -> void:
|
func _on_main_button_pressed() -> void:
|
||||||
Log.info("MainMenu: Main button pressed")
|
Log.info("MainMenu: Main button pressed")
|
||||||
play_button.set_disabled(true)
|
play_button.set_disabled(true)
|
||||||
|
if (ServerManager as ServerManagerClass).is_local_web_mode():
|
||||||
|
_on_login_in()
|
||||||
|
return
|
||||||
if UserDataManager.get_device_settings().teacher:
|
if UserDataManager.get_device_settings().teacher:
|
||||||
Log.info("MainMenu: Device settings has teacher info, so start auto login-in")
|
Log.info("MainMenu: Device settings has teacher info, so start auto login-in")
|
||||||
_on_login_in()
|
_on_login_in()
|
||||||
|
|||||||
@@ -38,14 +38,20 @@ var words_path: String = get_language_folder() + "/words/"
|
|||||||
var additional_word_list: Dictionary = {}
|
var additional_word_list: Dictionary = {}
|
||||||
var is_open: bool = false
|
var is_open: bool = false
|
||||||
|
|
||||||
@onready var db: SQLite = SQLite.new()
|
@onready var db: Object = (
|
||||||
|
ClassDB.instantiate(&"SQLite") if ClassDB.class_exists(&"SQLite") else null
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func _exit_tree() -> void:
|
func _exit_tree() -> void:
|
||||||
db.close_db()
|
if db:
|
||||||
|
db.close_db()
|
||||||
|
|
||||||
|
|
||||||
func connect_to_db() -> void:
|
func connect_to_db() -> void:
|
||||||
|
if not db:
|
||||||
|
Log.error("Database: SQLite extension is not available")
|
||||||
|
return
|
||||||
if is_open:
|
if is_open:
|
||||||
Log.trace("Database: Opening database while a connection is already opened. Closing the previous one.")
|
Log.trace("Database: Opening database while a connection is already opened. Closing the previous one.")
|
||||||
db.close_db()
|
db.close_db()
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ var environment_setting: int = 1
|
|||||||
@onready var loading_rect: TextureRect = $TextureRect
|
@onready var loading_rect: TextureRect = $TextureRect
|
||||||
|
|
||||||
|
|
||||||
|
func is_local_web_mode() -> bool:
|
||||||
|
return OS.has_feature("web") and bool(ProjectSettings.get_setting("application/custom/local_web_mode", false))
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
var config: ConfigFile = ConfigFile.new()
|
var config: ConfigFile = ConfigFile.new()
|
||||||
var load_error: Error = config.load(CONFIG_PATH)
|
var load_error: Error = config.load(CONFIG_PATH)
|
||||||
@@ -84,6 +88,8 @@ func _save_environment_config() -> void:
|
|||||||
|
|
||||||
|
|
||||||
func first_login_student() -> void:
|
func first_login_student() -> void:
|
||||||
|
if is_local_web_mode():
|
||||||
|
return
|
||||||
await _post_json_request("submit_student_session", {"student_id": UserDataManager.student})
|
await _post_json_request("submit_student_session", {"student_id": UserDataManager.student})
|
||||||
|
|
||||||
|
|
||||||
@@ -185,6 +191,8 @@ func reset_language(language: String) -> Dictionary:
|
|||||||
#region Sender functions
|
#region Sender functions
|
||||||
|
|
||||||
func check_internet_access() -> bool:
|
func check_internet_access() -> bool:
|
||||||
|
if is_local_web_mode():
|
||||||
|
return false
|
||||||
Log.trace("ServerManager: Sending simple request to " + INTERNET_CHECK_URL + " to check if internet is available")
|
Log.trace("ServerManager: Sending simple request to " + INTERNET_CHECK_URL + " to check if internet is available")
|
||||||
var res: Error = internet_check.request(INTERNET_CHECK_URL)
|
var res: Error = internet_check.request(INTERNET_CHECK_URL)
|
||||||
if res == OK:
|
if res == OK:
|
||||||
|
|||||||
@@ -107,6 +107,8 @@ func _process(_delta: float) -> void:
|
|||||||
#region synchronization
|
#region synchronization
|
||||||
|
|
||||||
func start_synchronization_timer() -> void:
|
func start_synchronization_timer() -> void:
|
||||||
|
if (ServerManager as ServerManagerClass).is_local_web_mode():
|
||||||
|
return
|
||||||
if not synchronization_timer_running:
|
if not synchronization_timer_running:
|
||||||
synchronization_timer = 0
|
synchronization_timer = 0
|
||||||
synchronization_timer_running = true
|
synchronization_timer_running = true
|
||||||
@@ -117,6 +119,52 @@ func stop_synchronization_timer() -> void:
|
|||||||
synchronization_timer_running = false
|
synchronization_timer_running = false
|
||||||
Log.trace("UserDataManager: Synchronization timer stopped")
|
Log.trace("UserDataManager: Synchronization timer stopped")
|
||||||
|
|
||||||
|
|
||||||
|
func ensure_joy_local_profile() -> void:
|
||||||
|
if not (ServerManager as ServerManagerClass).is_local_web_mode():
|
||||||
|
return
|
||||||
|
|
||||||
|
var settings: DeviceSettings = get_device_settings()
|
||||||
|
settings.language = "fr_FR"
|
||||||
|
settings.teacher = "profil-local"
|
||||||
|
settings.device_id = 1
|
||||||
|
_save_device_settings()
|
||||||
|
|
||||||
|
var teacher_path: String = get_teacher_settings_path()
|
||||||
|
if FileAccess.file_exists(teacher_path):
|
||||||
|
_load_teacher_settings()
|
||||||
|
if not teacher_settings:
|
||||||
|
teacher_settings = TeacherSettings.new()
|
||||||
|
teacher_settings.account_type = TeacherSettings.AccountType.PARENT
|
||||||
|
teacher_settings.education_method = TeacherSettings.EducationMethod.APP_ONLY
|
||||||
|
teacher_settings.email = "profil-local"
|
||||||
|
teacher_settings.token = "local-only"
|
||||||
|
teacher_settings.language = "fr_FR"
|
||||||
|
teacher_settings.server_language_validated = true
|
||||||
|
var joy: StudentData = StudentData.new()
|
||||||
|
joy.code = 123
|
||||||
|
joy.name = "Joy"
|
||||||
|
joy.age = 5
|
||||||
|
joy.level = StudentData.Level.BEGINNER
|
||||||
|
var joy_students: Array[StudentData] = [joy]
|
||||||
|
teacher_settings.students[1] = joy_students
|
||||||
|
DirAccess.make_dir_recursive_absolute(get_teacher_folder())
|
||||||
|
save_teacher_settings()
|
||||||
|
else:
|
||||||
|
teacher_settings.language = "fr_FR"
|
||||||
|
teacher_settings.server_language_validated = true
|
||||||
|
if not teacher_settings.students.has(1) or (teacher_settings.students[1] as Array).is_empty():
|
||||||
|
var joy: StudentData = StudentData.new()
|
||||||
|
joy.code = 123
|
||||||
|
joy.name = "Joy"
|
||||||
|
joy.age = 5
|
||||||
|
joy.level = StudentData.Level.BEGINNER
|
||||||
|
var joy_students: Array[StudentData] = [joy]
|
||||||
|
teacher_settings.students[1] = joy_students
|
||||||
|
save_teacher_settings()
|
||||||
|
|
||||||
|
TranslationServer.set_locale("fr_FR")
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Registering and logging in
|
#region Registering and logging in
|
||||||
|
|||||||
Reference in New Issue
Block a user