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:
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:
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:
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?¶
- Site Management — create, update, and delete sites
- Content Management — upload and download files
- API Reference — complete method quick-reference