diff --git a/libretranslate/app.py b/libretranslate/app.py index af78b88..26dc85b 100644 --- a/libretranslate/app.py +++ b/libretranslate/app.py @@ -8,8 +8,8 @@ from timeit import default_timer import argostranslatefiles from argostranslatefiles import get_supported_formats -from flask import (Flask, abort, jsonify, render_template, request, send_file, - url_for, Response) +from flask import (abort, Blueprint, Flask, jsonify, render_template, request, + Response, send_file, url_for) from flask_swagger import swagger from flask_swagger_ui import get_swaggerui_blueprint from translatehtml import translate_html @@ -106,10 +106,7 @@ def create_app(args): from libretranslate.language import load_languages - app = Flask(__name__) - - if args.debug: - app.config["TEMPLATES_AUTO_RELOAD"] = True + bp = Blueprint('Main app', __name__) if not args.disable_files_translation: remove_translated_files.setup(get_upload_dir()) @@ -161,7 +158,7 @@ def create_app(args): from flask_limiter import Limiter limiter = Limiter( - app, + bp, key_func=get_remote_address, default_limits=get_routes_limits( args.req_limit, args.daily_req_limit, api_keys_db @@ -181,7 +178,7 @@ def create_app(args): if args.metrics: from prometheus_client import CONTENT_TYPE_LATEST, Summary, Gauge, CollectorRegistry, multiprocess, generate_latest - @app.route("/metrics") + @bp.route("/metrics") @limiter.exempt def prometheus_metrics(): if args.metrics_auth_token: @@ -252,24 +249,24 @@ def create_app(args): else: return func - @app.errorhandler(400) + @bp.errorhandler(400) def invalid_api(e): return jsonify({"error": str(e.description)}), 400 - @app.errorhandler(500) + @bp.errorhandler(500) def server_error(e): return jsonify({"error": str(e.description)}), 500 - @app.errorhandler(429) + @bp.errorhandler(429) def slow_down_error(e): flood.report(get_remote_address()) return jsonify({"error": "Slowdown: " + str(e.description)}), 429 - @app.errorhandler(403) + @bp.errorhandler(403) def denied(e): return jsonify({"error": str(e.description)}), 403 - @app.route("/") + @bp.route("/") @limiter.exempt def index(): if args.disable_web_ui: @@ -285,7 +282,7 @@ def create_app(args): version=get_version() ) - @app.get("/javascript-licenses") + @bp.get("/javascript-licenses") @limiter.exempt def javascript_licenses(): if args.disable_web_ui: @@ -293,7 +290,7 @@ def create_app(args): return render_template("javascript-licenses.html") - @app.get("/languages") + @bp.get("/languages") @limiter.exempt def langs(): """ @@ -325,7 +322,7 @@ def create_app(args): return jsonify([{"code": l.code, "name": l.name, "targets": language_pairs.get(l.code, [])} for l in languages]) # Add cors - @app.after_request + @bp.after_request def after_request(response): response.headers.add("Access-Control-Allow-Origin", "*") response.headers.add( @@ -337,7 +334,7 @@ def create_app(args): response.headers.add("Access-Control-Max-Age", 60 * 60 * 24 * 20) return response - @app.post("/translate") + @bp.post("/translate") @access_check def translate(): """ @@ -577,7 +574,7 @@ def create_app(args): except Exception as e: abort(500, description="Cannot translate text: %s" % str(e)) - @app.post("/translate_file") + @bp.post("/translate_file") @access_check def translate_file(): """ @@ -710,7 +707,7 @@ def create_app(args): except Exception as e: abort(500, description=e) - @app.get("/download_file/") + @bp.get("/download_file/") def download_file(filename: str): """ Download a translated file @@ -737,7 +734,7 @@ def create_app(args): return send_file(return_data, as_attachment=True, download_name=download_filename) - @app.post("/detect") + @bp.post("/detect") @access_check def detect(): """ @@ -831,7 +828,7 @@ def create_app(args): return jsonify(detect_languages(q)) - @app.route("/frontend/settings") + @bp.route("/frontend/settings") @limiter.exempt def frontend_settings(): """ @@ -910,7 +907,7 @@ def create_app(args): } ) - @app.post("/suggest") + @bp.post("/suggest") @access_check def suggest(): """ @@ -987,6 +984,14 @@ def create_app(args): SuggestionsDatabase().add(q, s, source_lang, target_lang) return jsonify({"success": True}) + app = Flask(__name__) + if args.debug: + app.config["TEMPLATES_AUTO_RELOAD"] = True + if args.url_prefix: + app.register_blueprint(bp, url_prefix=args.url_prefix) + else: + app.register_blueprint(bp) + swag = swagger(app) swag["info"]["version"] = "1.3.1" swag["info"]["title"] = "LibreTranslate" @@ -1001,7 +1006,9 @@ def create_app(args): # Call factory function to create our blueprint swaggerui_blueprint = get_swaggerui_blueprint(SWAGGER_URL, API_URL) - - app.register_blueprint(swaggerui_blueprint) + if args.url_prefix: + app.register_blueprint(swaggerui_blueprint, url_prefix=args.url_prefix) + else: + app.register_blueprint(swaggerui_blueprint) return app diff --git a/libretranslate/default_values.py b/libretranslate/default_values.py index a36f966..c066cbb 100644 --- a/libretranslate/default_values.py +++ b/libretranslate/default_values.py @@ -171,6 +171,11 @@ _default_options_objects = [ 'default_value': '', 'value_type': 'str' }, + { + 'name': 'URL_PREFIX', + 'default_value': '', + 'value_type': 'str' + }, ] diff --git a/libretranslate/main.py b/libretranslate/main.py index 27827ae..aa6aabf 100644 --- a/libretranslate/main.py +++ b/libretranslate/main.py @@ -159,14 +159,23 @@ def get_args(): type=str, help="Protect the /metrics endpoint by allowing only clients that have a valid Authorization Bearer token (%(default)s)", ) - return parser.parse_args() + parser.add_argument( + "--url-prefix", + default=DEFARGS['URL_PREFIX'], + type=str, + help="Add prefix to URL: example.com:5000/url-prefix/", + ) + args = parser.parse_args() + if args.url_prefix and not args.url_prefix.startswith('/'): + args.url_prefix = '/' + args.url_prefix + return args def main(): args = get_args() app = create_app(args) - if sys.argv[0] == '--wsgi': + if '--wsgi' in sys.argv: return app else: if args.debug: @@ -175,7 +184,7 @@ def main(): from waitress import serve url_scheme = "https" if args.ssl else "http" - print("Running on %s://%s:%s" % (url_scheme, args.host, args.port)) + print("Running on %s://%s:%s%s" % (url_scheme, args.host, args.port, args.url_prefix)) serve( app, diff --git a/libretranslate/static/js/app.js b/libretranslate/static/js/app.js index 34740e5..3e9129d 100644 --- a/libretranslate/static/js/app.js +++ b/libretranslate/static/js/app.js @@ -1,6 +1,7 @@ // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0 // API host/endpoint -var BaseUrl = window.location.protocol + "//" + window.location.host; +//var BaseUrl = window.location.protocol + "//" + window.location.host + window.location.pathname.length > 1 ? window.location.pathname : "" ; +var BaseUrl = window.location.href.slice(0, -1) var htmlRegex = /<(.*)>.*?|<(.*)\/>/; document.addEventListener('DOMContentLoaded', function(){ var sidenavElems = document.querySelectorAll('.sidenav');