##// END OF EJS Templates
exception-tracker: enable send email on exception
marcink -
r4276:cf661342 default
parent child Browse files
Show More
@@ -0,0 +1,18 b''
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base.mako"/>
3 <%namespace name="base" file="base.mako"/>
4
5 <%def name="subject()" filter="n,trim,whitespace_filter">
6 ${email_prefix} ${exc_type_name} (${exc_id})
7 </%def>
8
9 ## plain text version of the email. Empty by default
10 <%def name="body_plaintext()" filter="n,trim">
11 NO PLAINTEXT VERSION
12 </%def>
13
14 <h4>${_('Exception `{}` generated on UTC date: {}').format(exc_traceback.get('exc_type', 'NO_TYPE'), exc_traceback.get('exc_utc_date', 'NO_DATE'))}</h4>
15 <p>
16 View exception <a href="${exc_url}">${exc_id}</a>
17 </p>
18 <pre>${exc_traceback.get('exc_message', 'NO_MESSAGE')}</pre>
@@ -1,833 +1,844 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 ; #########################################
3 ; #########################################
4 ; RHODECODE COMMUNITY EDITION CONFIGURATION
4 ; RHODECODE COMMUNITY EDITION CONFIGURATION
5 ; #########################################
5 ; #########################################
6
6
7 [DEFAULT]
7 [DEFAULT]
8 ; Debug flag sets all loggers to debug, and enables request tracking
8 ; Debug flag sets all loggers to debug, and enables request tracking
9 debug = true
9 debug = true
10
10
11 ; ########################################################################
11 ; ########################################################################
12 ; EMAIL CONFIGURATION
12 ; EMAIL CONFIGURATION
13 ; These settings will be used by the RhodeCode mailing system
13 ; These settings will be used by the RhodeCode mailing system
14 ; ########################################################################
14 ; ########################################################################
15
15
16 ; prefix all emails subjects with given prefix, helps filtering out emails
16 ; prefix all emails subjects with given prefix, helps filtering out emails
17 #email_prefix = [RhodeCode]
17 #email_prefix = [RhodeCode]
18
18
19 ; email FROM address all mails will be sent
19 ; email FROM address all mails will be sent
20 #app_email_from = rhodecode-noreply@localhost
20 #app_email_from = rhodecode-noreply@localhost
21
21
22 #smtp_server = mail.server.com
22 #smtp_server = mail.server.com
23 #smtp_username =
23 #smtp_username =
24 #smtp_password =
24 #smtp_password =
25 #smtp_port =
25 #smtp_port =
26 #smtp_use_tls = false
26 #smtp_use_tls = false
27 #smtp_use_ssl = true
27 #smtp_use_ssl = true
28
28
29 [server:main]
29 [server:main]
30 ; COMMON HOST/IP CONFIG
30 ; COMMON HOST/IP CONFIG
31 host = 127.0.0.1
31 host = 127.0.0.1
32 port = 5000
32 port = 5000
33
33
34 ; ##################################################
34 ; ##################################################
35 ; WAITRESS WSGI SERVER - Recommended for Development
35 ; WAITRESS WSGI SERVER - Recommended for Development
36 ; ##################################################
36 ; ##################################################
37
37
38 ; use server type
38 ; use server type
39 use = egg:waitress#main
39 use = egg:waitress#main
40
40
41 ; number of worker threads
41 ; number of worker threads
42 threads = 5
42 threads = 5
43
43
44 ; MAX BODY SIZE 100GB
44 ; MAX BODY SIZE 100GB
45 max_request_body_size = 107374182400
45 max_request_body_size = 107374182400
46
46
47 ; Use poll instead of select, fixes file descriptors limits problems.
47 ; Use poll instead of select, fixes file descriptors limits problems.
48 ; May not work on old windows systems.
48 ; May not work on old windows systems.
49 asyncore_use_poll = true
49 asyncore_use_poll = true
50
50
51
51
52 ; ###########################
52 ; ###########################
53 ; GUNICORN APPLICATION SERVER
53 ; GUNICORN APPLICATION SERVER
54 ; ###########################
54 ; ###########################
55
55
56 ; run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini
56 ; run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini
57
57
58 ; Module to use, this setting shouldn't be changed
58 ; Module to use, this setting shouldn't be changed
59 #use = egg:gunicorn#main
59 #use = egg:gunicorn#main
60
60
61 ; Sets the number of process workers. More workers means more concurrent connections
61 ; Sets the number of process workers. More workers means more concurrent connections
62 ; RhodeCode can handle at the same time. Each additional worker also it increases
62 ; RhodeCode can handle at the same time. Each additional worker also it increases
63 ; memory usage as each has it's own set of caches.
63 ; memory usage as each has it's own set of caches.
64 ; Recommended value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers, but no more
64 ; Recommended value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers, but no more
65 ; than 8-10 unless for really big deployments .e.g 700-1000 users.
65 ; than 8-10 unless for really big deployments .e.g 700-1000 users.
66 ; `instance_id = *` must be set in the [app:main] section below (which is the default)
66 ; `instance_id = *` must be set in the [app:main] section below (which is the default)
67 ; when using more than 1 worker.
67 ; when using more than 1 worker.
68 #workers = 2
68 #workers = 2
69
69
70 ; Gunicorn access log level
70 ; Gunicorn access log level
71 #loglevel = info
71 #loglevel = info
72
72
73 ; Process name visible in process list
73 ; Process name visible in process list
74 #proc_name = rhodecode
74 #proc_name = rhodecode
75
75
76 ; Type of worker class, one of `sync`, `gevent`
76 ; Type of worker class, one of `sync`, `gevent`
77 ; Recommended type is `gevent`
77 ; Recommended type is `gevent`
78 #worker_class = gevent
78 #worker_class = gevent
79
79
80 ; The maximum number of simultaneous clients. Valid only for gevent
80 ; The maximum number of simultaneous clients. Valid only for gevent
81 #worker_connections = 10
81 #worker_connections = 10
82
82
83 ; Max number of requests that worker will handle before being gracefully restarted.
83 ; Max number of requests that worker will handle before being gracefully restarted.
84 ; Prevents memory leaks, jitter adds variability so not all workers are restarted at once.
84 ; Prevents memory leaks, jitter adds variability so not all workers are restarted at once.
85 #max_requests = 1000
85 #max_requests = 1000
86 #max_requests_jitter = 30
86 #max_requests_jitter = 30
87
87
88 ; Amount of time a worker can spend with handling a request before it
88 ; Amount of time a worker can spend with handling a request before it
89 ; gets killed and restarted. By default set to 21600 (6hrs)
89 ; gets killed and restarted. By default set to 21600 (6hrs)
90 ; Examples: 1800 (30min), 3600 (1hr), 7200 (2hr), 43200 (12h)
90 ; Examples: 1800 (30min), 3600 (1hr), 7200 (2hr), 43200 (12h)
91 #timeout = 21600
91 #timeout = 21600
92
92
93 ; The maximum size of HTTP request line in bytes.
93 ; The maximum size of HTTP request line in bytes.
94 ; 0 for unlimited
94 ; 0 for unlimited
95 #limit_request_line = 0
95 #limit_request_line = 0
96
96
97 ; Limit the number of HTTP headers fields in a request.
97 ; Limit the number of HTTP headers fields in a request.
98 ; By default this value is 100 and can't be larger than 32768.
98 ; By default this value is 100 and can't be larger than 32768.
99 #limit_request_fields = 32768
99 #limit_request_fields = 32768
100
100
101 ; Limit the allowed size of an HTTP request header field.
101 ; Limit the allowed size of an HTTP request header field.
102 ; Value is a positive number or 0.
102 ; Value is a positive number or 0.
103 ; Setting it to 0 will allow unlimited header field sizes.
103 ; Setting it to 0 will allow unlimited header field sizes.
104 #limit_request_field_size = 0
104 #limit_request_field_size = 0
105
105
106 ; Timeout for graceful workers restart.
106 ; Timeout for graceful workers restart.
107 ; After receiving a restart signal, workers have this much time to finish
107 ; After receiving a restart signal, workers have this much time to finish
108 ; serving requests. Workers still alive after the timeout (starting from the
108 ; serving requests. Workers still alive after the timeout (starting from the
109 ; receipt of the restart signal) are force killed.
109 ; receipt of the restart signal) are force killed.
110 ; Examples: 1800 (30min), 3600 (1hr), 7200 (2hr), 43200 (12h)
110 ; Examples: 1800 (30min), 3600 (1hr), 7200 (2hr), 43200 (12h)
111 #graceful_timeout = 3600
111 #graceful_timeout = 3600
112
112
113 # The number of seconds to wait for requests on a Keep-Alive connection.
113 # The number of seconds to wait for requests on a Keep-Alive connection.
114 # Generally set in the 1-5 seconds range.
114 # Generally set in the 1-5 seconds range.
115 #keepalive = 2
115 #keepalive = 2
116
116
117 ; Maximum memory usage that each worker can use before it will receive a
117 ; Maximum memory usage that each worker can use before it will receive a
118 ; graceful restart signal 0 = memory monitoring is disabled
118 ; graceful restart signal 0 = memory monitoring is disabled
119 ; Examples: 268435456 (256MB), 536870912 (512MB)
119 ; Examples: 268435456 (256MB), 536870912 (512MB)
120 ; 1073741824 (1GB), 2147483648 (2GB), 4294967296 (4GB)
120 ; 1073741824 (1GB), 2147483648 (2GB), 4294967296 (4GB)
121 #memory_max_usage = 0
121 #memory_max_usage = 0
122
122
123 ; How often in seconds to check for memory usage for each gunicorn worker
123 ; How often in seconds to check for memory usage for each gunicorn worker
124 #memory_usage_check_interval = 60
124 #memory_usage_check_interval = 60
125
125
126 ; Threshold value for which we don't recycle worker if GarbageCollection
126 ; Threshold value for which we don't recycle worker if GarbageCollection
127 ; frees up enough resources. Before each restart we try to run GC on worker
127 ; frees up enough resources. Before each restart we try to run GC on worker
128 ; in case we get enough free memory after that, restart will not happen.
128 ; in case we get enough free memory after that, restart will not happen.
129 #memory_usage_recovery_threshold = 0.8
129 #memory_usage_recovery_threshold = 0.8
130
130
131
131
132 ; Prefix middleware for RhodeCode.
132 ; Prefix middleware for RhodeCode.
133 ; recommended when using proxy setup.
133 ; recommended when using proxy setup.
134 ; allows to set RhodeCode under a prefix in server.
134 ; allows to set RhodeCode under a prefix in server.
135 ; eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
135 ; eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
136 ; And set your prefix like: `prefix = /custom_prefix`
136 ; And set your prefix like: `prefix = /custom_prefix`
137 ; be sure to also set beaker.session.cookie_path = /custom_prefix if you need
137 ; be sure to also set beaker.session.cookie_path = /custom_prefix if you need
138 ; to make your cookies only work on prefix url
138 ; to make your cookies only work on prefix url
139 [filter:proxy-prefix]
139 [filter:proxy-prefix]
140 use = egg:PasteDeploy#prefix
140 use = egg:PasteDeploy#prefix
141 prefix = /
141 prefix = /
142
142
143 [app:main]
143 [app:main]
144 ; The %(here)s variable will be replaced with the absolute path of parent directory
144 ; The %(here)s variable will be replaced with the absolute path of parent directory
145 ; of this file
145 ; of this file
146 ; In addition ENVIRONMENT variables usage is possible, e.g
146 ; In addition ENVIRONMENT variables usage is possible, e.g
147 ; sqlalchemy.db1.url = {ENV_RC_DB_URL}
147 ; sqlalchemy.db1.url = {ENV_RC_DB_URL}
148
148
149 use = egg:rhodecode-enterprise-ce
149 use = egg:rhodecode-enterprise-ce
150
150
151 ; enable proxy prefix middleware, defined above
151 ; enable proxy prefix middleware, defined above
152 #filter-with = proxy-prefix
152 #filter-with = proxy-prefix
153
153
154 ; #############
154 ; #############
155 ; DEBUG OPTIONS
155 ; DEBUG OPTIONS
156 ; #############
156 ; #############
157
157
158 pyramid.reload_templates = true
158 pyramid.reload_templates = true
159
159
160 # During development the we want to have the debug toolbar enabled
160 # During development the we want to have the debug toolbar enabled
161 pyramid.includes =
161 pyramid.includes =
162 pyramid_debugtoolbar
162 pyramid_debugtoolbar
163
163
164 debugtoolbar.hosts = 0.0.0.0/0
164 debugtoolbar.hosts = 0.0.0.0/0
165 debugtoolbar.exclude_prefixes =
165 debugtoolbar.exclude_prefixes =
166 /css
166 /css
167 /fonts
167 /fonts
168 /images
168 /images
169 /js
169 /js
170
170
171 ## RHODECODE PLUGINS ##
171 ## RHODECODE PLUGINS ##
172 rhodecode.includes =
172 rhodecode.includes =
173 rhodecode.api
173 rhodecode.api
174
174
175
175
176 # api prefix url
176 # api prefix url
177 rhodecode.api.url = /_admin/api
177 rhodecode.api.url = /_admin/api
178
178
179 ; enable debug style page
179 ; enable debug style page
180 debug_style = true
180 debug_style = true
181
181
182 ; #################
182 ; #################
183 ; END DEBUG OPTIONS
183 ; END DEBUG OPTIONS
184 ; #################
184 ; #################
185
185
186 ; encryption key used to encrypt social plugin tokens,
186 ; encryption key used to encrypt social plugin tokens,
187 ; remote_urls with credentials etc, if not set it defaults to
187 ; remote_urls with credentials etc, if not set it defaults to
188 ; `beaker.session.secret`
188 ; `beaker.session.secret`
189 #rhodecode.encrypted_values.secret =
189 #rhodecode.encrypted_values.secret =
190
190
191 ; decryption strict mode (enabled by default). It controls if decryption raises
191 ; decryption strict mode (enabled by default). It controls if decryption raises
192 ; `SignatureVerificationError` in case of wrong key, or damaged encryption data.
192 ; `SignatureVerificationError` in case of wrong key, or damaged encryption data.
193 #rhodecode.encrypted_values.strict = false
193 #rhodecode.encrypted_values.strict = false
194
194
195 ; Pick algorithm for encryption. Either fernet (more secure) or aes (default)
195 ; Pick algorithm for encryption. Either fernet (more secure) or aes (default)
196 ; fernet is safer, and we strongly recommend switching to it.
196 ; fernet is safer, and we strongly recommend switching to it.
197 ; Due to backward compatibility aes is used as default.
197 ; Due to backward compatibility aes is used as default.
198 #rhodecode.encrypted_values.algorithm = fernet
198 #rhodecode.encrypted_values.algorithm = fernet
199
199
200 ; Return gzipped responses from RhodeCode (static files/application)
200 ; Return gzipped responses from RhodeCode (static files/application)
201 gzip_responses = false
201 gzip_responses = false
202
202
203 ; Auto-generate javascript routes file on startup
203 ; Auto-generate javascript routes file on startup
204 generate_js_files = false
204 generate_js_files = false
205
205
206 ; System global default language.
206 ; System global default language.
207 ; All available languages: en (default), be, de, es, fr, it, ja, pl, pt, ru, zh
207 ; All available languages: en (default), be, de, es, fr, it, ja, pl, pt, ru, zh
208 lang = en
208 lang = en
209
209
210 ; Perform a full repository scan and import on each server start.
210 ; Perform a full repository scan and import on each server start.
211 ; Settings this to true could lead to very long startup time.
211 ; Settings this to true could lead to very long startup time.
212 startup.import_repos = false
212 startup.import_repos = false
213
213
214 ; Uncomment and set this path to use archive download cache.
214 ; Uncomment and set this path to use archive download cache.
215 ; Once enabled, generated archives will be cached at this location
215 ; Once enabled, generated archives will be cached at this location
216 ; and served from the cache during subsequent requests for the same archive of
216 ; and served from the cache during subsequent requests for the same archive of
217 ; the repository.
217 ; the repository.
218 #archive_cache_dir = /tmp/tarballcache
218 #archive_cache_dir = /tmp/tarballcache
219
219
220 ; URL at which the application is running. This is used for Bootstrapping
220 ; URL at which the application is running. This is used for Bootstrapping
221 ; requests in context when no web request is available. Used in ishell, or
221 ; requests in context when no web request is available. Used in ishell, or
222 ; SSH calls. Set this for events to receive proper url for SSH calls.
222 ; SSH calls. Set this for events to receive proper url for SSH calls.
223 app.base_url = http://rhodecode.local
223 app.base_url = http://rhodecode.local
224
224
225 ; Unique application ID. Should be a random unique string for security.
225 ; Unique application ID. Should be a random unique string for security.
226 app_instance_uuid = rc-production
226 app_instance_uuid = rc-production
227
227
228 ; Cut off limit for large diffs (size in bytes). If overall diff size on
228 ; Cut off limit for large diffs (size in bytes). If overall diff size on
229 ; commit, or pull request exceeds this limit this diff will be displayed
229 ; commit, or pull request exceeds this limit this diff will be displayed
230 ; partially. E.g 512000 == 512Kb
230 ; partially. E.g 512000 == 512Kb
231 cut_off_limit_diff = 512000
231 cut_off_limit_diff = 512000
232
232
233 ; Cut off limit for large files inside diffs (size in bytes). Each individual
233 ; Cut off limit for large files inside diffs (size in bytes). Each individual
234 ; file inside diff which exceeds this limit will be displayed partially.
234 ; file inside diff which exceeds this limit will be displayed partially.
235 ; E.g 128000 == 128Kb
235 ; E.g 128000 == 128Kb
236 cut_off_limit_file = 128000
236 cut_off_limit_file = 128000
237
237
238 ; Use cached version of vcs repositories everywhere. Recommended to be `true`
238 ; Use cached version of vcs repositories everywhere. Recommended to be `true`
239 vcs_full_cache = true
239 vcs_full_cache = true
240
240
241 ; Force https in RhodeCode, fixes https redirects, assumes it's always https.
241 ; Force https in RhodeCode, fixes https redirects, assumes it's always https.
242 ; Normally this is controlled by proper flags sent from http server such as Nginx or Apache
242 ; Normally this is controlled by proper flags sent from http server such as Nginx or Apache
243 force_https = false
243 force_https = false
244
244
245 ; use Strict-Transport-Security headers
245 ; use Strict-Transport-Security headers
246 use_htsts = false
246 use_htsts = false
247
247
248 ; Set to true if your repos are exposed using the dumb protocol
248 ; Set to true if your repos are exposed using the dumb protocol
249 git_update_server_info = false
249 git_update_server_info = false
250
250
251 ; RSS/ATOM feed options
251 ; RSS/ATOM feed options
252 rss_cut_off_limit = 256000
252 rss_cut_off_limit = 256000
253 rss_items_per_page = 10
253 rss_items_per_page = 10
254 rss_include_diff = false
254 rss_include_diff = false
255
255
256 ; gist URL alias, used to create nicer urls for gist. This should be an
256 ; gist URL alias, used to create nicer urls for gist. This should be an
257 ; url that does rewrites to _admin/gists/{gistid}.
257 ; url that does rewrites to _admin/gists/{gistid}.
258 ; example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
258 ; example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
259 ; RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
259 ; RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
260 gist_alias_url =
260 gist_alias_url =
261
261
262 ; List of views (using glob pattern syntax) that AUTH TOKENS could be
262 ; List of views (using glob pattern syntax) that AUTH TOKENS could be
263 ; used for access.
263 ; used for access.
264 ; Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
264 ; Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
265 ; came from the the logged in user who own this authentication token.
265 ; came from the the logged in user who own this authentication token.
266 ; Additionally @TOKEN syntax can be used to bound the view to specific
266 ; Additionally @TOKEN syntax can be used to bound the view to specific
267 ; authentication token. Such view would be only accessible when used together
267 ; authentication token. Such view would be only accessible when used together
268 ; with this authentication token
268 ; with this authentication token
269 ; list of all views can be found under `/_admin/permissions/auth_token_access`
269 ; list of all views can be found under `/_admin/permissions/auth_token_access`
270 ; The list should be "," separated and on a single line.
270 ; The list should be "," separated and on a single line.
271 ; Most common views to enable:
271 ; Most common views to enable:
272
272
273 # RepoCommitsView:repo_commit_download
273 # RepoCommitsView:repo_commit_download
274 # RepoCommitsView:repo_commit_patch
274 # RepoCommitsView:repo_commit_patch
275 # RepoCommitsView:repo_commit_raw
275 # RepoCommitsView:repo_commit_raw
276 # RepoCommitsView:repo_commit_raw@TOKEN
276 # RepoCommitsView:repo_commit_raw@TOKEN
277 # RepoFilesView:repo_files_diff
277 # RepoFilesView:repo_files_diff
278 # RepoFilesView:repo_archivefile
278 # RepoFilesView:repo_archivefile
279 # RepoFilesView:repo_file_raw
279 # RepoFilesView:repo_file_raw
280 # GistView:*
280 # GistView:*
281 api_access_controllers_whitelist =
281 api_access_controllers_whitelist =
282
282
283 ; Default encoding used to convert from and to unicode
283 ; Default encoding used to convert from and to unicode
284 ; can be also a comma separated list of encoding in case of mixed encodings
284 ; can be also a comma separated list of encoding in case of mixed encodings
285 default_encoding = UTF-8
285 default_encoding = UTF-8
286
286
287 ; instance-id prefix
287 ; instance-id prefix
288 ; a prefix key for this instance used for cache invalidation when running
288 ; a prefix key for this instance used for cache invalidation when running
289 ; multiple instances of RhodeCode, make sure it's globally unique for
289 ; multiple instances of RhodeCode, make sure it's globally unique for
290 ; all running RhodeCode instances. Leave empty if you don't use it
290 ; all running RhodeCode instances. Leave empty if you don't use it
291 instance_id =
291 instance_id =
292
292
293 ; Fallback authentication plugin. Set this to a plugin ID to force the usage
293 ; Fallback authentication plugin. Set this to a plugin ID to force the usage
294 ; of an authentication plugin also if it is disabled by it's settings.
294 ; of an authentication plugin also if it is disabled by it's settings.
295 ; This could be useful if you are unable to log in to the system due to broken
295 ; This could be useful if you are unable to log in to the system due to broken
296 ; authentication settings. Then you can enable e.g. the internal RhodeCode auth
296 ; authentication settings. Then you can enable e.g. the internal RhodeCode auth
297 ; module to log in again and fix the settings.
297 ; module to log in again and fix the settings.
298 ; Available builtin plugin IDs (hash is part of the ID):
298 ; Available builtin plugin IDs (hash is part of the ID):
299 ; egg:rhodecode-enterprise-ce#rhodecode
299 ; egg:rhodecode-enterprise-ce#rhodecode
300 ; egg:rhodecode-enterprise-ce#pam
300 ; egg:rhodecode-enterprise-ce#pam
301 ; egg:rhodecode-enterprise-ce#ldap
301 ; egg:rhodecode-enterprise-ce#ldap
302 ; egg:rhodecode-enterprise-ce#jasig_cas
302 ; egg:rhodecode-enterprise-ce#jasig_cas
303 ; egg:rhodecode-enterprise-ce#headers
303 ; egg:rhodecode-enterprise-ce#headers
304 ; egg:rhodecode-enterprise-ce#crowd
304 ; egg:rhodecode-enterprise-ce#crowd
305
305
306 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
306 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
307
307
308 ; Flag to control loading of legacy plugins in py:/path format
308 ; Flag to control loading of legacy plugins in py:/path format
309 auth_plugin.import_legacy_plugins = true
309 auth_plugin.import_legacy_plugins = true
310
310
311 ; alternative return HTTP header for failed authentication. Default HTTP
311 ; alternative return HTTP header for failed authentication. Default HTTP
312 ; response is 401 HTTPUnauthorized. Currently HG clients have troubles with
312 ; response is 401 HTTPUnauthorized. Currently HG clients have troubles with
313 ; handling that causing a series of failed authentication calls.
313 ; handling that causing a series of failed authentication calls.
314 ; Set this variable to 403 to return HTTPForbidden, or any other HTTP code
314 ; Set this variable to 403 to return HTTPForbidden, or any other HTTP code
315 ; This will be served instead of default 401 on bad authentication
315 ; This will be served instead of default 401 on bad authentication
316 auth_ret_code =
316 auth_ret_code =
317
317
318 ; use special detection method when serving auth_ret_code, instead of serving
318 ; use special detection method when serving auth_ret_code, instead of serving
319 ; ret_code directly, use 401 initially (Which triggers credentials prompt)
319 ; ret_code directly, use 401 initially (Which triggers credentials prompt)
320 ; and then serve auth_ret_code to clients
320 ; and then serve auth_ret_code to clients
321 auth_ret_code_detection = false
321 auth_ret_code_detection = false
322
322
323 ; locking return code. When repository is locked return this HTTP code. 2XX
323 ; locking return code. When repository is locked return this HTTP code. 2XX
324 ; codes don't break the transactions while 4XX codes do
324 ; codes don't break the transactions while 4XX codes do
325 lock_ret_code = 423
325 lock_ret_code = 423
326
326
327 ; allows to change the repository location in settings page
327 ; allows to change the repository location in settings page
328 allow_repo_location_change = true
328 allow_repo_location_change = true
329
329
330 ; allows to setup custom hooks in settings page
330 ; allows to setup custom hooks in settings page
331 allow_custom_hooks_settings = true
331 allow_custom_hooks_settings = true
332
332
333 ; Generated license token required for EE edition license.
333 ; Generated license token required for EE edition license.
334 ; New generated token value can be found in Admin > settings > license page.
334 ; New generated token value can be found in Admin > settings > license page.
335 license_token =
335 license_token =
336
336
337 ; This flag hides sensitive information on the license page such as token, and license data
337 ; This flag hides sensitive information on the license page such as token, and license data
338 license.hide_license_info = false
338 license.hide_license_info = false
339
339
340 ; supervisor connection uri, for managing supervisor and logs.
340 ; supervisor connection uri, for managing supervisor and logs.
341 supervisor.uri =
341 supervisor.uri =
342
342
343 ; supervisord group name/id we only want this RC instance to handle
343 ; supervisord group name/id we only want this RC instance to handle
344 supervisor.group_id = dev
344 supervisor.group_id = dev
345
345
346 ; Display extended labs settings
346 ; Display extended labs settings
347 labs_settings_active = true
347 labs_settings_active = true
348
348
349 ; Custom exception store path, defaults to TMPDIR
349 ; Custom exception store path, defaults to TMPDIR
350 ; This is used to store exception from RhodeCode in shared directory
350 ; This is used to store exception from RhodeCode in shared directory
351 #exception_tracker.store_path =
351 #exception_tracker.store_path =
352
352
353 ; Send email with exception details when it happens
354 #exception_tracker.send_email = false
355
356 ; Comma separated list of recipients for exception emails,
357 ; e.g admin@rhodecode.com,devops@rhodecode.com
358 ; Can be left empty, then emails will be sent to ALL super-admins
359 #exception_tracker.send_email_recipients =
360
361 ; optional prefix to Add to email Subject
362 #exception_tracker.email_prefix = [RHODECODE ERROR]
363
353 ; File store configuration. This is used to store and serve uploaded files
364 ; File store configuration. This is used to store and serve uploaded files
354 file_store.enabled = true
365 file_store.enabled = true
355
366
356 ; Storage backend, available options are: local
367 ; Storage backend, available options are: local
357 file_store.backend = local
368 file_store.backend = local
358
369
359 ; path to store the uploaded binaries
370 ; path to store the uploaded binaries
360 file_store.storage_path = %(here)s/data/file_store
371 file_store.storage_path = %(here)s/data/file_store
361
372
362
373
363 ; #############
374 ; #############
364 ; CELERY CONFIG
375 ; CELERY CONFIG
365 ; #############
376 ; #############
366
377
367 ; manually run celery: /path/to/celery worker -E --beat --app rhodecode.lib.celerylib.loader --scheduler rhodecode.lib.celerylib.scheduler.RcScheduler --loglevel DEBUG --ini /path/to/rhodecode.ini
378 ; manually run celery: /path/to/celery worker -E --beat --app rhodecode.lib.celerylib.loader --scheduler rhodecode.lib.celerylib.scheduler.RcScheduler --loglevel DEBUG --ini /path/to/rhodecode.ini
368
379
369 use_celery = false
380 use_celery = false
370
381
371 ; connection url to the message broker (default redis)
382 ; connection url to the message broker (default redis)
372 celery.broker_url = redis://localhost:6379/8
383 celery.broker_url = redis://localhost:6379/8
373
384
374 ; rabbitmq example
385 ; rabbitmq example
375 #celery.broker_url = amqp://rabbitmq:qweqwe@localhost:5672/rabbitmqhost
386 #celery.broker_url = amqp://rabbitmq:qweqwe@localhost:5672/rabbitmqhost
376
387
377 ; maximum tasks to execute before worker restart
388 ; maximum tasks to execute before worker restart
378 celery.max_tasks_per_child = 100
389 celery.max_tasks_per_child = 100
379
390
380 ; tasks will never be sent to the queue, but executed locally instead.
391 ; tasks will never be sent to the queue, but executed locally instead.
381 celery.task_always_eager = false
392 celery.task_always_eager = false
382
393
383 ; #############
394 ; #############
384 ; DOGPILE CACHE
395 ; DOGPILE CACHE
385 ; #############
396 ; #############
386
397
387 ; Default cache dir for caches. Putting this into a ramdisk can boost performance.
398 ; Default cache dir for caches. Putting this into a ramdisk can boost performance.
388 ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space
399 ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space
389 cache_dir = %(here)s/data
400 cache_dir = %(here)s/data
390
401
391 ; *********************************************
402 ; *********************************************
392 ; `sql_cache_short` cache for heavy SQL queries
403 ; `sql_cache_short` cache for heavy SQL queries
393 ; Only supported backend is `memory_lru`
404 ; Only supported backend is `memory_lru`
394 ; *********************************************
405 ; *********************************************
395 rc_cache.sql_cache_short.backend = dogpile.cache.rc.memory_lru
406 rc_cache.sql_cache_short.backend = dogpile.cache.rc.memory_lru
396 rc_cache.sql_cache_short.expiration_time = 30
407 rc_cache.sql_cache_short.expiration_time = 30
397
408
398
409
399 ; *****************************************************
410 ; *****************************************************
400 ; `cache_repo_longterm` cache for repo object instances
411 ; `cache_repo_longterm` cache for repo object instances
401 ; Only supported backend is `memory_lru`
412 ; Only supported backend is `memory_lru`
402 ; *****************************************************
413 ; *****************************************************
403 rc_cache.cache_repo_longterm.backend = dogpile.cache.rc.memory_lru
414 rc_cache.cache_repo_longterm.backend = dogpile.cache.rc.memory_lru
404 ; by default we use 30 Days, cache is still invalidated on push
415 ; by default we use 30 Days, cache is still invalidated on push
405 rc_cache.cache_repo_longterm.expiration_time = 2592000
416 rc_cache.cache_repo_longterm.expiration_time = 2592000
406 ; max items in LRU cache, set to smaller number to save memory, and expire last used caches
417 ; max items in LRU cache, set to smaller number to save memory, and expire last used caches
407 rc_cache.cache_repo_longterm.max_size = 10000
418 rc_cache.cache_repo_longterm.max_size = 10000
408
419
409
420
410 ; *************************************************
421 ; *************************************************
411 ; `cache_perms` cache for permission tree, auth TTL
422 ; `cache_perms` cache for permission tree, auth TTL
412 ; *************************************************
423 ; *************************************************
413 rc_cache.cache_perms.backend = dogpile.cache.rc.file_namespace
424 rc_cache.cache_perms.backend = dogpile.cache.rc.file_namespace
414 rc_cache.cache_perms.expiration_time = 300
425 rc_cache.cache_perms.expiration_time = 300
415 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
426 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
416 #rc_cache.cache_perms.arguments.filename = /tmp/cache_perms.db
427 #rc_cache.cache_perms.arguments.filename = /tmp/cache_perms.db
417
428
418 ; alternative `cache_perms` redis backend with distributed lock
429 ; alternative `cache_perms` redis backend with distributed lock
419 #rc_cache.cache_perms.backend = dogpile.cache.rc.redis
430 #rc_cache.cache_perms.backend = dogpile.cache.rc.redis
420 #rc_cache.cache_perms.expiration_time = 300
431 #rc_cache.cache_perms.expiration_time = 300
421
432
422 ; redis_expiration_time needs to be greater then expiration_time
433 ; redis_expiration_time needs to be greater then expiration_time
423 #rc_cache.cache_perms.arguments.redis_expiration_time = 7200
434 #rc_cache.cache_perms.arguments.redis_expiration_time = 7200
424
435
425 #rc_cache.cache_perms.arguments.host = localhost
436 #rc_cache.cache_perms.arguments.host = localhost
426 #rc_cache.cache_perms.arguments.port = 6379
437 #rc_cache.cache_perms.arguments.port = 6379
427 #rc_cache.cache_perms.arguments.db = 0
438 #rc_cache.cache_perms.arguments.db = 0
428 #rc_cache.cache_perms.arguments.socket_timeout = 30
439 #rc_cache.cache_perms.arguments.socket_timeout = 30
429 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
440 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
430 #rc_cache.cache_perms.arguments.distributed_lock = true
441 #rc_cache.cache_perms.arguments.distributed_lock = true
431
442
432
443
433 ; ***************************************************
444 ; ***************************************************
434 ; `cache_repo` cache for file tree, Readme, RSS FEEDS
445 ; `cache_repo` cache for file tree, Readme, RSS FEEDS
435 ; ***************************************************
446 ; ***************************************************
436 rc_cache.cache_repo.backend = dogpile.cache.rc.file_namespace
447 rc_cache.cache_repo.backend = dogpile.cache.rc.file_namespace
437 rc_cache.cache_repo.expiration_time = 2592000
448 rc_cache.cache_repo.expiration_time = 2592000
438 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
449 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
439 #rc_cache.cache_repo.arguments.filename = /tmp/cache_repo.db
450 #rc_cache.cache_repo.arguments.filename = /tmp/cache_repo.db
440
451
441 ; alternative `cache_repo` redis backend with distributed lock
452 ; alternative `cache_repo` redis backend with distributed lock
442 #rc_cache.cache_repo.backend = dogpile.cache.rc.redis
453 #rc_cache.cache_repo.backend = dogpile.cache.rc.redis
443 #rc_cache.cache_repo.expiration_time = 2592000
454 #rc_cache.cache_repo.expiration_time = 2592000
444
455
445 ; redis_expiration_time needs to be greater then expiration_time
456 ; redis_expiration_time needs to be greater then expiration_time
446 #rc_cache.cache_repo.arguments.redis_expiration_time = 2678400
457 #rc_cache.cache_repo.arguments.redis_expiration_time = 2678400
447
458
448 #rc_cache.cache_repo.arguments.host = localhost
459 #rc_cache.cache_repo.arguments.host = localhost
449 #rc_cache.cache_repo.arguments.port = 6379
460 #rc_cache.cache_repo.arguments.port = 6379
450 #rc_cache.cache_repo.arguments.db = 1
461 #rc_cache.cache_repo.arguments.db = 1
451 #rc_cache.cache_repo.arguments.socket_timeout = 30
462 #rc_cache.cache_repo.arguments.socket_timeout = 30
452 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
463 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
453 #rc_cache.cache_repo.arguments.distributed_lock = true
464 #rc_cache.cache_repo.arguments.distributed_lock = true
454
465
455
466
456 ; ##############
467 ; ##############
457 ; BEAKER SESSION
468 ; BEAKER SESSION
458 ; ##############
469 ; ##############
459
470
460 ; beaker.session.type is type of storage options for the logged users sessions. Current allowed
471 ; beaker.session.type is type of storage options for the logged users sessions. Current allowed
461 ; types are file, ext:redis, ext:database, ext:memcached, and memory (default if not specified).
472 ; types are file, ext:redis, ext:database, ext:memcached, and memory (default if not specified).
462 ; Fastest ones are Redis and ext:database
473 ; Fastest ones are Redis and ext:database
463 beaker.session.type = file
474 beaker.session.type = file
464 beaker.session.data_dir = %(here)s/data/sessions
475 beaker.session.data_dir = %(here)s/data/sessions
465
476
466 ; Redis based sessions
477 ; Redis based sessions
467 #beaker.session.type = ext:redis
478 #beaker.session.type = ext:redis
468 #beaker.session.url = redis://127.0.0.1:6379/2
479 #beaker.session.url = redis://127.0.0.1:6379/2
469
480
470 ; DB based session, fast, and allows easy management over logged in users
481 ; DB based session, fast, and allows easy management over logged in users
471 #beaker.session.type = ext:database
482 #beaker.session.type = ext:database
472 #beaker.session.table_name = db_session
483 #beaker.session.table_name = db_session
473 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
484 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
474 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
485 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
475 #beaker.session.sa.pool_recycle = 3600
486 #beaker.session.sa.pool_recycle = 3600
476 #beaker.session.sa.echo = false
487 #beaker.session.sa.echo = false
477
488
478 beaker.session.key = rhodecode
489 beaker.session.key = rhodecode
479 beaker.session.secret = develop-rc-uytcxaz
490 beaker.session.secret = develop-rc-uytcxaz
480 beaker.session.lock_dir = %(here)s/data/sessions/lock
491 beaker.session.lock_dir = %(here)s/data/sessions/lock
481
492
482 ; Secure encrypted cookie. Requires AES and AES python libraries
493 ; Secure encrypted cookie. Requires AES and AES python libraries
483 ; you must disable beaker.session.secret to use this
494 ; you must disable beaker.session.secret to use this
484 #beaker.session.encrypt_key = key_for_encryption
495 #beaker.session.encrypt_key = key_for_encryption
485 #beaker.session.validate_key = validation_key
496 #beaker.session.validate_key = validation_key
486
497
487 ; Sets session as invalid (also logging out user) if it haven not been
498 ; Sets session as invalid (also logging out user) if it haven not been
488 ; accessed for given amount of time in seconds
499 ; accessed for given amount of time in seconds
489 beaker.session.timeout = 2592000
500 beaker.session.timeout = 2592000
490 beaker.session.httponly = true
501 beaker.session.httponly = true
491
502
492 ; Path to use for the cookie. Set to prefix if you use prefix middleware
503 ; Path to use for the cookie. Set to prefix if you use prefix middleware
493 #beaker.session.cookie_path = /custom_prefix
504 #beaker.session.cookie_path = /custom_prefix
494
505
495 ; Set https secure cookie
506 ; Set https secure cookie
496 beaker.session.secure = false
507 beaker.session.secure = false
497
508
498 ; default cookie expiration time in seconds, set to `true` to set expire
509 ; default cookie expiration time in seconds, set to `true` to set expire
499 ; at browser close
510 ; at browser close
500 #beaker.session.cookie_expires = 3600
511 #beaker.session.cookie_expires = 3600
501
512
502 ; #############################
513 ; #############################
503 ; SEARCH INDEXING CONFIGURATION
514 ; SEARCH INDEXING CONFIGURATION
504 ; #############################
515 ; #############################
505
516
506 ; Full text search indexer is available in rhodecode-tools under
517 ; Full text search indexer is available in rhodecode-tools under
507 ; `rhodecode-tools index` command
518 ; `rhodecode-tools index` command
508
519
509 ; WHOOSH Backend, doesn't require additional services to run
520 ; WHOOSH Backend, doesn't require additional services to run
510 ; it works good with few dozen repos
521 ; it works good with few dozen repos
511 search.module = rhodecode.lib.index.whoosh
522 search.module = rhodecode.lib.index.whoosh
512 search.location = %(here)s/data/index
523 search.location = %(here)s/data/index
513
524
514 ; ####################
525 ; ####################
515 ; CHANNELSTREAM CONFIG
526 ; CHANNELSTREAM CONFIG
516 ; ####################
527 ; ####################
517
528
518 ; channelstream enables persistent connections and live notification
529 ; channelstream enables persistent connections and live notification
519 ; in the system. It's also used by the chat system
530 ; in the system. It's also used by the chat system
520
531
521 channelstream.enabled = false
532 channelstream.enabled = false
522
533
523 ; server address for channelstream server on the backend
534 ; server address for channelstream server on the backend
524 channelstream.server = 127.0.0.1:9800
535 channelstream.server = 127.0.0.1:9800
525
536
526 ; location of the channelstream server from outside world
537 ; location of the channelstream server from outside world
527 ; use ws:// for http or wss:// for https. This address needs to be handled
538 ; use ws:// for http or wss:// for https. This address needs to be handled
528 ; by external HTTP server such as Nginx or Apache
539 ; by external HTTP server such as Nginx or Apache
529 ; see Nginx/Apache configuration examples in our docs
540 ; see Nginx/Apache configuration examples in our docs
530 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
541 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
531 channelstream.secret = secret
542 channelstream.secret = secret
532 channelstream.history.location = %(here)s/channelstream_history
543 channelstream.history.location = %(here)s/channelstream_history
533
544
534 ; Internal application path that Javascript uses to connect into.
545 ; Internal application path that Javascript uses to connect into.
535 ; If you use proxy-prefix the prefix should be added before /_channelstream
546 ; If you use proxy-prefix the prefix should be added before /_channelstream
536 channelstream.proxy_path = /_channelstream
547 channelstream.proxy_path = /_channelstream
537
548
538
549
539 ; ##############################
550 ; ##############################
540 ; MAIN RHODECODE DATABASE CONFIG
551 ; MAIN RHODECODE DATABASE CONFIG
541 ; ##############################
552 ; ##############################
542
553
543 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
554 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
544 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
555 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
545 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode?charset=utf8
556 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode?charset=utf8
546 ; pymysql is an alternative driver for MySQL, use in case of problems with default one
557 ; pymysql is an alternative driver for MySQL, use in case of problems with default one
547 #sqlalchemy.db1.url = mysql+pymysql://root:qweqwe@localhost/rhodecode
558 #sqlalchemy.db1.url = mysql+pymysql://root:qweqwe@localhost/rhodecode
548
559
549 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
560 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
550
561
551 ; see sqlalchemy docs for other advanced settings
562 ; see sqlalchemy docs for other advanced settings
552 ; print the sql statements to output
563 ; print the sql statements to output
553 sqlalchemy.db1.echo = false
564 sqlalchemy.db1.echo = false
554
565
555 ; recycle the connections after this amount of seconds
566 ; recycle the connections after this amount of seconds
556 sqlalchemy.db1.pool_recycle = 3600
567 sqlalchemy.db1.pool_recycle = 3600
557 sqlalchemy.db1.convert_unicode = true
568 sqlalchemy.db1.convert_unicode = true
558
569
559 ; the number of connections to keep open inside the connection pool.
570 ; the number of connections to keep open inside the connection pool.
560 ; 0 indicates no limit
571 ; 0 indicates no limit
561 #sqlalchemy.db1.pool_size = 5
572 #sqlalchemy.db1.pool_size = 5
562
573
563 ; The number of connections to allow in connection pool "overflow", that is
574 ; The number of connections to allow in connection pool "overflow", that is
564 ; connections that can be opened above and beyond the pool_size setting,
575 ; connections that can be opened above and beyond the pool_size setting,
565 ; which defaults to five.
576 ; which defaults to five.
566 #sqlalchemy.db1.max_overflow = 10
577 #sqlalchemy.db1.max_overflow = 10
567
578
568 ; Connection check ping, used to detect broken database connections
579 ; Connection check ping, used to detect broken database connections
569 ; could be enabled to better handle cases if MySQL has gone away errors
580 ; could be enabled to better handle cases if MySQL has gone away errors
570 #sqlalchemy.db1.ping_connection = true
581 #sqlalchemy.db1.ping_connection = true
571
582
572 ; ##########
583 ; ##########
573 ; VCS CONFIG
584 ; VCS CONFIG
574 ; ##########
585 ; ##########
575 vcs.server.enable = true
586 vcs.server.enable = true
576 vcs.server = localhost:9900
587 vcs.server = localhost:9900
577
588
578 ; Web server connectivity protocol, responsible for web based VCS operations
589 ; Web server connectivity protocol, responsible for web based VCS operations
579 ; Available protocols are:
590 ; Available protocols are:
580 ; `http` - use http-rpc backend (default)
591 ; `http` - use http-rpc backend (default)
581 vcs.server.protocol = http
592 vcs.server.protocol = http
582
593
583 ; Push/Pull operations protocol, available options are:
594 ; Push/Pull operations protocol, available options are:
584 ; `http` - use http-rpc backend (default)
595 ; `http` - use http-rpc backend (default)
585 vcs.scm_app_implementation = http
596 vcs.scm_app_implementation = http
586
597
587 ; Push/Pull operations hooks protocol, available options are:
598 ; Push/Pull operations hooks protocol, available options are:
588 ; `http` - use http-rpc backend (default)
599 ; `http` - use http-rpc backend (default)
589 vcs.hooks.protocol = http
600 vcs.hooks.protocol = http
590
601
591 ; Host on which this instance is listening for hooks. If vcsserver is in other location
602 ; Host on which this instance is listening for hooks. If vcsserver is in other location
592 ; this should be adjusted.
603 ; this should be adjusted.
593 vcs.hooks.host = 127.0.0.1
604 vcs.hooks.host = 127.0.0.1
594
605
595 ; Start VCSServer with this instance as a subprocess, useful for development
606 ; Start VCSServer with this instance as a subprocess, useful for development
596 vcs.start_server = false
607 vcs.start_server = false
597
608
598 ; List of enabled VCS backends, available options are:
609 ; List of enabled VCS backends, available options are:
599 ; `hg` - mercurial
610 ; `hg` - mercurial
600 ; `git` - git
611 ; `git` - git
601 ; `svn` - subversion
612 ; `svn` - subversion
602 vcs.backends = hg, git, svn
613 vcs.backends = hg, git, svn
603
614
604 ; Wait this number of seconds before killing connection to the vcsserver
615 ; Wait this number of seconds before killing connection to the vcsserver
605 vcs.connection_timeout = 3600
616 vcs.connection_timeout = 3600
606
617
607 ; Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
618 ; Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
608 ; Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
619 ; Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
609 #vcs.svn.compatible_version = pre-1.8-compatible
620 #vcs.svn.compatible_version = pre-1.8-compatible
610
621
611
622
612 ; ####################################################
623 ; ####################################################
613 ; Subversion proxy support (mod_dav_svn)
624 ; Subversion proxy support (mod_dav_svn)
614 ; Maps RhodeCode repo groups into SVN paths for Apache
625 ; Maps RhodeCode repo groups into SVN paths for Apache
615 ; ####################################################
626 ; ####################################################
616
627
617 ; Enable or disable the config file generation.
628 ; Enable or disable the config file generation.
618 svn.proxy.generate_config = false
629 svn.proxy.generate_config = false
619
630
620 ; Generate config file with `SVNListParentPath` set to `On`.
631 ; Generate config file with `SVNListParentPath` set to `On`.
621 svn.proxy.list_parent_path = true
632 svn.proxy.list_parent_path = true
622
633
623 ; Set location and file name of generated config file.
634 ; Set location and file name of generated config file.
624 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
635 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
625
636
626 ; alternative mod_dav config template. This needs to be a valid mako template
637 ; alternative mod_dav config template. This needs to be a valid mako template
627 ; Example template can be found in the source code:
638 ; Example template can be found in the source code:
628 ; rhodecode/apps/svn_support/templates/mod-dav-svn.conf.mako
639 ; rhodecode/apps/svn_support/templates/mod-dav-svn.conf.mako
629 #svn.proxy.config_template = ~/.rccontrol/enterprise-1/custom_svn_conf.mako
640 #svn.proxy.config_template = ~/.rccontrol/enterprise-1/custom_svn_conf.mako
630
641
631 ; Used as a prefix to the `Location` block in the generated config file.
642 ; Used as a prefix to the `Location` block in the generated config file.
632 ; In most cases it should be set to `/`.
643 ; In most cases it should be set to `/`.
633 svn.proxy.location_root = /
644 svn.proxy.location_root = /
634
645
635 ; Command to reload the mod dav svn configuration on change.
646 ; Command to reload the mod dav svn configuration on change.
636 ; Example: `/etc/init.d/apache2 reload` or /home/USER/apache_reload.sh
647 ; Example: `/etc/init.d/apache2 reload` or /home/USER/apache_reload.sh
637 ; Make sure user who runs RhodeCode process is allowed to reload Apache
648 ; Make sure user who runs RhodeCode process is allowed to reload Apache
638 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
649 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
639
650
640 ; If the timeout expires before the reload command finishes, the command will
651 ; If the timeout expires before the reload command finishes, the command will
641 ; be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
652 ; be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
642 #svn.proxy.reload_timeout = 10
653 #svn.proxy.reload_timeout = 10
643
654
644 ; ####################
655 ; ####################
645 ; SSH Support Settings
656 ; SSH Support Settings
646 ; ####################
657 ; ####################
647
658
648 ; Defines if a custom authorized_keys file should be created and written on
659 ; Defines if a custom authorized_keys file should be created and written on
649 ; any change user ssh keys. Setting this to false also disables possibility
660 ; any change user ssh keys. Setting this to false also disables possibility
650 ; of adding SSH keys by users from web interface. Super admins can still
661 ; of adding SSH keys by users from web interface. Super admins can still
651 ; manage SSH Keys.
662 ; manage SSH Keys.
652 ssh.generate_authorized_keyfile = false
663 ssh.generate_authorized_keyfile = false
653
664
654 ; Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
665 ; Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
655 # ssh.authorized_keys_ssh_opts =
666 # ssh.authorized_keys_ssh_opts =
656
667
657 ; Path to the authorized_keys file where the generate entries are placed.
668 ; Path to the authorized_keys file where the generate entries are placed.
658 ; It is possible to have multiple key files specified in `sshd_config` e.g.
669 ; It is possible to have multiple key files specified in `sshd_config` e.g.
659 ; AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
670 ; AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
660 ssh.authorized_keys_file_path = ~/.ssh/authorized_keys_rhodecode
671 ssh.authorized_keys_file_path = ~/.ssh/authorized_keys_rhodecode
661
672
662 ; Command to execute the SSH wrapper. The binary is available in the
673 ; Command to execute the SSH wrapper. The binary is available in the
663 ; RhodeCode installation directory.
674 ; RhodeCode installation directory.
664 ; e.g ~/.rccontrol/community-1/profile/bin/rc-ssh-wrapper
675 ; e.g ~/.rccontrol/community-1/profile/bin/rc-ssh-wrapper
665 ssh.wrapper_cmd = ~/.rccontrol/community-1/rc-ssh-wrapper
676 ssh.wrapper_cmd = ~/.rccontrol/community-1/rc-ssh-wrapper
666
677
667 ; Allow shell when executing the ssh-wrapper command
678 ; Allow shell when executing the ssh-wrapper command
668 ssh.wrapper_cmd_allow_shell = false
679 ssh.wrapper_cmd_allow_shell = false
669
680
670 ; Enables logging, and detailed output send back to the client during SSH
681 ; Enables logging, and detailed output send back to the client during SSH
671 ; operations. Useful for debugging, shouldn't be used in production.
682 ; operations. Useful for debugging, shouldn't be used in production.
672 ssh.enable_debug_logging = true
683 ssh.enable_debug_logging = true
673
684
674 ; Paths to binary executable, by default they are the names, but we can
685 ; Paths to binary executable, by default they are the names, but we can
675 ; override them if we want to use a custom one
686 ; override them if we want to use a custom one
676 ssh.executable.hg = ~/.rccontrol/vcsserver-1/profile/bin/hg
687 ssh.executable.hg = ~/.rccontrol/vcsserver-1/profile/bin/hg
677 ssh.executable.git = ~/.rccontrol/vcsserver-1/profile/bin/git
688 ssh.executable.git = ~/.rccontrol/vcsserver-1/profile/bin/git
678 ssh.executable.svn = ~/.rccontrol/vcsserver-1/profile/bin/svnserve
689 ssh.executable.svn = ~/.rccontrol/vcsserver-1/profile/bin/svnserve
679
690
680 ; Enables SSH key generator web interface. Disabling this still allows users
691 ; Enables SSH key generator web interface. Disabling this still allows users
681 ; to add their own keys.
692 ; to add their own keys.
682 ssh.enable_ui_key_generator = true
693 ssh.enable_ui_key_generator = true
683
694
684
695
685 ; #################
696 ; #################
686 ; APPENLIGHT CONFIG
697 ; APPENLIGHT CONFIG
687 ; #################
698 ; #################
688
699
689 ; Appenlight is tailored to work with RhodeCode, see
700 ; Appenlight is tailored to work with RhodeCode, see
690 ; http://appenlight.rhodecode.com for details how to obtain an account
701 ; http://appenlight.rhodecode.com for details how to obtain an account
691
702
692 ; Appenlight integration enabled
703 ; Appenlight integration enabled
693 appenlight = false
704 appenlight = false
694
705
695 appenlight.server_url = https://api.appenlight.com
706 appenlight.server_url = https://api.appenlight.com
696 appenlight.api_key = YOUR_API_KEY
707 appenlight.api_key = YOUR_API_KEY
697 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
708 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
698
709
699 ; used for JS client
710 ; used for JS client
700 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
711 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
701
712
702 ; TWEAK AMOUNT OF INFO SENT HERE
713 ; TWEAK AMOUNT OF INFO SENT HERE
703
714
704 ; enables 404 error logging (default False)
715 ; enables 404 error logging (default False)
705 appenlight.report_404 = false
716 appenlight.report_404 = false
706
717
707 ; time in seconds after request is considered being slow (default 1)
718 ; time in seconds after request is considered being slow (default 1)
708 appenlight.slow_request_time = 1
719 appenlight.slow_request_time = 1
709
720
710 ; record slow requests in application
721 ; record slow requests in application
711 ; (needs to be enabled for slow datastore recording and time tracking)
722 ; (needs to be enabled for slow datastore recording and time tracking)
712 appenlight.slow_requests = true
723 appenlight.slow_requests = true
713
724
714 ; enable hooking to application loggers
725 ; enable hooking to application loggers
715 appenlight.logging = true
726 appenlight.logging = true
716
727
717 ; minimum log level for log capture
728 ; minimum log level for log capture
718 appenlight.logging.level = WARNING
729 appenlight.logging.level = WARNING
719
730
720 ; send logs only from erroneous/slow requests
731 ; send logs only from erroneous/slow requests
721 ; (saves API quota for intensive logging)
732 ; (saves API quota for intensive logging)
722 appenlight.logging_on_error = false
733 appenlight.logging_on_error = false
723
734
724 ; list of additional keywords that should be grabbed from environ object
735 ; list of additional keywords that should be grabbed from environ object
725 ; can be string with comma separated list of words in lowercase
736 ; can be string with comma separated list of words in lowercase
726 ; (by default client will always send following info:
737 ; (by default client will always send following info:
727 ; 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
738 ; 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
728 ; start with HTTP* this list be extended with additional keywords here
739 ; start with HTTP* this list be extended with additional keywords here
729 appenlight.environ_keys_whitelist =
740 appenlight.environ_keys_whitelist =
730
741
731 ; list of keywords that should be blanked from request object
742 ; list of keywords that should be blanked from request object
732 ; can be string with comma separated list of words in lowercase
743 ; can be string with comma separated list of words in lowercase
733 ; (by default client will always blank keys that contain following words
744 ; (by default client will always blank keys that contain following words
734 ; 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
745 ; 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
735 ; this list be extended with additional keywords set here
746 ; this list be extended with additional keywords set here
736 appenlight.request_keys_blacklist =
747 appenlight.request_keys_blacklist =
737
748
738 ; list of namespaces that should be ignores when gathering log entries
749 ; list of namespaces that should be ignores when gathering log entries
739 ; can be string with comma separated list of namespaces
750 ; can be string with comma separated list of namespaces
740 ; (by default the client ignores own entries: appenlight_client.client)
751 ; (by default the client ignores own entries: appenlight_client.client)
741 appenlight.log_namespace_blacklist =
752 appenlight.log_namespace_blacklist =
742
753
743 ; Dummy marker to add new entries after.
754 ; Dummy marker to add new entries after.
744 ; Add any custom entries below. Please don't remove this marker.
755 ; Add any custom entries below. Please don't remove this marker.
745 custom.conf = 1
756 custom.conf = 1
746
757
747
758
748 ; #####################
759 ; #####################
749 ; LOGGING CONFIGURATION
760 ; LOGGING CONFIGURATION
750 ; #####################
761 ; #####################
751 [loggers]
762 [loggers]
752 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
763 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
753
764
754 [handlers]
765 [handlers]
755 keys = console, console_sql
766 keys = console, console_sql
756
767
757 [formatters]
768 [formatters]
758 keys = generic, color_formatter, color_formatter_sql
769 keys = generic, color_formatter, color_formatter_sql
759
770
760 ; #######
771 ; #######
761 ; LOGGERS
772 ; LOGGERS
762 ; #######
773 ; #######
763 [logger_root]
774 [logger_root]
764 level = NOTSET
775 level = NOTSET
765 handlers = console
776 handlers = console
766
777
767 [logger_sqlalchemy]
778 [logger_sqlalchemy]
768 level = INFO
779 level = INFO
769 handlers = console_sql
780 handlers = console_sql
770 qualname = sqlalchemy.engine
781 qualname = sqlalchemy.engine
771 propagate = 0
782 propagate = 0
772
783
773 [logger_beaker]
784 [logger_beaker]
774 level = DEBUG
785 level = DEBUG
775 handlers =
786 handlers =
776 qualname = beaker.container
787 qualname = beaker.container
777 propagate = 1
788 propagate = 1
778
789
779 [logger_rhodecode]
790 [logger_rhodecode]
780 level = DEBUG
791 level = DEBUG
781 handlers =
792 handlers =
782 qualname = rhodecode
793 qualname = rhodecode
783 propagate = 1
794 propagate = 1
784
795
785 [logger_ssh_wrapper]
796 [logger_ssh_wrapper]
786 level = DEBUG
797 level = DEBUG
787 handlers =
798 handlers =
788 qualname = ssh_wrapper
799 qualname = ssh_wrapper
789 propagate = 1
800 propagate = 1
790
801
791 [logger_celery]
802 [logger_celery]
792 level = DEBUG
803 level = DEBUG
793 handlers =
804 handlers =
794 qualname = celery
805 qualname = celery
795
806
796
807
797 ; ########
808 ; ########
798 ; HANDLERS
809 ; HANDLERS
799 ; ########
810 ; ########
800
811
801 [handler_console]
812 [handler_console]
802 class = StreamHandler
813 class = StreamHandler
803 args = (sys.stderr, )
814 args = (sys.stderr, )
804 level = DEBUG
815 level = DEBUG
805 formatter = color_formatter
816 formatter = color_formatter
806
817
807 [handler_console_sql]
818 [handler_console_sql]
808 ; "level = DEBUG" logs SQL queries and results.
819 ; "level = DEBUG" logs SQL queries and results.
809 ; "level = INFO" logs SQL queries.
820 ; "level = INFO" logs SQL queries.
810 ; "level = WARN" logs neither. (Recommended for production systems.)
821 ; "level = WARN" logs neither. (Recommended for production systems.)
811 class = StreamHandler
822 class = StreamHandler
812 args = (sys.stderr, )
823 args = (sys.stderr, )
813 level = WARN
824 level = WARN
814 formatter = color_formatter_sql
825 formatter = color_formatter_sql
815
826
816 ; ##########
827 ; ##########
817 ; FORMATTERS
828 ; FORMATTERS
818 ; ##########
829 ; ##########
819
830
820 [formatter_generic]
831 [formatter_generic]
821 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
832 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
822 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
833 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
823 datefmt = %Y-%m-%d %H:%M:%S
834 datefmt = %Y-%m-%d %H:%M:%S
824
835
825 [formatter_color_formatter]
836 [formatter_color_formatter]
826 class = rhodecode.lib.logging_formatter.ColorFormatter
837 class = rhodecode.lib.logging_formatter.ColorFormatter
827 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
838 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
828 datefmt = %Y-%m-%d %H:%M:%S
839 datefmt = %Y-%m-%d %H:%M:%S
829
840
830 [formatter_color_formatter_sql]
841 [formatter_color_formatter_sql]
831 class = rhodecode.lib.logging_formatter.ColorFormatterSql
842 class = rhodecode.lib.logging_formatter.ColorFormatterSql
832 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
843 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
833 datefmt = %Y-%m-%d %H:%M:%S
844 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,784 +1,795 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 ; #########################################
3 ; #########################################
4 ; RHODECODE COMMUNITY EDITION CONFIGURATION
4 ; RHODECODE COMMUNITY EDITION CONFIGURATION
5 ; #########################################
5 ; #########################################
6
6
7 [DEFAULT]
7 [DEFAULT]
8 ; Debug flag sets all loggers to debug, and enables request tracking
8 ; Debug flag sets all loggers to debug, and enables request tracking
9 debug = false
9 debug = false
10
10
11 ; ########################################################################
11 ; ########################################################################
12 ; EMAIL CONFIGURATION
12 ; EMAIL CONFIGURATION
13 ; These settings will be used by the RhodeCode mailing system
13 ; These settings will be used by the RhodeCode mailing system
14 ; ########################################################################
14 ; ########################################################################
15
15
16 ; prefix all emails subjects with given prefix, helps filtering out emails
16 ; prefix all emails subjects with given prefix, helps filtering out emails
17 #email_prefix = [RhodeCode]
17 #email_prefix = [RhodeCode]
18
18
19 ; email FROM address all mails will be sent
19 ; email FROM address all mails will be sent
20 #app_email_from = rhodecode-noreply@localhost
20 #app_email_from = rhodecode-noreply@localhost
21
21
22 #smtp_server = mail.server.com
22 #smtp_server = mail.server.com
23 #smtp_username =
23 #smtp_username =
24 #smtp_password =
24 #smtp_password =
25 #smtp_port =
25 #smtp_port =
26 #smtp_use_tls = false
26 #smtp_use_tls = false
27 #smtp_use_ssl = true
27 #smtp_use_ssl = true
28
28
29 [server:main]
29 [server:main]
30 ; COMMON HOST/IP CONFIG
30 ; COMMON HOST/IP CONFIG
31 host = 127.0.0.1
31 host = 127.0.0.1
32 port = 5000
32 port = 5000
33
33
34
34
35 ; ###########################
35 ; ###########################
36 ; GUNICORN APPLICATION SERVER
36 ; GUNICORN APPLICATION SERVER
37 ; ###########################
37 ; ###########################
38
38
39 ; run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini
39 ; run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini
40
40
41 ; Module to use, this setting shouldn't be changed
41 ; Module to use, this setting shouldn't be changed
42 use = egg:gunicorn#main
42 use = egg:gunicorn#main
43
43
44 ; Sets the number of process workers. More workers means more concurrent connections
44 ; Sets the number of process workers. More workers means more concurrent connections
45 ; RhodeCode can handle at the same time. Each additional worker also it increases
45 ; RhodeCode can handle at the same time. Each additional worker also it increases
46 ; memory usage as each has it's own set of caches.
46 ; memory usage as each has it's own set of caches.
47 ; Recommended value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers, but no more
47 ; Recommended value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers, but no more
48 ; than 8-10 unless for really big deployments .e.g 700-1000 users.
48 ; than 8-10 unless for really big deployments .e.g 700-1000 users.
49 ; `instance_id = *` must be set in the [app:main] section below (which is the default)
49 ; `instance_id = *` must be set in the [app:main] section below (which is the default)
50 ; when using more than 1 worker.
50 ; when using more than 1 worker.
51 workers = 2
51 workers = 2
52
52
53 ; Gunicorn access log level
53 ; Gunicorn access log level
54 loglevel = info
54 loglevel = info
55
55
56 ; Process name visible in process list
56 ; Process name visible in process list
57 proc_name = rhodecode
57 proc_name = rhodecode
58
58
59 ; Type of worker class, one of `sync`, `gevent`
59 ; Type of worker class, one of `sync`, `gevent`
60 ; Recommended type is `gevent`
60 ; Recommended type is `gevent`
61 worker_class = gevent
61 worker_class = gevent
62
62
63 ; The maximum number of simultaneous clients per worker. Valid only for gevent
63 ; The maximum number of simultaneous clients per worker. Valid only for gevent
64 worker_connections = 10
64 worker_connections = 10
65
65
66 ; Max number of requests that worker will handle before being gracefully restarted.
66 ; Max number of requests that worker will handle before being gracefully restarted.
67 ; Prevents memory leaks, jitter adds variability so not all workers are restarted at once.
67 ; Prevents memory leaks, jitter adds variability so not all workers are restarted at once.
68 max_requests = 1000
68 max_requests = 1000
69 max_requests_jitter = 30
69 max_requests_jitter = 30
70
70
71 ; Amount of time a worker can spend with handling a request before it
71 ; Amount of time a worker can spend with handling a request before it
72 ; gets killed and restarted. By default set to 21600 (6hrs)
72 ; gets killed and restarted. By default set to 21600 (6hrs)
73 ; Examples: 1800 (30min), 3600 (1hr), 7200 (2hr), 43200 (12h)
73 ; Examples: 1800 (30min), 3600 (1hr), 7200 (2hr), 43200 (12h)
74 timeout = 21600
74 timeout = 21600
75
75
76 ; The maximum size of HTTP request line in bytes.
76 ; The maximum size of HTTP request line in bytes.
77 ; 0 for unlimited
77 ; 0 for unlimited
78 limit_request_line = 0
78 limit_request_line = 0
79
79
80 ; Limit the number of HTTP headers fields in a request.
80 ; Limit the number of HTTP headers fields in a request.
81 ; By default this value is 100 and can't be larger than 32768.
81 ; By default this value is 100 and can't be larger than 32768.
82 limit_request_fields = 32768
82 limit_request_fields = 32768
83
83
84 ; Limit the allowed size of an HTTP request header field.
84 ; Limit the allowed size of an HTTP request header field.
85 ; Value is a positive number or 0.
85 ; Value is a positive number or 0.
86 ; Setting it to 0 will allow unlimited header field sizes.
86 ; Setting it to 0 will allow unlimited header field sizes.
87 limit_request_field_size = 0
87 limit_request_field_size = 0
88
88
89 ; Timeout for graceful workers restart.
89 ; Timeout for graceful workers restart.
90 ; After receiving a restart signal, workers have this much time to finish
90 ; After receiving a restart signal, workers have this much time to finish
91 ; serving requests. Workers still alive after the timeout (starting from the
91 ; serving requests. Workers still alive after the timeout (starting from the
92 ; receipt of the restart signal) are force killed.
92 ; receipt of the restart signal) are force killed.
93 ; Examples: 1800 (30min), 3600 (1hr), 7200 (2hr), 43200 (12h)
93 ; Examples: 1800 (30min), 3600 (1hr), 7200 (2hr), 43200 (12h)
94 graceful_timeout = 3600
94 graceful_timeout = 3600
95
95
96 # The number of seconds to wait for requests on a Keep-Alive connection.
96 # The number of seconds to wait for requests on a Keep-Alive connection.
97 # Generally set in the 1-5 seconds range.
97 # Generally set in the 1-5 seconds range.
98 keepalive = 2
98 keepalive = 2
99
99
100 ; Maximum memory usage that each worker can use before it will receive a
100 ; Maximum memory usage that each worker can use before it will receive a
101 ; graceful restart signal 0 = memory monitoring is disabled
101 ; graceful restart signal 0 = memory monitoring is disabled
102 ; Examples: 268435456 (256MB), 536870912 (512MB)
102 ; Examples: 268435456 (256MB), 536870912 (512MB)
103 ; 1073741824 (1GB), 2147483648 (2GB), 4294967296 (4GB)
103 ; 1073741824 (1GB), 2147483648 (2GB), 4294967296 (4GB)
104 memory_max_usage = 0
104 memory_max_usage = 0
105
105
106 ; How often in seconds to check for memory usage for each gunicorn worker
106 ; How often in seconds to check for memory usage for each gunicorn worker
107 memory_usage_check_interval = 60
107 memory_usage_check_interval = 60
108
108
109 ; Threshold value for which we don't recycle worker if GarbageCollection
109 ; Threshold value for which we don't recycle worker if GarbageCollection
110 ; frees up enough resources. Before each restart we try to run GC on worker
110 ; frees up enough resources. Before each restart we try to run GC on worker
111 ; in case we get enough free memory after that, restart will not happen.
111 ; in case we get enough free memory after that, restart will not happen.
112 memory_usage_recovery_threshold = 0.8
112 memory_usage_recovery_threshold = 0.8
113
113
114
114
115 ; Prefix middleware for RhodeCode.
115 ; Prefix middleware for RhodeCode.
116 ; recommended when using proxy setup.
116 ; recommended when using proxy setup.
117 ; allows to set RhodeCode under a prefix in server.
117 ; allows to set RhodeCode under a prefix in server.
118 ; eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
118 ; eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
119 ; And set your prefix like: `prefix = /custom_prefix`
119 ; And set your prefix like: `prefix = /custom_prefix`
120 ; be sure to also set beaker.session.cookie_path = /custom_prefix if you need
120 ; be sure to also set beaker.session.cookie_path = /custom_prefix if you need
121 ; to make your cookies only work on prefix url
121 ; to make your cookies only work on prefix url
122 [filter:proxy-prefix]
122 [filter:proxy-prefix]
123 use = egg:PasteDeploy#prefix
123 use = egg:PasteDeploy#prefix
124 prefix = /
124 prefix = /
125
125
126 [app:main]
126 [app:main]
127 ; The %(here)s variable will be replaced with the absolute path of parent directory
127 ; The %(here)s variable will be replaced with the absolute path of parent directory
128 ; of this file
128 ; of this file
129 ; In addition ENVIRONMENT variables usage is possible, e.g
129 ; In addition ENVIRONMENT variables usage is possible, e.g
130 ; sqlalchemy.db1.url = {ENV_RC_DB_URL}
130 ; sqlalchemy.db1.url = {ENV_RC_DB_URL}
131
131
132 use = egg:rhodecode-enterprise-ce
132 use = egg:rhodecode-enterprise-ce
133
133
134 ; enable proxy prefix middleware, defined above
134 ; enable proxy prefix middleware, defined above
135 #filter-with = proxy-prefix
135 #filter-with = proxy-prefix
136
136
137 ; encryption key used to encrypt social plugin tokens,
137 ; encryption key used to encrypt social plugin tokens,
138 ; remote_urls with credentials etc, if not set it defaults to
138 ; remote_urls with credentials etc, if not set it defaults to
139 ; `beaker.session.secret`
139 ; `beaker.session.secret`
140 #rhodecode.encrypted_values.secret =
140 #rhodecode.encrypted_values.secret =
141
141
142 ; decryption strict mode (enabled by default). It controls if decryption raises
142 ; decryption strict mode (enabled by default). It controls if decryption raises
143 ; `SignatureVerificationError` in case of wrong key, or damaged encryption data.
143 ; `SignatureVerificationError` in case of wrong key, or damaged encryption data.
144 #rhodecode.encrypted_values.strict = false
144 #rhodecode.encrypted_values.strict = false
145
145
146 ; Pick algorithm for encryption. Either fernet (more secure) or aes (default)
146 ; Pick algorithm for encryption. Either fernet (more secure) or aes (default)
147 ; fernet is safer, and we strongly recommend switching to it.
147 ; fernet is safer, and we strongly recommend switching to it.
148 ; Due to backward compatibility aes is used as default.
148 ; Due to backward compatibility aes is used as default.
149 #rhodecode.encrypted_values.algorithm = fernet
149 #rhodecode.encrypted_values.algorithm = fernet
150
150
151 ; Return gzipped responses from RhodeCode (static files/application)
151 ; Return gzipped responses from RhodeCode (static files/application)
152 gzip_responses = false
152 gzip_responses = false
153
153
154 ; Auto-generate javascript routes file on startup
154 ; Auto-generate javascript routes file on startup
155 generate_js_files = false
155 generate_js_files = false
156
156
157 ; System global default language.
157 ; System global default language.
158 ; All available languages: en (default), be, de, es, fr, it, ja, pl, pt, ru, zh
158 ; All available languages: en (default), be, de, es, fr, it, ja, pl, pt, ru, zh
159 lang = en
159 lang = en
160
160
161 ; Perform a full repository scan and import on each server start.
161 ; Perform a full repository scan and import on each server start.
162 ; Settings this to true could lead to very long startup time.
162 ; Settings this to true could lead to very long startup time.
163 startup.import_repos = false
163 startup.import_repos = false
164
164
165 ; Uncomment and set this path to use archive download cache.
165 ; Uncomment and set this path to use archive download cache.
166 ; Once enabled, generated archives will be cached at this location
166 ; Once enabled, generated archives will be cached at this location
167 ; and served from the cache during subsequent requests for the same archive of
167 ; and served from the cache during subsequent requests for the same archive of
168 ; the repository.
168 ; the repository.
169 #archive_cache_dir = /tmp/tarballcache
169 #archive_cache_dir = /tmp/tarballcache
170
170
171 ; URL at which the application is running. This is used for Bootstrapping
171 ; URL at which the application is running. This is used for Bootstrapping
172 ; requests in context when no web request is available. Used in ishell, or
172 ; requests in context when no web request is available. Used in ishell, or
173 ; SSH calls. Set this for events to receive proper url for SSH calls.
173 ; SSH calls. Set this for events to receive proper url for SSH calls.
174 app.base_url = http://rhodecode.local
174 app.base_url = http://rhodecode.local
175
175
176 ; Unique application ID. Should be a random unique string for security.
176 ; Unique application ID. Should be a random unique string for security.
177 app_instance_uuid = rc-production
177 app_instance_uuid = rc-production
178
178
179 ; Cut off limit for large diffs (size in bytes). If overall diff size on
179 ; Cut off limit for large diffs (size in bytes). If overall diff size on
180 ; commit, or pull request exceeds this limit this diff will be displayed
180 ; commit, or pull request exceeds this limit this diff will be displayed
181 ; partially. E.g 512000 == 512Kb
181 ; partially. E.g 512000 == 512Kb
182 cut_off_limit_diff = 512000
182 cut_off_limit_diff = 512000
183
183
184 ; Cut off limit for large files inside diffs (size in bytes). Each individual
184 ; Cut off limit for large files inside diffs (size in bytes). Each individual
185 ; file inside diff which exceeds this limit will be displayed partially.
185 ; file inside diff which exceeds this limit will be displayed partially.
186 ; E.g 128000 == 128Kb
186 ; E.g 128000 == 128Kb
187 cut_off_limit_file = 128000
187 cut_off_limit_file = 128000
188
188
189 ; Use cached version of vcs repositories everywhere. Recommended to be `true`
189 ; Use cached version of vcs repositories everywhere. Recommended to be `true`
190 vcs_full_cache = true
190 vcs_full_cache = true
191
191
192 ; Force https in RhodeCode, fixes https redirects, assumes it's always https.
192 ; Force https in RhodeCode, fixes https redirects, assumes it's always https.
193 ; Normally this is controlled by proper flags sent from http server such as Nginx or Apache
193 ; Normally this is controlled by proper flags sent from http server such as Nginx or Apache
194 force_https = false
194 force_https = false
195
195
196 ; use Strict-Transport-Security headers
196 ; use Strict-Transport-Security headers
197 use_htsts = false
197 use_htsts = false
198
198
199 ; Set to true if your repos are exposed using the dumb protocol
199 ; Set to true if your repos are exposed using the dumb protocol
200 git_update_server_info = false
200 git_update_server_info = false
201
201
202 ; RSS/ATOM feed options
202 ; RSS/ATOM feed options
203 rss_cut_off_limit = 256000
203 rss_cut_off_limit = 256000
204 rss_items_per_page = 10
204 rss_items_per_page = 10
205 rss_include_diff = false
205 rss_include_diff = false
206
206
207 ; gist URL alias, used to create nicer urls for gist. This should be an
207 ; gist URL alias, used to create nicer urls for gist. This should be an
208 ; url that does rewrites to _admin/gists/{gistid}.
208 ; url that does rewrites to _admin/gists/{gistid}.
209 ; example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
209 ; example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
210 ; RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
210 ; RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
211 gist_alias_url =
211 gist_alias_url =
212
212
213 ; List of views (using glob pattern syntax) that AUTH TOKENS could be
213 ; List of views (using glob pattern syntax) that AUTH TOKENS could be
214 ; used for access.
214 ; used for access.
215 ; Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
215 ; Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
216 ; came from the the logged in user who own this authentication token.
216 ; came from the the logged in user who own this authentication token.
217 ; Additionally @TOKEN syntax can be used to bound the view to specific
217 ; Additionally @TOKEN syntax can be used to bound the view to specific
218 ; authentication token. Such view would be only accessible when used together
218 ; authentication token. Such view would be only accessible when used together
219 ; with this authentication token
219 ; with this authentication token
220 ; list of all views can be found under `/_admin/permissions/auth_token_access`
220 ; list of all views can be found under `/_admin/permissions/auth_token_access`
221 ; The list should be "," separated and on a single line.
221 ; The list should be "," separated and on a single line.
222 ; Most common views to enable:
222 ; Most common views to enable:
223
223
224 # RepoCommitsView:repo_commit_download
224 # RepoCommitsView:repo_commit_download
225 # RepoCommitsView:repo_commit_patch
225 # RepoCommitsView:repo_commit_patch
226 # RepoCommitsView:repo_commit_raw
226 # RepoCommitsView:repo_commit_raw
227 # RepoCommitsView:repo_commit_raw@TOKEN
227 # RepoCommitsView:repo_commit_raw@TOKEN
228 # RepoFilesView:repo_files_diff
228 # RepoFilesView:repo_files_diff
229 # RepoFilesView:repo_archivefile
229 # RepoFilesView:repo_archivefile
230 # RepoFilesView:repo_file_raw
230 # RepoFilesView:repo_file_raw
231 # GistView:*
231 # GistView:*
232 api_access_controllers_whitelist =
232 api_access_controllers_whitelist =
233
233
234 ; Default encoding used to convert from and to unicode
234 ; Default encoding used to convert from and to unicode
235 ; can be also a comma separated list of encoding in case of mixed encodings
235 ; can be also a comma separated list of encoding in case of mixed encodings
236 default_encoding = UTF-8
236 default_encoding = UTF-8
237
237
238 ; instance-id prefix
238 ; instance-id prefix
239 ; a prefix key for this instance used for cache invalidation when running
239 ; a prefix key for this instance used for cache invalidation when running
240 ; multiple instances of RhodeCode, make sure it's globally unique for
240 ; multiple instances of RhodeCode, make sure it's globally unique for
241 ; all running RhodeCode instances. Leave empty if you don't use it
241 ; all running RhodeCode instances. Leave empty if you don't use it
242 instance_id =
242 instance_id =
243
243
244 ; Fallback authentication plugin. Set this to a plugin ID to force the usage
244 ; Fallback authentication plugin. Set this to a plugin ID to force the usage
245 ; of an authentication plugin also if it is disabled by it's settings.
245 ; of an authentication plugin also if it is disabled by it's settings.
246 ; This could be useful if you are unable to log in to the system due to broken
246 ; This could be useful if you are unable to log in to the system due to broken
247 ; authentication settings. Then you can enable e.g. the internal RhodeCode auth
247 ; authentication settings. Then you can enable e.g. the internal RhodeCode auth
248 ; module to log in again and fix the settings.
248 ; module to log in again and fix the settings.
249 ; Available builtin plugin IDs (hash is part of the ID):
249 ; Available builtin plugin IDs (hash is part of the ID):
250 ; egg:rhodecode-enterprise-ce#rhodecode
250 ; egg:rhodecode-enterprise-ce#rhodecode
251 ; egg:rhodecode-enterprise-ce#pam
251 ; egg:rhodecode-enterprise-ce#pam
252 ; egg:rhodecode-enterprise-ce#ldap
252 ; egg:rhodecode-enterprise-ce#ldap
253 ; egg:rhodecode-enterprise-ce#jasig_cas
253 ; egg:rhodecode-enterprise-ce#jasig_cas
254 ; egg:rhodecode-enterprise-ce#headers
254 ; egg:rhodecode-enterprise-ce#headers
255 ; egg:rhodecode-enterprise-ce#crowd
255 ; egg:rhodecode-enterprise-ce#crowd
256
256
257 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
257 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
258
258
259 ; Flag to control loading of legacy plugins in py:/path format
259 ; Flag to control loading of legacy plugins in py:/path format
260 auth_plugin.import_legacy_plugins = true
260 auth_plugin.import_legacy_plugins = true
261
261
262 ; alternative return HTTP header for failed authentication. Default HTTP
262 ; alternative return HTTP header for failed authentication. Default HTTP
263 ; response is 401 HTTPUnauthorized. Currently HG clients have troubles with
263 ; response is 401 HTTPUnauthorized. Currently HG clients have troubles with
264 ; handling that causing a series of failed authentication calls.
264 ; handling that causing a series of failed authentication calls.
265 ; Set this variable to 403 to return HTTPForbidden, or any other HTTP code
265 ; Set this variable to 403 to return HTTPForbidden, or any other HTTP code
266 ; This will be served instead of default 401 on bad authentication
266 ; This will be served instead of default 401 on bad authentication
267 auth_ret_code =
267 auth_ret_code =
268
268
269 ; use special detection method when serving auth_ret_code, instead of serving
269 ; use special detection method when serving auth_ret_code, instead of serving
270 ; ret_code directly, use 401 initially (Which triggers credentials prompt)
270 ; ret_code directly, use 401 initially (Which triggers credentials prompt)
271 ; and then serve auth_ret_code to clients
271 ; and then serve auth_ret_code to clients
272 auth_ret_code_detection = false
272 auth_ret_code_detection = false
273
273
274 ; locking return code. When repository is locked return this HTTP code. 2XX
274 ; locking return code. When repository is locked return this HTTP code. 2XX
275 ; codes don't break the transactions while 4XX codes do
275 ; codes don't break the transactions while 4XX codes do
276 lock_ret_code = 423
276 lock_ret_code = 423
277
277
278 ; allows to change the repository location in settings page
278 ; allows to change the repository location in settings page
279 allow_repo_location_change = true
279 allow_repo_location_change = true
280
280
281 ; allows to setup custom hooks in settings page
281 ; allows to setup custom hooks in settings page
282 allow_custom_hooks_settings = true
282 allow_custom_hooks_settings = true
283
283
284 ; Generated license token required for EE edition license.
284 ; Generated license token required for EE edition license.
285 ; New generated token value can be found in Admin > settings > license page.
285 ; New generated token value can be found in Admin > settings > license page.
286 license_token =
286 license_token =
287
287
288 ; This flag hides sensitive information on the license page such as token, and license data
288 ; This flag hides sensitive information on the license page such as token, and license data
289 license.hide_license_info = false
289 license.hide_license_info = false
290
290
291 ; supervisor connection uri, for managing supervisor and logs.
291 ; supervisor connection uri, for managing supervisor and logs.
292 supervisor.uri =
292 supervisor.uri =
293
293
294 ; supervisord group name/id we only want this RC instance to handle
294 ; supervisord group name/id we only want this RC instance to handle
295 supervisor.group_id = prod
295 supervisor.group_id = prod
296
296
297 ; Display extended labs settings
297 ; Display extended labs settings
298 labs_settings_active = true
298 labs_settings_active = true
299
299
300 ; Custom exception store path, defaults to TMPDIR
300 ; Custom exception store path, defaults to TMPDIR
301 ; This is used to store exception from RhodeCode in shared directory
301 ; This is used to store exception from RhodeCode in shared directory
302 #exception_tracker.store_path =
302 #exception_tracker.store_path =
303
303
304 ; Send email with exception details when it happens
305 #exception_tracker.send_email = false
306
307 ; Comma separated list of recipients for exception emails,
308 ; e.g admin@rhodecode.com,devops@rhodecode.com
309 ; Can be left empty, then emails will be sent to ALL super-admins
310 #exception_tracker.send_email_recipients =
311
312 ; optional prefix to Add to email Subject
313 #exception_tracker.email_prefix = [RHODECODE ERROR]
314
304 ; File store configuration. This is used to store and serve uploaded files
315 ; File store configuration. This is used to store and serve uploaded files
305 file_store.enabled = true
316 file_store.enabled = true
306
317
307 ; Storage backend, available options are: local
318 ; Storage backend, available options are: local
308 file_store.backend = local
319 file_store.backend = local
309
320
310 ; path to store the uploaded binaries
321 ; path to store the uploaded binaries
311 file_store.storage_path = %(here)s/data/file_store
322 file_store.storage_path = %(here)s/data/file_store
312
323
313
324
314 ; #############
325 ; #############
315 ; CELERY CONFIG
326 ; CELERY CONFIG
316 ; #############
327 ; #############
317
328
318 ; manually run celery: /path/to/celery worker -E --beat --app rhodecode.lib.celerylib.loader --scheduler rhodecode.lib.celerylib.scheduler.RcScheduler --loglevel DEBUG --ini /path/to/rhodecode.ini
329 ; manually run celery: /path/to/celery worker -E --beat --app rhodecode.lib.celerylib.loader --scheduler rhodecode.lib.celerylib.scheduler.RcScheduler --loglevel DEBUG --ini /path/to/rhodecode.ini
319
330
320 use_celery = false
331 use_celery = false
321
332
322 ; connection url to the message broker (default redis)
333 ; connection url to the message broker (default redis)
323 celery.broker_url = redis://localhost:6379/8
334 celery.broker_url = redis://localhost:6379/8
324
335
325 ; rabbitmq example
336 ; rabbitmq example
326 #celery.broker_url = amqp://rabbitmq:qweqwe@localhost:5672/rabbitmqhost
337 #celery.broker_url = amqp://rabbitmq:qweqwe@localhost:5672/rabbitmqhost
327
338
328 ; maximum tasks to execute before worker restart
339 ; maximum tasks to execute before worker restart
329 celery.max_tasks_per_child = 100
340 celery.max_tasks_per_child = 100
330
341
331 ; tasks will never be sent to the queue, but executed locally instead.
342 ; tasks will never be sent to the queue, but executed locally instead.
332 celery.task_always_eager = false
343 celery.task_always_eager = false
333
344
334 ; #############
345 ; #############
335 ; DOGPILE CACHE
346 ; DOGPILE CACHE
336 ; #############
347 ; #############
337
348
338 ; Default cache dir for caches. Putting this into a ramdisk can boost performance.
349 ; Default cache dir for caches. Putting this into a ramdisk can boost performance.
339 ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space
350 ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space
340 cache_dir = %(here)s/data
351 cache_dir = %(here)s/data
341
352
342 ; *********************************************
353 ; *********************************************
343 ; `sql_cache_short` cache for heavy SQL queries
354 ; `sql_cache_short` cache for heavy SQL queries
344 ; Only supported backend is `memory_lru`
355 ; Only supported backend is `memory_lru`
345 ; *********************************************
356 ; *********************************************
346 rc_cache.sql_cache_short.backend = dogpile.cache.rc.memory_lru
357 rc_cache.sql_cache_short.backend = dogpile.cache.rc.memory_lru
347 rc_cache.sql_cache_short.expiration_time = 30
358 rc_cache.sql_cache_short.expiration_time = 30
348
359
349
360
350 ; *****************************************************
361 ; *****************************************************
351 ; `cache_repo_longterm` cache for repo object instances
362 ; `cache_repo_longterm` cache for repo object instances
352 ; Only supported backend is `memory_lru`
363 ; Only supported backend is `memory_lru`
353 ; *****************************************************
364 ; *****************************************************
354 rc_cache.cache_repo_longterm.backend = dogpile.cache.rc.memory_lru
365 rc_cache.cache_repo_longterm.backend = dogpile.cache.rc.memory_lru
355 ; by default we use 30 Days, cache is still invalidated on push
366 ; by default we use 30 Days, cache is still invalidated on push
356 rc_cache.cache_repo_longterm.expiration_time = 2592000
367 rc_cache.cache_repo_longterm.expiration_time = 2592000
357 ; max items in LRU cache, set to smaller number to save memory, and expire last used caches
368 ; max items in LRU cache, set to smaller number to save memory, and expire last used caches
358 rc_cache.cache_repo_longterm.max_size = 10000
369 rc_cache.cache_repo_longterm.max_size = 10000
359
370
360
371
361 ; *************************************************
372 ; *************************************************
362 ; `cache_perms` cache for permission tree, auth TTL
373 ; `cache_perms` cache for permission tree, auth TTL
363 ; *************************************************
374 ; *************************************************
364 rc_cache.cache_perms.backend = dogpile.cache.rc.file_namespace
375 rc_cache.cache_perms.backend = dogpile.cache.rc.file_namespace
365 rc_cache.cache_perms.expiration_time = 300
376 rc_cache.cache_perms.expiration_time = 300
366 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
377 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
367 #rc_cache.cache_perms.arguments.filename = /tmp/cache_perms.db
378 #rc_cache.cache_perms.arguments.filename = /tmp/cache_perms.db
368
379
369 ; alternative `cache_perms` redis backend with distributed lock
380 ; alternative `cache_perms` redis backend with distributed lock
370 #rc_cache.cache_perms.backend = dogpile.cache.rc.redis
381 #rc_cache.cache_perms.backend = dogpile.cache.rc.redis
371 #rc_cache.cache_perms.expiration_time = 300
382 #rc_cache.cache_perms.expiration_time = 300
372
383
373 ; redis_expiration_time needs to be greater then expiration_time
384 ; redis_expiration_time needs to be greater then expiration_time
374 #rc_cache.cache_perms.arguments.redis_expiration_time = 7200
385 #rc_cache.cache_perms.arguments.redis_expiration_time = 7200
375
386
376 #rc_cache.cache_perms.arguments.host = localhost
387 #rc_cache.cache_perms.arguments.host = localhost
377 #rc_cache.cache_perms.arguments.port = 6379
388 #rc_cache.cache_perms.arguments.port = 6379
378 #rc_cache.cache_perms.arguments.db = 0
389 #rc_cache.cache_perms.arguments.db = 0
379 #rc_cache.cache_perms.arguments.socket_timeout = 30
390 #rc_cache.cache_perms.arguments.socket_timeout = 30
380 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
391 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
381 #rc_cache.cache_perms.arguments.distributed_lock = true
392 #rc_cache.cache_perms.arguments.distributed_lock = true
382
393
383
394
384 ; ***************************************************
395 ; ***************************************************
385 ; `cache_repo` cache for file tree, Readme, RSS FEEDS
396 ; `cache_repo` cache for file tree, Readme, RSS FEEDS
386 ; ***************************************************
397 ; ***************************************************
387 rc_cache.cache_repo.backend = dogpile.cache.rc.file_namespace
398 rc_cache.cache_repo.backend = dogpile.cache.rc.file_namespace
388 rc_cache.cache_repo.expiration_time = 2592000
399 rc_cache.cache_repo.expiration_time = 2592000
389 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
400 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
390 #rc_cache.cache_repo.arguments.filename = /tmp/cache_repo.db
401 #rc_cache.cache_repo.arguments.filename = /tmp/cache_repo.db
391
402
392 ; alternative `cache_repo` redis backend with distributed lock
403 ; alternative `cache_repo` redis backend with distributed lock
393 #rc_cache.cache_repo.backend = dogpile.cache.rc.redis
404 #rc_cache.cache_repo.backend = dogpile.cache.rc.redis
394 #rc_cache.cache_repo.expiration_time = 2592000
405 #rc_cache.cache_repo.expiration_time = 2592000
395
406
396 ; redis_expiration_time needs to be greater then expiration_time
407 ; redis_expiration_time needs to be greater then expiration_time
397 #rc_cache.cache_repo.arguments.redis_expiration_time = 2678400
408 #rc_cache.cache_repo.arguments.redis_expiration_time = 2678400
398
409
399 #rc_cache.cache_repo.arguments.host = localhost
410 #rc_cache.cache_repo.arguments.host = localhost
400 #rc_cache.cache_repo.arguments.port = 6379
411 #rc_cache.cache_repo.arguments.port = 6379
401 #rc_cache.cache_repo.arguments.db = 1
412 #rc_cache.cache_repo.arguments.db = 1
402 #rc_cache.cache_repo.arguments.socket_timeout = 30
413 #rc_cache.cache_repo.arguments.socket_timeout = 30
403 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
414 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
404 #rc_cache.cache_repo.arguments.distributed_lock = true
415 #rc_cache.cache_repo.arguments.distributed_lock = true
405
416
406
417
407 ; ##############
418 ; ##############
408 ; BEAKER SESSION
419 ; BEAKER SESSION
409 ; ##############
420 ; ##############
410
421
411 ; beaker.session.type is type of storage options for the logged users sessions. Current allowed
422 ; beaker.session.type is type of storage options for the logged users sessions. Current allowed
412 ; types are file, ext:redis, ext:database, ext:memcached, and memory (default if not specified).
423 ; types are file, ext:redis, ext:database, ext:memcached, and memory (default if not specified).
413 ; Fastest ones are Redis and ext:database
424 ; Fastest ones are Redis and ext:database
414 beaker.session.type = file
425 beaker.session.type = file
415 beaker.session.data_dir = %(here)s/data/sessions
426 beaker.session.data_dir = %(here)s/data/sessions
416
427
417 ; Redis based sessions
428 ; Redis based sessions
418 #beaker.session.type = ext:redis
429 #beaker.session.type = ext:redis
419 #beaker.session.url = redis://127.0.0.1:6379/2
430 #beaker.session.url = redis://127.0.0.1:6379/2
420
431
421 ; DB based session, fast, and allows easy management over logged in users
432 ; DB based session, fast, and allows easy management over logged in users
422 #beaker.session.type = ext:database
433 #beaker.session.type = ext:database
423 #beaker.session.table_name = db_session
434 #beaker.session.table_name = db_session
424 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
435 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
425 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
436 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
426 #beaker.session.sa.pool_recycle = 3600
437 #beaker.session.sa.pool_recycle = 3600
427 #beaker.session.sa.echo = false
438 #beaker.session.sa.echo = false
428
439
429 beaker.session.key = rhodecode
440 beaker.session.key = rhodecode
430 beaker.session.secret = production-rc-uytcxaz
441 beaker.session.secret = production-rc-uytcxaz
431 beaker.session.lock_dir = %(here)s/data/sessions/lock
442 beaker.session.lock_dir = %(here)s/data/sessions/lock
432
443
433 ; Secure encrypted cookie. Requires AES and AES python libraries
444 ; Secure encrypted cookie. Requires AES and AES python libraries
434 ; you must disable beaker.session.secret to use this
445 ; you must disable beaker.session.secret to use this
435 #beaker.session.encrypt_key = key_for_encryption
446 #beaker.session.encrypt_key = key_for_encryption
436 #beaker.session.validate_key = validation_key
447 #beaker.session.validate_key = validation_key
437
448
438 ; Sets session as invalid (also logging out user) if it haven not been
449 ; Sets session as invalid (also logging out user) if it haven not been
439 ; accessed for given amount of time in seconds
450 ; accessed for given amount of time in seconds
440 beaker.session.timeout = 2592000
451 beaker.session.timeout = 2592000
441 beaker.session.httponly = true
452 beaker.session.httponly = true
442
453
443 ; Path to use for the cookie. Set to prefix if you use prefix middleware
454 ; Path to use for the cookie. Set to prefix if you use prefix middleware
444 #beaker.session.cookie_path = /custom_prefix
455 #beaker.session.cookie_path = /custom_prefix
445
456
446 ; Set https secure cookie
457 ; Set https secure cookie
447 beaker.session.secure = false
458 beaker.session.secure = false
448
459
449 ; default cookie expiration time in seconds, set to `true` to set expire
460 ; default cookie expiration time in seconds, set to `true` to set expire
450 ; at browser close
461 ; at browser close
451 #beaker.session.cookie_expires = 3600
462 #beaker.session.cookie_expires = 3600
452
463
453 ; #############################
464 ; #############################
454 ; SEARCH INDEXING CONFIGURATION
465 ; SEARCH INDEXING CONFIGURATION
455 ; #############################
466 ; #############################
456
467
457 ; Full text search indexer is available in rhodecode-tools under
468 ; Full text search indexer is available in rhodecode-tools under
458 ; `rhodecode-tools index` command
469 ; `rhodecode-tools index` command
459
470
460 ; WHOOSH Backend, doesn't require additional services to run
471 ; WHOOSH Backend, doesn't require additional services to run
461 ; it works good with few dozen repos
472 ; it works good with few dozen repos
462 search.module = rhodecode.lib.index.whoosh
473 search.module = rhodecode.lib.index.whoosh
463 search.location = %(here)s/data/index
474 search.location = %(here)s/data/index
464
475
465 ; ####################
476 ; ####################
466 ; CHANNELSTREAM CONFIG
477 ; CHANNELSTREAM CONFIG
467 ; ####################
478 ; ####################
468
479
469 ; channelstream enables persistent connections and live notification
480 ; channelstream enables persistent connections and live notification
470 ; in the system. It's also used by the chat system
481 ; in the system. It's also used by the chat system
471
482
472 channelstream.enabled = false
483 channelstream.enabled = false
473
484
474 ; server address for channelstream server on the backend
485 ; server address for channelstream server on the backend
475 channelstream.server = 127.0.0.1:9800
486 channelstream.server = 127.0.0.1:9800
476
487
477 ; location of the channelstream server from outside world
488 ; location of the channelstream server from outside world
478 ; use ws:// for http or wss:// for https. This address needs to be handled
489 ; use ws:// for http or wss:// for https. This address needs to be handled
479 ; by external HTTP server such as Nginx or Apache
490 ; by external HTTP server such as Nginx or Apache
480 ; see Nginx/Apache configuration examples in our docs
491 ; see Nginx/Apache configuration examples in our docs
481 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
492 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
482 channelstream.secret = secret
493 channelstream.secret = secret
483 channelstream.history.location = %(here)s/channelstream_history
494 channelstream.history.location = %(here)s/channelstream_history
484
495
485 ; Internal application path that Javascript uses to connect into.
496 ; Internal application path that Javascript uses to connect into.
486 ; If you use proxy-prefix the prefix should be added before /_channelstream
497 ; If you use proxy-prefix the prefix should be added before /_channelstream
487 channelstream.proxy_path = /_channelstream
498 channelstream.proxy_path = /_channelstream
488
499
489
500
490 ; ##############################
501 ; ##############################
491 ; MAIN RHODECODE DATABASE CONFIG
502 ; MAIN RHODECODE DATABASE CONFIG
492 ; ##############################
503 ; ##############################
493
504
494 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
505 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
495 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
506 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
496 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode?charset=utf8
507 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode?charset=utf8
497 ; pymysql is an alternative driver for MySQL, use in case of problems with default one
508 ; pymysql is an alternative driver for MySQL, use in case of problems with default one
498 #sqlalchemy.db1.url = mysql+pymysql://root:qweqwe@localhost/rhodecode
509 #sqlalchemy.db1.url = mysql+pymysql://root:qweqwe@localhost/rhodecode
499
510
500 sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
511 sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
501
512
502 ; see sqlalchemy docs for other advanced settings
513 ; see sqlalchemy docs for other advanced settings
503 ; print the sql statements to output
514 ; print the sql statements to output
504 sqlalchemy.db1.echo = false
515 sqlalchemy.db1.echo = false
505
516
506 ; recycle the connections after this amount of seconds
517 ; recycle the connections after this amount of seconds
507 sqlalchemy.db1.pool_recycle = 3600
518 sqlalchemy.db1.pool_recycle = 3600
508 sqlalchemy.db1.convert_unicode = true
519 sqlalchemy.db1.convert_unicode = true
509
520
510 ; the number of connections to keep open inside the connection pool.
521 ; the number of connections to keep open inside the connection pool.
511 ; 0 indicates no limit
522 ; 0 indicates no limit
512 #sqlalchemy.db1.pool_size = 5
523 #sqlalchemy.db1.pool_size = 5
513
524
514 ; The number of connections to allow in connection pool "overflow", that is
525 ; The number of connections to allow in connection pool "overflow", that is
515 ; connections that can be opened above and beyond the pool_size setting,
526 ; connections that can be opened above and beyond the pool_size setting,
516 ; which defaults to five.
527 ; which defaults to five.
517 #sqlalchemy.db1.max_overflow = 10
528 #sqlalchemy.db1.max_overflow = 10
518
529
519 ; Connection check ping, used to detect broken database connections
530 ; Connection check ping, used to detect broken database connections
520 ; could be enabled to better handle cases if MySQL has gone away errors
531 ; could be enabled to better handle cases if MySQL has gone away errors
521 #sqlalchemy.db1.ping_connection = true
532 #sqlalchemy.db1.ping_connection = true
522
533
523 ; ##########
534 ; ##########
524 ; VCS CONFIG
535 ; VCS CONFIG
525 ; ##########
536 ; ##########
526 vcs.server.enable = true
537 vcs.server.enable = true
527 vcs.server = localhost:9900
538 vcs.server = localhost:9900
528
539
529 ; Web server connectivity protocol, responsible for web based VCS operations
540 ; Web server connectivity protocol, responsible for web based VCS operations
530 ; Available protocols are:
541 ; Available protocols are:
531 ; `http` - use http-rpc backend (default)
542 ; `http` - use http-rpc backend (default)
532 vcs.server.protocol = http
543 vcs.server.protocol = http
533
544
534 ; Push/Pull operations protocol, available options are:
545 ; Push/Pull operations protocol, available options are:
535 ; `http` - use http-rpc backend (default)
546 ; `http` - use http-rpc backend (default)
536 vcs.scm_app_implementation = http
547 vcs.scm_app_implementation = http
537
548
538 ; Push/Pull operations hooks protocol, available options are:
549 ; Push/Pull operations hooks protocol, available options are:
539 ; `http` - use http-rpc backend (default)
550 ; `http` - use http-rpc backend (default)
540 vcs.hooks.protocol = http
551 vcs.hooks.protocol = http
541
552
542 ; Host on which this instance is listening for hooks. If vcsserver is in other location
553 ; Host on which this instance is listening for hooks. If vcsserver is in other location
543 ; this should be adjusted.
554 ; this should be adjusted.
544 vcs.hooks.host = 127.0.0.1
555 vcs.hooks.host = 127.0.0.1
545
556
546 ; Start VCSServer with this instance as a subprocess, useful for development
557 ; Start VCSServer with this instance as a subprocess, useful for development
547 vcs.start_server = false
558 vcs.start_server = false
548
559
549 ; List of enabled VCS backends, available options are:
560 ; List of enabled VCS backends, available options are:
550 ; `hg` - mercurial
561 ; `hg` - mercurial
551 ; `git` - git
562 ; `git` - git
552 ; `svn` - subversion
563 ; `svn` - subversion
553 vcs.backends = hg, git, svn
564 vcs.backends = hg, git, svn
554
565
555 ; Wait this number of seconds before killing connection to the vcsserver
566 ; Wait this number of seconds before killing connection to the vcsserver
556 vcs.connection_timeout = 3600
567 vcs.connection_timeout = 3600
557
568
558 ; Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
569 ; Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
559 ; Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
570 ; Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
560 #vcs.svn.compatible_version = pre-1.8-compatible
571 #vcs.svn.compatible_version = pre-1.8-compatible
561
572
562
573
563 ; ####################################################
574 ; ####################################################
564 ; Subversion proxy support (mod_dav_svn)
575 ; Subversion proxy support (mod_dav_svn)
565 ; Maps RhodeCode repo groups into SVN paths for Apache
576 ; Maps RhodeCode repo groups into SVN paths for Apache
566 ; ####################################################
577 ; ####################################################
567
578
568 ; Enable or disable the config file generation.
579 ; Enable or disable the config file generation.
569 svn.proxy.generate_config = false
580 svn.proxy.generate_config = false
570
581
571 ; Generate config file with `SVNListParentPath` set to `On`.
582 ; Generate config file with `SVNListParentPath` set to `On`.
572 svn.proxy.list_parent_path = true
583 svn.proxy.list_parent_path = true
573
584
574 ; Set location and file name of generated config file.
585 ; Set location and file name of generated config file.
575 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
586 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
576
587
577 ; alternative mod_dav config template. This needs to be a valid mako template
588 ; alternative mod_dav config template. This needs to be a valid mako template
578 ; Example template can be found in the source code:
589 ; Example template can be found in the source code:
579 ; rhodecode/apps/svn_support/templates/mod-dav-svn.conf.mako
590 ; rhodecode/apps/svn_support/templates/mod-dav-svn.conf.mako
580 #svn.proxy.config_template = ~/.rccontrol/enterprise-1/custom_svn_conf.mako
591 #svn.proxy.config_template = ~/.rccontrol/enterprise-1/custom_svn_conf.mako
581
592
582 ; Used as a prefix to the `Location` block in the generated config file.
593 ; Used as a prefix to the `Location` block in the generated config file.
583 ; In most cases it should be set to `/`.
594 ; In most cases it should be set to `/`.
584 svn.proxy.location_root = /
595 svn.proxy.location_root = /
585
596
586 ; Command to reload the mod dav svn configuration on change.
597 ; Command to reload the mod dav svn configuration on change.
587 ; Example: `/etc/init.d/apache2 reload` or /home/USER/apache_reload.sh
598 ; Example: `/etc/init.d/apache2 reload` or /home/USER/apache_reload.sh
588 ; Make sure user who runs RhodeCode process is allowed to reload Apache
599 ; Make sure user who runs RhodeCode process is allowed to reload Apache
589 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
600 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
590
601
591 ; If the timeout expires before the reload command finishes, the command will
602 ; If the timeout expires before the reload command finishes, the command will
592 ; be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
603 ; be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
593 #svn.proxy.reload_timeout = 10
604 #svn.proxy.reload_timeout = 10
594
605
595 ; ####################
606 ; ####################
596 ; SSH Support Settings
607 ; SSH Support Settings
597 ; ####################
608 ; ####################
598
609
599 ; Defines if a custom authorized_keys file should be created and written on
610 ; Defines if a custom authorized_keys file should be created and written on
600 ; any change user ssh keys. Setting this to false also disables possibility
611 ; any change user ssh keys. Setting this to false also disables possibility
601 ; of adding SSH keys by users from web interface. Super admins can still
612 ; of adding SSH keys by users from web interface. Super admins can still
602 ; manage SSH Keys.
613 ; manage SSH Keys.
603 ssh.generate_authorized_keyfile = false
614 ssh.generate_authorized_keyfile = false
604
615
605 ; Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
616 ; Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
606 # ssh.authorized_keys_ssh_opts =
617 # ssh.authorized_keys_ssh_opts =
607
618
608 ; Path to the authorized_keys file where the generate entries are placed.
619 ; Path to the authorized_keys file where the generate entries are placed.
609 ; It is possible to have multiple key files specified in `sshd_config` e.g.
620 ; It is possible to have multiple key files specified in `sshd_config` e.g.
610 ; AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
621 ; AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
611 ssh.authorized_keys_file_path = ~/.ssh/authorized_keys_rhodecode
622 ssh.authorized_keys_file_path = ~/.ssh/authorized_keys_rhodecode
612
623
613 ; Command to execute the SSH wrapper. The binary is available in the
624 ; Command to execute the SSH wrapper. The binary is available in the
614 ; RhodeCode installation directory.
625 ; RhodeCode installation directory.
615 ; e.g ~/.rccontrol/community-1/profile/bin/rc-ssh-wrapper
626 ; e.g ~/.rccontrol/community-1/profile/bin/rc-ssh-wrapper
616 ssh.wrapper_cmd = ~/.rccontrol/community-1/rc-ssh-wrapper
627 ssh.wrapper_cmd = ~/.rccontrol/community-1/rc-ssh-wrapper
617
628
618 ; Allow shell when executing the ssh-wrapper command
629 ; Allow shell when executing the ssh-wrapper command
619 ssh.wrapper_cmd_allow_shell = false
630 ssh.wrapper_cmd_allow_shell = false
620
631
621 ; Enables logging, and detailed output send back to the client during SSH
632 ; Enables logging, and detailed output send back to the client during SSH
622 ; operations. Useful for debugging, shouldn't be used in production.
633 ; operations. Useful for debugging, shouldn't be used in production.
623 ssh.enable_debug_logging = false
634 ssh.enable_debug_logging = false
624
635
625 ; Paths to binary executable, by default they are the names, but we can
636 ; Paths to binary executable, by default they are the names, but we can
626 ; override them if we want to use a custom one
637 ; override them if we want to use a custom one
627 ssh.executable.hg = ~/.rccontrol/vcsserver-1/profile/bin/hg
638 ssh.executable.hg = ~/.rccontrol/vcsserver-1/profile/bin/hg
628 ssh.executable.git = ~/.rccontrol/vcsserver-1/profile/bin/git
639 ssh.executable.git = ~/.rccontrol/vcsserver-1/profile/bin/git
629 ssh.executable.svn = ~/.rccontrol/vcsserver-1/profile/bin/svnserve
640 ssh.executable.svn = ~/.rccontrol/vcsserver-1/profile/bin/svnserve
630
641
631 ; Enables SSH key generator web interface. Disabling this still allows users
642 ; Enables SSH key generator web interface. Disabling this still allows users
632 ; to add their own keys.
643 ; to add their own keys.
633 ssh.enable_ui_key_generator = true
644 ssh.enable_ui_key_generator = true
634
645
635
646
636 ; #################
647 ; #################
637 ; APPENLIGHT CONFIG
648 ; APPENLIGHT CONFIG
638 ; #################
649 ; #################
639
650
640 ; Appenlight is tailored to work with RhodeCode, see
651 ; Appenlight is tailored to work with RhodeCode, see
641 ; http://appenlight.rhodecode.com for details how to obtain an account
652 ; http://appenlight.rhodecode.com for details how to obtain an account
642
653
643 ; Appenlight integration enabled
654 ; Appenlight integration enabled
644 appenlight = false
655 appenlight = false
645
656
646 appenlight.server_url = https://api.appenlight.com
657 appenlight.server_url = https://api.appenlight.com
647 appenlight.api_key = YOUR_API_KEY
658 appenlight.api_key = YOUR_API_KEY
648 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
659 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
649
660
650 ; used for JS client
661 ; used for JS client
651 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
662 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
652
663
653 ; TWEAK AMOUNT OF INFO SENT HERE
664 ; TWEAK AMOUNT OF INFO SENT HERE
654
665
655 ; enables 404 error logging (default False)
666 ; enables 404 error logging (default False)
656 appenlight.report_404 = false
667 appenlight.report_404 = false
657
668
658 ; time in seconds after request is considered being slow (default 1)
669 ; time in seconds after request is considered being slow (default 1)
659 appenlight.slow_request_time = 1
670 appenlight.slow_request_time = 1
660
671
661 ; record slow requests in application
672 ; record slow requests in application
662 ; (needs to be enabled for slow datastore recording and time tracking)
673 ; (needs to be enabled for slow datastore recording and time tracking)
663 appenlight.slow_requests = true
674 appenlight.slow_requests = true
664
675
665 ; enable hooking to application loggers
676 ; enable hooking to application loggers
666 appenlight.logging = true
677 appenlight.logging = true
667
678
668 ; minimum log level for log capture
679 ; minimum log level for log capture
669 appenlight.logging.level = WARNING
680 appenlight.logging.level = WARNING
670
681
671 ; send logs only from erroneous/slow requests
682 ; send logs only from erroneous/slow requests
672 ; (saves API quota for intensive logging)
683 ; (saves API quota for intensive logging)
673 appenlight.logging_on_error = false
684 appenlight.logging_on_error = false
674
685
675 ; list of additional keywords that should be grabbed from environ object
686 ; list of additional keywords that should be grabbed from environ object
676 ; can be string with comma separated list of words in lowercase
687 ; can be string with comma separated list of words in lowercase
677 ; (by default client will always send following info:
688 ; (by default client will always send following info:
678 ; 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
689 ; 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
679 ; start with HTTP* this list be extended with additional keywords here
690 ; start with HTTP* this list be extended with additional keywords here
680 appenlight.environ_keys_whitelist =
691 appenlight.environ_keys_whitelist =
681
692
682 ; list of keywords that should be blanked from request object
693 ; list of keywords that should be blanked from request object
683 ; can be string with comma separated list of words in lowercase
694 ; can be string with comma separated list of words in lowercase
684 ; (by default client will always blank keys that contain following words
695 ; (by default client will always blank keys that contain following words
685 ; 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
696 ; 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
686 ; this list be extended with additional keywords set here
697 ; this list be extended with additional keywords set here
687 appenlight.request_keys_blacklist =
698 appenlight.request_keys_blacklist =
688
699
689 ; list of namespaces that should be ignores when gathering log entries
700 ; list of namespaces that should be ignores when gathering log entries
690 ; can be string with comma separated list of namespaces
701 ; can be string with comma separated list of namespaces
691 ; (by default the client ignores own entries: appenlight_client.client)
702 ; (by default the client ignores own entries: appenlight_client.client)
692 appenlight.log_namespace_blacklist =
703 appenlight.log_namespace_blacklist =
693
704
694 ; Dummy marker to add new entries after.
705 ; Dummy marker to add new entries after.
695 ; Add any custom entries below. Please don't remove this marker.
706 ; Add any custom entries below. Please don't remove this marker.
696 custom.conf = 1
707 custom.conf = 1
697
708
698
709
699 ; #####################
710 ; #####################
700 ; LOGGING CONFIGURATION
711 ; LOGGING CONFIGURATION
701 ; #####################
712 ; #####################
702 [loggers]
713 [loggers]
703 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
714 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
704
715
705 [handlers]
716 [handlers]
706 keys = console, console_sql
717 keys = console, console_sql
707
718
708 [formatters]
719 [formatters]
709 keys = generic, color_formatter, color_formatter_sql
720 keys = generic, color_formatter, color_formatter_sql
710
721
711 ; #######
722 ; #######
712 ; LOGGERS
723 ; LOGGERS
713 ; #######
724 ; #######
714 [logger_root]
725 [logger_root]
715 level = NOTSET
726 level = NOTSET
716 handlers = console
727 handlers = console
717
728
718 [logger_sqlalchemy]
729 [logger_sqlalchemy]
719 level = INFO
730 level = INFO
720 handlers = console_sql
731 handlers = console_sql
721 qualname = sqlalchemy.engine
732 qualname = sqlalchemy.engine
722 propagate = 0
733 propagate = 0
723
734
724 [logger_beaker]
735 [logger_beaker]
725 level = DEBUG
736 level = DEBUG
726 handlers =
737 handlers =
727 qualname = beaker.container
738 qualname = beaker.container
728 propagate = 1
739 propagate = 1
729
740
730 [logger_rhodecode]
741 [logger_rhodecode]
731 level = DEBUG
742 level = DEBUG
732 handlers =
743 handlers =
733 qualname = rhodecode
744 qualname = rhodecode
734 propagate = 1
745 propagate = 1
735
746
736 [logger_ssh_wrapper]
747 [logger_ssh_wrapper]
737 level = DEBUG
748 level = DEBUG
738 handlers =
749 handlers =
739 qualname = ssh_wrapper
750 qualname = ssh_wrapper
740 propagate = 1
751 propagate = 1
741
752
742 [logger_celery]
753 [logger_celery]
743 level = DEBUG
754 level = DEBUG
744 handlers =
755 handlers =
745 qualname = celery
756 qualname = celery
746
757
747
758
748 ; ########
759 ; ########
749 ; HANDLERS
760 ; HANDLERS
750 ; ########
761 ; ########
751
762
752 [handler_console]
763 [handler_console]
753 class = StreamHandler
764 class = StreamHandler
754 args = (sys.stderr, )
765 args = (sys.stderr, )
755 level = INFO
766 level = INFO
756 formatter = generic
767 formatter = generic
757
768
758 [handler_console_sql]
769 [handler_console_sql]
759 ; "level = DEBUG" logs SQL queries and results.
770 ; "level = DEBUG" logs SQL queries and results.
760 ; "level = INFO" logs SQL queries.
771 ; "level = INFO" logs SQL queries.
761 ; "level = WARN" logs neither. (Recommended for production systems.)
772 ; "level = WARN" logs neither. (Recommended for production systems.)
762 class = StreamHandler
773 class = StreamHandler
763 args = (sys.stderr, )
774 args = (sys.stderr, )
764 level = WARN
775 level = WARN
765 formatter = generic
776 formatter = generic
766
777
767 ; ##########
778 ; ##########
768 ; FORMATTERS
779 ; FORMATTERS
769 ; ##########
780 ; ##########
770
781
771 [formatter_generic]
782 [formatter_generic]
772 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
783 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
773 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
784 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
774 datefmt = %Y-%m-%d %H:%M:%S
785 datefmt = %Y-%m-%d %H:%M:%S
775
786
776 [formatter_color_formatter]
787 [formatter_color_formatter]
777 class = rhodecode.lib.logging_formatter.ColorFormatter
788 class = rhodecode.lib.logging_formatter.ColorFormatter
778 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
789 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
779 datefmt = %Y-%m-%d %H:%M:%S
790 datefmt = %Y-%m-%d %H:%M:%S
780
791
781 [formatter_color_formatter_sql]
792 [formatter_color_formatter_sql]
782 class = rhodecode.lib.logging_formatter.ColorFormatterSql
793 class = rhodecode.lib.logging_formatter.ColorFormatterSql
783 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
794 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
784 datefmt = %Y-%m-%d %H:%M:%S
795 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,386 +1,402 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import logging
22 import logging
23 import datetime
23 import datetime
24
24
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26 from pyramid.renderers import render_to_response
26 from pyramid.renderers import render_to_response
27 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.lib.celerylib import run_task, tasks
28 from rhodecode.lib.celerylib import run_task, tasks
29 from rhodecode.lib.utils2 import AttributeDict
29 from rhodecode.lib.utils2 import AttributeDict
30 from rhodecode.model.db import User
30 from rhodecode.model.db import User
31 from rhodecode.model.notification import EmailNotificationModel
31 from rhodecode.model.notification import EmailNotificationModel
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35
35
36 class DebugStyleView(BaseAppView):
36 class DebugStyleView(BaseAppView):
37 def load_default_context(self):
37 def load_default_context(self):
38 c = self._get_local_tmpl_context()
38 c = self._get_local_tmpl_context()
39
39
40 return c
40 return c
41
41
42 @view_config(
42 @view_config(
43 route_name='debug_style_home', request_method='GET',
43 route_name='debug_style_home', request_method='GET',
44 renderer=None)
44 renderer=None)
45 def index(self):
45 def index(self):
46 c = self.load_default_context()
46 c = self.load_default_context()
47 c.active = 'index'
47 c.active = 'index'
48
48
49 return render_to_response(
49 return render_to_response(
50 'debug_style/index.html', self._get_template_context(c),
50 'debug_style/index.html', self._get_template_context(c),
51 request=self.request)
51 request=self.request)
52
52
53 @view_config(
53 @view_config(
54 route_name='debug_style_email', request_method='GET',
54 route_name='debug_style_email', request_method='GET',
55 renderer=None)
55 renderer=None)
56 @view_config(
56 @view_config(
57 route_name='debug_style_email_plain_rendered', request_method='GET',
57 route_name='debug_style_email_plain_rendered', request_method='GET',
58 renderer=None)
58 renderer=None)
59 def render_email(self):
59 def render_email(self):
60 c = self.load_default_context()
60 c = self.load_default_context()
61 email_id = self.request.matchdict['email_id']
61 email_id = self.request.matchdict['email_id']
62 c.active = 'emails'
62 c.active = 'emails'
63
63
64 pr = AttributeDict(
64 pr = AttributeDict(
65 pull_request_id=123,
65 pull_request_id=123,
66 title='digital_ocean: fix redis, elastic search start on boot, '
66 title='digital_ocean: fix redis, elastic search start on boot, '
67 'fix fd limits on supervisor, set postgres 11 version',
67 'fix fd limits on supervisor, set postgres 11 version',
68 description='''
68 description='''
69 Check if we should use full-topic or mini-topic.
69 Check if we should use full-topic or mini-topic.
70
70
71 - full topic produces some problems with merge states etc
71 - full topic produces some problems with merge states etc
72 - server-mini-topic needs probably tweeks.
72 - server-mini-topic needs probably tweeks.
73 ''',
73 ''',
74 repo_name='foobar',
74 repo_name='foobar',
75 source_ref_parts=AttributeDict(type='branch', name='fix-ticket-2000'),
75 source_ref_parts=AttributeDict(type='branch', name='fix-ticket-2000'),
76 target_ref_parts=AttributeDict(type='branch', name='master'),
76 target_ref_parts=AttributeDict(type='branch', name='master'),
77 )
77 )
78 target_repo = AttributeDict(repo_name='repo_group/target_repo')
78 target_repo = AttributeDict(repo_name='repo_group/target_repo')
79 source_repo = AttributeDict(repo_name='repo_group/source_repo')
79 source_repo = AttributeDict(repo_name='repo_group/source_repo')
80 user = User.get_by_username(self.request.GET.get('user')) or self._rhodecode_db_user
80 user = User.get_by_username(self.request.GET.get('user')) or self._rhodecode_db_user
81 # file/commit changes for PR update
81 # file/commit changes for PR update
82 commit_changes = AttributeDict({
82 commit_changes = AttributeDict({
83 'added': ['aaaaaaabbbbb', 'cccccccddddddd'],
83 'added': ['aaaaaaabbbbb', 'cccccccddddddd'],
84 'removed': ['eeeeeeeeeee'],
84 'removed': ['eeeeeeeeeee'],
85 })
85 })
86 file_changes = AttributeDict({
86 file_changes = AttributeDict({
87 'added': ['a/file1.md', 'file2.py'],
87 'added': ['a/file1.md', 'file2.py'],
88 'modified': ['b/modified_file.rst'],
88 'modified': ['b/modified_file.rst'],
89 'removed': ['.idea'],
89 'removed': ['.idea'],
90 })
90 })
91
92 exc_traceback = {
93 'exc_utc_date': '2020-03-26T12:54:50.683281',
94 'exc_id': 139638856342656,
95 'exc_timestamp': '1585227290.683288',
96 'version': 'v1',
97 'exc_message': 'Traceback (most recent call last):\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/tweens.py", line 41, in excview_tween\n response = handler(request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/router.py", line 148, in handle_request\n registry, request, context, context_iface, view_name\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/view.py", line 667, in _call_view\n response = view_callable(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/config/views.py", line 188, in attr_view\n return view(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/config/views.py", line 214, in predicate_wrapper\n return view(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/viewderivers.py", line 401, in viewresult_to_response\n result = view(context, request)\n File "/nix/store/s43k2r9rysfbzmsjdqnxgzvvb7zjhkxb-python2.7-pyramid-1.10.4/lib/python2.7/site-packages/pyramid/viewderivers.py", line 132, in _class_view\n response = getattr(inst, attr)()\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/apps/debug_style/views.py", line 355, in render_email\n template_type, **email_kwargs.get(email_id, {}))\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/model/notification.py", line 402, in render_email\n body = email_template.render(None, **_kwargs)\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/lib/partial_renderer.py", line 95, in render\n return self._render_with_exc(tmpl, args, kwargs)\n File "/mnt/hgfs/marcink/workspace/rhodecode-enterprise-ce/rhodecode/lib/partial_renderer.py", line 79, in _render_with_exc\n return render_func.render(*args, **kwargs)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/template.py", line 476, in render\n return runtime._render(self, self.callable_, args, data)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/runtime.py", line 883, in _render\n **_kwargs_for_callable(callable_, data)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/runtime.py", line 920, in _render_context\n _exec_template(inherit, lclcontext, args=args, kwargs=kwargs)\n File "/nix/store/dakh34sxz4yfr435c0cwjz0sd6hnd5g3-python2.7-mako-1.1.0/lib/python2.7/site-packages/mako/runtime.py", line 947, in _exec_template\n callable_(context, *args, **kwargs)\n File "rhodecode_templates_email_templates_base_mako", line 63, in render_body\n File "rhodecode_templates_email_templates_exception_tracker_mako", line 43, in render_body\nAttributeError: \'str\' object has no attribute \'get\'\n',
98 'exc_type': 'AttributeError'
99 }
91 email_kwargs = {
100 email_kwargs = {
92 'test': {},
101 'test': {},
93 'message': {
102 'message': {
94 'body': 'message body !'
103 'body': 'message body !'
95 },
104 },
96 'email_test': {
105 'email_test': {
97 'user': user,
106 'user': user,
98 'date': datetime.datetime.now(),
107 'date': datetime.datetime.now(),
99 },
108 },
109 'exception': {
110 'email_prefix': '[RHODECODE ERROR]',
111 'exc_id': exc_traceback['exc_id'],
112 'exc_url': 'http://server-url/{}'.format(exc_traceback['exc_id']),
113 'exc_type_name': 'NameError',
114 'exc_traceback': exc_traceback,
115 },
100 'password_reset': {
116 'password_reset': {
101 'password_reset_url': 'http://example.com/reset-rhodecode-password/token',
117 'password_reset_url': 'http://example.com/reset-rhodecode-password/token',
102
118
103 'user': user,
119 'user': user,
104 'date': datetime.datetime.now(),
120 'date': datetime.datetime.now(),
105 'email': 'test@rhodecode.com',
121 'email': 'test@rhodecode.com',
106 'first_admin_email': User.get_first_super_admin().email
122 'first_admin_email': User.get_first_super_admin().email
107 },
123 },
108 'password_reset_confirmation': {
124 'password_reset_confirmation': {
109 'new_password': 'new-password-example',
125 'new_password': 'new-password-example',
110 'user': user,
126 'user': user,
111 'date': datetime.datetime.now(),
127 'date': datetime.datetime.now(),
112 'email': 'test@rhodecode.com',
128 'email': 'test@rhodecode.com',
113 'first_admin_email': User.get_first_super_admin().email
129 'first_admin_email': User.get_first_super_admin().email
114 },
130 },
115 'registration': {
131 'registration': {
116 'user': user,
132 'user': user,
117 'date': datetime.datetime.now(),
133 'date': datetime.datetime.now(),
118 },
134 },
119
135
120 'pull_request_comment': {
136 'pull_request_comment': {
121 'user': user,
137 'user': user,
122
138
123 'status_change': None,
139 'status_change': None,
124 'status_change_type': None,
140 'status_change_type': None,
125
141
126 'pull_request': pr,
142 'pull_request': pr,
127 'pull_request_commits': [],
143 'pull_request_commits': [],
128
144
129 'pull_request_target_repo': target_repo,
145 'pull_request_target_repo': target_repo,
130 'pull_request_target_repo_url': 'http://target-repo/url',
146 'pull_request_target_repo_url': 'http://target-repo/url',
131
147
132 'pull_request_source_repo': source_repo,
148 'pull_request_source_repo': source_repo,
133 'pull_request_source_repo_url': 'http://source-repo/url',
149 'pull_request_source_repo_url': 'http://source-repo/url',
134
150
135 'pull_request_url': 'http://localhost/pr1',
151 'pull_request_url': 'http://localhost/pr1',
136 'pr_comment_url': 'http://comment-url',
152 'pr_comment_url': 'http://comment-url',
137 'pr_comment_reply_url': 'http://comment-url#reply',
153 'pr_comment_reply_url': 'http://comment-url#reply',
138
154
139 'comment_file': None,
155 'comment_file': None,
140 'comment_line': None,
156 'comment_line': None,
141 'comment_type': 'note',
157 'comment_type': 'note',
142 'comment_body': 'This is my comment body. *I like !*',
158 'comment_body': 'This is my comment body. *I like !*',
143 'comment_id': 2048,
159 'comment_id': 2048,
144 'renderer_type': 'markdown',
160 'renderer_type': 'markdown',
145 'mention': True,
161 'mention': True,
146
162
147 },
163 },
148 'pull_request_comment+status': {
164 'pull_request_comment+status': {
149 'user': user,
165 'user': user,
150
166
151 'status_change': 'approved',
167 'status_change': 'approved',
152 'status_change_type': 'approved',
168 'status_change_type': 'approved',
153
169
154 'pull_request': pr,
170 'pull_request': pr,
155 'pull_request_commits': [],
171 'pull_request_commits': [],
156
172
157 'pull_request_target_repo': target_repo,
173 'pull_request_target_repo': target_repo,
158 'pull_request_target_repo_url': 'http://target-repo/url',
174 'pull_request_target_repo_url': 'http://target-repo/url',
159
175
160 'pull_request_source_repo': source_repo,
176 'pull_request_source_repo': source_repo,
161 'pull_request_source_repo_url': 'http://source-repo/url',
177 'pull_request_source_repo_url': 'http://source-repo/url',
162
178
163 'pull_request_url': 'http://localhost/pr1',
179 'pull_request_url': 'http://localhost/pr1',
164 'pr_comment_url': 'http://comment-url',
180 'pr_comment_url': 'http://comment-url',
165 'pr_comment_reply_url': 'http://comment-url#reply',
181 'pr_comment_reply_url': 'http://comment-url#reply',
166
182
167 'comment_type': 'todo',
183 'comment_type': 'todo',
168 'comment_file': None,
184 'comment_file': None,
169 'comment_line': None,
185 'comment_line': None,
170 'comment_body': '''
186 'comment_body': '''
171 I think something like this would be better
187 I think something like this would be better
172
188
173 ```py
189 ```py
174
190
175 def db():
191 def db():
176 global connection
192 global connection
177 return connection
193 return connection
178
194
179 ```
195 ```
180
196
181 ''',
197 ''',
182 'comment_id': 2048,
198 'comment_id': 2048,
183 'renderer_type': 'markdown',
199 'renderer_type': 'markdown',
184 'mention': True,
200 'mention': True,
185
201
186 },
202 },
187 'pull_request_comment+file': {
203 'pull_request_comment+file': {
188 'user': user,
204 'user': user,
189
205
190 'status_change': None,
206 'status_change': None,
191 'status_change_type': None,
207 'status_change_type': None,
192
208
193 'pull_request': pr,
209 'pull_request': pr,
194 'pull_request_commits': [],
210 'pull_request_commits': [],
195
211
196 'pull_request_target_repo': target_repo,
212 'pull_request_target_repo': target_repo,
197 'pull_request_target_repo_url': 'http://target-repo/url',
213 'pull_request_target_repo_url': 'http://target-repo/url',
198
214
199 'pull_request_source_repo': source_repo,
215 'pull_request_source_repo': source_repo,
200 'pull_request_source_repo_url': 'http://source-repo/url',
216 'pull_request_source_repo_url': 'http://source-repo/url',
201
217
202 'pull_request_url': 'http://localhost/pr1',
218 'pull_request_url': 'http://localhost/pr1',
203
219
204 'pr_comment_url': 'http://comment-url',
220 'pr_comment_url': 'http://comment-url',
205 'pr_comment_reply_url': 'http://comment-url#reply',
221 'pr_comment_reply_url': 'http://comment-url#reply',
206
222
207 'comment_file': 'rhodecode/model/db.py',
223 'comment_file': 'rhodecode/model/db.py',
208 'comment_line': 'o1210',
224 'comment_line': 'o1210',
209 'comment_type': 'todo',
225 'comment_type': 'todo',
210 'comment_body': '''
226 'comment_body': '''
211 I like this !
227 I like this !
212
228
213 But please check this code::
229 But please check this code::
214
230
215 def main():
231 def main():
216 print 'ok'
232 print 'ok'
217
233
218 This should work better !
234 This should work better !
219 ''',
235 ''',
220 'comment_id': 2048,
236 'comment_id': 2048,
221 'renderer_type': 'rst',
237 'renderer_type': 'rst',
222 'mention': True,
238 'mention': True,
223
239
224 },
240 },
225
241
226 'pull_request_update': {
242 'pull_request_update': {
227 'updating_user': user,
243 'updating_user': user,
228
244
229 'status_change': None,
245 'status_change': None,
230 'status_change_type': None,
246 'status_change_type': None,
231
247
232 'pull_request': pr,
248 'pull_request': pr,
233 'pull_request_commits': [],
249 'pull_request_commits': [],
234
250
235 'pull_request_target_repo': target_repo,
251 'pull_request_target_repo': target_repo,
236 'pull_request_target_repo_url': 'http://target-repo/url',
252 'pull_request_target_repo_url': 'http://target-repo/url',
237
253
238 'pull_request_source_repo': source_repo,
254 'pull_request_source_repo': source_repo,
239 'pull_request_source_repo_url': 'http://source-repo/url',
255 'pull_request_source_repo_url': 'http://source-repo/url',
240
256
241 'pull_request_url': 'http://localhost/pr1',
257 'pull_request_url': 'http://localhost/pr1',
242
258
243 # update comment links
259 # update comment links
244 'pr_comment_url': 'http://comment-url',
260 'pr_comment_url': 'http://comment-url',
245 'pr_comment_reply_url': 'http://comment-url#reply',
261 'pr_comment_reply_url': 'http://comment-url#reply',
246 'ancestor_commit_id': 'f39bd443',
262 'ancestor_commit_id': 'f39bd443',
247 'added_commits': commit_changes.added,
263 'added_commits': commit_changes.added,
248 'removed_commits': commit_changes.removed,
264 'removed_commits': commit_changes.removed,
249 'changed_files': (file_changes.added + file_changes.modified + file_changes.removed),
265 'changed_files': (file_changes.added + file_changes.modified + file_changes.removed),
250 'added_files': file_changes.added,
266 'added_files': file_changes.added,
251 'modified_files': file_changes.modified,
267 'modified_files': file_changes.modified,
252 'removed_files': file_changes.removed,
268 'removed_files': file_changes.removed,
253 },
269 },
254
270
255 'cs_comment': {
271 'cs_comment': {
256 'user': user,
272 'user': user,
257 'commit': AttributeDict(idx=123, raw_id='a'*40, message='Commit message'),
273 'commit': AttributeDict(idx=123, raw_id='a'*40, message='Commit message'),
258 'status_change': None,
274 'status_change': None,
259 'status_change_type': None,
275 'status_change_type': None,
260
276
261 'commit_target_repo_url': 'http://foo.example.com/#comment1',
277 'commit_target_repo_url': 'http://foo.example.com/#comment1',
262 'repo_name': 'test-repo',
278 'repo_name': 'test-repo',
263 'comment_type': 'note',
279 'comment_type': 'note',
264 'comment_file': None,
280 'comment_file': None,
265 'comment_line': None,
281 'comment_line': None,
266 'commit_comment_url': 'http://comment-url',
282 'commit_comment_url': 'http://comment-url',
267 'commit_comment_reply_url': 'http://comment-url#reply',
283 'commit_comment_reply_url': 'http://comment-url#reply',
268 'comment_body': 'This is my comment body. *I like !*',
284 'comment_body': 'This is my comment body. *I like !*',
269 'comment_id': 2048,
285 'comment_id': 2048,
270 'renderer_type': 'markdown',
286 'renderer_type': 'markdown',
271 'mention': True,
287 'mention': True,
272 },
288 },
273 'cs_comment+status': {
289 'cs_comment+status': {
274 'user': user,
290 'user': user,
275 'commit': AttributeDict(idx=123, raw_id='a' * 40, message='Commit message'),
291 'commit': AttributeDict(idx=123, raw_id='a' * 40, message='Commit message'),
276 'status_change': 'approved',
292 'status_change': 'approved',
277 'status_change_type': 'approved',
293 'status_change_type': 'approved',
278
294
279 'commit_target_repo_url': 'http://foo.example.com/#comment1',
295 'commit_target_repo_url': 'http://foo.example.com/#comment1',
280 'repo_name': 'test-repo',
296 'repo_name': 'test-repo',
281 'comment_type': 'note',
297 'comment_type': 'note',
282 'comment_file': None,
298 'comment_file': None,
283 'comment_line': None,
299 'comment_line': None,
284 'commit_comment_url': 'http://comment-url',
300 'commit_comment_url': 'http://comment-url',
285 'commit_comment_reply_url': 'http://comment-url#reply',
301 'commit_comment_reply_url': 'http://comment-url#reply',
286 'comment_body': '''
302 'comment_body': '''
287 Hello **world**
303 Hello **world**
288
304
289 This is a multiline comment :)
305 This is a multiline comment :)
290
306
291 - list
307 - list
292 - list2
308 - list2
293 ''',
309 ''',
294 'comment_id': 2048,
310 'comment_id': 2048,
295 'renderer_type': 'markdown',
311 'renderer_type': 'markdown',
296 'mention': True,
312 'mention': True,
297 },
313 },
298 'cs_comment+file': {
314 'cs_comment+file': {
299 'user': user,
315 'user': user,
300 'commit': AttributeDict(idx=123, raw_id='a' * 40, message='Commit message'),
316 'commit': AttributeDict(idx=123, raw_id='a' * 40, message='Commit message'),
301 'status_change': None,
317 'status_change': None,
302 'status_change_type': None,
318 'status_change_type': None,
303
319
304 'commit_target_repo_url': 'http://foo.example.com/#comment1',
320 'commit_target_repo_url': 'http://foo.example.com/#comment1',
305 'repo_name': 'test-repo',
321 'repo_name': 'test-repo',
306
322
307 'comment_type': 'note',
323 'comment_type': 'note',
308 'comment_file': 'test-file.py',
324 'comment_file': 'test-file.py',
309 'comment_line': 'n100',
325 'comment_line': 'n100',
310
326
311 'commit_comment_url': 'http://comment-url',
327 'commit_comment_url': 'http://comment-url',
312 'commit_comment_reply_url': 'http://comment-url#reply',
328 'commit_comment_reply_url': 'http://comment-url#reply',
313 'comment_body': 'This is my comment body. *I like !*',
329 'comment_body': 'This is my comment body. *I like !*',
314 'comment_id': 2048,
330 'comment_id': 2048,
315 'renderer_type': 'markdown',
331 'renderer_type': 'markdown',
316 'mention': True,
332 'mention': True,
317 },
333 },
318
334
319 'pull_request': {
335 'pull_request': {
320 'user': user,
336 'user': user,
321 'pull_request': pr,
337 'pull_request': pr,
322 'pull_request_commits': [
338 'pull_request_commits': [
323 ('472d1df03bf7206e278fcedc6ac92b46b01c4e21', '''\
339 ('472d1df03bf7206e278fcedc6ac92b46b01c4e21', '''\
324 my-account: moved email closer to profile as it's similar data just moved outside.
340 my-account: moved email closer to profile as it's similar data just moved outside.
325 '''),
341 '''),
326 ('cbfa3061b6de2696c7161ed15ba5c6a0045f90a7', '''\
342 ('cbfa3061b6de2696c7161ed15ba5c6a0045f90a7', '''\
327 users: description edit fixes
343 users: description edit fixes
328
344
329 - tests
345 - tests
330 - added metatags info
346 - added metatags info
331 '''),
347 '''),
332 ],
348 ],
333
349
334 'pull_request_target_repo': target_repo,
350 'pull_request_target_repo': target_repo,
335 'pull_request_target_repo_url': 'http://target-repo/url',
351 'pull_request_target_repo_url': 'http://target-repo/url',
336
352
337 'pull_request_source_repo': source_repo,
353 'pull_request_source_repo': source_repo,
338 'pull_request_source_repo_url': 'http://source-repo/url',
354 'pull_request_source_repo_url': 'http://source-repo/url',
339
355
340 'pull_request_url': 'http://code.rhodecode.com/_pull-request/123',
356 'pull_request_url': 'http://code.rhodecode.com/_pull-request/123',
341 }
357 }
342
358
343 }
359 }
344
360
345 template_type = email_id.split('+')[0]
361 template_type = email_id.split('+')[0]
346 (c.subject, c.headers, c.email_body,
362 (c.subject, c.headers, c.email_body,
347 c.email_body_plaintext) = EmailNotificationModel().render_email(
363 c.email_body_plaintext) = EmailNotificationModel().render_email(
348 template_type, **email_kwargs.get(email_id, {}))
364 template_type, **email_kwargs.get(email_id, {}))
349
365
350 test_email = self.request.GET.get('email')
366 test_email = self.request.GET.get('email')
351 if test_email:
367 if test_email:
352 recipients = [test_email]
368 recipients = [test_email]
353 run_task(tasks.send_email, recipients, c.subject,
369 run_task(tasks.send_email, recipients, c.subject,
354 c.email_body_plaintext, c.email_body)
370 c.email_body_plaintext, c.email_body)
355
371
356 if self.request.matched_route.name == 'debug_style_email_plain_rendered':
372 if self.request.matched_route.name == 'debug_style_email_plain_rendered':
357 template = 'debug_style/email_plain_rendered.mako'
373 template = 'debug_style/email_plain_rendered.mako'
358 else:
374 else:
359 template = 'debug_style/email.mako'
375 template = 'debug_style/email.mako'
360 return render_to_response(
376 return render_to_response(
361 template, self._get_template_context(c),
377 template, self._get_template_context(c),
362 request=self.request)
378 request=self.request)
363
379
364 @view_config(
380 @view_config(
365 route_name='debug_style_template', request_method='GET',
381 route_name='debug_style_template', request_method='GET',
366 renderer=None)
382 renderer=None)
367 def template(self):
383 def template(self):
368 t_path = self.request.matchdict['t_path']
384 t_path = self.request.matchdict['t_path']
369 c = self.load_default_context()
385 c = self.load_default_context()
370 c.active = os.path.splitext(t_path)[0]
386 c.active = os.path.splitext(t_path)[0]
371 c.came_from = ''
387 c.came_from = ''
372 c.email_types = {
388 c.email_types = {
373 'cs_comment+file': {},
389 'cs_comment+file': {},
374 'cs_comment+status': {},
390 'cs_comment+status': {},
375
391
376 'pull_request_comment+file': {},
392 'pull_request_comment+file': {},
377 'pull_request_comment+status': {},
393 'pull_request_comment+status': {},
378
394
379 'pull_request_update': {},
395 'pull_request_update': {},
380 }
396 }
381 c.email_types.update(EmailNotificationModel.email_types)
397 c.email_types.update(EmailNotificationModel.email_types)
382
398
383 return render_to_response(
399 return render_to_response(
384 'debug_style/' + t_path, self._get_template_context(c),
400 'debug_style/' + t_path, self._get_template_context(c),
385 request=self.request)
401 request=self.request)
386
402
@@ -1,753 +1,761 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import sys
22 import sys
23 import logging
23 import logging
24 import collections
24 import collections
25 import tempfile
25 import tempfile
26 import time
26 import time
27
27
28 from paste.gzipper import make_gzip_middleware
28 from paste.gzipper import make_gzip_middleware
29 import pyramid.events
29 import pyramid.events
30 from pyramid.wsgi import wsgiapp
30 from pyramid.wsgi import wsgiapp
31 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.authorization import ACLAuthorizationPolicy
32 from pyramid.config import Configurator
32 from pyramid.config import Configurator
33 from pyramid.settings import asbool, aslist
33 from pyramid.settings import asbool, aslist
34 from pyramid.httpexceptions import (
34 from pyramid.httpexceptions import (
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
36 from pyramid.renderers import render_to_response
36 from pyramid.renderers import render_to_response
37
37
38 from rhodecode.model import meta
38 from rhodecode.model import meta
39 from rhodecode.config import patches
39 from rhodecode.config import patches
40 from rhodecode.config import utils as config_utils
40 from rhodecode.config import utils as config_utils
41 from rhodecode.config.environment import load_pyramid_environment
41 from rhodecode.config.environment import load_pyramid_environment
42
42
43 import rhodecode.events
43 import rhodecode.events
44 from rhodecode.lib.middleware.vcs import VCSMiddleware
44 from rhodecode.lib.middleware.vcs import VCSMiddleware
45 from rhodecode.lib.request import Request
45 from rhodecode.lib.request import Request
46 from rhodecode.lib.vcs import VCSCommunicationError
46 from rhodecode.lib.vcs import VCSCommunicationError
47 from rhodecode.lib.exceptions import VCSServerUnavailable
47 from rhodecode.lib.exceptions import VCSServerUnavailable
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.https_fixup import HttpsFixup
49 from rhodecode.lib.middleware.https_fixup import HttpsFixup
50 from rhodecode.lib.celerylib.loader import configure_celery
50 from rhodecode.lib.celerylib.loader import configure_celery
51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
52 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
52 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
53 from rhodecode.lib.exc_tracking import store_exception
53 from rhodecode.lib.exc_tracking import store_exception
54 from rhodecode.subscribers import (
54 from rhodecode.subscribers import (
55 scan_repositories_if_enabled, write_js_routes_if_enabled,
55 scan_repositories_if_enabled, write_js_routes_if_enabled,
56 write_metadata_if_needed, inject_app_settings)
56 write_metadata_if_needed, inject_app_settings)
57
57
58
58
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 def is_http_error(response):
62 def is_http_error(response):
63 # error which should have traceback
63 # error which should have traceback
64 return response.status_code > 499
64 return response.status_code > 499
65
65
66
66
67 def should_load_all():
67 def should_load_all():
68 """
68 """
69 Returns if all application components should be loaded. In some cases it's
69 Returns if all application components should be loaded. In some cases it's
70 desired to skip apps loading for faster shell script execution
70 desired to skip apps loading for faster shell script execution
71 """
71 """
72 ssh_cmd = os.environ.get('RC_CMD_SSH_WRAPPER')
72 ssh_cmd = os.environ.get('RC_CMD_SSH_WRAPPER')
73 if ssh_cmd:
73 if ssh_cmd:
74 return False
74 return False
75
75
76 return True
76 return True
77
77
78
78
79 def make_pyramid_app(global_config, **settings):
79 def make_pyramid_app(global_config, **settings):
80 """
80 """
81 Constructs the WSGI application based on Pyramid.
81 Constructs the WSGI application based on Pyramid.
82
82
83 Specials:
83 Specials:
84
84
85 * The application can also be integrated like a plugin via the call to
85 * The application can also be integrated like a plugin via the call to
86 `includeme`. This is accompanied with the other utility functions which
86 `includeme`. This is accompanied with the other utility functions which
87 are called. Changing this should be done with great care to not break
87 are called. Changing this should be done with great care to not break
88 cases when these fragments are assembled from another place.
88 cases when these fragments are assembled from another place.
89
89
90 """
90 """
91
91
92 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
92 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
93 # will be replaced by the value of the environment variable "NAME" in this case.
93 # will be replaced by the value of the environment variable "NAME" in this case.
94 start_time = time.time()
94 start_time = time.time()
95
95
96 debug = asbool(global_config.get('debug'))
96 debug = asbool(global_config.get('debug'))
97 if debug:
97 if debug:
98 enable_debug()
98 enable_debug()
99
99
100 environ = {'ENV_{}'.format(key): value for key, value in os.environ.items()}
100 environ = {'ENV_{}'.format(key): value for key, value in os.environ.items()}
101
101
102 global_config = _substitute_values(global_config, environ)
102 global_config = _substitute_values(global_config, environ)
103 settings = _substitute_values(settings, environ)
103 settings = _substitute_values(settings, environ)
104
104
105 sanitize_settings_and_apply_defaults(global_config, settings)
105 sanitize_settings_and_apply_defaults(global_config, settings)
106
106
107 config = Configurator(settings=settings)
107 config = Configurator(settings=settings)
108
108
109 # Apply compatibility patches
109 # Apply compatibility patches
110 patches.inspect_getargspec()
110 patches.inspect_getargspec()
111
111
112 load_pyramid_environment(global_config, settings)
112 load_pyramid_environment(global_config, settings)
113
113
114 # Static file view comes first
114 # Static file view comes first
115 includeme_first(config)
115 includeme_first(config)
116
116
117 includeme(config)
117 includeme(config)
118
118
119 pyramid_app = config.make_wsgi_app()
119 pyramid_app = config.make_wsgi_app()
120 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
120 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
121 pyramid_app.config = config
121 pyramid_app.config = config
122
122
123 config.configure_celery(global_config['__file__'])
123 config.configure_celery(global_config['__file__'])
124 # creating the app uses a connection - return it after we are done
124 # creating the app uses a connection - return it after we are done
125 meta.Session.remove()
125 meta.Session.remove()
126 total_time = time.time() - start_time
126 total_time = time.time() - start_time
127 log.info('Pyramid app `%s` created and configured in %.2fs',
127 log.info('Pyramid app `%s` created and configured in %.2fs',
128 pyramid_app.func_name, total_time)
128 pyramid_app.func_name, total_time)
129
129
130 return pyramid_app
130 return pyramid_app
131
131
132
132
133 def not_found_view(request):
133 def not_found_view(request):
134 """
134 """
135 This creates the view which should be registered as not-found-view to
135 This creates the view which should be registered as not-found-view to
136 pyramid.
136 pyramid.
137 """
137 """
138
138
139 if not getattr(request, 'vcs_call', None):
139 if not getattr(request, 'vcs_call', None):
140 # handle like regular case with our error_handler
140 # handle like regular case with our error_handler
141 return error_handler(HTTPNotFound(), request)
141 return error_handler(HTTPNotFound(), request)
142
142
143 # handle not found view as a vcs call
143 # handle not found view as a vcs call
144 settings = request.registry.settings
144 settings = request.registry.settings
145 ae_client = getattr(request, 'ae_client', None)
145 ae_client = getattr(request, 'ae_client', None)
146 vcs_app = VCSMiddleware(
146 vcs_app = VCSMiddleware(
147 HTTPNotFound(), request.registry, settings,
147 HTTPNotFound(), request.registry, settings,
148 appenlight_client=ae_client)
148 appenlight_client=ae_client)
149
149
150 return wsgiapp(vcs_app)(None, request)
150 return wsgiapp(vcs_app)(None, request)
151
151
152
152
153 def error_handler(exception, request):
153 def error_handler(exception, request):
154 import rhodecode
154 import rhodecode
155 from rhodecode.lib import helpers
155 from rhodecode.lib import helpers
156
156
157 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
157 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
158
158
159 base_response = HTTPInternalServerError()
159 base_response = HTTPInternalServerError()
160 # prefer original exception for the response since it may have headers set
160 # prefer original exception for the response since it may have headers set
161 if isinstance(exception, HTTPException):
161 if isinstance(exception, HTTPException):
162 base_response = exception
162 base_response = exception
163 elif isinstance(exception, VCSCommunicationError):
163 elif isinstance(exception, VCSCommunicationError):
164 base_response = VCSServerUnavailable()
164 base_response = VCSServerUnavailable()
165
165
166 if is_http_error(base_response):
166 if is_http_error(base_response):
167 log.exception(
167 log.exception(
168 'error occurred handling this request for path: %s', request.path)
168 'error occurred handling this request for path: %s', request.path)
169
169
170 error_explanation = base_response.explanation or str(base_response)
170 error_explanation = base_response.explanation or str(base_response)
171 if base_response.status_code == 404:
171 if base_response.status_code == 404:
172 error_explanation += " Optionally you don't have permission to access this page."
172 error_explanation += " Optionally you don't have permission to access this page."
173 c = AttributeDict()
173 c = AttributeDict()
174 c.error_message = base_response.status
174 c.error_message = base_response.status
175 c.error_explanation = error_explanation
175 c.error_explanation = error_explanation
176 c.visual = AttributeDict()
176 c.visual = AttributeDict()
177
177
178 c.visual.rhodecode_support_url = (
178 c.visual.rhodecode_support_url = (
179 request.registry.settings.get('rhodecode_support_url') or
179 request.registry.settings.get('rhodecode_support_url') or
180 request.route_url('rhodecode_support')
180 request.route_url('rhodecode_support')
181 )
181 )
182 c.redirect_time = 0
182 c.redirect_time = 0
183 c.rhodecode_name = rhodecode_title
183 c.rhodecode_name = rhodecode_title
184 if not c.rhodecode_name:
184 if not c.rhodecode_name:
185 c.rhodecode_name = 'Rhodecode'
185 c.rhodecode_name = 'Rhodecode'
186
186
187 c.causes = []
187 c.causes = []
188 if is_http_error(base_response):
188 if is_http_error(base_response):
189 c.causes.append('Server is overloaded.')
189 c.causes.append('Server is overloaded.')
190 c.causes.append('Server database connection is lost.')
190 c.causes.append('Server database connection is lost.')
191 c.causes.append('Server expected unhandled error.')
191 c.causes.append('Server expected unhandled error.')
192
192
193 if hasattr(base_response, 'causes'):
193 if hasattr(base_response, 'causes'):
194 c.causes = base_response.causes
194 c.causes = base_response.causes
195
195
196 c.messages = helpers.flash.pop_messages(request=request)
196 c.messages = helpers.flash.pop_messages(request=request)
197
197
198 exc_info = sys.exc_info()
198 exc_info = sys.exc_info()
199 c.exception_id = id(exc_info)
199 c.exception_id = id(exc_info)
200 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
200 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
201 or base_response.status_code > 499
201 or base_response.status_code > 499
202 c.exception_id_url = request.route_url(
202 c.exception_id_url = request.route_url(
203 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
203 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
204
204
205 if c.show_exception_id:
205 if c.show_exception_id:
206 store_exception(c.exception_id, exc_info)
206 store_exception(c.exception_id, exc_info)
207
207
208 response = render_to_response(
208 response = render_to_response(
209 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
209 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
210 response=base_response)
210 response=base_response)
211
211
212 return response
212 return response
213
213
214
214
215 def includeme_first(config):
215 def includeme_first(config):
216 # redirect automatic browser favicon.ico requests to correct place
216 # redirect automatic browser favicon.ico requests to correct place
217 def favicon_redirect(context, request):
217 def favicon_redirect(context, request):
218 return HTTPFound(
218 return HTTPFound(
219 request.static_path('rhodecode:public/images/favicon.ico'))
219 request.static_path('rhodecode:public/images/favicon.ico'))
220
220
221 config.add_view(favicon_redirect, route_name='favicon')
221 config.add_view(favicon_redirect, route_name='favicon')
222 config.add_route('favicon', '/favicon.ico')
222 config.add_route('favicon', '/favicon.ico')
223
223
224 def robots_redirect(context, request):
224 def robots_redirect(context, request):
225 return HTTPFound(
225 return HTTPFound(
226 request.static_path('rhodecode:public/robots.txt'))
226 request.static_path('rhodecode:public/robots.txt'))
227
227
228 config.add_view(robots_redirect, route_name='robots')
228 config.add_view(robots_redirect, route_name='robots')
229 config.add_route('robots', '/robots.txt')
229 config.add_route('robots', '/robots.txt')
230
230
231 config.add_static_view(
231 config.add_static_view(
232 '_static/deform', 'deform:static')
232 '_static/deform', 'deform:static')
233 config.add_static_view(
233 config.add_static_view(
234 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
234 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
235
235
236
236
237 def includeme(config):
237 def includeme(config):
238 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
238 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
239 settings = config.registry.settings
239 settings = config.registry.settings
240 config.set_request_factory(Request)
240 config.set_request_factory(Request)
241
241
242 # plugin information
242 # plugin information
243 config.registry.rhodecode_plugins = collections.OrderedDict()
243 config.registry.rhodecode_plugins = collections.OrderedDict()
244
244
245 config.add_directive(
245 config.add_directive(
246 'register_rhodecode_plugin', register_rhodecode_plugin)
246 'register_rhodecode_plugin', register_rhodecode_plugin)
247
247
248 config.add_directive('configure_celery', configure_celery)
248 config.add_directive('configure_celery', configure_celery)
249
249
250 if asbool(settings.get('appenlight', 'false')):
250 if asbool(settings.get('appenlight', 'false')):
251 config.include('appenlight_client.ext.pyramid_tween')
251 config.include('appenlight_client.ext.pyramid_tween')
252
252
253 load_all = should_load_all()
253 load_all = should_load_all()
254
254
255 # Includes which are required. The application would fail without them.
255 # Includes which are required. The application would fail without them.
256 config.include('pyramid_mako')
256 config.include('pyramid_mako')
257 config.include('rhodecode.lib.rc_beaker')
257 config.include('rhodecode.lib.rc_beaker')
258 config.include('rhodecode.lib.rc_cache')
258 config.include('rhodecode.lib.rc_cache')
259
259
260 config.include('rhodecode.apps._base.navigation')
260 config.include('rhodecode.apps._base.navigation')
261 config.include('rhodecode.apps._base.subscribers')
261 config.include('rhodecode.apps._base.subscribers')
262 config.include('rhodecode.tweens')
262 config.include('rhodecode.tweens')
263 config.include('rhodecode.authentication')
263 config.include('rhodecode.authentication')
264
264
265 if load_all:
265 if load_all:
266 config.include('rhodecode.integrations')
266 config.include('rhodecode.integrations')
267
267
268 if load_all:
268 if load_all:
269 # load CE authentication plugins
269 # load CE authentication plugins
270 config.include('rhodecode.authentication.plugins.auth_crowd')
270 config.include('rhodecode.authentication.plugins.auth_crowd')
271 config.include('rhodecode.authentication.plugins.auth_headers')
271 config.include('rhodecode.authentication.plugins.auth_headers')
272 config.include('rhodecode.authentication.plugins.auth_jasig_cas')
272 config.include('rhodecode.authentication.plugins.auth_jasig_cas')
273 config.include('rhodecode.authentication.plugins.auth_ldap')
273 config.include('rhodecode.authentication.plugins.auth_ldap')
274 config.include('rhodecode.authentication.plugins.auth_pam')
274 config.include('rhodecode.authentication.plugins.auth_pam')
275 config.include('rhodecode.authentication.plugins.auth_rhodecode')
275 config.include('rhodecode.authentication.plugins.auth_rhodecode')
276 config.include('rhodecode.authentication.plugins.auth_token')
276 config.include('rhodecode.authentication.plugins.auth_token')
277
277
278 # Auto discover authentication plugins and include their configuration.
278 # Auto discover authentication plugins and include their configuration.
279 if asbool(settings.get('auth_plugin.import_legacy_plugins', 'true')):
279 if asbool(settings.get('auth_plugin.import_legacy_plugins', 'true')):
280 from rhodecode.authentication import discover_legacy_plugins
280 from rhodecode.authentication import discover_legacy_plugins
281 discover_legacy_plugins(config)
281 discover_legacy_plugins(config)
282
282
283 # apps
283 # apps
284 if load_all:
284 if load_all:
285 config.include('rhodecode.apps._base')
285 config.include('rhodecode.apps._base')
286 config.include('rhodecode.apps.hovercards')
286 config.include('rhodecode.apps.hovercards')
287 config.include('rhodecode.apps.ops')
287 config.include('rhodecode.apps.ops')
288 config.include('rhodecode.apps.admin')
288 config.include('rhodecode.apps.admin')
289 config.include('rhodecode.apps.channelstream')
289 config.include('rhodecode.apps.channelstream')
290 config.include('rhodecode.apps.file_store')
290 config.include('rhodecode.apps.file_store')
291 config.include('rhodecode.apps.login')
291 config.include('rhodecode.apps.login')
292 config.include('rhodecode.apps.home')
292 config.include('rhodecode.apps.home')
293 config.include('rhodecode.apps.journal')
293 config.include('rhodecode.apps.journal')
294 config.include('rhodecode.apps.repository')
294 config.include('rhodecode.apps.repository')
295 config.include('rhodecode.apps.repo_group')
295 config.include('rhodecode.apps.repo_group')
296 config.include('rhodecode.apps.user_group')
296 config.include('rhodecode.apps.user_group')
297 config.include('rhodecode.apps.search')
297 config.include('rhodecode.apps.search')
298 config.include('rhodecode.apps.user_profile')
298 config.include('rhodecode.apps.user_profile')
299 config.include('rhodecode.apps.user_group_profile')
299 config.include('rhodecode.apps.user_group_profile')
300 config.include('rhodecode.apps.my_account')
300 config.include('rhodecode.apps.my_account')
301 config.include('rhodecode.apps.svn_support')
301 config.include('rhodecode.apps.svn_support')
302 config.include('rhodecode.apps.ssh_support')
302 config.include('rhodecode.apps.ssh_support')
303 config.include('rhodecode.apps.gist')
303 config.include('rhodecode.apps.gist')
304 config.include('rhodecode.apps.debug_style')
304 config.include('rhodecode.apps.debug_style')
305 config.include('rhodecode.api')
305 config.include('rhodecode.api')
306
306
307 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
307 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
308 config.add_translation_dirs('rhodecode:i18n/')
308 config.add_translation_dirs('rhodecode:i18n/')
309 settings['default_locale_name'] = settings.get('lang', 'en')
309 settings['default_locale_name'] = settings.get('lang', 'en')
310
310
311 # Add subscribers.
311 # Add subscribers.
312 if load_all:
312 if load_all:
313 config.add_subscriber(inject_app_settings,
313 config.add_subscriber(inject_app_settings,
314 pyramid.events.ApplicationCreated)
314 pyramid.events.ApplicationCreated)
315 config.add_subscriber(scan_repositories_if_enabled,
315 config.add_subscriber(scan_repositories_if_enabled,
316 pyramid.events.ApplicationCreated)
316 pyramid.events.ApplicationCreated)
317 config.add_subscriber(write_metadata_if_needed,
317 config.add_subscriber(write_metadata_if_needed,
318 pyramid.events.ApplicationCreated)
318 pyramid.events.ApplicationCreated)
319 config.add_subscriber(write_js_routes_if_enabled,
319 config.add_subscriber(write_js_routes_if_enabled,
320 pyramid.events.ApplicationCreated)
320 pyramid.events.ApplicationCreated)
321
321
322 # request custom methods
322 # request custom methods
323 config.add_request_method(
323 config.add_request_method(
324 'rhodecode.lib.partial_renderer.get_partial_renderer',
324 'rhodecode.lib.partial_renderer.get_partial_renderer',
325 'get_partial_renderer')
325 'get_partial_renderer')
326
326
327 config.add_request_method(
327 config.add_request_method(
328 'rhodecode.lib.request_counter.get_request_counter',
328 'rhodecode.lib.request_counter.get_request_counter',
329 'request_count')
329 'request_count')
330
330
331 # Set the authorization policy.
331 # Set the authorization policy.
332 authz_policy = ACLAuthorizationPolicy()
332 authz_policy = ACLAuthorizationPolicy()
333 config.set_authorization_policy(authz_policy)
333 config.set_authorization_policy(authz_policy)
334
334
335 # Set the default renderer for HTML templates to mako.
335 # Set the default renderer for HTML templates to mako.
336 config.add_mako_renderer('.html')
336 config.add_mako_renderer('.html')
337
337
338 config.add_renderer(
338 config.add_renderer(
339 name='json_ext',
339 name='json_ext',
340 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
340 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
341
341
342 # include RhodeCode plugins
342 # include RhodeCode plugins
343 includes = aslist(settings.get('rhodecode.includes', []))
343 includes = aslist(settings.get('rhodecode.includes', []))
344 for inc in includes:
344 for inc in includes:
345 config.include(inc)
345 config.include(inc)
346
346
347 # custom not found view, if our pyramid app doesn't know how to handle
347 # custom not found view, if our pyramid app doesn't know how to handle
348 # the request pass it to potential VCS handling ap
348 # the request pass it to potential VCS handling ap
349 config.add_notfound_view(not_found_view)
349 config.add_notfound_view(not_found_view)
350 if not settings.get('debugtoolbar.enabled', False):
350 if not settings.get('debugtoolbar.enabled', False):
351 # disabled debugtoolbar handle all exceptions via the error_handlers
351 # disabled debugtoolbar handle all exceptions via the error_handlers
352 config.add_view(error_handler, context=Exception)
352 config.add_view(error_handler, context=Exception)
353
353
354 # all errors including 403/404/50X
354 # all errors including 403/404/50X
355 config.add_view(error_handler, context=HTTPError)
355 config.add_view(error_handler, context=HTTPError)
356
356
357
357
358 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
358 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
359 """
359 """
360 Apply outer WSGI middlewares around the application.
360 Apply outer WSGI middlewares around the application.
361 """
361 """
362 registry = config.registry
362 registry = config.registry
363 settings = registry.settings
363 settings = registry.settings
364
364
365 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
365 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
366 pyramid_app = HttpsFixup(pyramid_app, settings)
366 pyramid_app = HttpsFixup(pyramid_app, settings)
367
367
368 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
368 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
369 pyramid_app, settings)
369 pyramid_app, settings)
370 registry.ae_client = _ae_client
370 registry.ae_client = _ae_client
371
371
372 if settings['gzip_responses']:
372 if settings['gzip_responses']:
373 pyramid_app = make_gzip_middleware(
373 pyramid_app = make_gzip_middleware(
374 pyramid_app, settings, compress_level=1)
374 pyramid_app, settings, compress_level=1)
375
375
376 # this should be the outer most middleware in the wsgi stack since
376 # this should be the outer most middleware in the wsgi stack since
377 # middleware like Routes make database calls
377 # middleware like Routes make database calls
378 def pyramid_app_with_cleanup(environ, start_response):
378 def pyramid_app_with_cleanup(environ, start_response):
379 try:
379 try:
380 return pyramid_app(environ, start_response)
380 return pyramid_app(environ, start_response)
381 finally:
381 finally:
382 # Dispose current database session and rollback uncommitted
382 # Dispose current database session and rollback uncommitted
383 # transactions.
383 # transactions.
384 meta.Session.remove()
384 meta.Session.remove()
385
385
386 # In a single threaded mode server, on non sqlite db we should have
386 # In a single threaded mode server, on non sqlite db we should have
387 # '0 Current Checked out connections' at the end of a request,
387 # '0 Current Checked out connections' at the end of a request,
388 # if not, then something, somewhere is leaving a connection open
388 # if not, then something, somewhere is leaving a connection open
389 pool = meta.Base.metadata.bind.engine.pool
389 pool = meta.Base.metadata.bind.engine.pool
390 log.debug('sa pool status: %s', pool.status())
390 log.debug('sa pool status: %s', pool.status())
391 log.debug('Request processing finalized')
391 log.debug('Request processing finalized')
392
392
393 return pyramid_app_with_cleanup
393 return pyramid_app_with_cleanup
394
394
395
395
396 def sanitize_settings_and_apply_defaults(global_config, settings):
396 def sanitize_settings_and_apply_defaults(global_config, settings):
397 """
397 """
398 Applies settings defaults and does all type conversion.
398 Applies settings defaults and does all type conversion.
399
399
400 We would move all settings parsing and preparation into this place, so that
400 We would move all settings parsing and preparation into this place, so that
401 we have only one place left which deals with this part. The remaining parts
401 we have only one place left which deals with this part. The remaining parts
402 of the application would start to rely fully on well prepared settings.
402 of the application would start to rely fully on well prepared settings.
403
403
404 This piece would later be split up per topic to avoid a big fat monster
404 This piece would later be split up per topic to avoid a big fat monster
405 function.
405 function.
406 """
406 """
407
407
408 settings.setdefault('rhodecode.edition', 'Community Edition')
408 settings.setdefault('rhodecode.edition', 'Community Edition')
409
409
410 if 'mako.default_filters' not in settings:
410 if 'mako.default_filters' not in settings:
411 # set custom default filters if we don't have it defined
411 # set custom default filters if we don't have it defined
412 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
412 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
413 settings['mako.default_filters'] = 'h_filter'
413 settings['mako.default_filters'] = 'h_filter'
414
414
415 if 'mako.directories' not in settings:
415 if 'mako.directories' not in settings:
416 mako_directories = settings.setdefault('mako.directories', [
416 mako_directories = settings.setdefault('mako.directories', [
417 # Base templates of the original application
417 # Base templates of the original application
418 'rhodecode:templates',
418 'rhodecode:templates',
419 ])
419 ])
420 log.debug(
420 log.debug(
421 "Using the following Mako template directories: %s",
421 "Using the following Mako template directories: %s",
422 mako_directories)
422 mako_directories)
423
423
424 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
424 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
425 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
425 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
426 raw_url = settings['beaker.session.url']
426 raw_url = settings['beaker.session.url']
427 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
427 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
428 settings['beaker.session.url'] = 'redis://' + raw_url
428 settings['beaker.session.url'] = 'redis://' + raw_url
429
429
430 # Default includes, possible to change as a user
430 # Default includes, possible to change as a user
431 pyramid_includes = settings.setdefault('pyramid.includes', [])
431 pyramid_includes = settings.setdefault('pyramid.includes', [])
432 log.debug(
432 log.debug(
433 "Using the following pyramid.includes: %s",
433 "Using the following pyramid.includes: %s",
434 pyramid_includes)
434 pyramid_includes)
435
435
436 # TODO: johbo: Re-think this, usually the call to config.include
436 # TODO: johbo: Re-think this, usually the call to config.include
437 # should allow to pass in a prefix.
437 # should allow to pass in a prefix.
438 settings.setdefault('rhodecode.api.url', '/_admin/api')
438 settings.setdefault('rhodecode.api.url', '/_admin/api')
439 settings.setdefault('__file__', global_config.get('__file__'))
439 settings.setdefault('__file__', global_config.get('__file__'))
440
440
441 # Sanitize generic settings.
441 # Sanitize generic settings.
442 _list_setting(settings, 'default_encoding', 'UTF-8')
442 _list_setting(settings, 'default_encoding', 'UTF-8')
443 _bool_setting(settings, 'is_test', 'false')
443 _bool_setting(settings, 'is_test', 'false')
444 _bool_setting(settings, 'gzip_responses', 'false')
444 _bool_setting(settings, 'gzip_responses', 'false')
445
445
446 # Call split out functions that sanitize settings for each topic.
446 # Call split out functions that sanitize settings for each topic.
447 _sanitize_appenlight_settings(settings)
447 _sanitize_appenlight_settings(settings)
448 _sanitize_vcs_settings(settings)
448 _sanitize_vcs_settings(settings)
449 _sanitize_cache_settings(settings)
449 _sanitize_cache_settings(settings)
450
450
451 # configure instance id
451 # configure instance id
452 config_utils.set_instance_id(settings)
452 config_utils.set_instance_id(settings)
453
453
454 return settings
454 return settings
455
455
456
456
457 def enable_debug():
457 def enable_debug():
458 """
458 """
459 Helper to enable debug on running instance
459 Helper to enable debug on running instance
460 :return:
460 :return:
461 """
461 """
462 import tempfile
462 import tempfile
463 import textwrap
463 import textwrap
464 import logging.config
464 import logging.config
465
465
466 ini_template = textwrap.dedent("""
466 ini_template = textwrap.dedent("""
467 #####################################
467 #####################################
468 ### DEBUG LOGGING CONFIGURATION ####
468 ### DEBUG LOGGING CONFIGURATION ####
469 #####################################
469 #####################################
470 [loggers]
470 [loggers]
471 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
471 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
472
472
473 [handlers]
473 [handlers]
474 keys = console, console_sql
474 keys = console, console_sql
475
475
476 [formatters]
476 [formatters]
477 keys = generic, color_formatter, color_formatter_sql
477 keys = generic, color_formatter, color_formatter_sql
478
478
479 #############
479 #############
480 ## LOGGERS ##
480 ## LOGGERS ##
481 #############
481 #############
482 [logger_root]
482 [logger_root]
483 level = NOTSET
483 level = NOTSET
484 handlers = console
484 handlers = console
485
485
486 [logger_sqlalchemy]
486 [logger_sqlalchemy]
487 level = INFO
487 level = INFO
488 handlers = console_sql
488 handlers = console_sql
489 qualname = sqlalchemy.engine
489 qualname = sqlalchemy.engine
490 propagate = 0
490 propagate = 0
491
491
492 [logger_beaker]
492 [logger_beaker]
493 level = DEBUG
493 level = DEBUG
494 handlers =
494 handlers =
495 qualname = beaker.container
495 qualname = beaker.container
496 propagate = 1
496 propagate = 1
497
497
498 [logger_rhodecode]
498 [logger_rhodecode]
499 level = DEBUG
499 level = DEBUG
500 handlers =
500 handlers =
501 qualname = rhodecode
501 qualname = rhodecode
502 propagate = 1
502 propagate = 1
503
503
504 [logger_ssh_wrapper]
504 [logger_ssh_wrapper]
505 level = DEBUG
505 level = DEBUG
506 handlers =
506 handlers =
507 qualname = ssh_wrapper
507 qualname = ssh_wrapper
508 propagate = 1
508 propagate = 1
509
509
510 [logger_celery]
510 [logger_celery]
511 level = DEBUG
511 level = DEBUG
512 handlers =
512 handlers =
513 qualname = celery
513 qualname = celery
514
514
515
515
516 ##############
516 ##############
517 ## HANDLERS ##
517 ## HANDLERS ##
518 ##############
518 ##############
519
519
520 [handler_console]
520 [handler_console]
521 class = StreamHandler
521 class = StreamHandler
522 args = (sys.stderr, )
522 args = (sys.stderr, )
523 level = DEBUG
523 level = DEBUG
524 formatter = color_formatter
524 formatter = color_formatter
525
525
526 [handler_console_sql]
526 [handler_console_sql]
527 # "level = DEBUG" logs SQL queries and results.
527 # "level = DEBUG" logs SQL queries and results.
528 # "level = INFO" logs SQL queries.
528 # "level = INFO" logs SQL queries.
529 # "level = WARN" logs neither. (Recommended for production systems.)
529 # "level = WARN" logs neither. (Recommended for production systems.)
530 class = StreamHandler
530 class = StreamHandler
531 args = (sys.stderr, )
531 args = (sys.stderr, )
532 level = WARN
532 level = WARN
533 formatter = color_formatter_sql
533 formatter = color_formatter_sql
534
534
535 ################
535 ################
536 ## FORMATTERS ##
536 ## FORMATTERS ##
537 ################
537 ################
538
538
539 [formatter_generic]
539 [formatter_generic]
540 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
540 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
541 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
541 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
542 datefmt = %Y-%m-%d %H:%M:%S
542 datefmt = %Y-%m-%d %H:%M:%S
543
543
544 [formatter_color_formatter]
544 [formatter_color_formatter]
545 class = rhodecode.lib.logging_formatter.ColorRequestTrackingFormatter
545 class = rhodecode.lib.logging_formatter.ColorRequestTrackingFormatter
546 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
546 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
547 datefmt = %Y-%m-%d %H:%M:%S
547 datefmt = %Y-%m-%d %H:%M:%S
548
548
549 [formatter_color_formatter_sql]
549 [formatter_color_formatter_sql]
550 class = rhodecode.lib.logging_formatter.ColorFormatterSql
550 class = rhodecode.lib.logging_formatter.ColorFormatterSql
551 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
551 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
552 datefmt = %Y-%m-%d %H:%M:%S
552 datefmt = %Y-%m-%d %H:%M:%S
553 """)
553 """)
554
554
555 with tempfile.NamedTemporaryFile(prefix='rc_debug_logging_', suffix='.ini',
555 with tempfile.NamedTemporaryFile(prefix='rc_debug_logging_', suffix='.ini',
556 delete=False) as f:
556 delete=False) as f:
557 log.info('Saved Temporary DEBUG config at %s', f.name)
557 log.info('Saved Temporary DEBUG config at %s', f.name)
558 f.write(ini_template)
558 f.write(ini_template)
559
559
560 logging.config.fileConfig(f.name)
560 logging.config.fileConfig(f.name)
561 log.debug('DEBUG MODE ON')
561 log.debug('DEBUG MODE ON')
562 os.remove(f.name)
562 os.remove(f.name)
563
563
564
564
565 def _sanitize_appenlight_settings(settings):
565 def _sanitize_appenlight_settings(settings):
566 _bool_setting(settings, 'appenlight', 'false')
566 _bool_setting(settings, 'appenlight', 'false')
567
567
568
568
569 def _sanitize_vcs_settings(settings):
569 def _sanitize_vcs_settings(settings):
570 """
570 """
571 Applies settings defaults and does type conversion for all VCS related
571 Applies settings defaults and does type conversion for all VCS related
572 settings.
572 settings.
573 """
573 """
574 _string_setting(settings, 'vcs.svn.compatible_version', '')
574 _string_setting(settings, 'vcs.svn.compatible_version', '')
575 _string_setting(settings, 'vcs.hooks.protocol', 'http')
575 _string_setting(settings, 'vcs.hooks.protocol', 'http')
576 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
576 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
577 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
577 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
578 _string_setting(settings, 'vcs.server', '')
578 _string_setting(settings, 'vcs.server', '')
579 _string_setting(settings, 'vcs.server.protocol', 'http')
579 _string_setting(settings, 'vcs.server.protocol', 'http')
580 _bool_setting(settings, 'startup.import_repos', 'false')
580 _bool_setting(settings, 'startup.import_repos', 'false')
581 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
581 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
582 _bool_setting(settings, 'vcs.server.enable', 'true')
582 _bool_setting(settings, 'vcs.server.enable', 'true')
583 _bool_setting(settings, 'vcs.start_server', 'false')
583 _bool_setting(settings, 'vcs.start_server', 'false')
584 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
584 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
585 _int_setting(settings, 'vcs.connection_timeout', 3600)
585 _int_setting(settings, 'vcs.connection_timeout', 3600)
586
586
587 # Support legacy values of vcs.scm_app_implementation. Legacy
587 # Support legacy values of vcs.scm_app_implementation. Legacy
588 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
588 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
589 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
589 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
590 scm_app_impl = settings['vcs.scm_app_implementation']
590 scm_app_impl = settings['vcs.scm_app_implementation']
591 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
591 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
592 settings['vcs.scm_app_implementation'] = 'http'
592 settings['vcs.scm_app_implementation'] = 'http'
593
593
594
594
595 def _sanitize_cache_settings(settings):
595 def _sanitize_cache_settings(settings):
596 temp_store = tempfile.gettempdir()
596 temp_store = tempfile.gettempdir()
597 default_cache_dir = os.path.join(temp_store, 'rc_cache')
597 default_cache_dir = os.path.join(temp_store, 'rc_cache')
598
598
599 # save default, cache dir, and use it for all backends later.
599 # save default, cache dir, and use it for all backends later.
600 default_cache_dir = _string_setting(
600 default_cache_dir = _string_setting(
601 settings,
601 settings,
602 'cache_dir',
602 'cache_dir',
603 default_cache_dir, lower=False, default_when_empty=True)
603 default_cache_dir, lower=False, default_when_empty=True)
604
604
605 # ensure we have our dir created
605 # ensure we have our dir created
606 if not os.path.isdir(default_cache_dir):
606 if not os.path.isdir(default_cache_dir):
607 os.makedirs(default_cache_dir, mode=0o755)
607 os.makedirs(default_cache_dir, mode=0o755)
608
608
609 # exception store cache
609 # exception store cache
610 _string_setting(
610 _string_setting(
611 settings,
611 settings,
612 'exception_tracker.store_path',
612 'exception_tracker.store_path',
613 temp_store, lower=False, default_when_empty=True)
613 temp_store, lower=False, default_when_empty=True)
614 _bool_setting(
615 settings,
616 'exception_tracker.send_email',
617 'false')
618 _string_setting(
619 settings,
620 'exception_tracker.email_prefix',
621 '[RHODECODE ERROR]', lower=False, default_when_empty=True)
614
622
615 # cache_perms
623 # cache_perms
616 _string_setting(
624 _string_setting(
617 settings,
625 settings,
618 'rc_cache.cache_perms.backend',
626 'rc_cache.cache_perms.backend',
619 'dogpile.cache.rc.file_namespace', lower=False)
627 'dogpile.cache.rc.file_namespace', lower=False)
620 _int_setting(
628 _int_setting(
621 settings,
629 settings,
622 'rc_cache.cache_perms.expiration_time',
630 'rc_cache.cache_perms.expiration_time',
623 60)
631 60)
624 _string_setting(
632 _string_setting(
625 settings,
633 settings,
626 'rc_cache.cache_perms.arguments.filename',
634 'rc_cache.cache_perms.arguments.filename',
627 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
635 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
628
636
629 # cache_repo
637 # cache_repo
630 _string_setting(
638 _string_setting(
631 settings,
639 settings,
632 'rc_cache.cache_repo.backend',
640 'rc_cache.cache_repo.backend',
633 'dogpile.cache.rc.file_namespace', lower=False)
641 'dogpile.cache.rc.file_namespace', lower=False)
634 _int_setting(
642 _int_setting(
635 settings,
643 settings,
636 'rc_cache.cache_repo.expiration_time',
644 'rc_cache.cache_repo.expiration_time',
637 60)
645 60)
638 _string_setting(
646 _string_setting(
639 settings,
647 settings,
640 'rc_cache.cache_repo.arguments.filename',
648 'rc_cache.cache_repo.arguments.filename',
641 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
649 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
642
650
643 # cache_license
651 # cache_license
644 _string_setting(
652 _string_setting(
645 settings,
653 settings,
646 'rc_cache.cache_license.backend',
654 'rc_cache.cache_license.backend',
647 'dogpile.cache.rc.file_namespace', lower=False)
655 'dogpile.cache.rc.file_namespace', lower=False)
648 _int_setting(
656 _int_setting(
649 settings,
657 settings,
650 'rc_cache.cache_license.expiration_time',
658 'rc_cache.cache_license.expiration_time',
651 5*60)
659 5*60)
652 _string_setting(
660 _string_setting(
653 settings,
661 settings,
654 'rc_cache.cache_license.arguments.filename',
662 'rc_cache.cache_license.arguments.filename',
655 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
663 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
656
664
657 # cache_repo_longterm memory, 96H
665 # cache_repo_longterm memory, 96H
658 _string_setting(
666 _string_setting(
659 settings,
667 settings,
660 'rc_cache.cache_repo_longterm.backend',
668 'rc_cache.cache_repo_longterm.backend',
661 'dogpile.cache.rc.memory_lru', lower=False)
669 'dogpile.cache.rc.memory_lru', lower=False)
662 _int_setting(
670 _int_setting(
663 settings,
671 settings,
664 'rc_cache.cache_repo_longterm.expiration_time',
672 'rc_cache.cache_repo_longterm.expiration_time',
665 345600)
673 345600)
666 _int_setting(
674 _int_setting(
667 settings,
675 settings,
668 'rc_cache.cache_repo_longterm.max_size',
676 'rc_cache.cache_repo_longterm.max_size',
669 10000)
677 10000)
670
678
671 # sql_cache_short
679 # sql_cache_short
672 _string_setting(
680 _string_setting(
673 settings,
681 settings,
674 'rc_cache.sql_cache_short.backend',
682 'rc_cache.sql_cache_short.backend',
675 'dogpile.cache.rc.memory_lru', lower=False)
683 'dogpile.cache.rc.memory_lru', lower=False)
676 _int_setting(
684 _int_setting(
677 settings,
685 settings,
678 'rc_cache.sql_cache_short.expiration_time',
686 'rc_cache.sql_cache_short.expiration_time',
679 30)
687 30)
680 _int_setting(
688 _int_setting(
681 settings,
689 settings,
682 'rc_cache.sql_cache_short.max_size',
690 'rc_cache.sql_cache_short.max_size',
683 10000)
691 10000)
684
692
685
693
686 def _int_setting(settings, name, default):
694 def _int_setting(settings, name, default):
687 settings[name] = int(settings.get(name, default))
695 settings[name] = int(settings.get(name, default))
688 return settings[name]
696 return settings[name]
689
697
690
698
691 def _bool_setting(settings, name, default):
699 def _bool_setting(settings, name, default):
692 input_val = settings.get(name, default)
700 input_val = settings.get(name, default)
693 if isinstance(input_val, unicode):
701 if isinstance(input_val, unicode):
694 input_val = input_val.encode('utf8')
702 input_val = input_val.encode('utf8')
695 settings[name] = asbool(input_val)
703 settings[name] = asbool(input_val)
696 return settings[name]
704 return settings[name]
697
705
698
706
699 def _list_setting(settings, name, default):
707 def _list_setting(settings, name, default):
700 raw_value = settings.get(name, default)
708 raw_value = settings.get(name, default)
701
709
702 old_separator = ','
710 old_separator = ','
703 if old_separator in raw_value:
711 if old_separator in raw_value:
704 # If we get a comma separated list, pass it to our own function.
712 # If we get a comma separated list, pass it to our own function.
705 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
713 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
706 else:
714 else:
707 # Otherwise we assume it uses pyramids space/newline separation.
715 # Otherwise we assume it uses pyramids space/newline separation.
708 settings[name] = aslist(raw_value)
716 settings[name] = aslist(raw_value)
709 return settings[name]
717 return settings[name]
710
718
711
719
712 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
720 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
713 value = settings.get(name, default)
721 value = settings.get(name, default)
714
722
715 if default_when_empty and not value:
723 if default_when_empty and not value:
716 # use default value when value is empty
724 # use default value when value is empty
717 value = default
725 value = default
718
726
719 if lower:
727 if lower:
720 value = value.lower()
728 value = value.lower()
721 settings[name] = value
729 settings[name] = value
722 return settings[name]
730 return settings[name]
723
731
724
732
725 def _substitute_values(mapping, substitutions):
733 def _substitute_values(mapping, substitutions):
726 result = {}
734 result = {}
727
735
728 try:
736 try:
729 for key, value in mapping.items():
737 for key, value in mapping.items():
730 # initialize without substitution first
738 # initialize without substitution first
731 result[key] = value
739 result[key] = value
732
740
733 # Note: Cannot use regular replacements, since they would clash
741 # Note: Cannot use regular replacements, since they would clash
734 # with the implementation of ConfigParser. Using "format" instead.
742 # with the implementation of ConfigParser. Using "format" instead.
735 try:
743 try:
736 result[key] = value.format(**substitutions)
744 result[key] = value.format(**substitutions)
737 except KeyError as e:
745 except KeyError as e:
738 env_var = '{}'.format(e.args[0])
746 env_var = '{}'.format(e.args[0])
739
747
740 msg = 'Failed to substitute: `{key}={{{var}}}` with environment entry. ' \
748 msg = 'Failed to substitute: `{key}={{{var}}}` with environment entry. ' \
741 'Make sure your environment has {var} set, or remove this ' \
749 'Make sure your environment has {var} set, or remove this ' \
742 'variable from config file'.format(key=key, var=env_var)
750 'variable from config file'.format(key=key, var=env_var)
743
751
744 if env_var.startswith('ENV_'):
752 if env_var.startswith('ENV_'):
745 raise ValueError(msg)
753 raise ValueError(msg)
746 else:
754 else:
747 log.warning(msg)
755 log.warning(msg)
748
756
749 except ValueError as e:
757 except ValueError as e:
750 log.warning('Failed to substitute ENV variable: %s', e)
758 log.warning('Failed to substitute ENV variable: %s', e)
751 result = mapping
759 result = mapping
752
760
753 return result
761 return result
@@ -1,173 +1,217 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import time
22 import time
23 import datetime
23 import datetime
24 import msgpack
24 import msgpack
25 import logging
25 import logging
26 import traceback
26 import traceback
27 import tempfile
27 import tempfile
28 import glob
28 import glob
29
29
30
31 log = logging.getLogger(__name__)
30 log = logging.getLogger(__name__)
32
31
33 # NOTE: Any changes should be synced with exc_tracking at vcsserver.lib.exc_tracking
32 # NOTE: Any changes should be synced with exc_tracking at vcsserver.lib.exc_tracking
34 global_prefix = 'rhodecode'
33 global_prefix = 'rhodecode'
35 exc_store_dir_name = 'rc_exception_store_v1'
34 exc_store_dir_name = 'rc_exception_store_v1'
36
35
37
36
38 def exc_serialize(exc_id, tb, exc_type):
37 def exc_serialize(exc_id, tb, exc_type):
39
38
40 data = {
39 data = {
41 'version': 'v1',
40 'version': 'v1',
42 'exc_id': exc_id,
41 'exc_id': exc_id,
43 'exc_utc_date': datetime.datetime.utcnow().isoformat(),
42 'exc_utc_date': datetime.datetime.utcnow().isoformat(),
44 'exc_timestamp': repr(time.time()),
43 'exc_timestamp': repr(time.time()),
45 'exc_message': tb,
44 'exc_message': tb,
46 'exc_type': exc_type,
45 'exc_type': exc_type,
47 }
46 }
48 return msgpack.packb(data), data
47 return msgpack.packb(data), data
49
48
50
49
51 def exc_unserialize(tb):
50 def exc_unserialize(tb):
52 return msgpack.unpackb(tb)
51 return msgpack.unpackb(tb)
53
52
54 _exc_store = None
53 _exc_store = None
55
54
56
55
57 def get_exc_store():
56 def get_exc_store():
58 """
57 """
59 Get and create exception store if it's not existing
58 Get and create exception store if it's not existing
60 """
59 """
61 global _exc_store
60 global _exc_store
62 import rhodecode as app
61 import rhodecode as app
63
62
64 if _exc_store is not None:
63 if _exc_store is not None:
65 # quick global cache
64 # quick global cache
66 return _exc_store
65 return _exc_store
67
66
68 exc_store_dir = app.CONFIG.get('exception_tracker.store_path', '') or tempfile.gettempdir()
67 exc_store_dir = app.CONFIG.get('exception_tracker.store_path', '') or tempfile.gettempdir()
69 _exc_store_path = os.path.join(exc_store_dir, exc_store_dir_name)
68 _exc_store_path = os.path.join(exc_store_dir, exc_store_dir_name)
70
69
71 _exc_store_path = os.path.abspath(_exc_store_path)
70 _exc_store_path = os.path.abspath(_exc_store_path)
72 if not os.path.isdir(_exc_store_path):
71 if not os.path.isdir(_exc_store_path):
73 os.makedirs(_exc_store_path)
72 os.makedirs(_exc_store_path)
74 log.debug('Initializing exceptions store at %s', _exc_store_path)
73 log.debug('Initializing exceptions store at %s', _exc_store_path)
75 _exc_store = _exc_store_path
74 _exc_store = _exc_store_path
76
75
77 return _exc_store_path
76 return _exc_store_path
78
77
79
78
80 def _store_exception(exc_id, exc_type_name, exc_traceback, prefix):
79 def _store_exception(exc_id, exc_type_name, exc_traceback, prefix, send_email=None):
81 """
80 """
82 Low level function to store exception in the exception tracker
81 Low level function to store exception in the exception tracker
83 """
82 """
83 import rhodecode as app
84
84
85 exc_store_path = get_exc_store()
85 exc_store_path = get_exc_store()
86 exc_data, org_data = exc_serialize(exc_id, exc_traceback, exc_type_name)
86 exc_data, org_data = exc_serialize(exc_id, exc_traceback, exc_type_name)
87 exc_pref_id = '{}_{}_{}'.format(exc_id, prefix, org_data['exc_timestamp'])
87 exc_pref_id = '{}_{}_{}'.format(exc_id, prefix, org_data['exc_timestamp'])
88 if not os.path.isdir(exc_store_path):
88 if not os.path.isdir(exc_store_path):
89 os.makedirs(exc_store_path)
89 os.makedirs(exc_store_path)
90 stored_exc_path = os.path.join(exc_store_path, exc_pref_id)
90 stored_exc_path = os.path.join(exc_store_path, exc_pref_id)
91 with open(stored_exc_path, 'wb') as f:
91 with open(stored_exc_path, 'wb') as f:
92 f.write(exc_data)
92 f.write(exc_data)
93 log.debug('Stored generated exception %s as: %s', exc_id, stored_exc_path)
93 log.debug('Stored generated exception %s as: %s', exc_id, stored_exc_path)
94
94
95 if send_email is None:
96 # NOTE(marcink): read app config unless we specify explicitly
97 send_email = app.CONFIG.get('exception_tracker.send_email', False)
98
99 if send_email:
100 try:
101 send_exc_email(exc_id, exc_type_name)
102 except Exception:
103 log.exception('Failed to send exception email')
104 pass
105
106
107 def send_exc_email(exc_id, exc_type_name):
108 import rhodecode as app
109 from pyramid.threadlocal import get_current_request
110 from rhodecode.apps._base import TemplateArgs
111 from rhodecode.lib.utils2 import aslist
112 from rhodecode.lib.celerylib import run_task, tasks
113 from rhodecode.lib.base import attach_context_attributes
114 from rhodecode.model.notification import EmailNotificationModel
115
116 request = get_current_request()
117
118 recipients = aslist(app.CONFIG.get('exception_tracker.send_email_recipients', ''))
119 log.debug('Sending Email exception to: `%s`', recipients or 'all super admins')
120
121 # NOTE(marcink): needed for email template rendering
122 attach_context_attributes(TemplateArgs(), request, request.user.user_id)
123
124 email_kwargs = {
125 'email_prefix': app.CONFIG.get('exception_tracker.email_prefix', '') or '[RHODECODE ERROR]',
126 'exc_url': request.route_url('admin_settings_exception_tracker_show', exception_id=exc_id),
127 'exc_id': exc_id,
128 'exc_type_name': exc_type_name,
129 'exc_traceback': read_exception(exc_id, prefix=None),
130 }
131
132 (subject, headers, email_body,
133 email_body_plaintext) = EmailNotificationModel().render_email(
134 EmailNotificationModel.TYPE_EMAIL_EXCEPTION, **email_kwargs)
135
136 run_task(tasks.send_email, recipients, subject,
137 email_body_plaintext, email_body)
138
95
139
96 def _prepare_exception(exc_info):
140 def _prepare_exception(exc_info):
97 exc_type, exc_value, exc_traceback = exc_info
141 exc_type, exc_value, exc_traceback = exc_info
98 exc_type_name = exc_type.__name__
142 exc_type_name = exc_type.__name__
99
143
100 tb = ''.join(traceback.format_exception(
144 tb = ''.join(traceback.format_exception(
101 exc_type, exc_value, exc_traceback, None))
145 exc_type, exc_value, exc_traceback, None))
102
146
103 return exc_type_name, tb
147 return exc_type_name, tb
104
148
105
149
106 def store_exception(exc_id, exc_info, prefix=global_prefix):
150 def store_exception(exc_id, exc_info, prefix=global_prefix):
107 """
151 """
108 Example usage::
152 Example usage::
109
153
110 exc_info = sys.exc_info()
154 exc_info = sys.exc_info()
111 store_exception(id(exc_info), exc_info)
155 store_exception(id(exc_info), exc_info)
112 """
156 """
113
157
114 try:
158 try:
115 exc_type_name, exc_traceback = _prepare_exception(exc_info)
159 exc_type_name, exc_traceback = _prepare_exception(exc_info)
116 _store_exception(exc_id=exc_id, exc_type_name=exc_type_name,
160 _store_exception(exc_id=exc_id, exc_type_name=exc_type_name,
117 exc_traceback=exc_traceback, prefix=prefix)
161 exc_traceback=exc_traceback, prefix=prefix)
118 return exc_id, exc_type_name
162 return exc_id, exc_type_name
119 except Exception:
163 except Exception:
120 log.exception('Failed to store exception `%s` information', exc_id)
164 log.exception('Failed to store exception `%s` information', exc_id)
121 # there's no way this can fail, it will crash server badly if it does.
165 # there's no way this can fail, it will crash server badly if it does.
122 pass
166 pass
123
167
124
168
125 def _find_exc_file(exc_id, prefix=global_prefix):
169 def _find_exc_file(exc_id, prefix=global_prefix):
126 exc_store_path = get_exc_store()
170 exc_store_path = get_exc_store()
127 if prefix:
171 if prefix:
128 exc_id = '{}_{}'.format(exc_id, prefix)
172 exc_id = '{}_{}'.format(exc_id, prefix)
129 else:
173 else:
130 # search without a prefix
174 # search without a prefix
131 exc_id = '{}'.format(exc_id)
175 exc_id = '{}'.format(exc_id)
132
176
133 found_exc_id = None
177 found_exc_id = None
134 matches = glob.glob(os.path.join(exc_store_path, exc_id) + '*')
178 matches = glob.glob(os.path.join(exc_store_path, exc_id) + '*')
135 if matches:
179 if matches:
136 found_exc_id = matches[0]
180 found_exc_id = matches[0]
137
181
138 return found_exc_id
182 return found_exc_id
139
183
140
184
141 def _read_exception(exc_id, prefix):
185 def _read_exception(exc_id, prefix):
142 exc_id_file_path = _find_exc_file(exc_id=exc_id, prefix=prefix)
186 exc_id_file_path = _find_exc_file(exc_id=exc_id, prefix=prefix)
143 if exc_id_file_path:
187 if exc_id_file_path:
144 with open(exc_id_file_path, 'rb') as f:
188 with open(exc_id_file_path, 'rb') as f:
145 return exc_unserialize(f.read())
189 return exc_unserialize(f.read())
146 else:
190 else:
147 log.debug('Exception File `%s` not found', exc_id_file_path)
191 log.debug('Exception File `%s` not found', exc_id_file_path)
148 return None
192 return None
149
193
150
194
151 def read_exception(exc_id, prefix=global_prefix):
195 def read_exception(exc_id, prefix=global_prefix):
152 try:
196 try:
153 return _read_exception(exc_id=exc_id, prefix=prefix)
197 return _read_exception(exc_id=exc_id, prefix=prefix)
154 except Exception:
198 except Exception:
155 log.exception('Failed to read exception `%s` information', exc_id)
199 log.exception('Failed to read exception `%s` information', exc_id)
156 # there's no way this can fail, it will crash server badly if it does.
200 # there's no way this can fail, it will crash server badly if it does.
157 return None
201 return None
158
202
159
203
160 def delete_exception(exc_id, prefix=global_prefix):
204 def delete_exception(exc_id, prefix=global_prefix):
161 try:
205 try:
162 exc_id_file_path = _find_exc_file(exc_id, prefix=prefix)
206 exc_id_file_path = _find_exc_file(exc_id, prefix=prefix)
163 if exc_id_file_path:
207 if exc_id_file_path:
164 os.remove(exc_id_file_path)
208 os.remove(exc_id_file_path)
165
209
166 except Exception:
210 except Exception:
167 log.exception('Failed to remove exception `%s` information', exc_id)
211 log.exception('Failed to remove exception `%s` information', exc_id)
168 # there's no way this can fail, it will crash server badly if it does.
212 # there's no way this can fail, it will crash server badly if it does.
169 pass
213 pass
170
214
171
215
172 def generate_id():
216 def generate_id():
173 return id(object())
217 return id(object())
@@ -1,408 +1,411 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 Model for notifications
23 Model for notifications
24 """
24 """
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 import premailer
29 import premailer
30 from pyramid.threadlocal import get_current_request
30 from pyramid.threadlocal import get_current_request
31 from sqlalchemy.sql.expression import false, true
31 from sqlalchemy.sql.expression import false, true
32
32
33 import rhodecode
33 import rhodecode
34 from rhodecode.lib import helpers as h
34 from rhodecode.lib import helpers as h
35 from rhodecode.model import BaseModel
35 from rhodecode.model import BaseModel
36 from rhodecode.model.db import Notification, User, UserNotification
36 from rhodecode.model.db import Notification, User, UserNotification
37 from rhodecode.model.meta import Session
37 from rhodecode.model.meta import Session
38 from rhodecode.translation import TranslationString
38 from rhodecode.translation import TranslationString
39
39
40 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
41
41
42
42
43 class NotificationModel(BaseModel):
43 class NotificationModel(BaseModel):
44
44
45 cls = Notification
45 cls = Notification
46
46
47 def __get_notification(self, notification):
47 def __get_notification(self, notification):
48 if isinstance(notification, Notification):
48 if isinstance(notification, Notification):
49 return notification
49 return notification
50 elif isinstance(notification, (int, long)):
50 elif isinstance(notification, (int, long)):
51 return Notification.get(notification)
51 return Notification.get(notification)
52 else:
52 else:
53 if notification:
53 if notification:
54 raise Exception('notification must be int, long or Instance'
54 raise Exception('notification must be int, long or Instance'
55 ' of Notification got %s' % type(notification))
55 ' of Notification got %s' % type(notification))
56
56
57 def create(
57 def create(
58 self, created_by, notification_subject, notification_body,
58 self, created_by, notification_subject, notification_body,
59 notification_type=Notification.TYPE_MESSAGE, recipients=None,
59 notification_type=Notification.TYPE_MESSAGE, recipients=None,
60 mention_recipients=None, with_email=True, email_kwargs=None):
60 mention_recipients=None, with_email=True, email_kwargs=None):
61 """
61 """
62
62
63 Creates notification of given type
63 Creates notification of given type
64
64
65 :param created_by: int, str or User instance. User who created this
65 :param created_by: int, str or User instance. User who created this
66 notification
66 notification
67 :param notification_subject: subject of notification itself
67 :param notification_subject: subject of notification itself
68 :param notification_body: body of notification text
68 :param notification_body: body of notification text
69 :param notification_type: type of notification, based on that we
69 :param notification_type: type of notification, based on that we
70 pick templates
70 pick templates
71
71
72 :param recipients: list of int, str or User objects, when None
72 :param recipients: list of int, str or User objects, when None
73 is given send to all admins
73 is given send to all admins
74 :param mention_recipients: list of int, str or User objects,
74 :param mention_recipients: list of int, str or User objects,
75 that were mentioned
75 that were mentioned
76 :param with_email: send email with this notification
76 :param with_email: send email with this notification
77 :param email_kwargs: dict with arguments to generate email
77 :param email_kwargs: dict with arguments to generate email
78 """
78 """
79
79
80 from rhodecode.lib.celerylib import tasks, run_task
80 from rhodecode.lib.celerylib import tasks, run_task
81
81
82 if recipients and not getattr(recipients, '__iter__', False):
82 if recipients and not getattr(recipients, '__iter__', False):
83 raise Exception('recipients must be an iterable object')
83 raise Exception('recipients must be an iterable object')
84
84
85 created_by_obj = self._get_user(created_by)
85 created_by_obj = self._get_user(created_by)
86 # default MAIN body if not given
86 # default MAIN body if not given
87 email_kwargs = email_kwargs or {'body': notification_body}
87 email_kwargs = email_kwargs or {'body': notification_body}
88 mention_recipients = mention_recipients or set()
88 mention_recipients = mention_recipients or set()
89
89
90 if not created_by_obj:
90 if not created_by_obj:
91 raise Exception('unknown user %s' % created_by)
91 raise Exception('unknown user %s' % created_by)
92
92
93 if recipients is None:
93 if recipients is None:
94 # recipients is None means to all admins
94 # recipients is None means to all admins
95 recipients_objs = User.query().filter(User.admin == true()).all()
95 recipients_objs = User.query().filter(User.admin == true()).all()
96 log.debug('sending notifications %s to admins: %s',
96 log.debug('sending notifications %s to admins: %s',
97 notification_type, recipients_objs)
97 notification_type, recipients_objs)
98 else:
98 else:
99 recipients_objs = set()
99 recipients_objs = set()
100 for u in recipients:
100 for u in recipients:
101 obj = self._get_user(u)
101 obj = self._get_user(u)
102 if obj:
102 if obj:
103 recipients_objs.add(obj)
103 recipients_objs.add(obj)
104 else: # we didn't find this user, log the error and carry on
104 else: # we didn't find this user, log the error and carry on
105 log.error('cannot notify unknown user %r', u)
105 log.error('cannot notify unknown user %r', u)
106
106
107 if not recipients_objs:
107 if not recipients_objs:
108 raise Exception('no valid recipients specified')
108 raise Exception('no valid recipients specified')
109
109
110 log.debug('sending notifications %s to %s',
110 log.debug('sending notifications %s to %s',
111 notification_type, recipients_objs)
111 notification_type, recipients_objs)
112
112
113 # add mentioned users into recipients
113 # add mentioned users into recipients
114 final_recipients = set(recipients_objs).union(mention_recipients)
114 final_recipients = set(recipients_objs).union(mention_recipients)
115
115
116 notification = Notification.create(
116 notification = Notification.create(
117 created_by=created_by_obj, subject=notification_subject,
117 created_by=created_by_obj, subject=notification_subject,
118 body=notification_body, recipients=final_recipients,
118 body=notification_body, recipients=final_recipients,
119 type_=notification_type
119 type_=notification_type
120 )
120 )
121
121
122 if not with_email: # skip sending email, and just create notification
122 if not with_email: # skip sending email, and just create notification
123 return notification
123 return notification
124
124
125 # don't send email to person who created this comment
125 # don't send email to person who created this comment
126 rec_objs = set(recipients_objs).difference({created_by_obj})
126 rec_objs = set(recipients_objs).difference({created_by_obj})
127
127
128 # now notify all recipients in question
128 # now notify all recipients in question
129
129
130 for recipient in rec_objs.union(mention_recipients):
130 for recipient in rec_objs.union(mention_recipients):
131 # inject current recipient
131 # inject current recipient
132 email_kwargs['recipient'] = recipient
132 email_kwargs['recipient'] = recipient
133 email_kwargs['mention'] = recipient in mention_recipients
133 email_kwargs['mention'] = recipient in mention_recipients
134 (subject, headers, email_body,
134 (subject, headers, email_body,
135 email_body_plaintext) = EmailNotificationModel().render_email(
135 email_body_plaintext) = EmailNotificationModel().render_email(
136 notification_type, **email_kwargs)
136 notification_type, **email_kwargs)
137
137
138 log.debug(
138 log.debug(
139 'Creating notification email task for user:`%s`', recipient)
139 'Creating notification email task for user:`%s`', recipient)
140 task = run_task(
140 task = run_task(
141 tasks.send_email, recipient.email, subject,
141 tasks.send_email, recipient.email, subject,
142 email_body_plaintext, email_body)
142 email_body_plaintext, email_body)
143 log.debug('Created email task: %s', task)
143 log.debug('Created email task: %s', task)
144
144
145 return notification
145 return notification
146
146
147 def delete(self, user, notification):
147 def delete(self, user, notification):
148 # we don't want to remove actual notification just the assignment
148 # we don't want to remove actual notification just the assignment
149 try:
149 try:
150 notification = self.__get_notification(notification)
150 notification = self.__get_notification(notification)
151 user = self._get_user(user)
151 user = self._get_user(user)
152 if notification and user:
152 if notification and user:
153 obj = UserNotification.query()\
153 obj = UserNotification.query()\
154 .filter(UserNotification.user == user)\
154 .filter(UserNotification.user == user)\
155 .filter(UserNotification.notification == notification)\
155 .filter(UserNotification.notification == notification)\
156 .one()
156 .one()
157 Session().delete(obj)
157 Session().delete(obj)
158 return True
158 return True
159 except Exception:
159 except Exception:
160 log.error(traceback.format_exc())
160 log.error(traceback.format_exc())
161 raise
161 raise
162
162
163 def get_for_user(self, user, filter_=None):
163 def get_for_user(self, user, filter_=None):
164 """
164 """
165 Get mentions for given user, filter them if filter dict is given
165 Get mentions for given user, filter them if filter dict is given
166 """
166 """
167 user = self._get_user(user)
167 user = self._get_user(user)
168
168
169 q = UserNotification.query()\
169 q = UserNotification.query()\
170 .filter(UserNotification.user == user)\
170 .filter(UserNotification.user == user)\
171 .join((
171 .join((
172 Notification, UserNotification.notification_id ==
172 Notification, UserNotification.notification_id ==
173 Notification.notification_id))
173 Notification.notification_id))
174 if filter_ == ['all']:
174 if filter_ == ['all']:
175 q = q # no filter
175 q = q # no filter
176 elif filter_ == ['unread']:
176 elif filter_ == ['unread']:
177 q = q.filter(UserNotification.read == false())
177 q = q.filter(UserNotification.read == false())
178 elif filter_:
178 elif filter_:
179 q = q.filter(Notification.type_.in_(filter_))
179 q = q.filter(Notification.type_.in_(filter_))
180
180
181 return q
181 return q
182
182
183 def mark_read(self, user, notification):
183 def mark_read(self, user, notification):
184 try:
184 try:
185 notification = self.__get_notification(notification)
185 notification = self.__get_notification(notification)
186 user = self._get_user(user)
186 user = self._get_user(user)
187 if notification and user:
187 if notification and user:
188 obj = UserNotification.query()\
188 obj = UserNotification.query()\
189 .filter(UserNotification.user == user)\
189 .filter(UserNotification.user == user)\
190 .filter(UserNotification.notification == notification)\
190 .filter(UserNotification.notification == notification)\
191 .one()
191 .one()
192 obj.read = True
192 obj.read = True
193 Session().add(obj)
193 Session().add(obj)
194 return True
194 return True
195 except Exception:
195 except Exception:
196 log.error(traceback.format_exc())
196 log.error(traceback.format_exc())
197 raise
197 raise
198
198
199 def mark_all_read_for_user(self, user, filter_=None):
199 def mark_all_read_for_user(self, user, filter_=None):
200 user = self._get_user(user)
200 user = self._get_user(user)
201 q = UserNotification.query()\
201 q = UserNotification.query()\
202 .filter(UserNotification.user == user)\
202 .filter(UserNotification.user == user)\
203 .filter(UserNotification.read == false())\
203 .filter(UserNotification.read == false())\
204 .join((
204 .join((
205 Notification, UserNotification.notification_id ==
205 Notification, UserNotification.notification_id ==
206 Notification.notification_id))
206 Notification.notification_id))
207 if filter_ == ['unread']:
207 if filter_ == ['unread']:
208 q = q.filter(UserNotification.read == false())
208 q = q.filter(UserNotification.read == false())
209 elif filter_:
209 elif filter_:
210 q = q.filter(Notification.type_.in_(filter_))
210 q = q.filter(Notification.type_.in_(filter_))
211
211
212 # this is a little inefficient but sqlalchemy doesn't support
212 # this is a little inefficient but sqlalchemy doesn't support
213 # update on joined tables :(
213 # update on joined tables :(
214 for obj in q.all():
214 for obj in q.all():
215 obj.read = True
215 obj.read = True
216 Session().add(obj)
216 Session().add(obj)
217
217
218 def get_unread_cnt_for_user(self, user):
218 def get_unread_cnt_for_user(self, user):
219 user = self._get_user(user)
219 user = self._get_user(user)
220 return UserNotification.query()\
220 return UserNotification.query()\
221 .filter(UserNotification.read == false())\
221 .filter(UserNotification.read == false())\
222 .filter(UserNotification.user == user).count()
222 .filter(UserNotification.user == user).count()
223
223
224 def get_unread_for_user(self, user):
224 def get_unread_for_user(self, user):
225 user = self._get_user(user)
225 user = self._get_user(user)
226 return [x.notification for x in UserNotification.query()
226 return [x.notification for x in UserNotification.query()
227 .filter(UserNotification.read == false())
227 .filter(UserNotification.read == false())
228 .filter(UserNotification.user == user).all()]
228 .filter(UserNotification.user == user).all()]
229
229
230 def get_user_notification(self, user, notification):
230 def get_user_notification(self, user, notification):
231 user = self._get_user(user)
231 user = self._get_user(user)
232 notification = self.__get_notification(notification)
232 notification = self.__get_notification(notification)
233
233
234 return UserNotification.query()\
234 return UserNotification.query()\
235 .filter(UserNotification.notification == notification)\
235 .filter(UserNotification.notification == notification)\
236 .filter(UserNotification.user == user).scalar()
236 .filter(UserNotification.user == user).scalar()
237
237
238 def make_description(self, notification, translate, show_age=True):
238 def make_description(self, notification, translate, show_age=True):
239 """
239 """
240 Creates a human readable description based on properties
240 Creates a human readable description based on properties
241 of notification object
241 of notification object
242 """
242 """
243 _ = translate
243 _ = translate
244 _map = {
244 _map = {
245 notification.TYPE_CHANGESET_COMMENT: [
245 notification.TYPE_CHANGESET_COMMENT: [
246 _('%(user)s commented on commit %(date_or_age)s'),
246 _('%(user)s commented on commit %(date_or_age)s'),
247 _('%(user)s commented on commit at %(date_or_age)s'),
247 _('%(user)s commented on commit at %(date_or_age)s'),
248 ],
248 ],
249 notification.TYPE_MESSAGE: [
249 notification.TYPE_MESSAGE: [
250 _('%(user)s sent message %(date_or_age)s'),
250 _('%(user)s sent message %(date_or_age)s'),
251 _('%(user)s sent message at %(date_or_age)s'),
251 _('%(user)s sent message at %(date_or_age)s'),
252 ],
252 ],
253 notification.TYPE_MENTION: [
253 notification.TYPE_MENTION: [
254 _('%(user)s mentioned you %(date_or_age)s'),
254 _('%(user)s mentioned you %(date_or_age)s'),
255 _('%(user)s mentioned you at %(date_or_age)s'),
255 _('%(user)s mentioned you at %(date_or_age)s'),
256 ],
256 ],
257 notification.TYPE_REGISTRATION: [
257 notification.TYPE_REGISTRATION: [
258 _('%(user)s registered in RhodeCode %(date_or_age)s'),
258 _('%(user)s registered in RhodeCode %(date_or_age)s'),
259 _('%(user)s registered in RhodeCode at %(date_or_age)s'),
259 _('%(user)s registered in RhodeCode at %(date_or_age)s'),
260 ],
260 ],
261 notification.TYPE_PULL_REQUEST: [
261 notification.TYPE_PULL_REQUEST: [
262 _('%(user)s opened new pull request %(date_or_age)s'),
262 _('%(user)s opened new pull request %(date_or_age)s'),
263 _('%(user)s opened new pull request at %(date_or_age)s'),
263 _('%(user)s opened new pull request at %(date_or_age)s'),
264 ],
264 ],
265 notification.TYPE_PULL_REQUEST_UPDATE: [
265 notification.TYPE_PULL_REQUEST_UPDATE: [
266 _('%(user)s updated pull request %(date_or_age)s'),
266 _('%(user)s updated pull request %(date_or_age)s'),
267 _('%(user)s updated pull request at %(date_or_age)s'),
267 _('%(user)s updated pull request at %(date_or_age)s'),
268 ],
268 ],
269 notification.TYPE_PULL_REQUEST_COMMENT: [
269 notification.TYPE_PULL_REQUEST_COMMENT: [
270 _('%(user)s commented on pull request %(date_or_age)s'),
270 _('%(user)s commented on pull request %(date_or_age)s'),
271 _('%(user)s commented on pull request at %(date_or_age)s'),
271 _('%(user)s commented on pull request at %(date_or_age)s'),
272 ],
272 ],
273 }
273 }
274
274
275 templates = _map[notification.type_]
275 templates = _map[notification.type_]
276
276
277 if show_age:
277 if show_age:
278 template = templates[0]
278 template = templates[0]
279 date_or_age = h.age(notification.created_on)
279 date_or_age = h.age(notification.created_on)
280 if translate:
280 if translate:
281 date_or_age = translate(date_or_age)
281 date_or_age = translate(date_or_age)
282
282
283 if isinstance(date_or_age, TranslationString):
283 if isinstance(date_or_age, TranslationString):
284 date_or_age = date_or_age.interpolate()
284 date_or_age = date_or_age.interpolate()
285
285
286 else:
286 else:
287 template = templates[1]
287 template = templates[1]
288 date_or_age = h.format_date(notification.created_on)
288 date_or_age = h.format_date(notification.created_on)
289
289
290 return template % {
290 return template % {
291 'user': notification.created_by_user.username,
291 'user': notification.created_by_user.username,
292 'date_or_age': date_or_age,
292 'date_or_age': date_or_age,
293 }
293 }
294
294
295
295
296 class EmailNotificationModel(BaseModel):
296 class EmailNotificationModel(BaseModel):
297 TYPE_COMMIT_COMMENT = Notification.TYPE_CHANGESET_COMMENT
297 TYPE_COMMIT_COMMENT = Notification.TYPE_CHANGESET_COMMENT
298 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
298 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
299 TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
299 TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
300 TYPE_PULL_REQUEST_COMMENT = Notification.TYPE_PULL_REQUEST_COMMENT
300 TYPE_PULL_REQUEST_COMMENT = Notification.TYPE_PULL_REQUEST_COMMENT
301 TYPE_PULL_REQUEST_UPDATE = Notification.TYPE_PULL_REQUEST_UPDATE
301 TYPE_PULL_REQUEST_UPDATE = Notification.TYPE_PULL_REQUEST_UPDATE
302 TYPE_MAIN = Notification.TYPE_MESSAGE
302 TYPE_MAIN = Notification.TYPE_MESSAGE
303
303
304 TYPE_PASSWORD_RESET = 'password_reset'
304 TYPE_PASSWORD_RESET = 'password_reset'
305 TYPE_PASSWORD_RESET_CONFIRMATION = 'password_reset_confirmation'
305 TYPE_PASSWORD_RESET_CONFIRMATION = 'password_reset_confirmation'
306 TYPE_EMAIL_TEST = 'email_test'
306 TYPE_EMAIL_TEST = 'email_test'
307 TYPE_EMAIL_EXCEPTION = 'exception'
307 TYPE_TEST = 'test'
308 TYPE_TEST = 'test'
308
309
309 email_types = {
310 email_types = {
310 TYPE_MAIN:
311 TYPE_MAIN:
311 'rhodecode:templates/email_templates/main.mako',
312 'rhodecode:templates/email_templates/main.mako',
312 TYPE_TEST:
313 TYPE_TEST:
313 'rhodecode:templates/email_templates/test.mako',
314 'rhodecode:templates/email_templates/test.mako',
315 TYPE_EMAIL_EXCEPTION:
316 'rhodecode:templates/email_templates/exception_tracker.mako',
314 TYPE_EMAIL_TEST:
317 TYPE_EMAIL_TEST:
315 'rhodecode:templates/email_templates/email_test.mako',
318 'rhodecode:templates/email_templates/email_test.mako',
316 TYPE_REGISTRATION:
319 TYPE_REGISTRATION:
317 'rhodecode:templates/email_templates/user_registration.mako',
320 'rhodecode:templates/email_templates/user_registration.mako',
318 TYPE_PASSWORD_RESET:
321 TYPE_PASSWORD_RESET:
319 'rhodecode:templates/email_templates/password_reset.mako',
322 'rhodecode:templates/email_templates/password_reset.mako',
320 TYPE_PASSWORD_RESET_CONFIRMATION:
323 TYPE_PASSWORD_RESET_CONFIRMATION:
321 'rhodecode:templates/email_templates/password_reset_confirmation.mako',
324 'rhodecode:templates/email_templates/password_reset_confirmation.mako',
322 TYPE_COMMIT_COMMENT:
325 TYPE_COMMIT_COMMENT:
323 'rhodecode:templates/email_templates/commit_comment.mako',
326 'rhodecode:templates/email_templates/commit_comment.mako',
324 TYPE_PULL_REQUEST:
327 TYPE_PULL_REQUEST:
325 'rhodecode:templates/email_templates/pull_request_review.mako',
328 'rhodecode:templates/email_templates/pull_request_review.mako',
326 TYPE_PULL_REQUEST_COMMENT:
329 TYPE_PULL_REQUEST_COMMENT:
327 'rhodecode:templates/email_templates/pull_request_comment.mako',
330 'rhodecode:templates/email_templates/pull_request_comment.mako',
328 TYPE_PULL_REQUEST_UPDATE:
331 TYPE_PULL_REQUEST_UPDATE:
329 'rhodecode:templates/email_templates/pull_request_update.mako',
332 'rhodecode:templates/email_templates/pull_request_update.mako',
330 }
333 }
331
334
332 premailer_instance = premailer.Premailer(
335 premailer_instance = premailer.Premailer(
333 cssutils_logging_level=logging.DEBUG,
336 cssutils_logging_level=logging.DEBUG,
334 cssutils_logging_handler=logging.getLogger().handlers[0]
337 cssutils_logging_handler=logging.getLogger().handlers[0]
335 if logging.getLogger().handlers else None,
338 if logging.getLogger().handlers else None,
336 )
339 )
337
340
338 def __init__(self):
341 def __init__(self):
339 """
342 """
340 Example usage::
343 Example usage::
341
344
342 (subject, headers, email_body,
345 (subject, headers, email_body,
343 email_body_plaintext) = EmailNotificationModel().render_email(
346 email_body_plaintext) = EmailNotificationModel().render_email(
344 EmailNotificationModel.TYPE_TEST, **email_kwargs)
347 EmailNotificationModel.TYPE_TEST, **email_kwargs)
345
348
346 """
349 """
347 super(EmailNotificationModel, self).__init__()
350 super(EmailNotificationModel, self).__init__()
348 self.rhodecode_instance_name = rhodecode.CONFIG.get('rhodecode_title')
351 self.rhodecode_instance_name = rhodecode.CONFIG.get('rhodecode_title')
349
352
350 def _update_kwargs_for_render(self, kwargs):
353 def _update_kwargs_for_render(self, kwargs):
351 """
354 """
352 Inject params required for Mako rendering
355 Inject params required for Mako rendering
353
356
354 :param kwargs:
357 :param kwargs:
355 """
358 """
356
359
357 kwargs['rhodecode_instance_name'] = self.rhodecode_instance_name
360 kwargs['rhodecode_instance_name'] = self.rhodecode_instance_name
358 kwargs['rhodecode_version'] = rhodecode.__version__
361 kwargs['rhodecode_version'] = rhodecode.__version__
359 instance_url = h.route_url('home')
362 instance_url = h.route_url('home')
360 _kwargs = {
363 _kwargs = {
361 'instance_url': instance_url,
364 'instance_url': instance_url,
362 'whitespace_filter': self.whitespace_filter
365 'whitespace_filter': self.whitespace_filter
363 }
366 }
364 _kwargs.update(kwargs)
367 _kwargs.update(kwargs)
365 return _kwargs
368 return _kwargs
366
369
367 def whitespace_filter(self, text):
370 def whitespace_filter(self, text):
368 return text.replace('\n', '').replace('\t', '')
371 return text.replace('\n', '').replace('\t', '')
369
372
370 def get_renderer(self, type_, request):
373 def get_renderer(self, type_, request):
371 template_name = self.email_types[type_]
374 template_name = self.email_types[type_]
372 return request.get_partial_renderer(template_name)
375 return request.get_partial_renderer(template_name)
373
376
374 def render_email(self, type_, **kwargs):
377 def render_email(self, type_, **kwargs):
375 """
378 """
376 renders template for email, and returns a tuple of
379 renders template for email, and returns a tuple of
377 (subject, email_headers, email_html_body, email_plaintext_body)
380 (subject, email_headers, email_html_body, email_plaintext_body)
378 """
381 """
379 # translator and helpers inject
382 # translator and helpers inject
380 _kwargs = self._update_kwargs_for_render(kwargs)
383 _kwargs = self._update_kwargs_for_render(kwargs)
381 request = get_current_request()
384 request = get_current_request()
382 email_template = self.get_renderer(type_, request=request)
385 email_template = self.get_renderer(type_, request=request)
383
386
384 subject = email_template.render('subject', **_kwargs)
387 subject = email_template.render('subject', **_kwargs)
385
388
386 try:
389 try:
387 headers = email_template.render('headers', **_kwargs)
390 headers = email_template.render('headers', **_kwargs)
388 except AttributeError:
391 except AttributeError:
389 # it's not defined in template, ok we can skip it
392 # it's not defined in template, ok we can skip it
390 headers = ''
393 headers = ''
391
394
392 try:
395 try:
393 body_plaintext = email_template.render('body_plaintext', **_kwargs)
396 body_plaintext = email_template.render('body_plaintext', **_kwargs)
394 except AttributeError:
397 except AttributeError:
395 # it's not defined in template, ok we can skip it
398 # it's not defined in template, ok we can skip it
396 body_plaintext = ''
399 body_plaintext = ''
397
400
398 # render WHOLE template
401 # render WHOLE template
399 body = email_template.render(None, **_kwargs)
402 body = email_template.render(None, **_kwargs)
400
403
401 try:
404 try:
402 # Inline CSS styles and conversion
405 # Inline CSS styles and conversion
403 body = self.premailer_instance.transform(body)
406 body = self.premailer_instance.transform(body)
404 except Exception:
407 except Exception:
405 log.exception('Failed to parse body with premailer')
408 log.exception('Failed to parse body with premailer')
406 pass
409 pass
407
410
408 return subject, headers, body, body_plaintext
411 return subject, headers, body, body_plaintext
General Comments 0
You need to be logged in to leave comments. Login now