|
- #! /usr/bin/env python
-
- import functools
- import hashlib
- import json
- import os
- import time
- import traceback
- from typing import Any
-
- from earwigbot.wiki.copyvios import globalize
- from flask import Response, make_response, render_template, request
-
- from copyvios import app
- from copyvios.api import format_api_error, handle_api_request
- from copyvios.attribution import get_attribution_info
- from copyvios.background import get_background
- from copyvios.cache import cache
- from copyvios.checker import (
- T_POSSIBLE,
- T_SUSPECT,
- CopyvioCheckError,
- ErrorCode,
- do_check,
- )
- from copyvios.cookies import get_cookies, get_new_cookies
- from copyvios.highlighter import highlight_delta
- from copyvios.misc import get_notice, get_permalink
- from copyvios.query import CheckQuery
- from copyvios.settings import process_settings
- from copyvios.sites import update_sites
-
- AnyResponse = Response | str | bytes
-
- app.logger.info(f"Flask server started {time.asctime()}")
-
- globalize(num_workers=8)
-
-
- @app.errorhandler(Exception)
- def handle_errors(exc: Exception) -> AnyResponse:
- if app.debug:
- raise # Use built-in debugger
- app.logger.exception("Caught exception:")
- return render_template("error.html.jinja", traceback=traceback.format_exc())
-
-
- @app.context_processor
- def setup_context() -> dict[str, Any]:
- return {
- "T_POSSIBLE": T_POSSIBLE,
- "T_SUSPECT": T_SUSPECT,
- "ErrorCode": ErrorCode,
- "cache": cache,
- "dump_json": json.dumps,
- "get_attribution_info": get_attribution_info,
- "get_background": get_background,
- "get_cookies": get_cookies,
- "get_notice": get_notice,
- "get_permalink": get_permalink,
- "highlight_delta": highlight_delta,
- }
-
-
- @app.after_request
- def add_new_cookies(response: Response) -> Response:
- for cookie in get_new_cookies():
- response.headers.add("Set-Cookie", cookie)
- return response
-
-
- @app.after_request
- def write_access_log(response: Response) -> Response:
- app.logger.debug(
- f"{time.asctime()} {request.method} {request.path} "
- f"{request.values.to_dict()} -> {response.status_code}"
- )
- return response
-
-
- @functools.lru_cache
- def _get_hash(path: str, mtime: float) -> str:
- # mtime is used as part of the cache key
- with open(path, "rb") as fp:
- return hashlib.sha1(fp.read()).hexdigest()
-
-
- def external_url_handler(
- error: Exception, endpoint: str, values: dict[str, Any]
- ) -> str:
- if endpoint == "static" and "file" in values:
- assert app.static_folder is not None
- path = os.path.join(app.static_folder, values["file"])
- mtime = os.path.getmtime(path)
- hashstr = _get_hash(path, mtime)
- return f"/static/{values['file']}?v={hashstr}"
- raise error
-
-
- app.url_build_error_handlers.append(external_url_handler)
-
-
- @app.route("/")
- def index() -> AnyResponse:
- update_sites()
- query = CheckQuery.from_get_args()
- try:
- result = do_check(query)
- error = None
- except CopyvioCheckError as exc:
- app.logger.exception(f"Copyvio check failed on {query}")
- result = None
- error = exc
-
- return render_template(
- "index.html.jinja",
- query=query,
- result=result,
- error=error,
- splash=not result,
- )
-
-
- @app.route("/settings", methods=["GET", "POST"])
- def settings() -> AnyResponse:
- status = process_settings() if request.method == "POST" else None
- update_sites()
- return render_template(
- "settings.html.jinja",
- status=status,
- default_site=cache.bot.wiki.get_site(),
- splash=True,
- )
-
-
- @app.route("/api")
- def api() -> AnyResponse:
- return render_template("api_help.html.jinja")
-
-
- @app.route("/api.json")
- def api_json() -> AnyResponse:
- if not request.args:
- return render_template("api_help.html.jinja")
-
- format = request.args.get("format", "json")
- if format in ["json", "jsonfm"]:
- update_sites()
- try:
- result = handle_api_request()
- except Exception as exc:
- app.logger.exception("API request failed")
- result = format_api_error("unhandled_exception", exc)
- else:
- errmsg = f"Unknown format: {format!r}"
- result = format_api_error("unknown_format", errmsg)
-
- if format == "jsonfm":
- return render_template("api_result.html.jinja", result=result)
- resp = make_response(json.dumps(result))
- resp.mimetype = "application/json"
- resp.headers["Access-Control-Allow-Origin"] = "*"
- return resp
-
-
- if app.debug:
- # Silence browser 404s when testing
- @app.route("/favicon.ico")
- def favicon() -> AnyResponse:
- return app.send_static_file("favicon.ico")
-
-
- if __name__ == "__main__":
- app.run()
|