05/22 Implement license version

This commit is contained in:
2026-05-22 16:31:19 -04:00
parent e6657e60cf
commit 687216e332
16 changed files with 1373 additions and 37 deletions
+17
View File
@@ -1572,3 +1572,20 @@ def email_ingestion_run_now():
except Exception as exc:
logger.error(f'[ADMIN EMAIL INGEST] Manual run error: {exc}', exc_info=True)
return jsonify(ok=False, message=f'Run failed: {exc}')
@admin_bp.route('/license')
@login_required
@admin_required
def license_page():
from app.services.license_service import get_status, days_until_expiry, TIER_FEATURES
status = get_status()
days_left = days_until_expiry()
tier = status.get('tier', 'community')
features = TIER_FEATURES.get(tier, set())
return render_template(
'admin/license.html',
status = status,
days_left = days_left,
features = features,
)
+8
View File
@@ -130,6 +130,14 @@ def _call_groq(api_key, history, user_msg, kb_context=''):
@login_required
@limiter.limit('20 per minute; 100 per hour')
def chat():
from app.services.license_service import feature_enabled
if not feature_enabled('chatbot'):
return jsonify({
'reply' : 'The AI chatbot is not available on the Community plan. '
'Upgrade to a Business or Enterprise license to enable it.',
'ticket': None,
}), 403
data = request.get_json(force=True)
history = data.get('history', []) # [{role, content}, ...]
user_msg = data.get('message', '').strip()
+15 -2
View File
@@ -999,6 +999,10 @@ def ticket_survey(token):
@tickets_bp.route('/tickets/<int:ticket_id>/watch', methods=['POST'])
@login_required
def watch_ticket(ticket_id):
from app.services.license_service import feature_enabled
from flask import jsonify
if not feature_enabled('watchers'):
return jsonify({'error': 'Ticket watchers require a Business or Enterprise license.'}), 403
ticket = db.session.get(Ticket, ticket_id) or abort(404)
if not current_user.is_it_staff and ticket.created_by_id != current_user.id:
abort(403)
@@ -1006,19 +1010,21 @@ def watch_ticket(ticket_id):
if not existing:
db.session.add(TicketWatcher(ticket_id=ticket_id, user_id=current_user.id))
db.session.commit()
from flask import jsonify
return jsonify({'watching': True})
@tickets_bp.route('/tickets/<int:ticket_id>/unwatch', methods=['POST'])
@login_required
def unwatch_ticket(ticket_id):
from app.services.license_service import feature_enabled
from flask import jsonify
if not feature_enabled('watchers'):
return jsonify({'error': 'Ticket watchers require a Business or Enterprise license.'}), 403
ticket = db.session.get(Ticket, ticket_id) or abort(404)
if not current_user.is_it_staff and ticket.created_by_id != current_user.id:
abort(403)
TicketWatcher.query.filter_by(ticket_id=ticket_id, user_id=current_user.id).delete()
db.session.commit()
from flask import jsonify
return jsonify({'watching': False})
@@ -1027,6 +1033,13 @@ def unwatch_ticket(ticket_id):
@tickets_bp.route('/tickets/<int:ticket_id>/log-time', methods=['POST'])
@login_required
def log_time(ticket_id):
from app.services.license_service import feature_enabled
if not feature_enabled('time_tracking'):
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
from flask import jsonify
return jsonify({'error': 'Time tracking requires a Business or Enterprise license.'}), 403
flash('Time tracking requires a Business or Enterprise license.', 'warning')
return redirect(url_for('tickets.ticket_detail', ticket_id=ticket_id))
if not current_user.is_it_staff:
abort(403)
ticket = db.session.get(Ticket, ticket_id) or abort(404)