├── .dockerignore ├── tests ├── __init__.py ├── e2e │ ├── homepage.spec.ts │ ├── README.md │ ├── fixtures │ │ ├── temp-files.fixture.ts │ │ └── create-test-assets.fixture.ts │ ├── create_assets.spec.ts │ └── search_assets.spec.ts └── test_routes.py ├── webapp ├── __init__.py ├── lib │ ├── __init__.py │ ├── http_helpers.py │ ├── python_helpers.py │ ├── url_helpers.py │ ├── file_helpers.py │ └── processors.py ├── dummy-data │ ├── dummy.pdf │ ├── ubuntu.png │ └── ubuntu.svg ├── auth.py ├── database.py ├── utils.py ├── alembic │ ├── script.py.mako │ ├── versions │ │ ├── eb9f8639d610_add_tokens.py │ │ ├── a8efd843e4ad_add_deperecated_option_to_assets.py │ │ ├── 2ab5564cfe99_add_assets.py │ │ ├── a2f0126f69b8_add_redirects.py │ │ ├── 14e1e1dfca79_squash_assets_improvements.py │ │ ├── 6652ef3aa77f_add_tags.py │ │ ├── def1b50e89fa_add_categories_to_assets.py │ │ ├── 7059dcc76605_added_salesforce_campaign_and_asset_.py │ │ └── 62f9c9a26cce_.py │ └── env.py ├── decorators.py ├── param_parser.py ├── dataclass.py ├── integrations │ └── trino_service.py ├── sso.py ├── art │ ├── 404.ascii │ └── chbs.ascii ├── config.py ├── swift.py ├── app.py ├── commands.py └── models.py ├── app.py ├── alembic.ini ├── charm ├── requirements.txt ├── .gitignore ├── src │ └── charm.py ├── pyproject.toml ├── charmcraft.yaml ├── tox.ini └── lib │ └── charms │ └── redis_k8s │ └── v0 │ └── redis.py ├── migrate.sh ├── renovate.json ├── templates ├── _asset-list.html ├── shared │ ├── _asset-author.html │ └── _asset-card-actions.html ├── details.html ├── create-readonly.html ├── index.html ├── error.html ├── created.html ├── _search-form.html ├── _pagination.html ├── _asset-card-image.html └── _layout.html ├── CONTRIBUTING.md ├── .github ├── pull_request_template.md └── workflows │ ├── pr.yaml │ └── deploy.yaml ├── docker-compose.yaml ├── requirements.txt ├── static ├── sass │ ├── icons.scss │ └── main.scss └── js │ └── src │ ├── date-picker.js │ ├── navigation.js │ ├── generic-fields.js │ ├── search-and-filter-overflow.js │ ├── sf_campaign-search.js │ ├── authors-search.js │ └── main.js ├── entrypoint ├── playwright.config.ts ├── .env ├── .gitignore ├── HACKING.md ├── rockcraft.yaml ├── Dockerfile ├── package.json ├── config.yaml └── README.md /.dockerignore: -------------------------------------------------------------------------------- 1 | .venv -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webapp/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webapp/lib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from webapp.app import app # noqa: F401 2 | -------------------------------------------------------------------------------- /alembic.ini: -------------------------------------------------------------------------------- 1 | [alembic] 2 | script_location = webapp/alembic 3 | -------------------------------------------------------------------------------- /charm/requirements.txt: -------------------------------------------------------------------------------- 1 | ops ~= 2.17 2 | paas-charm>=1.0,<2 3 | -------------------------------------------------------------------------------- /migrate.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | alembic upgrade head 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "github>canonical-web-and-design/renovate-websites" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /templates/_asset-list.html: -------------------------------------------------------------------------------- 1 | {% for asset in assets %} 2 | {% include "_asset-card-image.html" %} 3 | {% endfor %} 4 | -------------------------------------------------------------------------------- /webapp/dummy-data/dummy.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canonical/assets.ubuntu.com/main/webapp/dummy-data/dummy.pdf -------------------------------------------------------------------------------- /webapp/dummy-data/ubuntu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canonical/assets.ubuntu.com/main/webapp/dummy-data/ubuntu.png -------------------------------------------------------------------------------- /charm/.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | build/ 3 | *.charm 4 | .tox/ 5 | .coverage 6 | __pycache__/ 7 | *.py[cod] 8 | .idea 9 | .vscode/ 10 | -------------------------------------------------------------------------------- /webapp/auth.py: -------------------------------------------------------------------------------- 1 | from webapp.database import db_session 2 | from webapp.models import Token 3 | 4 | 5 | def authenticate(token): 6 | """Check if this authentication token is valid (i.e. exists)""" 7 | 8 | return bool( 9 | db_session.query(Token).filter(Token.token == token).one_or_none() 10 | ) 11 | -------------------------------------------------------------------------------- /templates/shared/_asset-author.html: -------------------------------------------------------------------------------- 1 |
2 | Author: 3 | {% if asset.author | trim %} 4 | 7 | {% endif %} 8 |
9 | -------------------------------------------------------------------------------- /webapp/database.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine 2 | from sqlalchemy.orm import scoped_session, sessionmaker 3 | 4 | from webapp.config import config 5 | 6 | 7 | db_engine = create_engine(config.database_url.get_secret_value()) 8 | db_session = scoped_session( 9 | sessionmaker(autocommit=False, autoflush=False, bind=db_engine) 10 | ) 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to assets-server 2 | 3 | Awesome you want to contribute to our server! 4 | 5 | ## Bugs and Issues 6 | 7 | *Add something here about bugs and issues* 8 | 9 | ## Pull requests 10 | 11 | Template for PRs is: 12 | 13 | # Details 14 | ## Done 15 | ## QA 16 | 17 | ## Feature requests 18 | 19 | *How do we want the community to ask for new features* 20 | -------------------------------------------------------------------------------- /webapp/lib/http_helpers.py: -------------------------------------------------------------------------------- 1 | def set_headers_for_type(response, content_type=None): 2 | """ 3 | Setup all requires response headers appropriate for this file 4 | """ 5 | 6 | if not content_type: 7 | content_type = response.headers["Content-Type"] 8 | 9 | if "font" in content_type: 10 | response.headers["Access-Control-Allow-Origin"] = "*" 11 | 12 | return response 13 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Done 2 | 3 | [List of work items including drive-bys] 4 | 5 | ## QA 6 | 7 | - Check out this feature branch 8 | - Run the site using the command `dotrun` 9 | - View the site locally in your web browser at: http://0.0.0.0:8017 10 | - [List additional steps to QA the new features or prove the bug has been resolved] 11 | 12 | ## Issue / Card 13 | 14 | Fixes # 15 | 16 | ## Screenshots 17 | 18 | [if relevant, include a screenshot] 19 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | image: postgres:17 4 | environment: 5 | POSTGRES_DB: assets 6 | POSTGRES_USER: assets 7 | POSTGRES_PASSWORD: password 8 | volumes: 9 | - postgres-data:/var/lib/postgresql/data 10 | ports: 11 | - "5432:5432" 12 | swift: 13 | image: openstackswift/saio:latest 14 | ports: 15 | - "8080:8080" 16 | volumes: 17 | - swift-data:/srv 18 | volumes: 19 | postgres-data: 20 | swift-data: 21 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | alembic==1.13.2 2 | canonicalwebteam.flask-base==2.6.0 3 | django-openid-auth==0.17 4 | filetype==1.2.0 5 | Flask-OpenID==1.3.1 6 | Flask-WTF==1.2.1 7 | more-itertools==10.3.0 8 | Pillow==10.4.0 9 | psycopg2-binary==2.9.9 10 | python-keystoneclient==5.4.0 11 | python-swiftclient==4.6.0 12 | requests==2.32.3 13 | scour==0.38.2 14 | sh==2.0.7 15 | sqlalchemy==2.0.31 16 | Wand==0.6.13 17 | pydantic==2.11.7 18 | pydantic-settings==2.10.1 19 | python-slugify==8.0.4 20 | trino==0.335.0 21 | google-auth==2.40.3 22 | cryptography==46.0.1 -------------------------------------------------------------------------------- /webapp/utils.py: -------------------------------------------------------------------------------- 1 | import time 2 | from functools import lru_cache as _lru_cache 3 | 4 | 5 | def lru_cache(*, ttl_seconds, maxsize=128): 6 | def deco(foo): 7 | @_lru_cache(maxsize=maxsize) 8 | def cached_with_ttl(*args, ttl_hash, **kwargs): 9 | return foo(*args, **kwargs) 10 | 11 | def inner(*args, **kwargs): 12 | return cached_with_ttl( 13 | *args, ttl_hash=round(time.time() / ttl_seconds), **kwargs 14 | ) 15 | 16 | return inner 17 | 18 | return deco 19 | -------------------------------------------------------------------------------- /templates/details.html: -------------------------------------------------------------------------------- 1 | {% extends "_layout.html" %} 2 | 3 | {% block title %}Asset details{% endblock %} 4 | 5 | {% block content %} 6 |8 | We are in the process of migrating the asset manager to a new platform. Creating new assets is temporarily disabled. 9 |
10 |
11 | For more information, please reach out to the web team on Mattermost: ~web-design.
12 |
26 | {{ total_assets }} asset{{ "s" if total_assets > 1 }} match{{ "es" if total_assets < 2 }} your search 27 |
28 | 29 |Something's gone wrong.
18 | {% if message %} 19 |
20 | {{ message }}
21 |
22 | {% endif %}
23 | {% if code != 404 %}
24 | 25 | Try reloading the page. 26 | If the error persists, please note that it may be a known issue. 28 | If not, please file a new issue. 30 |
31 | {% endif %} 32 |