Skip to content

Advanced Topics

This page covers features that go beyond the basics: cloning an entire site, hosting a custom Docker application, enabling CORS and WebDAV, controlling search-engine indexing, and triggering the publish workflow.


Cloning a site

Cloning copies every file from one site to another. Internally the library downloads all content to a local temporary folder and then uploads it to the destination site.

from sites.sdk.sites import Site, Authenticator

# Source site and its authenticator
source_site = Site.from_space_and_name(space='production', name='docs')
source_auth = Authenticator.from_token(token='<source_site_bearer_token>')

# Destination site and its authenticator
dest_site = Site.from_space_and_name(space='staging', name='docs-copy')
dest_auth = Authenticator.from_token(token='<dest_site_bearer_token>')

# Get the source content manager and clone
source_mgr = source_site.get_content_manager(authenticator=source_auth)
source_mgr.clone(
    to_site=dest_site,
    authenticator=dest_auth,
    cleanup_after=True      # remove the local copy once the upload is done
)

Parameters

Parameter Type Default Description
to_site Site None Destination site. If not provided, content is only downloaded locally.
authenticator Authenticator current auth Authenticator for the destination site
cleanup_after bool False Delete the local copy after uploading
ignore_errors bool False Continue if individual file uploads fail
local_path str '/tmp/sites/' Directory to use for the intermediate local copy

Download locally without uploading:

# Useful for creating an offline backup
source_mgr.clone(local_path='/backup/my-site/', cleanup_after=False)

Custom application hosting

You can serve a custom Docker container behind your site. The container runs alongside the site and the platform proxies requests to it.

Setting up a custom application

from sites.sdk import SitesClient
from sites.sdk.sites import Site, Authenticator
from sites.sdk.sites.utils import ProxyType, ContextRoot

authenticator = Authenticator.from_credentials(username='myuser', password='mypassword')
client = SitesClient(authenticator=authenticator)

site = Site.from_space_and_name(space='apps', name='my-app')
site.load_from_server()

# Docker image to run
site.custom_application_image = 'registry.example.com/my-docker-image:1.2.3'

# How requests are proxied to the container
site.custom_application_proxy = ProxyType.ProxyPass.value  # 'proxy'
# or:
# site.custom_application_proxy = ProxyType.FastCGI.value  # 'fastcgi'

# Where the app is rooted
site.custom_application_context_root = ContextRoot.SiteContext.value  # '/apps/my-app/'
# or:
# site.custom_application_context_root = ContextRoot.RootContext.value  # '/'

# Extra environment variables passed to the container at runtime
site.custom_application_extra_env = [
    'DATABASE_URL=postgres://...',
    'LOG_LEVEL=info'
]

client.update_site(space='apps', name='my-app', site=site)

Proxy types

Value Enum When to use
'proxy' ProxyType.ProxyPass Standard HTTP applications (Flask, FastAPI, Node.js, etc.)
'fastcgi' ProxyType.FastCGI PHP or other FastCGI-based applications

Context root

Value Enum URL where the app is served
'root_context' ContextRoot.RootContext https://sites.ecmwf.int/
'site_context' ContextRoot.SiteContext https://sites.ecmwf.int/<space>/<name>/

CORS (Cross-Origin Resource Sharing)

Enable CORS if your site serves resources (e.g., JSON data or fonts) consumed by scripts running on a different domain.

site.cors_enabled = True

# Optionally allow additional domains beyond the platform default
site.cors_extra_domains = ['https://my-dashboard.example.com', 'https://partner-app.org']

client.update_site(space='data', name='my-api', site=site)

When cors_enabled is True the platform adds the appropriate Access-Control-Allow-Origin headers to responses.


WebDAV access

WebDAV lets users mount your site as a network drive in their operating system's file manager. Enable it with:

site.webdav_enabled = True
client.update_site(space='docs', name='my-project', site=site)

Once enabled, users can connect to https://sites.ecmwf.int/docs/my-project/ using any WebDAV client (macOS Finder, Windows Explorer mapped drive, Cyberduck, etc.).


Search engine indexing (robots.txt)

By default, search engines can index your site. To prevent indexing — for example on staging or internal sites — enable the no_robots_enabled flag:

site.no_robots_enabled = True
client.update_site(space='staging', name='internal-docs', site=site)

This causes the platform to serve a robots.txt file with Disallow: / (noindex), signalling to compliant search engine crawlers that the site should not be indexed.


Site monitoring

Request that the platform monitors your site for uptime and health:

site.site_monitored = True
client.update_site(space='production', name='my-api', site=site)

When monitoring is enabled, platform alerts are generated if the site becomes unhealthy.


Site visibility: Public vs Private

Control who can access your site without authentication:

from sites.sdk.sites.utils import SiteType

# Public: accessible by anyone without an account
site.site_type = SiteType.Public.value    # 'public'

# Private: only listed users/groups can access it (default)
site.site_type = SiteType.Private.value  # 'private'

For private sites, use access_users and access_groups to define who is allowed:

site.site_type = 'private'
site.access_users = ['alice', 'bob']
site.access_groups = ['ecmwf_staff', 'external_partners']

Site templates

Templates pre-configure a site with a specific layout, application, or directory structure. Use them when you want to start from a known baseline:

from sites.sdk.sites import Site

site = Site.from_template(template='ecbox')
# or: 'iver', 'repository', or any other template supported by the platform

Publishing workflow

If you want to make your site public, you can request publishing with:

content_manager = site.get_content_manager(
    authenticator=Authenticator.from_token(token='<admin_site_bearer_token>')
)
result = content_manager.publish()
print(result)

Complete example: deploy a custom application

from sites.sdk import SitesClient
from sites.sdk.sites import Site, Authenticator
from sites.sdk.sites.utils import ProxyType, ContextRoot

auth = Authenticator.from_credentials(username='myuser', password='mypassword')
client = SitesClient(authenticator=auth)

# Create the site
site = Site.from_space_and_name(space='apps', name='weather-dashboard')
site.description = 'Weather data dashboard'
site.quota = 5
site.site_type = 'private'
site.access_groups = ['ecmwf_staff']
site.cors_enabled = True
site.site_monitored = True
site.no_robots_enabled = True     # staging — do not index

# Custom application
site.custom_application_image = 'registry.ecmwf.int/weather-dashboard:3.1'
site.custom_application_proxy = ProxyType.ProxyPass.value
site.custom_application_context_root = ContextRoot.SiteContext.value
site.custom_application_extra_env = ['API_KEY=secret123', 'ENV=production']

client.create_site(site=site)
print(f'Site created at https://sites.ecmwf.int/apps/weather-dashboard/')

What's next?