{"payload":{"feedbackUrl":"https://github.com/orgs/community/discussions/53140","repo":{"id":45704206,"defaultBranch":"main","name":"territory-bro","ownerLogin":"luontola","currentUserCanPush":false,"isFork":false,"isEmpty":false,"createdAt":"2015-11-06T19:57:13.000Z","ownerAvatar":"https://avatars.githubusercontent.com/u/42678?v=4","public":true,"private":false,"isOrgOwned":false},"refInfo":{"name":"","listCacheKey":"v0:1720985168.0","currentOid":""},"activityList":{"items":[{"before":"bc0bfb7170fce33411bfb412fcb28884c7868f4f","after":"8782834275cfa72f93e90d22f76dc8bb1b836b97","ref":"refs/heads/main","pushedAt":"2024-07-17T14:44:56.000Z","pushType":"force_push","commitsCount":0,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"โœ… Test copying to clipboard, also on the join page\n\nWhy:\n- There is a risk of breaking that feature when removing the SPA site,\n because the clipboard code is in same files as the React components.","shortMessageHtmlLink":"โœ… Test copying to clipboard, also on the join page"}},{"before":"ad7307fcd5b76654587e07e9e24da39f78d50e26","after":"bc0bfb7170fce33411bfb412fcb28884c7868f4f","ref":"refs/heads/main","pushedAt":"2024-07-17T13:40:14.000Z","pushType":"push","commitsCount":2,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"๐Ÿ”’๏ธ Use HTTPS for map tiles in QGIS\n\nWhy:\n- They all support it, and it's more secure.","shortMessageHtmlLink":"๐Ÿ”’๏ธ Use HTTPS for map tiles in QGIS"}},{"before":"5fafc2785ce84d5738b6a0dd54a2c405dde44d3a","after":"ad7307fcd5b76654587e07e9e24da39f78d50e26","ref":"refs/heads/main","pushedAt":"2024-07-14T19:22:45.000Z","pushType":"push","commitsCount":1,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"๐Ÿ› Fix showing the wrong join page URL in instructions\n\nWhy:\n- The code was hard-coded to show \"http://localhost:8080/join\"\n- The link however pointed to the correct page, because it was relative.","shortMessageHtmlLink":"๐Ÿ› Fix showing the wrong join page URL in instructions"}},{"before":"ee89642242b9bb994d41c8647a3f3ae63b459c1f","after":"5fafc2785ce84d5738b6a0dd54a2c405dde44d3a","ref":"refs/heads/main","pushedAt":"2024-07-14T19:04:47.000Z","pushType":"push","commitsCount":2,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"๐Ÿ› Fix authentication mis-detecting its redirect URI\n\nWhy:\n- The login-callback failed in prod with the error \"The redirect URI is\n wrong. You sent https://beta.territorybro.com:80, and we expected\n https://beta.territorybro.com\".\n- The Auth0 library uses javax.servlet.http.HttpServletRequest#getRequestURL\n to verify the callback URL, but it incorrectly produced\n \"https://beta.territorybro.com:80/login-callback\" because the Ring\n request had the values:\n {:request-method :get\n :scheme :https\n :server-name \"beta.territorybro.com\"\n :server-port 80\n :uri \"/login-callback\"}\n- It's not known where the server-port 80 came from. Jetty was running\n in port 8080 and the reverse proxy was in https port 443. The most\n reasonable explanation is that the server-port came from the default\n http port, because Jetty was running using http.\n- As a workaround we disable any auto-detection based on the request\n object, and always use the server's public URL from the configuration.\n\n2024-07-14 18:22:59,762 [qtp513113647-29] WARN territory-bro.infra.auth0 - Login failed\ncom.auth0.IdentityVerificationException: An error occurred while exchanging the authorization code.\n\tat com.auth0.RequestProcessor.getVerifiedTokens(RequestProcessor.java:238)\n\tat com.auth0.RequestProcessor.process(RequestProcessor.java:196)\n\tat com.auth0.AuthenticationController.handle(AuthenticationController.java:306)\n\tat territory_bro.infra.auth0$login_callback_handler.invokeStatic(auth0.clj:142)\n\tat territory_bro.infra.auth0$login_callback_handler.invoke(auth0.clj:138)\n\tat territory_bro.ui$wrap_json_api_compat$fn__27997.invoke(ui.clj:30)\n\tat ring.middleware.http_response$wrap_http_response$fn__14946.invoke(http_response.clj:19)\n\tat territory_bro.ui$wrap_current_user$fn__28000$fn__28001.invoke(ui.clj:35)\n\tat territory_bro.infra.authentication$with_user_STAR_.invokeStatic(authentication.clj:34)\n\tat territory_bro.infra.authentication$with_user_STAR_.invoke(authentication.clj:31)\n\tat territory_bro.ui$wrap_current_user$fn__28000.invoke(ui.clj:34)\n\tat territory_bro.ui.i18n$wrap_current_language$fn__16548.invoke(i18n.clj:48)\n\tat territory_bro.infra.auth0$wrap_redirect_to_login$fn__12371.invoke(auth0.clj:110)\n\tat territory_bro.ui.html$wrap_page_path$fn__16527.invoke(html.clj:83)\n\tat reitit.ring$ring_handler$fn__19766.invoke(ring.cljc:357)\n\tat clojure.lang.AFn.applyToHelper(AFn.java:154)\n\tat clojure.lang.AFn.applyTo(AFn.java:144)\n\tat clojure.lang.AFunction$1.doInvoke(AFunction.java:33)\n\tat clojure.lang.RestFn.invoke(RestFn.java:411)\n\tat clojure.lang.Var.invoke(Var.java:386)\n\tat compojure.core$routing$fn__2397.invoke(core.clj:185)\n\tat clojure.core$some.invokeStatic(core.clj:2718)\n\tat clojure.core$some.invoke(core.clj:2709)\n\tat compojure.core$routing.invokeStatic(core.clj:185)\n\tat compojure.core$routing.doInvoke(core.clj:182)\n\tat clojure.lang.RestFn.applyTo(RestFn.java:142)\n\tat clojure.core$apply.invokeStatic(core.clj:669)\n\tat clojure.core$apply.invoke(core.clj:662)\n\tat compojure.core$routes$fn__2401.invoke(core.clj:192)\n\tat territory_bro.infra.middleware$wrap_auto_refresh_projections$fn__18994.invoke(middleware.clj:74)\n\tat territory_bro.infra.middleware$wrap_sqlexception_chain$fn__18980.invoke(middleware.clj:38)\n\tat ring.middleware.http_response$wrap_http_response$fn__14946.invoke(http_response.clj:19)\n\tat ring.middleware.format_params$wrap_format_params$fn__14534.invoke(format_params.clj:113)\n\tat ring.middleware.format_params$wrap_format_params$fn__14534.invoke(format_params.clj:113)\n\tat ring.middleware.format_params$wrap_format_params$fn__14534.invoke(format_params.clj:113)\n\tat ring.middleware.format_response$wrap_format_response$fn__14847.invoke(format_response.clj:175)\n\tat territory_bro.infra.middleware$wrap_default_content_type$fn__18984.invoke(middleware.clj:51)\n\tat territory_bro.ui.error_page$wrap_error_pages$fn__18967.invoke(error_page.clj:38)\n\tat ring.logger$wrap_log_response$fn__13001.invoke(logger.clj:159)\n\tat ring.logger$wrap_log_request_params$fn__12982.invoke(logger.clj:64)\n\tat ring.logger$wrap_log_request_start$fn__12988.invoke(logger.clj:99)\n\tat ring.middleware.session$wrap_session$fn__13254.invoke(session.clj:112)\n\tat ring.middleware.keyword_params$wrap_keyword_params$fn__13339.invoke(keyword_params.clj:53)\n\tat ring.middleware.nested_params$wrap_nested_params$fn__13389.invoke(nested_params.clj:89)\n\tat ring.middleware.multipart_params$handle_request_and_errors$fn__13620$fn__13621.invoke(multipart_params.clj:136)\n\tat ring.middleware.multipart_params$handle_request_and_errors.invokeStatic(multipart_params.clj:134)\n\tat ring.middleware.multipart_params$handle_request_and_errors.invoke(multipart_params.clj:133)\n\tat ring.middleware.multipart_params$wrap_multipart_params$fn__13626.invoke(multipart_params.clj:188)\n\tat ring.middleware.params$wrap_params$fn__13655.invoke(params.clj:75)\n\tat ring.middleware.cookies$wrap_cookies$fn__13216.invoke(cookies.clj:198)\n\tat ring.middleware.resource$wrap_resource_prefer_resources$fn__13663.invoke(resource.clj:25)\n\tat ring.middleware.content_type$wrap_content_type$fn__13724.invoke(content_type.clj:34)\n\tat ring.middleware.default_charset$wrap_default_charset$fn__13740.invoke(default_charset.clj:31)\n\tat ring.middleware.not_modified$wrap_not_modified$fn__13713.invoke(not_modified.clj:61)\n\tat ring.middleware.x_headers$wrap_x_header$fn__13023.invoke(x_headers.clj:22)\n\tat ring.middleware.x_headers$wrap_x_header$fn__13023.invoke(x_headers.clj:22)\n\tat ring.middleware.ssl$wrap_forwarded_scheme$fn__13770.invoke(ssl.clj:35)\n\tat ring.middleware.proxy_headers$wrap_forwarded_remote_addr$fn__13800.invoke(proxy_headers.clj:20)\n\tat territory_bro.infra.middleware$wrap_cache_control$fn__19000.invoke(middleware.clj:88)\n\tat territory_bro.infra.middleware$wrap_internal_error$fn__18975.invoke(middleware.clj:25)\n\tat clojure.lang.Var.invoke(Var.java:386)\n\tat ring.adapter.jetty$proxy_handler$fn__28401.invoke(jetty.clj:107)\n\tat ring.adapter.jetty.proxy$org.eclipse.jetty.servlet.ServletHandler$ff19274a.doHandle(Unknown Source)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)\n\tat org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1381)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)\n\tat org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)\n\tat ring.adapter.jetty.proxy$org.eclipse.jetty.servlet.ServletHandler$ff19274a.doScope(Unknown Source)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)\n\tat org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1303)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)\n\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\n\tat org.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:822)\n\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\n\tat org.eclipse.jetty.server.Server.handle(Server.java:563)\n\tat org.eclipse.jetty.server.HttpChannel$RequestDispatchable.dispatch(HttpChannel.java:1598)\n\tat org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:753)\n\tat org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:501)\n\tat org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:287)\n\tat org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)\n\tat org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)\n\tat org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)\n\tat org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:421)\n\tat org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:390)\n\tat org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:277)\n\tat org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.run(AdaptiveExecutionStrategy.java:199)\n\tat org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:411)\n\tat org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)\n\tat org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)\n\tat org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)\n\tat java.base/java.lang.Thread.run(Unknown Source)\nCaused by: com.auth0.exception.APIException: Request failed with status code 403: The redirect URI is wrong. You sent https://beta.territorybro.com:80, and we expected https://beta.territorybro.com\n\tat com.auth0.net.ExtendedBaseRequest.createResponseException(ExtendedBaseRequest.java:131)\n\tat com.auth0.net.ExtendedBaseRequest.parseResponse(ExtendedBaseRequest.java:66)\n\tat com.auth0.net.BaseRequest.execute(BaseRequest.java:35)\n\tat com.auth0.RequestProcessor.exchangeCodeForTokens(RequestProcessor.java:349)\n\tat com.auth0.RequestProcessor.getVerifiedTokens(RequestProcessor.java:226)\n\t... 90 common frames omitted","shortMessageHtmlLink":"๐Ÿ› Fix authentication mis-detecting its redirect URI"}},{"before":"d0f74b7fb71be8c5509bcd0c2a6a65ef76e993cb","after":"ee89642242b9bb994d41c8647a3f3ae63b459c1f","ref":"refs/heads/main","pushedAt":"2024-07-14T17:33:30.000Z","pushType":"push","commitsCount":2,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"โœจ Use the same map tiles for the minimap as the territory map\n\nWhy:\n- If the default tile server is down, it will be necessary to allow\n switching to another server. Previously the minimap's tile server\n could not be changed, so it had no backup.","shortMessageHtmlLink":"โœจ Use the same map tiles for the minimap as the territory map"}},{"before":"bf3d1e46cdadfc30bfb44da9ce4b112b806b08a7","after":"d0f74b7fb71be8c5509bcd0c2a6a65ef76e993cb","ref":"refs/heads/main","pushedAt":"2024-07-14T14:47:14.000Z","pushType":"push","commitsCount":5,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"๐Ÿ› Fix deadlock when generating multiple QR codes in parallel\n\nWhy:\n- The SPA UI makes only one request to generate all QR codes. The SSR UI\n makes one request per QR codes. This causes lots of concurrent\n requests, which triggered a deadlock in the QR code generator.\n- The prepare_new_event trigger locks the event table to ensure a single\n writer, and normally that is enough to avoid transaction conflicts.\n But since territory-bro.api/generate-qr-code! reads the event table\n (in current-state-for-request) before writing to it, the transaction\n acquires an ACCESS SHARE lock on the table. Then when concurrent\n transactions try to promote their locks to SHARE ROW EXCLUSIVE, it\n creates a deadlock.\n- The solution is to acquire the SHARE ROW EXCLUSIVE first, before\n reading the table. Then the concurrent transactions will block before\n doing anything else, and won't create a cyclic dependency on each\n other and deadlock.\n\n2024-07-12 21:11:54,835 [qtp832702336-90] WARN territory-bro.dispatcher - Command failed: org.postgresql.util.PSQLException: ERROR: deadlock detected\n Detail: Process 163 waits for ShareRowExclusiveLock on relation 20140 of database 16384; blocked by process 162.\nProcess 162 waits for ShareRowExclusiveLock on relation 20140 of database 16384; blocked by process 163.\n Hint: See server log for query details.\n Where: SQL statement \"lock table territorybro.event in share row exclusive mode\"\nPL/pgSQL function prepare_new_event() line 9 at SQL statement\n2024-07-12 21:11:54,835 [qtp832702336-90] WARN territory-bro.api - Deadlock detected: {:command/type :share.command/create-share, :share/id #uuid \"9159c197-fec7-4c29-bc1b-506782e04b9b\", :share/key 9F_9L51gImA, :share/type :qr-code, :congregation/id #uuid \"89f8bcde-fa65-47cb-b5cc-9b11d01e410d\", :territory/id #uuid \"d7579f69-d312-4443-9a73-44974722d1e3\", :command/time #object[java.time.Instant 0x7b5c7b71 2024-07-12T18:11:54.798809Z], :command/user #uuid \"b86105a3-a8bc-46fe-84b6-01a8d3fd2719\"}\norg.postgresql.util.PSQLException: ERROR: deadlock detected\n Detail: Process 163 waits for ShareRowExclusiveLock on relation 20140 of database 16384; blocked by process 162.\nProcess 162 waits for ShareRowExclusiveLock on relation 20140 of database 16384; blocked by process 163.\n Hint: See server log for query details.\n Where: SQL statement \"lock table territorybro.event in share row exclusive mode\"\nPL/pgSQL function prepare_new_event() line 9 at SQL statement\n\tat org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2725)\n\tat org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2412)\n\tat org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:371)\n\tat org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:502)\n\tat org.postgresql.jdbc.PgStatement.execute(PgStatement.java:419)\n\tat org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:194)\n\tat org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:137)\n\tat com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52)\n\tat com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java)\n\tat clojure.java.jdbc$execute_query_with_params.invokeStatic(jdbc.clj:1090)\n\tat clojure.java.jdbc$execute_query_with_params.invoke(jdbc.clj:1084)\n\tat clojure.java.jdbc$db_query_with_resultset_STAR_.invokeStatic(jdbc.clj:1106)\n\tat clojure.java.jdbc$db_query_with_resultset_STAR_.invoke(jdbc.clj:1093)\n\tat clojure.java.jdbc$query.invokeStatic(jdbc.clj:1182)\n\tat clojure.java.jdbc$query.invoke(jdbc.clj:1144)\n\tat clojure.java.jdbc$query.invokeStatic(jdbc.clj:1160)\n\tat clojure.java.jdbc$query.invoke(jdbc.clj:1144)\n\tat clojure.lang.AFn.applyToHelper(AFn.java:156)\n\tat clojure.lang.AFn.applyTo(AFn.java:144)\n\tat clojure.core$apply.invokeStatic(core.clj:671)\n\tat clojure.core$apply.invoke(core.clj:662)\n\tat hugsql.adapter.clojure_java_jdbc.HugsqlAdapterClojureJavaJdbc.query(clojure_java_jdbc.clj:15)\n\tat hugsql.adapter$eval8165$fn__8181$G__8147__8186.invoke(adapter.clj:3)\n\tat hugsql.adapter$eval8165$fn__8181$G__8146__8192.invoke(adapter.clj:3)\n\tat clojure.lang.Var.invoke(Var.java:401)\n\tat hugsql.core$db_fn_STAR_$y__9940.doInvoke(core.clj:472)\n\tat clojure.lang.RestFn.invoke(RestFn.java:448)\n\tat hugsql.core$db_fn_STAR_$y__9940.invoke(core.clj:462)\n\tat clojure.lang.AFn.applyToHelper(AFn.java:156)\n\tat clojure.lang.RestFn.applyTo(RestFn.java:135)\n\tat clojure.core$apply.invokeStatic(core.clj:669)\n\tat clojure.core$apply.invoke(core.clj:662)\n\tat territory_bro.infra.db$query_BANG_.invokeStatic(db.clj:256)\n\tat territory_bro.infra.db$query_BANG_.invoke(db.clj:240)\n\tat territory_bro.infra.db$compile_queries$fn__35099.doInvoke(db.clj:266)\n\tat clojure.lang.RestFn.invoke(RestFn.java:445)\n\tat territory_bro.infra.event_store$save_event_BANG_.invokeStatic(event_store.clj:61)\n\tat territory_bro.infra.event_store$save_event_BANG_.invoke(event_store.clj:59)\n\tat territory_bro.infra.event_store$save_BANG_$fn__35873.invoke(event_store.clj:81)\n\tat clojure.core$map_indexed$mapi__8676$fn__8677.invoke(core.clj:7500)\n\tat clojure.lang.LazySeq.force(LazySeq.java:50)\n\tat clojure.lang.LazySeq.realize(LazySeq.java:89)\n\tat clojure.lang.LazySeq.seq(LazySeq.java:106)\n\tat clojure.lang.RT.seq(RT.java:555)\n\tat clojure.core$seq__5484.invokeStatic(core.clj:139)\n\tat clojure.core$dorun.invokeStatic(core.clj:3141)\n\tat clojure.core$doall.invokeStatic(core.clj:3156)\n\tat clojure.core$doall.invoke(core.clj:3156)\n\tat territory_bro.infra.event_store$save_BANG_.invokeStatic(event_store.clj:87)\n\tat territory_bro.infra.event_store$save_BANG_.invoke(event_store.clj:73)\n\tat territory_bro.dispatcher$append_stream_BANG_.invokeStatic(dispatcher.clj:82)\n\tat territory_bro.dispatcher$append_stream_BANG_.invoke(dispatcher.clj:79)\n\tat territory_bro.dispatcher$share_command_BANG_.invokeStatic(dispatcher.clj:148)\n\tat territory_bro.dispatcher$share_command_BANG_.invoke(dispatcher.clj:146)\n\tat territory_bro.dispatcher$command_BANG_.invokeStatic(dispatcher.clj:186)\n\tat territory_bro.dispatcher$command_BANG_.invoke(dispatcher.clj:178)\n\tat territory_bro.api$dispatch_BANG_.invokeStatic(api.clj:292)\n\tat territory_bro.api$dispatch_BANG_.invoke(api.clj:289)\n\tat territory_bro.api$generate_qr_code_BANG_.invokeStatic(api.clj:440)\n\tat territory_bro.api$generate_qr_code_BANG_.invoke(api.clj:437)\n\tat territory_bro.api$generate_qr_codes$fn__36753$fn__36767$iter__36768__36772$fn__36773$fn__36774.invoke(api.clj:470)\n\tat territory_bro.api$generate_qr_codes$fn__36753$fn__36767$iter__36768__36772$fn__36773.invoke(api.clj:468)\n\tat clojure.lang.LazySeq.force(LazySeq.java:50)\n\tat clojure.lang.LazySeq.realize(LazySeq.java:89)\n\tat clojure.lang.LazySeq.seq(LazySeq.java:106)\n\tat clojure.lang.RT.seq(RT.java:555)\n\tat clojure.core$seq__5484.invokeStatic(core.clj:139)\n\tat clojure.core.protocols$seq_reduce.invokeStatic(protocols.clj:24)\n\tat clojure.core.protocols$fn__8258.invokeStatic(protocols.clj:74)\n\tat clojure.core.protocols$fn__8258.invoke(protocols.clj:74)\n\tat clojure.core.protocols$fn__8199$G__8194__8212.invoke(protocols.clj:13)\n\tat clojure.core$reduce.invokeStatic(core.clj:6965)\n\tat clojure.core$into.invokeStatic(core.clj:7038)\n\tat clojure.core$into.invoke(core.clj:7029)\n\tat territory_bro.api$generate_qr_codes$fn__36753$fn__36767.invoke(api.clj:468)\n\tat clojure.java.jdbc$db_transaction_STAR_.invokeStatic(jdbc.clj:807)\n\tat clojure.java.jdbc$db_transaction_STAR_.invoke(jdbc.clj:776)\n\tat clojure.java.jdbc$db_transaction_STAR_.invokeStatic(jdbc.clj:852)\n\tat clojure.java.jdbc$db_transaction_STAR_.invoke(jdbc.clj:776)\n\tat territory_bro.api$generate_qr_codes$fn__36753.invoke(api.clj:464)\n\tat territory_bro.infra.authentication$with_user_STAR_.invokeStatic(authentication.clj:34)\n\tat territory_bro.infra.authentication$with_user_STAR_.invoke(authentication.clj:31)\n\tat territory_bro.api$generate_qr_codes.invokeStatic(api.clj:449)\n\tat territory_bro.api$generate_qr_codes.invoke(api.clj:448)\n\tat territory_bro.ui.printouts_page$fn__44708.invokeStatic(printouts_page.clj:234)\n\tat territory_bro.ui.printouts_page$fn__44708.invoke(printouts_page.clj:232)\n\tat territory_bro.ui.html$wrap_page_path$fn__36927.invoke(html.clj:83)\n\tat territory_bro.ui$wrap_json_api_compat$fn__47765.invoke(ui.clj:30)\n\tat ring.middleware.http_response$wrap_http_response$fn__13545.invoke(http_response.clj:19)\n\tat territory_bro.ui$wrap_current_user$fn__47768$fn__47769.invoke(ui.clj:35)\n\tat territory_bro.infra.authentication$with_user_STAR_.invokeStatic(authentication.clj:34)\n\tat territory_bro.infra.authentication$with_user_STAR_.invoke(authentication.clj:31)\n\tat territory_bro.ui$wrap_current_user$fn__47768.invoke(ui.clj:34)\n\tat territory_bro.ui.i18n$wrap_current_language$fn__37037.invoke(i18n.clj:48)\n\tat territory_bro.infra.auth0$wrap_redirect_to_login$fn__36884.invoke(auth0.clj:108)\n\tat territory_bro.ui.html$wrap_page_path$fn__36927.invoke(html.clj:83)\n\tat reitit.ring$ring_handler$fn__24175.invoke(ring.cljc:357)\n\tat clojure.lang.AFn.applyToHelper(AFn.java:154)\n\tat clojure.lang.AFn.applyTo(AFn.java:144)\n\tat clojure.lang.AFunction$1.doInvoke(AFunction.java:33)\n\tat clojure.lang.RestFn.invoke(RestFn.java:411)\n\tat clojure.lang.Var.invoke(Var.java:386)\n\tat compojure.core$routing$fn__13389.invoke(core.clj:185)\n\tat clojure.core$some.invokeStatic(core.clj:2718)\n\tat clojure.core$some.invoke(core.clj:2709)\n\tat compojure.core$routing.invokeStatic(core.clj:185)\n\tat compojure.core$routing.doInvoke(core.clj:182)\n\tat clojure.lang.RestFn.applyTo(RestFn.java:142)\n\tat clojure.core$apply.invokeStatic(core.clj:669)\n\tat clojure.core$apply.invoke(core.clj:662)\n\tat compojure.core$routes$fn__13393.invoke(core.clj:192)\n\tat compojure.core$wrap_routes$fn__13494.invoke(core.clj:353)\n\tat compojure.core$wrap_routes$fn__13494.invoke(core.clj:353)\n\tat territory_bro.infra.middleware$wrap_auto_refresh_projections$fn__40894.invoke(middleware.clj:74)\n\tat ring.middleware.reload$wrap_reload$fn__19505.invoke(reload.clj:39)\n\tat territory_bro.infra.middleware$wrap_sqlexception_chain$fn__40880.invoke(middleware.clj:38)\n\tat ring.middleware.http_response$wrap_http_response$fn__13545.invoke(http_response.clj:19)\n\tat territory_bro.infra.middleware$wrap_default_content_type$fn__40884.invoke(middleware.clj:51)\n\tat territory_bro.ui.error_page$wrap_error_pages$fn__40859.invoke(error_page.clj:38)\n\tat ring.logger$wrap_log_response$fn__16902.invoke(logger.clj:159)\n\tat ring.logger$wrap_log_request_params$fn__16883.invoke(logger.clj:64)\n\tat ring.logger$wrap_log_request_start$fn__16889.invoke(logger.clj:99)\n\tat ring.middleware.session$wrap_session$fn__17225.invoke(session.clj:112)\n\tat ring.middleware.keyword_params$wrap_keyword_params$fn__17346.invoke(keyword_params.clj:53)\n\tat ring.middleware.nested_params$wrap_nested_params$fn__17404.invoke(nested_params.clj:89)\n\tat ring.middleware.multipart_params$handle_request_and_errors$fn__17707$fn__17708.invoke(multipart_params.clj:136)\n\tat ring.middleware.multipart_params$handle_request_and_errors.invokeStatic(multipart_params.clj:134)\n\tat ring.middleware.multipart_params$handle_request_and_errors.invoke(multipart_params.clj:133)\n\tat ring.middleware.multipart_params$wrap_multipart_params$fn__17713.invoke(multipart_params.clj:188)\n\tat ring.middleware.params$wrap_params$fn__17750.invoke(params.clj:75)\n\tat ring.middleware.cookies$wrap_cookies$fn__17175.invoke(cookies.clj:198)\n\tat ring.middleware.resource$wrap_resource_prefer_resources$fn__17766.invoke(resource.clj:25)\n\tat ring.middleware.content_type$wrap_content_type$fn__17851.invoke(content_type.clj:34)\n\tat ring.middleware.default_charset$wrap_default_charset$fn__17875.invoke(default_charset.clj:31)\n\tat ring.middleware.not_modified$wrap_not_modified$fn__17832.invoke(not_modified.clj:61)\n\tat ring.middleware.x_headers$wrap_x_header$fn__16938.invoke(x_headers.clj:22)\n\tat ring.middleware.x_headers$wrap_x_header$fn__16938.invoke(x_headers.clj:22)\n\tat ring.middleware.ssl$wrap_forwarded_scheme$fn__17921.invoke(ssl.clj:35)\n\tat ring.middleware.proxy_headers$wrap_forwarded_remote_addr$fn__17959.invoke(proxy_headers.clj:20)\n\tat territory_bro.infra.middleware$wrap_cache_control$fn__40900.invoke(middleware.clj:88)\n\tat territory_bro.infra.middleware$wrap_internal_error$fn__40875.invoke(middleware.clj:25)\n\tat clojure.lang.Var.invoke(Var.java:386)\n\tat ring.adapter.jetty$proxy_handler$fn__1818.invoke(jetty.clj:107)\n\tat ring.adapter.jetty.proxy$org.eclipse.jetty.servlet.ServletHandler$ff19274a.doHandle(Unknown Source)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)\n\tat org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1381)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)\n\tat org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)\n\tat ring.adapter.jetty.proxy$org.eclipse.jetty.servlet.ServletHandler$ff19274a.doScope(Unknown Source)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)\n\tat org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1303)\n\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)\n\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\n\tat org.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:822)\n\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)\n\tat org.eclipse.jetty.server.Server.handle(Server.java:563)\n\tat org.eclipse.jetty.server.HttpChannel$RequestDispatchable.dispatch(HttpChannel.java:1598)\n\tat org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:753)\n\tat org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:501)\n\tat org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:287)\n\tat org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)\n\tat org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)\n\tat org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)\n\tat org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:421)\n\tat org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:390)\n\tat org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:277)\n\tat org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.run(AdaptiveExecutionStrategy.java:199)\n\tat org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:411)\n\tat org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)\n\tat org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)\n\tat org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)\n\tat java.base/java.lang.Thread.run(Thread.java:840)","shortMessageHtmlLink":"๐Ÿ› Fix deadlock when generating multiple QR codes in parallel"}},{"before":"39aff9607b4ac5c95368cbaed8dc938dc98fbd03","after":"bf3d1e46cdadfc30bfb44da9ce4b112b806b08a7","ref":"refs/heads/main","pushedAt":"2024-07-12T15:36:32.000Z","pushType":"push","commitsCount":1,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"โšก๏ธ Enable automatic HTTP response gzip compression\n\nWhy:\n- There is no longer an nginx reverse proxy in front of the backend, so\n the backend needs to do the compression itself.","shortMessageHtmlLink":"โšก๏ธ Enable automatic HTTP response gzip compression"}},{"before":"0b832f7b487f5baddd4f35c4b099a3090fa54cad","after":"39aff9607b4ac5c95368cbaed8dc938dc98fbd03","ref":"refs/heads/main","pushedAt":"2024-07-12T15:09:48.000Z","pushType":"force_push","commitsCount":0,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"๐Ÿ”ง Fix OutOfMemoryError on startup\n\nWhy:\n- When the app container started, it often failed with\n \"OutOfMemoryError: Java heap space\" (see below) when the container had\n a memory limit of 500MB.\n- Enabling -XX:+PrintCommandLineFlags showed the effective memory\n settings -XX:InitialHeapSize=8192000 (8MB), -XX:MaxHeapSize=131072000\n (128MB), -XX:MinHeapSize=6815736 (6MB).\n- The JVM defaults the maximum heap size to 1/4 of physical memory.\n https://docs.oracle.com/en/java/javase/17/gctuning/ergonomics.html\n- The solution is to tell the JVM to use more memory. We opted to\n setting InitialRAMPercentage and MaxRAMPercentage (with the\n recommended value 70) instead of a fixed memory limit, so that the\n limit could be increased in production without rebuild as the\n in-memory database increases in size over time.\n https://www.alibabacloud.com/help/en/sae/use-cases/best-practices-for-jvm-heap-size-configuration\n\njava.lang.OutOfMemoryError: Java heap space\n\tat java.base/java.lang.StringLatin1.newString(Unknown Source)\n\tat java.base/java.lang.String.substring(Unknown Source)\n\tat java.base/java.lang.Class.getPackageName(Unknown Source)\n\tat java.base/sun.invoke.util.VerifyAccess.isSamePackage(Unknown Source)\n\tat java.base/sun.invoke.util.VerifyAccess.isClassAccessible(Unknown Source)\n\tat java.base/java.lang.invoke.MethodHandles$Lookup.isClassAccessible(Unknown Source)\n\tat java.base/java.lang.invoke.MethodHandles$Lookup.checkSymbolicClass(Unknown Source)\n\tat java.base/java.lang.invoke.MethodHandles$Lookup.linkMethodHandleConstant(Unknown Source)\n\tat java.base/java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(Unknown Source)\n\tat java.base/java.util.ResourceBundle$ResourceBundleControlProviderHolder.(Unknown Source)\n\tat java.base/java.util.ResourceBundle.getDefaultControl(Unknown Source)\n\tat java.base/java.util.ResourceBundle.getDefaultControl(Unknown Source)\n\tat java.base/java.util.ResourceBundle.getBundle(Unknown Source)\n\tat org.postgresql.util.GT.(GT.java:35)\n\tat org.postgresql.util.GT.(GT.java:24)\n\tat org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2383)\n\tat org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:371)\n\tat org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:502)\n\tat org.postgresql.jdbc.PgStatement.execute(PgStatement.java:419)\n\tat org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:194)\n\tat org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:137)\n\tat com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52)\n\tat com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java)\n\tat clojure.java.jdbc$execute_query_with_params.invokeStatic(jdbc.clj:1090)\n\tat clojure.java.jdbc$execute_query_with_params.invoke(jdbc.clj:1084)\n\tat clojure.java.jdbc$db_query_with_resultset_STAR_.invokeStatic(jdbc.clj:1106)\n\tat clojure.java.jdbc$db_query_with_resultset_STAR_.invoke(jdbc.clj:1093)\n\tat clojure.java.jdbc$query.invokeStatic(jdbc.clj:1182)\n\tat clojure.java.jdbc$query.invoke(jdbc.clj:1144)\n\tat clojure.java.jdbc$query.invokeStatic(jdbc.clj:1160)\n\tat clojure.java.jdbc$query.invoke(jdbc.clj:1144)\n\tat clojure.lang.AFn.applyToHelper(AFn.java:156)","shortMessageHtmlLink":"๐Ÿ”ง Fix OutOfMemoryError on startup"}},{"before":"3f1bb3219169706425af21a1722a00d0ab66342c","after":"0b832f7b487f5baddd4f35c4b099a3090fa54cad","ref":"refs/heads/main","pushedAt":"2024-07-12T14:29:16.000Z","pushType":"force_push","commitsCount":0,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"๐Ÿ‘ท Switch to using SSR\n\nWhy:\n- Continuing the migration from SPA to SSR.\n- The SSR site is now feature complete with the SPA site, so we can\n remove the SPA site and switch to using SSR.\n- Changed the test and build to happen outside the docker build to\n simplify the docker build and to make CI faster.","shortMessageHtmlLink":"๐Ÿ‘ท Switch to using SSR"}},{"before":"0b832f7b487f5baddd4f35c4b099a3090fa54cad","after":"3f1bb3219169706425af21a1722a00d0ab66342c","ref":"refs/heads/main","pushedAt":"2024-07-12T14:28:32.000Z","pushType":"push","commitsCount":1,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"Java 21?","shortMessageHtmlLink":"Java 21?"}},{"before":"4551d9df014e01cf4c8aab9c44f002b9c011bdf5","after":"0b832f7b487f5baddd4f35c4b099a3090fa54cad","ref":"refs/heads/main","pushedAt":"2024-07-12T14:23:36.000Z","pushType":"force_push","commitsCount":0,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"๐Ÿ‘ท Switch to using SSR\n\nWhy:\n- Continuing the migration from SPA to SSR.\n- The SSR site is now feature complete with the SPA site, so we can\n remove the SPA site and switch to using SSR.\n- Changed the test and build to happen outside the docker build to\n simplify the docker build and to make CI faster.","shortMessageHtmlLink":"๐Ÿ‘ท Switch to using SSR"}},{"before":"b4949416a4fa1613aa68d76345905dce9a42d919","after":"4551d9df014e01cf4c8aab9c44f002b9c011bdf5","ref":"refs/heads/main","pushedAt":"2024-07-12T14:22:13.000Z","pushType":"force_push","commitsCount":0,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"๐Ÿ‘ท Switch to using SSR\n\nWhy:\n- Continuing the migration from SPA to SSR.\n- The SSR site is now feature complete with the SPA site, so we can\n remove the SPA site and switch to using SSR.\n- Changed the test and build to happen outside the docker build to\n simplify the docker build and to make CI faster.","shortMessageHtmlLink":"๐Ÿ‘ท Switch to using SSR"}},{"before":"5bc43caf8af3dcbf6fbb6268053a0e877342eccf","after":"b4949416a4fa1613aa68d76345905dce9a42d919","ref":"refs/heads/main","pushedAt":"2024-07-12T14:21:11.000Z","pushType":"force_push","commitsCount":0,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"๐Ÿ‘ท Switch to using SSR\n\nWhy:\n- Continuing the migration from SPA to SSR.\n- The SSR site is now feature complete with the SPA site, so we can\n remove the SPA site and switch to using SSR.\n- Changed the test and build to happen outside the docker build to\n simplify the docker build and to make CI faster.","shortMessageHtmlLink":"๐Ÿ‘ท Switch to using SSR"}},{"before":"3a70a28e801b8e52db924167b70ebf99ec8dff3f","after":"5bc43caf8af3dcbf6fbb6268053a0e877342eccf","ref":"refs/heads/main","pushedAt":"2024-07-12T14:17:53.000Z","pushType":"force_push","commitsCount":0,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"๐Ÿ‘ท Switch to using SSR\n\nWhy:\n- Continuing the migration from SPA to SSR.\n- The SSR site is now feature complete with the SPA site, so we can\n remove the SPA site and switch to using SSR.\n- Changed the test and build to happen outside the docker build to\n simplify the docker build and to make CI faster.","shortMessageHtmlLink":"๐Ÿ‘ท Switch to using SSR"}},{"before":"036f13c194b5b7ff0657cce339962da0ad87cb7a","after":"3a70a28e801b8e52db924167b70ebf99ec8dff3f","ref":"refs/heads/main","pushedAt":"2024-07-12T14:07:09.000Z","pushType":"push","commitsCount":1,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"๐Ÿ‘ท Switch to using SSR\n\nWhy:\n- Continuing the migration from SPA to SSR.\n- The SSR site is now feature complete with the SPA site, so we can\n remove the SPA site and switch to using SSR.\n- Changed the test and build to happen outside the docker build to\n simplify the docker build.","shortMessageHtmlLink":"๐Ÿ‘ท Switch to using SSR"}},{"before":"0494f40c4cc6449243a3fa2720c1850384875c8a","after":"036f13c194b5b7ff0657cce339962da0ad87cb7a","ref":"refs/heads/main","pushedAt":"2024-07-12T11:36:42.000Z","pushType":"push","commitsCount":5,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"๐Ÿ—๏ธ Configure Cache-Control headers\n\nWhy:\n- On the SPA site, the reverse proxy (nginx) handled to cache-control\n headers. With the SSR site, the settings need to be changed and there\n will not be a separate reverse proxy which serves static assets, so it\n needs to be done in Ring middleware.","shortMessageHtmlLink":"๐Ÿ—๏ธ Configure Cache-Control headers"}},{"before":"3a3912f05e1c64f7272006ce1feb5904582645a5","after":"0494f40c4cc6449243a3fa2720c1850384875c8a","ref":"refs/heads/main","pushedAt":"2024-07-11T18:43:26.000Z","pushType":"push","commitsCount":4,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"๐Ÿ› Submit the print options form on page load to sync browser state\n\nWhy:\n- When the browser navigates backward and then again forward, the\n browser remembers the state of all forms. But that does not trigger a\n change event on the form elements. This causes the form selection to\n be out of sync with what is shown on the page. It's not known if there\n is an event that could be listened. As a workaround we submit the\n form right after the form is loaded.","shortMessageHtmlLink":"๐Ÿ› Submit the print options form on page load to sync browser state"}},{"before":"26672c5708516d38231474b1620efbdc4c4e46eb","after":"3a3912f05e1c64f7272006ce1feb5904582645a5","ref":"refs/heads/main","pushedAt":"2024-07-11T13:44:38.000Z","pushType":"force_push","commitsCount":0,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"๐Ÿšง Printouts page: neighborhood card\n\nWhy:\n- Continuing the migration from SPA to SSR.","shortMessageHtmlLink":"๐Ÿšง Printouts page: neighborhood card"}},{"before":"2d705663bbb0d2d243b2fa99a94fc19a1f66882b","after":"26672c5708516d38231474b1620efbdc4c4e46eb","ref":"refs/heads/main","pushedAt":"2024-07-11T13:06:40.000Z","pushType":"force_push","commitsCount":0,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"๐Ÿ”’๏ธ Don't show loans to anonymous users\n\nWhy:\n- Follow the principle of least privilege. Later a publisher could\n perhaps see how long they've had a territory, but even then the\n highlighting on the territory map should not be needed.","shortMessageHtmlLink":"๐Ÿ”’๏ธ Don't show loans to anonymous users"}},{"before":"a01151522745c53f08d8666d10338abb1ddcd996","after":"2d705663bbb0d2d243b2fa99a94fc19a1f66882b","ref":"refs/heads/main","pushedAt":"2024-07-10T12:38:29.000Z","pushType":"push","commitsCount":3,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"โ™ป๏ธ Rename TerritoryMapElement.location to territory\n\nWhy:\n- Unify naming with TerritoryMiniMapElement.","shortMessageHtmlLink":"โ™ป๏ธ Rename TerritoryMapElement.location to territory"}},{"before":"e0b0394b808c5eca65d8bbe340e9f36a3dcce058","after":"a01151522745c53f08d8666d10338abb1ddcd996","ref":"refs/heads/main","pushedAt":"2024-07-08T15:13:23.000Z","pushType":"push","commitsCount":3,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"๐Ÿšง Printouts page: QR code\n\nWhy:\n- Continuing the migration from SPA to SSR.\n- Evaluated multiple QR code generator libraries for Java, before ending\n up using Naoyuki:\n - QRGen https://github.com/kenglxn/QRGen\n - ๐Ÿ‘Ž Produces a 20px border around the SVG, no option to remove it\n - ๐Ÿ‘Ž Generates very verbose SVG\n - ๐Ÿ‘Ž Not available in Maven Central\n - ๐Ÿ‘Ž Brings it lots of transitive dependencies\n - Okapi Barcode https://github.com/woo-j/OkapiBarcode\n - ๐Ÿ‘Ž Doesn't set the SVG viewbox\n - ๐Ÿ‘Ž Generates verbose SVG\n - Nayuki's QR Code generator https://github.com/nayuki/QR-Code-generator\n - ๐Ÿ‘ Generates dense SVG\n - ๐Ÿ‘Ž Requires hand-writing the image/SVG generator. We had to\n copy-paste and adapt the examples from\n https://github.com/nayuki/QR-Code-generator/blob/master/java/QrCodeGeneratorDemo.java\n - ๐Ÿ’ก The qrcode.react library we've used previously is based on this\n - ๐Ÿ‘ Zero dependencies\n - A few other Java and Clojure libraries were found as well, but they\n didn't seem very well maintained.\n- To avoid generating lots of QR codes which will never be used, they\n will be cached for some time. Previously they were cached in the\n user's session, but HTMX makes it easy to cache them in the browser's\n request cache.","shortMessageHtmlLink":"๐Ÿšง Printouts page: QR code"}},{"before":"a98434b67d6a8e61ab25f89fe676449e522adac8","after":"e0b0394b808c5eca65d8bbe340e9f36a3dcce058","ref":"refs/heads/main","pushedAt":"2024-07-06T21:27:15.000Z","pushType":"push","commitsCount":11,"pusher":{"login":"luontola","name":"Esko Luontola","path":"/luontola","primaryAvatarUrl":"https://avatars.githubusercontent.com/u/42678?s=80&v=4"},"commit":{"message":"โฌ‡๏ธ Downgrade to htmx 1.9.12\n\nWhy:\n- Fixes lazy loading the territory-list-map. htmx 2.0.0 breaks web\n components which contain