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