diff --git a/app/src/web/templates/index.html.j2 b/app/src/web/templates/page_index.html.j2
similarity index 100%
rename from app/src/web/templates/index.html.j2
rename to app/src/web/templates/page_index.html.j2
diff --git a/app/src/web/templates/page_logging.html.j2 b/app/src/web/templates/page_logging.html.j2
new file mode 100644
index 0000000..701b3b5
--- /dev/null
+++ b/app/src/web/templates/page_logging.html.j2
@@ -0,0 +1,11 @@
+{% extends 'base.html.j2' %}
+
+{% block title %} TSUN Proxy - Downloads {% endblock title%}
+{% block menu3_class %}w3-blue{% endblock %}
+{% block headline %}
{{_('Log Files')}}{% endblock headline %}
+{% block content %}
+
+{% endblock content%}
+
+{% block footer %}{% endblock footer %}
+
diff --git a/app/src/web/templates/conn_table.html.j2 b/app/src/web/templates/templ_conn_table.html.j2
similarity index 97%
rename from app/src/web/templates/conn_table.html.j2
rename to app/src/web/templates/templ_conn_table.html.j2
index c6c5f62..6dd69b1 100644
--- a/app/src/web/templates/conn_table.html.j2
+++ b/app/src/web/templates/templ_conn_table.html.j2
@@ -12,7 +12,7 @@
{% endif %}
{%- endmacro%}
-
Connections
+
{{_('Connections')}}
{% if table.thead is defined%}
diff --git a/app/src/web/templates/templ_log_files_list.html.j2 b/app/src/web/templates/templ_log_files_list.html.j2
new file mode 100644
index 0000000..9df7ee5
--- /dev/null
+++ b/app/src/web/templates/templ_log_files_list.html.j2
@@ -0,0 +1,31 @@
+
+{% for file in dir_list %}
+
+
+
+
+
+
+ {% for idx, name in [('created',_('Created')), ('modified', _('Modified')), ('size', _('Size'))]%}
+
+ | {{_(name)}}: |
+ {{file[idx]}} |
+
+ {% endfor %}
+
+
+
+
+
+
+{% if 0 == (loop.index%4) and not last %}
+
+
+{% endif %}
+{% endfor %}
+
+
diff --git a/app/src/web/templates/notes_list.html.j2 b/app/src/web/templates/templ_notes_list.html.j2
similarity index 100%
rename from app/src/web/templates/notes_list.html.j2
rename to app/src/web/templates/templ_notes_list.html.j2
diff --git a/app/tests/log/sub_dir/not_reachable.txt b/app/tests/log/sub_dir/not_reachable.txt
new file mode 100644
index 0000000..e69de29
diff --git a/app/tests/log/test.txt b/app/tests/log/test.txt
new file mode 100644
index 0000000..d7c6d5e
--- /dev/null
+++ b/app/tests/log/test.txt
@@ -0,0 +1,19 @@
+2025-04-30 00:01:23 INFO | root | Server "proxy - unknown" will be started
+2025-04-30 00:01:23 INFO | root | current dir: /Users/sallius/tsun/tsun-gen3-proxy
+2025-04-30 00:01:23 INFO | root | config_path: ./config/
+2025-04-30 00:01:23 INFO | root | json_config: None
+2025-04-30 00:01:23 INFO | root | toml_config: None
+2025-04-30 00:01:23 INFO | root | trans_path: ../translations/
+2025-04-30 00:01:23 INFO | root | rel_urls: False
+2025-04-30 00:01:23 INFO | root | log_path: ./log/
+2025-04-30 00:01:23 INFO | root | log_backups: unlimited
+2025-04-30 00:01:23 INFO | root | LOG_LVL : None
+2025-04-30 00:01:23 INFO | root | ******
+2025-04-30 00:01:23 INFO | root | Read from /Users/sallius/tsun/tsun-gen3-proxy/app/src/cnf/default_config.toml => ok
+2025-04-30 00:01:23 INFO | root | Read from environment => ok
+2025-04-30 00:01:23 INFO | root | Read from ./config/config.json => n/a
+2025-04-30 00:01:23 INFO | root | Read from ./config/config.toml => n/a
+2025-04-30 00:01:23 INFO | root | ******
+2025-04-30 00:01:23 INFO | root | listen on port: 5005 for inverters
+2025-04-30 00:01:23 INFO | root | listen on port: 10000 for inverters
+2025-04-30 00:01:23 INFO | root | Start Quart
\ No newline at end of file
diff --git a/app/tests/test_inverter_g3p.py b/app/tests/test_inverter_g3p.py
index f16a2d8..f1bb398 100644
--- a/app/tests/test_inverter_g3p.py
+++ b/app/tests/test_inverter_g3p.py
@@ -37,6 +37,7 @@ def config_conn():
},
'solarman':{'enabled': True, 'host': 'test_cloud.local', 'port': 1234}, 'inverters':{'allow_all':True}
}
+ Config.log_path='app/tests/log/'
@pytest.fixture(scope="module", autouse=True)
def module_init():
diff --git a/app/tests/test_web_route.py b/app/tests/test_web_route.py
index 966f57e..9b36649 100644
--- a/app/tests/test_web_route.py
+++ b/app/tests/test_web_route.py
@@ -5,6 +5,7 @@ from web import Web, web
from async_stream import AsyncStreamClient
from gen3plus.inverter_g3p import InverterG3P
from test_inverter_g3p import FakeReader, FakeWriter, config_conn
+from cnf.config import Config
pytest_plugins = ('pytest_asyncio',)
@@ -64,6 +65,13 @@ async def test_rel_page(client):
assert response.mimetype == 'text/html'
web.build_relative_urls = False
+@pytest.mark.asyncio
+async def test_logging(client):
+ """Test the logging page route."""
+ response = await client.get('/logging')
+ assert response.status_code == 200
+ assert response.mimetype == 'text/html'
+
@pytest.mark.asyncio
async def test_favicon96(client):
"""Test the favicon-96x96.png route."""
@@ -101,7 +109,7 @@ async def test_manifest(client):
@pytest.mark.asyncio
async def test_data_fetch(create_inverter):
- """Test the healthy route."""
+ """Test the data-fetch route."""
_ = create_inverter
client = app.test_client()
response = await client.get('/data-fetch')
@@ -112,7 +120,7 @@ async def test_data_fetch(create_inverter):
@pytest.mark.asyncio
async def test_data_fetch1(create_inverter_server):
- """Test the healthy route."""
+ """Test the data-fetch route with server connection."""
_ = create_inverter_server
client = app.test_client()
response = await client.get('/data-fetch')
@@ -123,7 +131,7 @@ async def test_data_fetch1(create_inverter_server):
@pytest.mark.asyncio
async def test_data_fetch2(create_inverter_client):
- """Test the healthy route."""
+ """Test the data-fetch route with client connection."""
_ = create_inverter_client
client = app.test_client()
response = await client.get('/data-fetch')
@@ -134,9 +142,11 @@ async def test_data_fetch2(create_inverter_client):
@pytest.mark.asyncio
async def test_language_en(client):
- """Test the language/en route."""
- response = await client.get('/language/en')
+ """Test the language/en route and cookie."""
+ response = await client.get('/language/en', headers={'referer': '/index'})
assert response.status_code == 302
+ assert response.content_language.pop() == 'en'
+ assert response.location == '/index'
assert response.mimetype == 'text/html'
client.set_cookie('test', key='language', value='de')
@@ -146,14 +156,53 @@ async def test_language_en(client):
@pytest.mark.asyncio
async def test_language_de(client):
- """Test the language/en route."""
- response = await client.get('/language/de')
+ """Test the language/de route."""
+ response = await client.get('/language/de', headers={'referer': '/'})
assert response.status_code == 302
+ assert response.content_language.pop() == 'de'
+ assert response.location == '/'
assert response.mimetype == 'text/html'
+
@pytest.mark.asyncio
async def test_language_unknown(client):
- """Test the language/en route."""
- response = await client.get('/language/unknonw')
+ """Test the language/unknown route."""
+ response = await client.get('/language/unknown')
assert response.status_code == 404
assert response.mimetype == 'text/html'
+
+
+@pytest.mark.asyncio
+async def test_file_fetch(client, config_conn):
+ """Test the data-fetch route."""
+ _ = config_conn
+ assert Config.log_path == 'app/tests/log/'
+ response = await client.get('/file-fetch')
+ assert response.status_code == 200
+
+
+@pytest.mark.asyncio
+async def test_send_file(client, config_conn):
+ """Test the send-file route."""
+ _ = config_conn
+ assert Config.log_path == 'app/tests/log/'
+ response = await client.get('/send-file/test.txt')
+ assert response.status_code == 200
+
+
+@pytest.mark.asyncio
+async def test_missing_send_file(client, config_conn):
+ """Test the send-file route (file not found)."""
+ _ = config_conn
+ assert Config.log_path == 'app/tests/log/'
+ response = await client.get('/send-file/no_file.log')
+ assert response.status_code == 404
+
+
+@pytest.mark.asyncio
+async def test_invalid_send_file(client, config_conn):
+ """Test the send-file route (invalid filename)."""
+ _ = config_conn
+ assert Config.log_path == 'app/tests/log/'
+ response = await client.get('/send-file/../test_web_route.py')
+ assert response.status_code == 404
diff --git a/app/translations/de/LC_MESSAGES/messages.po b/app/translations/de/LC_MESSAGES/messages.po
index a18f69e..b09eca8 100644
--- a/app/translations/de/LC_MESSAGES/messages.po
+++ b/app/translations/de/LC_MESSAGES/messages.po
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: tsun-gen3-proxy 0.14.0\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
-"POT-Creation-Date: 2025-04-28 21:00+0200\n"
+"POT-Creation-Date: 2025-05-01 17:48+0200\n"
"PO-Revision-Date: 2025-04-18 16:24+0200\n"
"Last-Translator: FULL NAME \n"
"Language: de\n"
@@ -44,42 +44,64 @@ msgid "Updated:"
msgstr "Aktualisiert:"
#: src/web/templates/base.html.j2:58
+#: src/web/templates/templ_conn_table.html.j2:15
msgid "Connections"
msgstr "Verbindungen"
-#: src/web/templates/index.html.j2:5
+#: src/web/templates/base.html.j2:60 src/web/templates/page_logging.html.j2:5
+msgid "Log Files"
+msgstr "Log Dateien"
+
+#: src/web/templates/page_index.html.j2:5
msgid "Proxy Connection Overview"
msgstr "Proxy Verbindungen"
-#: src/web/templates/index.html.j2:16
+#: src/web/templates/page_index.html.j2:16
msgid "Server Mode"
msgstr "Server Modus"
-#: src/web/templates/index.html.j2:17
+#: src/web/templates/page_index.html.j2:17
msgid "Established from device to proxy"
msgstr "Vom Gerät zum Proxy aufgebaut"
-#: src/web/templates/index.html.j2:27
+#: src/web/templates/page_index.html.j2:27
msgid "Client Mode"
msgstr "Client Modus"
-#: src/web/templates/index.html.j2:28
+#: src/web/templates/page_index.html.j2:28
msgid "Established from proxy to device"
msgstr "Vom Proxy zum Gerät aufgebaut"
-#: src/web/templates/index.html.j2:38
+#: src/web/templates/page_index.html.j2:38
msgid "Proxy Mode"
msgstr "Proxy Modus"
-#: src/web/templates/index.html.j2:39
+#: src/web/templates/page_index.html.j2:39
msgid "Forwarding data to cloud"
msgstr "Weiterleitung in die Cloud"
-#: src/web/templates/index.html.j2:49
+#: src/web/templates/page_index.html.j2:49
msgid "Emu Mode"
msgstr "Emu Modus"
-#: src/web/templates/index.html.j2:50
+#: src/web/templates/page_index.html.j2:50
msgid "Emulation sends data to cloud"
msgstr "Emulation sendet in die Cloud"
+#: src/web/templates/templ_log_files_list.html.j2:11
+msgid "Created"
+msgstr "Erzeugt"
+
+#: src/web/templates/templ_log_files_list.html.j2:11
+msgid "Modified"
+msgstr "Modifiziert"
+
+#: src/web/templates/templ_log_files_list.html.j2:11
+msgid "Size"
+msgstr "Größe"
+
+#: src/web/templates/templ_log_files_list.html.j2:20
+msgid "Download File"
+msgstr "Datei Download"
+
+