Coverage for webapp/app.py: 99%
70 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-20 22:09 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-20 22:09 +0000
1"""
2A Flask application for snapcraft.io.
4The web frontend for the snap store.
5"""
7# We import the config module before anything else to make sure env vars are
8# loaded properly and the FLASK_* prefix is stripped before they are parsed
9import webapp.config # noqa: F401
11import sentry_sdk
12from flask import send_from_directory
14from canonicalwebteam.flask_base.app import FlaskBase
15from webapp.blog.views import init_blog
16from webapp.docs.views import init_docs
17from webapp.extensions import csrf, vite
18from webapp.handlers import set_handlers
19from webapp.login.views import login
20from webapp.login.oauth_views import oauth
21from webapp.publisher.snaps.views import publisher_snaps
22from webapp.publisher.github.views import publisher_github
23from webapp.admin.views import admin
24from webapp.publisher.views import account
25from webapp.snapcraft.views import snapcraft_blueprint
26from webapp.store.views import store_blueprint
27from webapp.tutorials.views import init_tutorials
28from webapp.packages.store_packages import store_packages
29from webapp.endpoints.views import endpoints
30from webapp.endpoints.signing_keys import signing_keys
31from webapp.endpoints.models import models
32from webapp.endpoints.members import members
33from webapp.endpoints.snaps import snaps
34from webapp.endpoints.snap_search import snap_search
35from webapp.endpoints.validation_sets import validation_sets
36from webapp.endpoints.invites import invites
37from webapp.endpoints.settings import settings
38from webapp.feeds.feeds import feeds
39from webapp.config import SENTRY_DSN
42def create_app(testing=False):
43 sentry_sdk.init(dsn=SENTRY_DSN)
45 app = FlaskBase(
46 __name__,
47 "snapcraft.io",
48 favicon_url="https://assets.ubuntu.com/v1/d4ca039f-favicon_16px.png",
49 template_404="404.html",
50 template_folder="../templates",
51 static_folder="../static",
52 )
53 app.config.from_object("webapp.config")
54 app.name = "snapcraft"
55 app.testing = testing
57 # This is required because Pragma icons are served from `/icons` at the
58 # root of the project, which is a problem for snapcraft.io as that is
59 # the URL pattern we use for snap names. To resolve this, we are
60 # serving any requests to `/icons/<ICON_NAME>.svg` from static icons,
61 # which leaves the possibility for a snap called "icons" and its
62 # subsequent publisher pages.
63 #
64 # DS icons are exposed through the tracked `static/icons` symlink.
65 def serve_ds_icon(icon_name):
66 return send_from_directory(
67 f"{app.static_folder}/icons",
68 f"{icon_name}.svg",
69 )
71 app.add_url_rule(
72 "/icons/<icon_name>.svg",
73 "ds-icons",
74 serve_ds_icon,
75 )
77 init_extensions(app)
78 set_handlers(app)
80 app.register_blueprint(snapcraft_blueprint())
81 app.register_blueprint(store_packages)
82 app.register_blueprint(login)
83 app.register_blueprint(oauth)
84 app.register_blueprint(store_blueprint())
85 app.register_blueprint(account, url_prefix="/account")
86 app.register_blueprint(publisher_snaps)
87 app.register_blueprint(publisher_github)
88 app.register_blueprint(admin)
89 app.register_blueprint(endpoints)
90 app.register_blueprint(signing_keys)
91 app.register_blueprint(models)
92 app.register_blueprint(members)
93 app.register_blueprint(snaps)
94 app.register_blueprint(snap_search)
95 app.register_blueprint(validation_sets)
96 app.register_blueprint(invites)
97 app.register_blueprint(settings)
98 app.register_blueprint(feeds)
99 init_docs(app, "/docs")
100 init_blog(app, "/blog")
101 init_tutorials(app, "/tutorials")
103 return app
106def init_extensions(app: FlaskBase):
107 vite.init_app(app)
109 if not app.testing:
110 csrf.init_app(app)
111 else:
112 # add a helper for injecting a mock CSRF token
113 @app.context_processor
114 def inject_csrf_token():
115 return dict(csrf_token=lambda: "mocked_csrf_token")