chore: add watchdog support for backend HMR

This commit is contained in:
Sam Chau
2024-09-07 17:17:25 +09:30
parent ae80d50e7e
commit ed12fff994
7 changed files with 915 additions and 756 deletions

View File

@@ -1,10 +1,6 @@
FROM python:3.9
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "run.py"]
CMD ["python", "run_dev.py"]

View File

@@ -16,6 +16,7 @@ os.makedirs(FORMAT_DIR, exist_ok=True)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@bp.route('', methods=['GET', 'POST'])
def handle_formats():
if request.method == 'POST':
@@ -26,6 +27,7 @@ def handle_formats():
formats = load_all_formats()
return jsonify(formats)
@bp.route('/<int:id>', methods=['GET', 'PUT', 'DELETE'])
def handle_format(id):
if request.method == 'GET':
@@ -46,14 +48,17 @@ def handle_format(id):
return jsonify(result), 400
return jsonify(result), 200
def is_format_used_in_profile(format_id):
profiles = load_all_profiles()
for profile in profiles:
for custom_format in profile.get('custom_formats', []):
if custom_format.get('id') == format_id and custom_format.get('score', 0) != 0:
if custom_format.get('id') == format_id and custom_format.get(
'score', 0) != 0:
return True
return False
def save_format(data):
logger.info("Received data for saving format: %s", data)
@@ -71,9 +76,11 @@ def save_format(data):
existing_filename = os.path.join(FORMAT_DIR, f"{format_id}.yml")
if os.path.exists(existing_filename):
existing_data = load_format(format_id)
date_created = existing_data.get('date_created', get_current_timestamp())
date_created = existing_data.get('date_created',
get_current_timestamp())
else:
raise FileNotFoundError(f"No existing file found for ID: {format_id}")
raise FileNotFoundError(
f"No existing file found for ID: {format_id}")
date_modified = get_current_timestamp()
@@ -81,12 +88,11 @@ def save_format(data):
conditions = []
for condition in data.get('conditions', []):
logger.info("Processing condition: %s", condition)
cond_dict = OrderedDict([
('type', condition['type']),
('name', sanitize_input(condition['name'])),
('negate', condition.get('negate', False)),
('required', condition.get('required', False))
])
cond_dict = OrderedDict([('type', condition['type']),
('name', sanitize_input(condition['name'])),
('negate', condition.get('negate', False)),
('required', condition.get('required',
False))])
if condition['type'] == 'regex':
cond_dict['regex_id'] = condition['regex_id']
elif condition['type'] == 'size':
@@ -100,25 +106,25 @@ def save_format(data):
tags = [sanitize_input(tag) for tag in data.get('tags', [])]
# Construct the ordered data
ordered_data = OrderedDict([
('id', format_id),
('name', name),
('description', description),
('date_created', str(date_created)),
('date_modified', str(date_modified)),
('conditions', conditions),
('tags', tags)
])
ordered_data = OrderedDict([('id', format_id), ('name', name),
('description', description),
('date_created', str(date_created)),
('date_modified', str(date_modified)),
('conditions', conditions), ('tags', tags)])
# Generate the filename using only the ID
filename = os.path.join(FORMAT_DIR, f"{format_id}.yml")
# Write to the file
with open(filename, 'w') as file:
yaml.dump(ordered_data, file, default_flow_style=False, Dumper=yaml.SafeDumper)
yaml.dump(ordered_data,
file,
default_flow_style=False,
Dumper=yaml.SafeDumper)
return ordered_data
def load_format(id):
filename = os.path.join(FORMAT_DIR, f"{id}.yml")
if os.path.exists(filename):
@@ -127,12 +133,16 @@ def load_format(id):
return data
return None
def delete_format(id):
if is_format_used_in_profile(id):
return {"error": "Format in use", "message": "This format is being used in one or more profiles."}
return {
"error": "Format in use",
"message": "This format is being used in one or more profiles."
}
filename = os.path.join(FORMAT_DIR, f"{id}.yml")
if os.path.exists(filename):
os.remove(filename)
return {"message": f"Format with ID {id} deleted."}
return {"error": f"Format with ID {id} not found."}
return {"error": f"Format with ID {id} not found."}

View File

@@ -3,4 +3,5 @@ Flask-CORS==3.0.10
PyYAML==5.4.1
requests==2.26.0
Werkzeug==2.0.1
GitPython==3.1.24
GitPython==3.1.24
watchdog

View File

@@ -2,4 +2,4 @@ from app import create_app
if __name__ == '__main__':
app = create_app()
app.run(debug=True, host='0.0.0.0')
app.run(debug=True, host='0.0.0.0')

49
backend/run_dev.py Normal file
View File

@@ -0,0 +1,49 @@
import sys
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import subprocess
import os
class Reloader(FileSystemEventHandler):
def __init__(self):
self.process = None
self.last_restart = 0
self.start_app()
def on_any_event(self, event):
if event.src_path.endswith(
'.py') and not event.src_path.endswith('run_dev.py'):
current_time = time.time()
if current_time - self.last_restart > 1: # Prevent rapid restarts
print(f"Detected change in {event.src_path}, restarting...")
self.restart_app()
self.last_restart = current_time
def start_app(self):
env = os.environ.copy()
env['FLASK_ENV'] = 'development'
self.process = subprocess.Popen([sys.executable, 'run.py'], env=env)
def restart_app(self):
if self.process:
self.process.terminate()
self.process.wait()
self.start_app()
if __name__ == "__main__":
path = '.'
event_handler = Reloader()
observer = Observer()
observer.schedule(event_handler, path, recursive=True)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()

View File

@@ -1,23 +1,24 @@
version: '3.8'
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
volumes:
- ./frontend:/app
- /app/node_modules
environment:
- VITE_API_URL=http://192.168.1.111:5000 # Replace with your host machine's IP
- CHOKIDAR_USEPOLLING=true
backend:
build: ./backend
ports:
- "5000:5000"
volumes:
- ./backend:/app
- backend_data:/app/data
frontend:
build: ./frontend
ports:
- '3000:3000'
volumes:
- ./frontend:/app
- /app/node_modules
environment:
- VITE_API_URL=http://192.168.1.111:5000 # Replace with your host machine's IP
- CHOKIDAR_USEPOLLING=true
backend:
build: ./backend
ports:
- '5000:5000'
volumes:
- ./backend:/app
- backend_data:/app/data
environment:
- FLASK_ENV=development
volumes:
backend_data:
backend_data:

File diff suppressed because it is too large Load Diff