Working auth system
This commit is contained in:
parent
b1c2264cc6
commit
2c91d6f100
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"ipmi_host": "192.168.5.191",
|
||||||
|
"ipmi_username": "root",
|
||||||
|
"ipmi_password": "calvin",
|
||||||
|
"ipmi_port": 623,
|
||||||
|
"ssh_enabled": false,
|
||||||
|
"ssh_host": null,
|
||||||
|
"ssh_username": null,
|
||||||
|
"ssh_password": null,
|
||||||
|
"ssh_use_key": false,
|
||||||
|
"ssh_key_file": null,
|
||||||
|
"ssh_port": 22,
|
||||||
|
"enabled": true,
|
||||||
|
"interval": 10,
|
||||||
|
"min_speed": 10,
|
||||||
|
"max_speed": 100,
|
||||||
|
"fan_curve": [
|
||||||
|
{
|
||||||
|
"temp": 30,
|
||||||
|
"speed": 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"temp": 40,
|
||||||
|
"speed": 25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"temp": 50,
|
||||||
|
"speed": 40
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"temp": 60,
|
||||||
|
"speed": 60
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"temp": 70,
|
||||||
|
"speed": 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"temp": 80,
|
||||||
|
"speed": 100
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"panic_temp": 85,
|
||||||
|
"panic_speed": 100
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
{"users": {"admin": "ecd71870d1963316a97e3ac3408c9835ad8cf0f3c1bc703527c30265534f75ae"}}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Reset password for fan controller"""
|
||||||
|
import json
|
||||||
|
import hashlib
|
||||||
|
import sys
|
||||||
|
|
||||||
|
USERS_FILE = "/home/devmatrix/projects/fan-controller-v2/data/users.json"
|
||||||
|
|
||||||
|
def hash_password(password):
|
||||||
|
return hashlib.sha256(password.encode()).hexdigest()
|
||||||
|
|
||||||
|
if len(sys.argv) != 3:
|
||||||
|
print("Usage: reset_password.py <username> <new_password>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
username = sys.argv[1]
|
||||||
|
password = sys.argv[2]
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(USERS_FILE) as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
data["users"][username] = hash_password(password)
|
||||||
|
|
||||||
|
with open(USERS_FILE, 'w') as f:
|
||||||
|
json.dump(data, f)
|
||||||
|
|
||||||
|
print(f"Password reset for user: {username}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
252
server.log
252
server.log
|
|
@ -1,7 +1,255 @@
|
||||||
/home/devmatrix/projects/fan-controller-v2/web_server.py:49: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/
|
/home/devmatrix/projects/fan-controller-v2/web_server.py:49: PydanticDeprecatedSince20: Pydantic V1 style `@validator` validators are deprecated. You should migrate to Pydantic V2 style `@field_validator` validators, see the migration guide for more details. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.5/migration/
|
||||||
@validator('new_password')
|
@validator('new_password')
|
||||||
INFO: Started server process [888347]
|
INFO: Started server process [893663]
|
||||||
INFO: Waiting for application startup.
|
INFO: Waiting for application startup.
|
||||||
INFO: Application startup complete.
|
INFO: Application startup complete.
|
||||||
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
|
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
|
||||||
INFO: 127.0.0.1:44244 - "GET /api/status HTTP/1.1" 401 Unauthorized
|
INFO: 192.168.5.30:56770 - "GET /api/status HTTP/1.1" 401 Unauthorized
|
||||||
|
INFO: 192.168.5.30:56770 - "GET /login HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:49736 - "POST /api/auth/login HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:34494 - "GET /api/status HTTP/1.1" 401 Unauthorized
|
||||||
|
/home/devmatrix/projects/fan-controller-v2/web_server.py:141: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||||
|
expiry = datetime.utcnow() + timedelta(days=7)
|
||||||
|
INFO: 192.168.5.30:49736 - "POST /api/auth/login HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:49736 - "GET / HTTP/1.1" 200 OK
|
||||||
|
/home/devmatrix/projects/fan-controller-v2/web_server.py:149: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||||
|
if datetime.utcnow() > expiry:
|
||||||
|
2026-02-20 15:37:16,554 - fan_controller - INFO - Loaded config from /home/devmatrix/projects/fan-controller-v2/data/config.json
|
||||||
|
INFO: 192.168.5.30:49736 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:49736 - "GET /favicon.ico HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:49736 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:34498 - "POST /api/auth/login HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:34506 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:49736 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
2026-02-20 15:37:24,066 - fan_controller - INFO - Saved config to /home/devmatrix/projects/fan-controller-v2/data/config.json
|
||||||
|
2026-02-20 15:37:24,068 - fan_controller - ERROR - IPMI command error: [Errno 2] No such file or directory: 'ipmitool'
|
||||||
|
2026-02-20 15:37:24,069 - fan_controller - ERROR - Failed to connect to IPMI at 192.168.5.191
|
||||||
|
INFO: 192.168.5.30:49736 - "POST /api/config/ipmi HTTP/1.1" 200 OK
|
||||||
|
/home/devmatrix/projects/fan-controller-v2/web_server.py:149: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||||
|
if datetime.utcnow() > expiry:
|
||||||
|
INFO: 192.168.5.30:49736 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:49736 - "GET /favicon.ico HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:49736 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
2026-02-20 15:37:27,369 - fan_controller - INFO - Saved config to /home/devmatrix/projects/fan-controller-v2/data/config.json
|
||||||
|
2026-02-20 15:37:27,370 - fan_controller - ERROR - IPMI command error: [Errno 2] No such file or directory: 'ipmitool'
|
||||||
|
2026-02-20 15:37:27,371 - fan_controller - ERROR - Failed to connect to IPMI at 192.168.5.191
|
||||||
|
2026-02-20 15:37:27,371 - fan_controller - ERROR - Cannot start service - IPMI connection failed
|
||||||
|
INFO: 192.168.5.30:49736 - "POST /api/control/auto HTTP/1.1" 200 OK
|
||||||
|
/home/devmatrix/projects/fan-controller-v2/web_server.py:149: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||||
|
if datetime.utcnow() > expiry:
|
||||||
|
INFO: 192.168.5.30:49736 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:49736 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:37104 - "GET / HTTP/1.1" 200 OK
|
||||||
|
INFO: 127.0.0.1:37118 - "GET /login HTTP/1.1" 200 OK
|
||||||
|
/home/devmatrix/projects/fan-controller-v2/web_server.py:141: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||||
|
expiry = datetime.utcnow() + timedelta(days=7)
|
||||||
|
INFO: 127.0.0.1:37130 - "POST /api/auth/login HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:49736 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
2026-02-20 15:37:33,643 - fan_controller - INFO - Saved config to /home/devmatrix/projects/fan-controller-v2/data/config.json
|
||||||
|
INFO: 192.168.5.30:49736 - "POST /api/control/auto HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:49736 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:49736 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:49736 - "POST /api/control/manual HTTP/1.1" 500 Internal Server Error
|
||||||
|
ERROR: Exception in ASGI application
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 419, in run_asgi
|
||||||
|
result = await app( # type: ignore[func-returns-value]
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
|
||||||
|
return await self.app(scope, receive, send)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/applications.py", line 1054, in __call__
|
||||||
|
await super().__call__(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/applications.py", line 123, in __call__
|
||||||
|
await self.middleware_stack(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 186, in __call__
|
||||||
|
raise exc
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 164, in __call__
|
||||||
|
await self.app(scope, receive, _send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/cors.py", line 91, in __call__
|
||||||
|
await self.simple_response(scope, receive, send, request_headers=headers)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/cors.py", line 146, in simple_response
|
||||||
|
await self.app(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
|
||||||
|
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
|
||||||
|
raise exc
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
|
||||||
|
await app(scope, receive, sender)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 762, in __call__
|
||||||
|
await self.middleware_stack(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 782, in app
|
||||||
|
await route.handle(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 297, in handle
|
||||||
|
await self.app(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 77, in app
|
||||||
|
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
|
||||||
|
raise exc
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
|
||||||
|
await app(scope, receive, sender)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 72, in app
|
||||||
|
response = await func(request)
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/routing.py", line 299, in app
|
||||||
|
raise e
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/routing.py", line 294, in app
|
||||||
|
raw_response = await run_endpoint_function(
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/routing.py", line 191, in run_endpoint_function
|
||||||
|
return await dependant.call(**values)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/devmatrix/projects/fan-controller-v2/web_server.py", line 1352, in api_control_manual
|
||||||
|
if not service._init_controller():
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
AttributeError: 'FanControlService' object has no attribute '_init_controller'. Did you mean: '_init_ipmi_controller'?
|
||||||
|
2026-02-20 15:37:36,531 - fan_controller - INFO - Saved config to /home/devmatrix/projects/fan-controller-v2/data/config.json
|
||||||
|
2026-02-20 15:37:36,533 - fan_controller - ERROR - IPMI command error: [Errno 2] No such file or directory: 'ipmitool'
|
||||||
|
2026-02-20 15:37:36,533 - fan_controller - ERROR - Failed to connect to IPMI at 192.168.5.191
|
||||||
|
2026-02-20 15:37:36,533 - fan_controller - ERROR - Cannot start service - IPMI connection failed
|
||||||
|
INFO: 192.168.5.30:62376 - "POST /api/control/auto HTTP/1.1" 200 OK
|
||||||
|
/home/devmatrix/projects/fan-controller-v2/web_server.py:149: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
|
||||||
|
if datetime.utcnow() > expiry:
|
||||||
|
INFO: 192.168.5.30:62376 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:62376 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:62376 - "POST /api/control/manual HTTP/1.1" 500 Internal Server Error
|
||||||
|
ERROR: Exception in ASGI application
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 419, in run_asgi
|
||||||
|
result = await app( # type: ignore[func-returns-value]
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
|
||||||
|
return await self.app(scope, receive, send)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/applications.py", line 1054, in __call__
|
||||||
|
await super().__call__(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/applications.py", line 123, in __call__
|
||||||
|
await self.middleware_stack(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 186, in __call__
|
||||||
|
raise exc
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 164, in __call__
|
||||||
|
await self.app(scope, receive, _send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/cors.py", line 91, in __call__
|
||||||
|
await self.simple_response(scope, receive, send, request_headers=headers)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/cors.py", line 146, in simple_response
|
||||||
|
await self.app(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
|
||||||
|
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
|
||||||
|
raise exc
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
|
||||||
|
await app(scope, receive, sender)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 762, in __call__
|
||||||
|
await self.middleware_stack(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 782, in app
|
||||||
|
await route.handle(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 297, in handle
|
||||||
|
await self.app(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 77, in app
|
||||||
|
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
|
||||||
|
raise exc
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
|
||||||
|
await app(scope, receive, sender)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 72, in app
|
||||||
|
response = await func(request)
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/routing.py", line 299, in app
|
||||||
|
raise e
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/routing.py", line 294, in app
|
||||||
|
raw_response = await run_endpoint_function(
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/routing.py", line 191, in run_endpoint_function
|
||||||
|
return await dependant.call(**values)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/devmatrix/projects/fan-controller-v2/web_server.py", line 1352, in api_control_manual
|
||||||
|
if not service._init_controller():
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
AttributeError: 'FanControlService' object has no attribute '_init_controller'. Did you mean: '_init_ipmi_controller'?
|
||||||
|
INFO: 192.168.5.30:55640 - "POST /api/test HTTP/1.1" 500 Internal Server Error
|
||||||
|
ERROR: Exception in ASGI application
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 419, in run_asgi
|
||||||
|
result = await app( # type: ignore[func-returns-value]
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 84, in __call__
|
||||||
|
return await self.app(scope, receive, send)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/applications.py", line 1054, in __call__
|
||||||
|
await super().__call__(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/applications.py", line 123, in __call__
|
||||||
|
await self.middleware_stack(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 186, in __call__
|
||||||
|
raise exc
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 164, in __call__
|
||||||
|
await self.app(scope, receive, _send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/cors.py", line 91, in __call__
|
||||||
|
await self.simple_response(scope, receive, send, request_headers=headers)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/cors.py", line 146, in simple_response
|
||||||
|
await self.app(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
|
||||||
|
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
|
||||||
|
raise exc
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
|
||||||
|
await app(scope, receive, sender)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 762, in __call__
|
||||||
|
await self.middleware_stack(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 782, in app
|
||||||
|
await route.handle(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 297, in handle
|
||||||
|
await self.app(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 77, in app
|
||||||
|
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
|
||||||
|
raise exc
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
|
||||||
|
await app(scope, receive, sender)
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/starlette/routing.py", line 72, in app
|
||||||
|
response = await func(request)
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/routing.py", line 299, in app
|
||||||
|
raise e
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/routing.py", line 294, in app
|
||||||
|
raw_response = await run_endpoint_function(
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/devmatrix/.local/lib/python3.12/site-packages/fastapi/routing.py", line 191, in run_endpoint_function
|
||||||
|
return await dependant.call(**values)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "/home/devmatrix/projects/fan-controller-v2/web_server.py", line 1323, in api_test
|
||||||
|
if not service._init_controller():
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
AttributeError: 'FanControlService' object has no attribute '_init_controller'. Did you mean: '_init_ipmi_controller'?
|
||||||
|
INFO: 192.168.5.30:61984 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:61984 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:61984 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:61984 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:61984 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:61984 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:61984 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:61984 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:61984 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:61984 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:61984 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:61984 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:61984 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
INFO: 192.168.5.30:63389 - "GET /api/status HTTP/1.1" 200 OK
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,7 @@ LOGIN_HTML = '''<!DOCTYPE html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.ico">
|
||||||
<title>Login - IPMI Fan Controller</title>
|
<title>Login - IPMI Fan Controller</title>
|
||||||
<style>
|
<style>
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
|
@ -251,26 +252,36 @@ LOGIN_HTML = '''<!DOCTYPE html>
|
||||||
const errorEl = document.getElementById('error');
|
const errorEl = document.getElementById('error');
|
||||||
errorEl.style.display = 'none';
|
errorEl.style.display = 'none';
|
||||||
|
|
||||||
|
const username = document.getElementById('username').value;
|
||||||
|
const password = document.getElementById('password').value;
|
||||||
|
|
||||||
|
console.log('Attempting login for:', username);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/auth/login', {
|
const res = await fetch('/api/auth/login', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({username, password})
|
||||||
username: document.getElementById('username').value,
|
|
||||||
password: document.getElementById('password').value
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
|
||||||
|
|
||||||
if (data.success) {
|
console.log('Response status:', res.status);
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
console.log('Response data:', data);
|
||||||
|
|
||||||
|
if (data.success && data.token) {
|
||||||
|
console.log('Login successful, storing token');
|
||||||
localStorage.setItem('token', data.token);
|
localStorage.setItem('token', data.token);
|
||||||
|
console.log('Token stored, redirecting...');
|
||||||
window.location.href = '/';
|
window.location.href = '/';
|
||||||
} else {
|
} else {
|
||||||
|
console.log('Login failed:', data.error);
|
||||||
errorEl.textContent = data.error || 'Login failed';
|
errorEl.textContent = data.error || 'Login failed';
|
||||||
errorEl.style.display = 'block';
|
errorEl.style.display = 'block';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
errorEl.textContent = 'Connection error';
|
console.error('Login error:', e);
|
||||||
|
errorEl.textContent = 'Connection error: ' + e.message;
|
||||||
errorEl.style.display = 'block';
|
errorEl.style.display = 'block';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -283,6 +294,7 @@ SETUP_HTML = '''<!DOCTYPE html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.ico">
|
||||||
<title>Setup - IPMI Fan Controller</title>
|
<title>Setup - IPMI Fan Controller</title>
|
||||||
<style>
|
<style>
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
|
@ -453,6 +465,7 @@ DASHBOARD_HTML = '''<!DOCTYPE html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.ico">
|
||||||
<title>IPMI Fan Controller v2</title>
|
<title>IPMI Fan Controller v2</title>
|
||||||
<style>
|
<style>
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
|
@ -862,6 +875,11 @@ DASHBOARD_HTML = '''<!DOCTYPE html>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
// Check auth on page load
|
||||||
|
if (!localStorage.getItem('token')) {
|
||||||
|
window.location.replace('/login');
|
||||||
|
}
|
||||||
|
|
||||||
let currentStatus = {};
|
let currentStatus = {};
|
||||||
let currentConfig = {};
|
let currentConfig = {};
|
||||||
|
|
||||||
|
|
@ -1214,18 +1232,32 @@ app.add_middleware(
|
||||||
)
|
)
|
||||||
|
|
||||||
# Routes
|
# Routes
|
||||||
|
@app.get("/favicon.ico")
|
||||||
|
async def favicon():
|
||||||
|
"""Return a simple favicon to prevent 404 errors."""
|
||||||
|
# Return a transparent 1x1 PNG
|
||||||
|
transparent_png = bytes([
|
||||||
|
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, # PNG signature
|
||||||
|
0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, # IHDR chunk
|
||||||
|
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, # 1x1 pixel
|
||||||
|
0x08, 0x06, 0x00, 0x00, 0x00, 0x1F, 0x15, 0xC4, 0x89,
|
||||||
|
0x00, 0x00, 0x00, 0x0D, 0x49, 0x44, 0x41, 0x54, # IDAT chunk (transparent)
|
||||||
|
0x08, 0xD7, 0x63, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00,
|
||||||
|
0x02, 0x00, 0x01, 0xE2, 0x21, 0xBC, 0x33,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, # IEND chunk
|
||||||
|
0xAE, 0x42, 0x60, 0x82
|
||||||
|
])
|
||||||
|
from fastapi.responses import Response
|
||||||
|
return Response(content=transparent_png, media_type="image/png")
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
async def root(request: Request):
|
async def root(request: Request):
|
||||||
"""Main dashboard or redirect to setup/login."""
|
"""Main dashboard - always returns dashboard HTML, JS handles auth."""
|
||||||
# Check if setup is needed
|
# Check if setup is needed
|
||||||
if not user_manager.is_setup_complete():
|
if not user_manager.is_setup_complete():
|
||||||
return HTMLResponse(content=SETUP_HTML)
|
return HTMLResponse(content=SETUP_HTML)
|
||||||
|
|
||||||
# Check auth
|
# Always return dashboard - JavaScript will check token and redirect if needed
|
||||||
token = request.cookies.get("token") or request.headers.get("authorization", "").replace("Bearer ", "")
|
|
||||||
if not token or not user_manager.verify_token(token):
|
|
||||||
return HTMLResponse(content=LOGIN_HTML)
|
|
||||||
|
|
||||||
return HTMLResponse(content=DASHBOARD_HTML)
|
return HTMLResponse(content=DASHBOARD_HTML)
|
||||||
|
|
||||||
@app.get("/login")
|
@app.get("/login")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue