Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/enforce-branch-flow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Enforce Branch Protection Flow (Development → Staging → Main)

on:
pull_request:
types:
- opened
- reopened
- synchronize

jobs:
enforce-branch-flow:
runs-on: ubuntu-latest
steps:
- name: Fail if PR→staging doesn't come from development
if: >
github.event.pull_request.base.ref == 'staging' &&
github.event.pull_request.head.ref != 'development'
run: |
echo "::error ::Pull requests into 'staging' must originate from branch 'development'."
exit 1

- name: Fail if PR→main doesn't come from staging
if: >
github.event.pull_request.base.ref == 'main' &&
github.event.pull_request.head.ref != 'staging'
run: |
echo "::error ::Pull requests into 'main' must originate from branch 'staging'."
exit 1

- name: Branch flow validated
run: echo "✅ Branch flow validation passed."
20 changes: 0 additions & 20 deletions .github/workflows/enforce-dev-to-main.yml

This file was deleted.

74 changes: 51 additions & 23 deletions application/single_app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
os.makedirs(app.config['SESSION_FILE_DIR'], exist_ok=True)
except Exception as e:
print(f"WARNING: Unable to create session directory {app.config.get('SESSION_FILE_DIR')}: {e}")
log_event(f"Unable to create session directory {app.config.get('SESSION_FILE_DIR')}: {e}", level=logging.ERROR)

Session(app)

Expand Down Expand Up @@ -186,6 +187,7 @@ def configure_sessions(settings):
app.config['SESSION_TYPE'] = 'filesystem'
except Exception as e:
print(f"⚠️ WARNING: Session configuration error; falling back to filesystem: {e}")
log_event(f"Session configuration error; falling back to filesystem: {e}", level=logging.ERROR)
app.config['SESSION_TYPE'] = 'filesystem'

# Initialize session interface
Expand Down Expand Up @@ -266,6 +268,7 @@ def check_logging_timers():

except Exception as e:
print(f"Error in logging timer check: {e}")
log_event(f"Error in logging timer check: {e}", level=logging.ERROR)

# Check every 60 seconds
time.sleep(60)
Expand All @@ -286,6 +289,7 @@ def check_expired_approvals():
print(f"Auto-denied {denied_count} expired approval request(s).")
except Exception as e:
print(f"Error in approval expiration check: {e}")
log_event(f"Error in approval expiration check: {e}", level=logging.ERROR)

# Check every 6 hours (21600 seconds)
time.sleep(21600)
Expand All @@ -309,14 +313,34 @@ def check_retention_policy():

if personal_enabled or group_enabled or public_enabled:
current_time = datetime.now(timezone.utc)
execution_hour = settings.get('retention_policy_execution_hour', 2)

# Check if we're in the execution hour
if current_time.hour == execution_hour:
# Check if we haven't run today yet
# Check if next scheduled run time has passed
next_run = settings.get('retention_policy_next_run')
should_run = False

if next_run:
try:
next_run_dt = datetime.fromisoformat(next_run)
# Run if we've passed the scheduled time
if current_time >= next_run_dt:
should_run = True
except Exception as parse_error:
print(f"Error parsing next_run timestamp: {parse_error}")
# If we can't parse, fall back to checking last_run
last_run = settings.get('retention_policy_last_run')
if last_run:
try:
last_run_dt = datetime.fromisoformat(last_run)
# Run if last run was more than 23 hours ago
if (current_time - last_run_dt).total_seconds() > (23 * 3600):
should_run = True
except:
should_run = True
else:
should_run = True
else:
# No next_run set, check last_run instead
last_run = settings.get('retention_policy_last_run')
should_run = False

if last_run:
try:
last_run_dt = datetime.fromisoformat(last_run)
Expand All @@ -326,29 +350,31 @@ def check_retention_policy():
except:
should_run = True
else:
# Never run before, execute now
should_run = True

if should_run:
print(f"Executing scheduled retention policy at {current_time.isoformat()}")
from functions_retention_policy import execute_retention_policy
results = execute_retention_policy(manual_execution=False)

if should_run:
print(f"Executing scheduled retention policy at {current_time.isoformat()}")
from functions_retention_policy import execute_retention_policy
results = execute_retention_policy(manual_execution=False)

if results.get('success'):
print(f"Retention policy execution completed: "
f"{results['personal']['conversations']} personal conversations, "
f"{results['personal']['documents']} personal documents, "
f"{results['group']['conversations']} group conversations, "
f"{results['group']['documents']} group documents, "
f"{results['public']['conversations']} public conversations, "
f"{results['public']['documents']} public documents deleted.")
else:
print(f"Retention policy execution failed: {results.get('errors')}")
if results.get('success'):
print(f"Retention policy execution completed: "
f"{results['personal']['conversations']} personal conversations, "
f"{results['personal']['documents']} personal documents, "
f"{results['group']['conversations']} group conversations, "
f"{results['group']['documents']} group documents, "
f"{results['public']['conversations']} public conversations, "
f"{results['public']['documents']} public documents deleted.")
else:
print(f"Retention policy execution failed: {results.get('errors')}")

except Exception as e:
print(f"Error in retention policy check: {e}")
log_event(f"Error in retention policy check: {e}", level=logging.ERROR)

# Check every hour
time.sleep(3600)
# Check every 5 minutes for more responsive scheduling
time.sleep(300)

# Start the retention policy check thread
retention_thread = threading.Thread(target=check_retention_policy, daemon=True)
Expand Down Expand Up @@ -377,6 +403,8 @@ def inject_settings():
from functions_settings import get_user_settings
user_settings = get_user_settings(user_id) or {}
except Exception as e:
print(f"Error injecting user settings: {e}")
log_event(f"Error injecting user settings: {e}", level=logging.ERROR)
user_settings = {}
return dict(app_settings=public_settings, user_settings=user_settings)

Expand Down
2 changes: 1 addition & 1 deletion application/single_app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
EXECUTOR_TYPE = 'thread'
EXECUTOR_MAX_WORKERS = 30
SESSION_TYPE = 'filesystem'
VERSION = "0.235.012"
VERSION = "0.235.025"


SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production')
Expand Down
Loading