update shop
This commit is contained in:
@@ -22,10 +22,12 @@ RUN mkdir -p /app/instance /app/assets
|
|||||||
# Default port
|
# Default port
|
||||||
ENV FLET_PORT=8080
|
ENV FLET_PORT=8080
|
||||||
|
|
||||||
EXPOSE 8080
|
# Copy the entrypoint script and make it executable
|
||||||
|
COPY entrypoint.sh .
|
||||||
|
RUN chmod +x entrypoint.sh
|
||||||
|
|
||||||
# Health check
|
# Expose both ports (Flet 8080 and Flask 9000)
|
||||||
HEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD curl -fsS http://127.0.0.1:8080/ || exit 1
|
EXPOSE 8080 9000
|
||||||
|
|
||||||
# Run entrypoint to create superuser and start the app
|
# Use the script to start the container
|
||||||
ENTRYPOINT ["sh", "-c", "python create_super_user.py && python main.py"]
|
ENTRYPOINT ["./entrypoint.sh"]
|
||||||
@@ -38,6 +38,15 @@ class AdminChat:
|
|||||||
on_click=self.on_send_click,
|
on_click=self.on_send_click,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Notification sound to be played on new client messages
|
||||||
|
# Make sure the file path points to a valid audio file in your assets.
|
||||||
|
# Example: place `chat_notify.mp3` in `assets/sounds/`.
|
||||||
|
self.notification_audio = ft.Audio(
|
||||||
|
src="assets/sounds/chat_notify_triple.mp3",
|
||||||
|
autoplay=True,
|
||||||
|
volume=1.0,
|
||||||
|
)
|
||||||
|
|
||||||
# Subscribe to PubSub for live updates from clients
|
# Subscribe to PubSub for live updates from clients
|
||||||
try:
|
try:
|
||||||
self.page.pubsub.subscribe(self.on_pubsub_message)
|
self.page.pubsub.subscribe(self.on_pubsub_message)
|
||||||
@@ -107,7 +116,19 @@ class AdminChat:
|
|||||||
vertical_alignment=ft.CrossAxisAlignment.CENTER,
|
vertical_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
controls=[
|
controls=[
|
||||||
self.header_label,
|
self.header_label,
|
||||||
ft.Icon(ft.Icons.SUPPORT_AGENT),
|
ft.Row(
|
||||||
|
[
|
||||||
|
ft.Icon(ft.Icons.SUPPORT_AGENT),
|
||||||
|
ft.WebView(
|
||||||
|
url="/enable-notifications", # small HTML page with a single 'Activează notificările' button
|
||||||
|
width=160,
|
||||||
|
height=40,
|
||||||
|
expand=False,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
vertical_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
|
spacing=8,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
ft.Divider(),
|
ft.Divider(),
|
||||||
@@ -120,6 +141,7 @@ class AdminChat:
|
|||||||
vertical_alignment=ft.CrossAxisAlignment.CENTER,
|
vertical_alignment=ft.CrossAxisAlignment.CENTER,
|
||||||
controls=[self.input_field, self.send_button],
|
controls=[self.input_field, self.send_button],
|
||||||
),
|
),
|
||||||
|
self.notification_audio,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -165,7 +187,7 @@ class AdminChat:
|
|||||||
title = f"Chat #{chat_id}"
|
title = f"Chat #{chat_id}"
|
||||||
if user_id:
|
if user_id:
|
||||||
user = self.user_namager.get(user_id)
|
user = self.user_namager.get(user_id)
|
||||||
title += f" • User {user['name'].replace("~", " ")}"
|
title += f" • User {user['name'].replace('~', ' ')}"
|
||||||
|
|
||||||
subtitle_parts = []
|
subtitle_parts = []
|
||||||
if status == "open":
|
if status == "open":
|
||||||
@@ -232,7 +254,7 @@ class AdminChat:
|
|||||||
label = f"Chat #{chat_id}"
|
label = f"Chat #{chat_id}"
|
||||||
if user_id:
|
if user_id:
|
||||||
user = self.user_namager.get(user_id)
|
user = self.user_namager.get(user_id)
|
||||||
label += f" • User {user['name'].replace("~", " ")}"
|
label += f" • User {user['name'].replace('~', ' ')}"
|
||||||
self.header_label.value = label
|
self.header_label.value = label
|
||||||
else:
|
else:
|
||||||
self.header_label.value = f"Chat #{chat_id}"
|
self.header_label.value = f"Chat #{chat_id}"
|
||||||
@@ -362,6 +384,14 @@ class AdminChat:
|
|||||||
if data.get("type") == "chat_message" and data.get("sender") == "client":
|
if data.get("type") == "chat_message" and data.get("sender") == "client":
|
||||||
chat_id = data.get("chat_id")
|
chat_id = data.get("chat_id")
|
||||||
|
|
||||||
|
# Play notification sound for any incoming client message
|
||||||
|
try:
|
||||||
|
print('Play sound')
|
||||||
|
self.notification_audio.play()
|
||||||
|
print('Played sound')
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
# If this is the currently open chat, append bubble directly
|
# If this is the currently open chat, append bubble directly
|
||||||
if self.active_chat_id == chat_id:
|
if self.active_chat_id == chat_id:
|
||||||
text = data.get("text", "")
|
text = data.get("text", "")
|
||||||
|
|||||||
BIN
UI_V2/assets/sounds/chat_notify.wav
Normal file
BIN
UI_V2/assets/sounds/chat_notify.wav
Normal file
Binary file not shown.
BIN
UI_V2/assets/sounds/chat_notify_triple.wav
Normal file
BIN
UI_V2/assets/sounds/chat_notify_triple.wav
Normal file
Binary file not shown.
14
UI_V2/chat/chat.py
Normal file
14
UI_V2/chat/chat.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import flet as ft
|
||||||
|
|
||||||
|
class Chat:
|
||||||
|
def __init__(self, page: ft.Page):
|
||||||
|
self.page = page
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
return ft.Container(
|
||||||
|
content=ft.Column(
|
||||||
|
[
|
||||||
|
ft.Text("Aveti nevoie de ajutor?")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
@@ -25,6 +25,7 @@ services:
|
|||||||
# SHOP_DB_PATH: "/app/instance/shop.db" # custom var
|
# SHOP_DB_PATH: "/app/instance/shop.db" # custom var
|
||||||
expose:
|
expose:
|
||||||
- "8080"
|
- "8080"
|
||||||
|
- "9000"
|
||||||
volumes:
|
volumes:
|
||||||
- tg_instance:/app/instance
|
- tg_instance:/app/instance
|
||||||
- tg_assets:/app/assets
|
- tg_assets:/app/assets
|
||||||
|
|||||||
12
UI_V2/entrypoint.sh
Normal file
12
UI_V2/entrypoint.sh
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# 1. Run your superuser script
|
||||||
|
python create_super_user.py
|
||||||
|
|
||||||
|
# 2. Start Flask in the background (&)
|
||||||
|
# We use & to let it run while the script continues
|
||||||
|
python flask_server.py &
|
||||||
|
|
||||||
|
# 3. Start Flet (Main process)
|
||||||
|
# This stays in the foreground so the container doesn't exit
|
||||||
|
python main.py
|
||||||
62
UI_V2/flask_server.py
Normal file
62
UI_V2/flask_server.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
from logging.handlers import RotatingFileHandler
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
from flask_cors import CORS
|
||||||
|
|
||||||
|
try:
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
load_dotenv()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
from UI_V2.helpers.netopia import verify_ipn, get_status
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
CORS(app, resources={r"/api/*": {"origins": "*"}})
|
||||||
|
|
||||||
|
# ---------- Logging ----------
|
||||||
|
app.logger.setLevel(logging.INFO)
|
||||||
|
_log_dir = os.getenv("LOG_DIR", "logs")
|
||||||
|
os.makedirs(_log_dir, exist_ok=True)
|
||||||
|
_handler = RotatingFileHandler(os.path.join(_log_dir, "netopia_api.log"), maxBytes=1_000_000, backupCount=3)
|
||||||
|
_handler.setLevel(logging.INFO)
|
||||||
|
_handler.setFormatter(logging.Formatter("%(asctime)s [%(levelname)s] %(message)s"))
|
||||||
|
app.logger.addHandler(_handler)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/healthz")
|
||||||
|
def healthz():
|
||||||
|
return {"ok": True}, 200
|
||||||
|
|
||||||
|
@app.post("/api/payments/ipn")
|
||||||
|
def ipn():
|
||||||
|
try:
|
||||||
|
raw = request.data
|
||||||
|
data = verify_ipn(raw)
|
||||||
|
app.logger.info("IPN OK: %s", data)
|
||||||
|
return jsonify({"ok": True, "data": data}), 200
|
||||||
|
except Exception as e:
|
||||||
|
app.logger.exception("IPN verification failed: %s", e)
|
||||||
|
return jsonify({"ok": False, "error": str(e)}), 400
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/payments/status")
|
||||||
|
def status():
|
||||||
|
ntp_id = request.args.get("ntpID")
|
||||||
|
order_id = request.args.get("orderID")
|
||||||
|
try:
|
||||||
|
resp = get_status(ntp_id=ntp_id, order_id=order_id)
|
||||||
|
return jsonify({"ok": True, "data": resp}), 200
|
||||||
|
except Exception as e:
|
||||||
|
app.logger.exception("Status query failed: %s", e)
|
||||||
|
return jsonify({"ok": False, "error": str(e)}), 400
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
host = os.getenv("API_HOST", "0.0.0.0")
|
||||||
|
port = int(os.getenv("API_PORT", "9000"))
|
||||||
|
app.logger.info("Starting NETOPIA Flask sidecar on %s:%s", host, port)
|
||||||
|
app.run(host=host, port=port)
|
||||||
@@ -22,7 +22,7 @@ from dotenv import load_dotenv
|
|||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
def main(page: ft.Page):
|
def main(page: ft.Page):
|
||||||
page.title = "Taina Gustului"
|
page.title = "Taina Gustului - Condimente, nuci și fructe uscate în Craiova"
|
||||||
page.theme_mode = ft.ThemeMode.LIGHT
|
page.theme_mode = ft.ThemeMode.LIGHT
|
||||||
page.theme = ft.Theme(color_scheme=ft.ColorScheme(primary=ft.Colors.BROWN))
|
page.theme = ft.Theme(color_scheme=ft.ColorScheme(primary=ft.Colors.BROWN))
|
||||||
page.vertical_alignment = ft.MainAxisAlignment.CENTER
|
page.vertical_alignment = ft.MainAxisAlignment.CENTER
|
||||||
|
|||||||
Reference in New Issue
Block a user