py-docker-admin 0.6.0__tar.gz → 0.6.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/PKG-INFO +76 -2
  2. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/README.md +75 -1
  3. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/py_docker_admin/__init__.py +1 -1
  4. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/py_docker_admin/models.py +41 -0
  5. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/py_docker_admin/stack.py +23 -0
  6. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/py_docker_admin/templates/nginx_config.j2 +14 -0
  7. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/py_docker_admin/utils.py +233 -0
  8. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/pyproject.toml +1 -1
  9. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/templates/nginx_config.j2 +14 -0
  10. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/.gitignore +0 -0
  11. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/AUTHORS +0 -0
  12. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/LICENSE +0 -0
  13. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/py_docker_admin/__main__.py +0 -0
  14. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/py_docker_admin/cli.py +0 -0
  15. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/py_docker_admin/docker.py +0 -0
  16. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/py_docker_admin/exceptions.py +0 -0
  17. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/py_docker_admin/librechat.py +0 -0
  18. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/py_docker_admin/nginx.py +0 -0
  19. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/py_docker_admin/portainer.py +0 -0
  20. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/py_docker_admin/templates/landing_page.html.j2 +0 -0
  21. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/py_docker_admin/templates/site_stylesheet.css +0 -0
  22. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/templates/landing_page.html.j2 +0 -0
  23. {py_docker_admin-0.6.0 → py_docker_admin-0.6.2}/templates/site_stylesheet.css +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: py-docker-admin
3
- Version: 0.6.0
3
+ Version: 0.6.2
4
4
  Summary: A Python package for automating Docker and Portainer installation
5
5
  Project-URL: Repository, https://gitlab.com/grenzfall/py-docker-admin
6
6
  Project-URL: Issues, https://gitlab.com/grenzfall/py-docker-admin/-/issues
@@ -134,6 +134,80 @@ portainer:
134
134
 
135
135
  The `base_url` must start with a forward slash (e.g., `/portainer`, `/docker-admin`). When configured, Portainer container will be started with the `--base-url` parameter, ensuring proper path handling behind reverse proxies.
136
136
 
137
+ ## Persistent Folder Configuration
138
+
139
+ The `persistent_folder` feature allows you to automatically set up and manage persistent storage for Docker containers. This is particularly useful for databases, application data, or any service that requires data persistence across container restarts and updates.
140
+
141
+ ### How It Works
142
+
143
+ 1. **Creates host directory** - Ensures the specified host directory exists (creates it if it doesn't)
144
+ 2. **Copies initial content** - Optionally copies files or directories to the host directory before deployment
145
+ 3. **Mounts to container** - The directory is mounted as a volume in the Docker container
146
+
147
+ ### Configuration
148
+
149
+ Add `persistent_folder` to your stack configuration in `config.yaml`:
150
+
151
+ ```yaml
152
+ stacks:
153
+ - name: myapp
154
+ compose_file: ./docker-compose.yml
155
+ env_file: ./docker-compose.env
156
+ persistent_folder:
157
+ mount_name: app_data # Must match volume name in docker-compose.yml
158
+ host_directory: /var/app_data # Absolute path on host system
159
+ contents: ./initial_data # Optional: file/dir to copy to host_directory
160
+ ```
161
+
162
+ ### Docker Compose Integration
163
+
164
+ The `mount_name` must match a volume name defined in your `docker-compose.yml`:
165
+
166
+ ```yaml
167
+ version: '3.8'
168
+
169
+ services:
170
+ myapp:
171
+ image: myapp:latest
172
+ volumes:
173
+ - app_data:/app/data # This matches the mount_name in config
174
+
175
+ volumes:
176
+ app_data: # Volume name that matches mount_name
177
+ driver: local
178
+ ```
179
+
180
+ ### Configuration Options
181
+
182
+ - **`mount_name`** (required): Name of the volume in your docker-compose.yml file
183
+ - **`host_directory`** (required): Absolute path on the host system where data will be stored
184
+ - **`contents`** (optional): Path to files or directories to copy to the host_directory before deployment
185
+
186
+ ### Example Use Cases
187
+
188
+ **Database persistence:**
189
+ ```yaml
190
+ stacks:
191
+ - name: postgres-db
192
+ compose_file: ./postgres-compose.yml
193
+ persistent_folder:
194
+ mount_name: pg_data
195
+ host_directory: /var/persistent/postgres
196
+ contents: ./initial_db_setup
197
+ ```
198
+
199
+ **Application data:**
200
+ ```yaml
201
+ stacks:
202
+ - name: web-app
203
+ compose_file: ./webapp-compose.yml
204
+ persistent_folder:
205
+ mount_name: app_storage
206
+ host_directory: /var/www/app_data
207
+ ```
208
+
209
+ This ensures your container data persists even when containers are recreated or updated, providing reliable data storage across deployments.
210
+
137
211
  ## Requirements
138
212
 
139
213
  - Python 3.12+
@@ -199,4 +273,4 @@ Contributions are welcome! Please open an issue or submit a merge request on our
199
273
 
200
274
  ## Support
201
275
 
202
- For issues and questions, please use the [GitLab issue tracker](https://gitlab.com/grenzfall/py-docker-admin/-/issues).
276
+ For issues and questions, please use the [GitLab issue tracker](https://gitlab.com/grenzfall/py-docker-admin/-/issues).
@@ -101,6 +101,80 @@ portainer:
101
101
 
102
102
  The `base_url` must start with a forward slash (e.g., `/portainer`, `/docker-admin`). When configured, Portainer container will be started with the `--base-url` parameter, ensuring proper path handling behind reverse proxies.
103
103
 
104
+ ## Persistent Folder Configuration
105
+
106
+ The `persistent_folder` feature allows you to automatically set up and manage persistent storage for Docker containers. This is particularly useful for databases, application data, or any service that requires data persistence across container restarts and updates.
107
+
108
+ ### How It Works
109
+
110
+ 1. **Creates host directory** - Ensures the specified host directory exists (creates it if it doesn't)
111
+ 2. **Copies initial content** - Optionally copies files or directories to the host directory before deployment
112
+ 3. **Mounts to container** - The directory is mounted as a volume in the Docker container
113
+
114
+ ### Configuration
115
+
116
+ Add `persistent_folder` to your stack configuration in `config.yaml`:
117
+
118
+ ```yaml
119
+ stacks:
120
+ - name: myapp
121
+ compose_file: ./docker-compose.yml
122
+ env_file: ./docker-compose.env
123
+ persistent_folder:
124
+ mount_name: app_data # Must match volume name in docker-compose.yml
125
+ host_directory: /var/app_data # Absolute path on host system
126
+ contents: ./initial_data # Optional: file/dir to copy to host_directory
127
+ ```
128
+
129
+ ### Docker Compose Integration
130
+
131
+ The `mount_name` must match a volume name defined in your `docker-compose.yml`:
132
+
133
+ ```yaml
134
+ version: '3.8'
135
+
136
+ services:
137
+ myapp:
138
+ image: myapp:latest
139
+ volumes:
140
+ - app_data:/app/data # This matches the mount_name in config
141
+
142
+ volumes:
143
+ app_data: # Volume name that matches mount_name
144
+ driver: local
145
+ ```
146
+
147
+ ### Configuration Options
148
+
149
+ - **`mount_name`** (required): Name of the volume in your docker-compose.yml file
150
+ - **`host_directory`** (required): Absolute path on the host system where data will be stored
151
+ - **`contents`** (optional): Path to files or directories to copy to the host_directory before deployment
152
+
153
+ ### Example Use Cases
154
+
155
+ **Database persistence:**
156
+ ```yaml
157
+ stacks:
158
+ - name: postgres-db
159
+ compose_file: ./postgres-compose.yml
160
+ persistent_folder:
161
+ mount_name: pg_data
162
+ host_directory: /var/persistent/postgres
163
+ contents: ./initial_db_setup
164
+ ```
165
+
166
+ **Application data:**
167
+ ```yaml
168
+ stacks:
169
+ - name: web-app
170
+ compose_file: ./webapp-compose.yml
171
+ persistent_folder:
172
+ mount_name: app_storage
173
+ host_directory: /var/www/app_data
174
+ ```
175
+
176
+ This ensures your container data persists even when containers are recreated or updated, providing reliable data storage across deployments.
177
+
104
178
  ## Requirements
105
179
 
106
180
  - Python 3.12+
@@ -166,4 +240,4 @@ Contributions are welcome! Please open an issue or submit a merge request on our
166
240
 
167
241
  ## Support
168
242
 
169
- For issues and questions, please use the [GitLab issue tracker](https://gitlab.com/grenzfall/py-docker-admin/-/issues).
243
+ For issues and questions, please use the [GitLab issue tracker](https://gitlab.com/grenzfall/py-docker-admin/-/issues).
@@ -33,7 +33,7 @@ from .models import (
33
33
  StackConfig,
34
34
  )
35
35
 
36
- __version__ = "0.6.0"
36
+ __version__ = "0.6.2"
37
37
  __all__ = [
38
38
  "app",
39
39
  "MainConfig",
@@ -16,6 +16,7 @@
16
16
 
17
17
  """Pydantic models for py-docker-admin configuration."""
18
18
 
19
+ import os
19
20
  from typing import Literal
20
21
 
21
22
  from pydantic import BaseModel, Field, field_validator
@@ -94,6 +95,42 @@ class PortainerConfig(BaseModel):
94
95
  return v
95
96
 
96
97
 
98
+ class PersistentFolderConfig(BaseModel):
99
+ """Configuration for persistent folder mounting and content copying."""
100
+
101
+ mount_name: str = Field(
102
+ description="Name of the mount in docker-compose (must match volume name in compose file)"
103
+ )
104
+ host_directory: str = Field(
105
+ description="Host directory to mount (will be created if it doesn't exist)"
106
+ )
107
+ contents: str | None = Field(
108
+ default=None,
109
+ description="Path to file or directory to copy to host_directory before deployment. "
110
+ "If pointing to a file, copies the file. If pointing to a directory, copies the directory contents.",
111
+ )
112
+
113
+ @field_validator("mount_name")
114
+ def validate_mount_name(cls, v: str) -> str:
115
+ """Validate mount_name format."""
116
+ if not v:
117
+ raise ValueError("mount_name cannot be empty")
118
+ if not v.isidentifier():
119
+ raise ValueError(
120
+ "mount_name must be a valid identifier (letters, numbers, underscores)"
121
+ )
122
+ return v
123
+
124
+ @field_validator("host_directory")
125
+ def validate_host_directory(cls, v: str) -> str:
126
+ """Validate host_directory format."""
127
+ if not v:
128
+ raise ValueError("host_directory cannot be empty")
129
+ if not os.path.isabs(v):
130
+ raise ValueError("host_directory must be an absolute path")
131
+ return v
132
+
133
+
97
134
  class StackConfig(BaseModel):
98
135
  """Configuration for a single stack to be deployed.
99
136
 
@@ -114,6 +151,10 @@ class StackConfig(BaseModel):
114
151
  description="Path to environment file (optional, can be relative to CWD or absolute). "
115
152
  "This file takes precedence over any env_file in docker-compose.yml",
116
153
  )
154
+ persistent_folder: PersistentFolderConfig | None = Field(
155
+ default=None,
156
+ description="Configuration for persistent folder mounting and content copying",
157
+ )
117
158
  endpoint_id: int | None = Field(
118
159
  default=None, description="Portainer endpoint ID (optional)"
119
160
  )
@@ -350,6 +350,7 @@ class StackManager:
350
350
 
351
351
  for stack_config in stack_configs:
352
352
  try:
353
+ self._setup_persistent_folder(stack_config)
353
354
  self.deploy_stack(stack_config)
354
355
  except StackCreationError as e:
355
356
  self.logger.error(f"Failed to deploy stack {stack_config.name}: {e}")
@@ -363,3 +364,25 @@ class StackManager:
363
364
  ) from e
364
365
 
365
366
  self.logger.info("All stacks deployed successfully")
367
+
368
+ def _setup_persistent_folder(self, stack_config: "StackConfig") -> None:
369
+ """Set up persistent folder for a stack if configured.
370
+
371
+ Args:
372
+ stack_config: Stack configuration to check for persistent folder
373
+
374
+ Raises:
375
+ StackCreationError: If persistent folder setup fails
376
+ """
377
+ from .utils import setup_persistent_folder
378
+
379
+ if stack_config.persistent_folder:
380
+ try:
381
+ self.logger.info(
382
+ f"Setting up persistent folder for stack: {stack_config.name}"
383
+ )
384
+ setup_persistent_folder(stack_config.persistent_folder)
385
+ except Exception as e:
386
+ raise StackCreationError(
387
+ f"Failed to set up persistent folder for stack {stack_config.name}: {e}"
388
+ )
@@ -93,6 +93,13 @@ http {
93
93
  {% for site in sites %}
94
94
  {% if not site.server_site.startswith('/') %}
95
95
 
96
+ # HTTP to HTTPS redirect for {{ site.site_name }} on port {{ site.server_site }}
97
+ server {
98
+ listen {{ site.server_site }};
99
+ server_name {{ server_name }};
100
+ return 301 https://$host:$server_port$request_uri;
101
+ }
102
+
96
103
  # New server block for {{ site.site_name }} on port {{ site.server_site }}
97
104
  server {
98
105
  listen {{ site.server_site }} ssl;
@@ -116,9 +123,16 @@ http {
116
123
  proxy_set_header X-Real-IP $remote_addr;
117
124
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
118
125
  proxy_set_header X-Forwarded-Proto $scheme;
126
+ proxy_set_header X-Forwarded-Port $server_port;
127
+ proxy_set_header X-Forwarded-Host $host;
128
+ proxy_set_header X-Script-Name /;
119
129
  proxy_set_header Upgrade $http_upgrade;
120
130
  proxy_set_header Connection "upgrade";
121
131
  proxy_http_version 1.1;
132
+
133
+ # Rewrite redirects to use correct protocol and port
134
+ proxy_redirect http:// https://;
135
+ proxy_redirect http://$host/ https://$host:{{ site.server_site }}/;
122
136
  }
123
137
  }
124
138
  {% endif %}
@@ -16,6 +16,113 @@
16
16
 
17
17
  """Utility functions for py-docker-admin."""
18
18
 
19
+ from .models import PersistentFolderConfig
20
+ import os
21
+ import shutil
22
+ from pathlib import Path
23
+ from typing import Any, Dict, List, Optional
24
+
25
+ import yaml
26
+
27
+ from .exceptions import DockerAdminError
28
+
29
+
30
+ def check_port_in_use(port: int) -> Optional[Dict[str, Any]]:
31
+ """Check if a port is currently in use.
32
+
33
+ Args:
34
+ port: Port number to check
35
+
36
+ Returns:
37
+ Dictionary with process information if port is in use, None otherwise
38
+ """
39
+ import subprocess
40
+ import re
41
+
42
+ try:
43
+ # Check TCP ports
44
+ result = subprocess.run(
45
+ ["lsof", "-i", f":{port}"],
46
+ capture_output=True,
47
+ text=True,
48
+ check=True,
49
+ )
50
+ lines = result.stdout.strip().split("\n")
51
+ if len(lines) > 1:
52
+ # Parse the process information from the lsof output
53
+ header = lines[0]
54
+ process_info = lines[1]
55
+
56
+ # Extract relevant fields
57
+ pid_match = re.search(r"\s(\d+)\s", process_info)
58
+ command_match = re.search(r"(\S+)\s+\d+", process_info)
59
+
60
+ if pid_match and command_match:
61
+ return {
62
+ "port": port,
63
+ "pid": pid_match.group(1),
64
+ "process": command_match.group(1),
65
+ }
66
+
67
+ except subprocess.CalledProcessError:
68
+ # Port not in use
69
+ pass
70
+ except Exception as e:
71
+ # Log any unexpected errors
72
+ logger = logging.getLogger(__name__)
73
+ logger.warning(f"Error checking port {port}: {e}")
74
+
75
+ return None
76
+
77
+
78
+ def extract_ports_from_compose(compose_path: str) -> List[int]:
79
+ """Extract port numbers from a docker-compose file.
80
+
81
+ Args:
82
+ compose_path: Path to docker-compose file
83
+
84
+ Returns:
85
+ List of port numbers found in the compose file
86
+ """
87
+ try:
88
+ with open(compose_path) as f:
89
+ compose_content = f.read()
90
+
91
+ # Parse YAML content
92
+ compose_data = yaml.safe_load(compose_content) or {}
93
+
94
+ ports = []
95
+ services = compose_data.get("services", {})
96
+ for service_name, service_config in services.items():
97
+ # Check port mappings in service configuration
98
+ ports_config = service_config.get("ports", [])
99
+ for port_mapping in ports_config:
100
+ if isinstance(port_mapping, str):
101
+ # Port mapping format: "HOST_PORT:CONTAINER_PORT"
102
+ port_parts = port_mapping.split(":")
103
+ if len(port_parts) > 0:
104
+ host_port = port_parts[0]
105
+ # Handle port ranges and individual ports
106
+ if "-" in host_port:
107
+ port_range = host_port.split("-")
108
+ if len(port_range) == 2:
109
+ start_port = int(port_range[0])
110
+ end_port = int(port_range[1])
111
+ ports.extend(range(start_port, end_port + 1))
112
+ else:
113
+ try:
114
+ ports.append(int(host_port))
115
+ except ValueError:
116
+ pass
117
+
118
+ return ports
119
+
120
+ except Exception as e:
121
+ logger = logging.getLogger(__name__)
122
+ logger.debug(f"Error extracting ports from compose file: {e}")
123
+ return []
124
+
125
+
19
126
  import logging
20
127
  import shlex
21
128
  import subprocess
@@ -221,6 +328,132 @@ def check_port_in_use(port: int) -> dict[str, str] | None:
221
328
  return None
222
329
 
223
330
 
331
+ def create_directory_if_not_exists(path: str) -> None:
332
+ """Create a directory if it doesn't exist.
333
+
334
+ Args:
335
+ path: Path to directory to create
336
+
337
+ Raises:
338
+ DockerAdminError: If directory cannot be created
339
+ """
340
+ logger = logging.getLogger(__name__)
341
+
342
+ try:
343
+ Path(path).mkdir(parents=True, exist_ok=True)
344
+ logger.debug(f"Directory created or already exists: {path}")
345
+ except Exception as e:
346
+ raise DockerAdminError(f"Failed to create directory {path}: {e}")
347
+
348
+
349
+ def copy_file_or_directory(src: str, dest: str) -> None:
350
+ """Copy a file or directory to a destination.
351
+
352
+ Args:
353
+ src: Source file or directory path
354
+ dest: Destination path
355
+
356
+ Raises:
357
+ DockerAdminError: If copy operation fails
358
+ """
359
+ logger = logging.getLogger(__name__)
360
+
361
+ try:
362
+ if os.path.isfile(src):
363
+ # Copy file
364
+ shutil.copy2(src, dest)
365
+ logger.debug(f"Copied file from {src} to {dest}")
366
+ elif os.path.isdir(src):
367
+ # Copy directory contents
368
+ if os.path.exists(dest):
369
+ if not os.path.isdir(dest):
370
+ raise DockerAdminError(
371
+ f"Destination {dest} exists but is not a directory"
372
+ )
373
+ else:
374
+ os.makedirs(dest, exist_ok=True)
375
+
376
+ for item in os.listdir(src):
377
+ s = os.path.join(src, item)
378
+ d = os.path.join(dest, item)
379
+ if os.path.isdir(s):
380
+ shutil.copytree(s, d, dirs_exist_ok=True)
381
+ else:
382
+ shutil.copy2(s, d)
383
+
384
+ logger.debug(f"Copied directory contents from {src} to {dest}")
385
+ else:
386
+ raise DockerAdminError(
387
+ f"Source {src} does not exist or is not a file/directory"
388
+ )
389
+ except Exception as e:
390
+ raise DockerAdminError(f"Failed to copy {src} to {dest}: {e}")
391
+
392
+
393
+ def validate_persistent_folder_config(config: PersistentFolderConfig) -> None:
394
+ """Validate persistent folder configuration.
395
+
396
+ Args:
397
+ config: PersistentFolderConfig to validate
398
+
399
+ Raises:
400
+ DockerAdminError: If configuration is invalid
401
+ """
402
+ logger = logging.getLogger(__name__)
403
+
404
+ # Validate mount_name exists in docker-compose
405
+ if config.mount_name:
406
+ try:
407
+ compose_path = (
408
+ config.compose_file if hasattr(config, "compose_file") else None
409
+ )
410
+ if compose_path:
411
+ with open(compose_path) as f:
412
+ compose_content = f.read()
413
+ if config.mount_name not in compose_content:
414
+ raise DockerAdminError(
415
+ f"Mount name '{config.mount_name}' not found in docker-compose file"
416
+ )
417
+ except Exception as e:
418
+ logger.warning(f"Could not validate mount name in compose file: {e}")
419
+
420
+ # Validate contents path if specified
421
+ if config.contents:
422
+ if not os.path.exists(config.contents):
423
+ raise DockerAdminError(f"Contents path '{config.contents}' does not exist")
424
+ if os.path.isdir(config.contents) and not os.listdir(config.contents):
425
+ logger.warning(f"Contents directory '{config.contents}' is empty")
426
+
427
+
428
+ def setup_persistent_folder(config: PersistentFolderConfig) -> None:
429
+ """Set up persistent folder according to configuration.
430
+
431
+ Args:
432
+ config: PersistentFolderConfig to apply
433
+
434
+ Raises:
435
+ DockerAdminError: If setup fails
436
+ """
437
+ logger = logging.getLogger(__name__)
438
+
439
+ # Create host directory
440
+ create_directory_if_not_exists(config.host_directory)
441
+
442
+ # Copy contents if specified
443
+ if config.contents:
444
+ if os.path.isfile(config.contents):
445
+ # Copy file to host directory
446
+ dest_file = os.path.join(
447
+ config.host_directory, os.path.basename(config.contents)
448
+ )
449
+ copy_file_or_directory(config.contents, dest_file)
450
+ elif os.path.isdir(config.contents):
451
+ # Copy directory contents to host directory
452
+ copy_file_or_directory(config.contents, config.host_directory)
453
+
454
+ logger.info(f"Persistent folder setup complete: {config.host_directory}")
455
+
456
+
224
457
  def extract_ports_from_compose(compose_file_path: str) -> set[int]:
225
458
  """Extract all host ports from a docker-compose.yml file.
226
459
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "py-docker-admin"
3
- version = "0.6.0"
3
+ version = "0.6.2"
4
4
  description = "A Python package for automating Docker and Portainer installation"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -93,6 +93,13 @@ http {
93
93
  {% for site in sites %}
94
94
  {% if not site.server_site.startswith('/') %}
95
95
 
96
+ # HTTP to HTTPS redirect for {{ site.site_name }} on port {{ site.server_site }}
97
+ server {
98
+ listen {{ site.server_site }};
99
+ server_name {{ server_name }};
100
+ return 301 https://$host:$server_port$request_uri;
101
+ }
102
+
96
103
  # New server block for {{ site.site_name }} on port {{ site.server_site }}
97
104
  server {
98
105
  listen {{ site.server_site }} ssl;
@@ -116,9 +123,16 @@ http {
116
123
  proxy_set_header X-Real-IP $remote_addr;
117
124
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
118
125
  proxy_set_header X-Forwarded-Proto $scheme;
126
+ proxy_set_header X-Forwarded-Port $server_port;
127
+ proxy_set_header X-Forwarded-Host $host;
128
+ proxy_set_header X-Script-Name /;
119
129
  proxy_set_header Upgrade $http_upgrade;
120
130
  proxy_set_header Connection "upgrade";
121
131
  proxy_http_version 1.1;
132
+
133
+ # Rewrite redirects to use correct protocol and port
134
+ proxy_redirect http:// https://;
135
+ proxy_redirect http://$host/ https://$host:{{ site.server_site }}/;
122
136
  }
123
137
  }
124
138
  {% endif %}
File without changes
File without changes