Coverage for gws-app/gws/base/web/site.py: 69%
93 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
1from typing import Optional
3import re
5import gws
6import gws.lib.net
9class CorsConfig(gws.Config):
10 """CORS configuration."""
12 allowCredentials: bool = False
13 """Access-Control-Allow-Credentials header."""
14 allowHeaders: str = ''
15 """Access-Control-Allow-Headers header."""
16 allowMethods: str = ''
17 """Access-Control-Allow-Methods header."""
18 allowOrigin: str = ''
19 """Access-Control-Allow-Origin header."""
20 maxAge: int = 5
21 """Access-Control-Max-Age header."""
24class RewriteRuleConfig(gws.Config):
25 """Rewrite rule configuration."""
27 pattern: gws.Regex
28 """Expression to match the url against."""
29 target: str
30 """Target url with placeholders."""
31 options: Optional[dict]
32 """Additional options."""
33 reversed: bool = False
34 """Reversed rewrite rule."""
37class SSLConfig(gws.Config):
38 """SSL configuration."""
40 crt: gws.FilePath
41 """Crt bundle location."""
42 key: gws.FilePath
43 """Key file location."""
44 hsts: gws.Duration = "365d"
45 """HSTS max age. (added in 8.1)"""
48class WebDocumentRootConfig(gws.Config):
49 """Web-accessible directory."""
51 dir: gws.DirPath
52 """Directory path."""
53 allowMime: Optional[list[str]]
54 """Allowed mime types."""
55 denyMime: Optional[list[str]]
56 """Disallowed mime types (from the standard list)."""
59class Config(gws.Config):
60 """Site (virtual host) configuration"""
62 assets: Optional[WebDocumentRootConfig]
63 """Root directory for assets."""
64 cors: Optional[CorsConfig]
65 """Cors configuration."""
66 contentSecurityPolicy: str = "default-src 'self'; img-src * data: blob:"
67 """Content Security Policy for this site. (added in 8.1)"""
68 permissionsPolicy: str = "geolocation=(self), camera=(), microphone=()"
69 """Permissions Policy for this site. (added in 8.1)"""
70 errorPage: Optional[gws.ext.config.template]
71 """Error page template."""
72 host: str = '*'
73 """Host name."""
74 rewrite: Optional[list[RewriteRuleConfig]]
75 """Rewrite rules."""
76 canonicalHost: str = ''
77 """Hostname for reversed URL rewriting."""
78 root: WebDocumentRootConfig
79 """Root directory for static documents."""
82class Object(gws.WebSite):
83 canonicalHost: str
84 ssl: bool
85 contentSecurityPolicy: str
86 permissionsPolicy: str
88 def configure(self):
90 self.host = self.cfg('host', default='*')
91 self.canonicalHost = self.cfg('canonicalHost')
93 self.staticRoot = gws.WebDocumentRoot(self.cfg('root'))
95 p = self.cfg('assets')
96 self.assetsRoot = gws.WebDocumentRoot(p) if p else None
98 self.ssl = self.cfg('ssl')
100 self.rewriteRules = self.cfg('rewrite', default=[])
101 for r in self.rewriteRules:
102 if not gws.lib.net.is_abs_url(r.target):
103 # ensure rewriting from root
104 r.target = '/' + r.target.lstrip('/')
106 self.errorPage = self.create_child_if_configured(gws.ext.object.template, self.cfg('errorPage'))
107 self.corsOptions = self.cfg('cors')
109 self.contentSecurityPolicy = self.cfg('contentSecurityPolicy')
110 self.permissionsPolicy = self.cfg('permissionsPolicy')
112 def url_for(self, req, path, **params):
113 if gws.lib.net.is_abs_url(path):
114 return gws.lib.net.add_params(path, params)
116 proto = 'https' if self.ssl else 'http'
117 host = self.canonicalHost or (req.env('HTTP_HOST') if self.host == '*' else self.host)
118 base = proto + '://' + host
120 for rule in self.rewriteRules:
121 if rule.reversed:
122 m = re.match(rule.pattern, path)
123 if m:
124 # we use nginx syntax $1, need python's \1
125 t = rule.target.replace('$', '\\')
126 s = re.sub(rule.pattern, t, path)
127 url = s if gws.lib.net.is_abs_url(s) else base + '/' + s.lstrip('/')
128 return gws.lib.net.add_params(url, params)
130 url = base + '/' + path.lstrip('/')
131 return gws.lib.net.add_params(url, params)