Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch aleph Excluding Merge-Ins
This is equivalent to a diff from 00de2fb20c to 7d5a77599c
2024-02-11
| ||
21:20 | Added HTTP1 Leaf check-in: 7d5a77599c user: scstarkey tags: aleph | |
21:17 | Borrowing some improvements from the aleph branch check-in: a94f0852b9 user: scstarkey tags: trunk | |
19:50 | More memory check-in: 30d4dfba91 user: scstarkey tags: aleph | |
18:15 | Testing aleph support check-in: b018440299 user: scstarkey tags: aleph | |
16:37 | Much less memory usage check-in: 00de2fb20c user: scstarkey tags: trunk | |
2024-02-08
| ||
17:33 | Many, including delete post, move login link, activity report emails And don't let us package with any test-refresh/focus tests And a bit less logging And preparation for allowing anybody to webmention even without the protocol check-in: 6c75bd0bdf user: scstarkey tags: trunk | |
Changes to containerfiles/mylife.containerfile.
1 2 3 | FROM clojure:lein-2.10.0-alpine AS build WORKDIR /root COPY project.clj ./project.clj | | | 1 2 3 4 5 6 7 8 9 10 11 | FROM clojure:lein-2.10.0-alpine AS build WORKDIR /root COPY project.clj ./project.clj RUN apk add --no-cache bash git libstdc++ RUN lein deps RUN lein with-profile dev,test,uberjar deps COPY src ./src COPY src-dev ./src-dev COPY src-prod ./src-prod COPY test ./test |
︙ | ︙ | |||
24 25 26 27 28 29 30 | ENV HOT_RELOAD_ENABLED=false RUN mkdir -p /usr/local/mylife/.server RUN apt-get update && apt-get install -y fontconfig libfreetype6 && apt-get clean COPY --from=build /root/target/uberjar/mylife.jar /usr/local/mylife | | | 24 25 26 27 28 29 30 31 | ENV HOT_RELOAD_ENABLED=false RUN mkdir -p /usr/local/mylife/.server RUN apt-get update && apt-get install -y fontconfig libfreetype6 && apt-get clean COPY --from=build /root/target/uberjar/mylife.jar /usr/local/mylife CMD java -XX:+UseZGC -XX:+ZGenerational -Xmx340m -jar /usr/local/mylife/mylife.jar |
Changes to project.clj.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | (def slf4j-version "2.0.7") (def acme4j-version "2.16") (def reitit-version "0.6.0") (def bouncy-version "1.75") (defproject me.calmabiding.mylife "0.1.0-SNAPSHOT" :description "My Life. On the Web. In your RSS reader." :url "https://calmabiding.me/mylife" :license {:name "Affero GNU GPL 3.0" :url "https://www.gnu.org/licenses/agpl-3.0.en.html"} :repositories {"jitpack" {:url "https://jitpack.io"}} :dependencies [[org.clojure/clojure "1.11.1"] [clj-cron-parse "0.1.5"] [clj-http "3.12.3"] [clj-rss "0.4.0"] [clojure.java-time "1.4.2"] [clojure-humanize "0.2.2"] [com.draines/postal "2.0.5"] [com.fzakaria/slf4j-timbre "0.4.1"] [com.github.seancorfield/next.jdbc "1.3.909"] [com.github.multiformats/java-multibase "1.1.1"] [com.taoensso/timbre "6.3.1"] [com.vladsch.flexmark/flexmark-all "0.64.8"] [com.xtdb/xtdb-core "1.24.3"] [com.xtdb/xtdb-jdbc "1.24.3"] [commons-io "2.15.1"] [conman "0.9.6"] [coreagile/defenv "3.0.0"] [expound "0.9.0"] [org.clj-commons/hickory "0.7.4"] | > < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | (def slf4j-version "2.0.7") (def acme4j-version "2.16") (def reitit-version "0.6.0") (def bouncy-version "1.75") (defproject me.calmabiding.mylife "0.1.0-SNAPSHOT" :description "My Life. On the Web. In your RSS reader." :url "https://calmabiding.me/mylife" :license {:name "Affero GNU GPL 3.0" :url "https://www.gnu.org/licenses/agpl-3.0.en.html"} :repositories {"jitpack" {:url "https://jitpack.io"}} :dependencies [[org.clojure/clojure "1.11.1"] [aleph "0.7.1"] [clj-cron-parse "0.1.5"] [clj-http "3.12.3"] [clj-rss "0.4.0"] [clojure.java-time "1.4.2"] [clojure-humanize "0.2.2"] [com.draines/postal "2.0.5"] [com.fzakaria/slf4j-timbre "0.4.1"] [com.github.seancorfield/next.jdbc "1.3.909"] [com.github.multiformats/java-multibase "1.1.1"] [com.taoensso/timbre "6.3.1"] [com.vladsch.flexmark/flexmark-all "0.64.8"] [com.xtdb/xtdb-core "1.24.3"] [com.xtdb/xtdb-jdbc "1.24.3"] [commons-io "2.15.1"] [conman "0.9.6"] [coreagile/defenv "3.0.0"] [expound "0.9.0"] [org.clj-commons/hickory "0.7.4"] [io.sunshower.arcus/arcus-identicon "1.41.50.Final"] [jdbc-ring-session "1.5.3"] [markdown-clj "1.11.8"] [metosin/ring-http-response "0.9.3"] [mount "0.1.17"] [nano-id "1.1.0"] [org.apache.xmlgraphics/batik-all "1.17"] |
︙ | ︙ |
Changes to src/mylife/config.clj.
︙ | ︙ | |||
447 448 449 450 451 452 453 454 455 456 457 458 459 460 | (defn server-url [] (let [{:keys [domain ssl-port]} (env)] (if (= 443 ssl-port) (format "https://%s" domain) (format "https://%s:%s" domain ssl-port)))) (defn external-connect-url [] (or (env :ext-url-override) (env :external-url) (server-url))) (defn get-site-owner [] | > > > > > > | 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 | (defn server-url [] (let [{:keys [domain ssl-port]} (env)] (if (= 443 ssl-port) (format "https://%s" domain) (format "https://%s:%s" domain ssl-port)))) (defn internal-server-url [] (let [{:keys [domain port]} (env)] (if (= 80 port) (format "http://%s" domain) (format "http://%s:%s" domain port)))) (defn external-connect-url [] (or (env :ext-url-override) (env :external-url) (server-url))) (defn get-site-owner [] |
︙ | ︙ |
Changes to src/mylife/core.clj.
1 | (ns mylife.core | > | | > > > < | > > > > > > | | > | > > > | | | | > | > | > | > > > | > | > > > > > > > > > > > > | > > > > > > | > > > > | > | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | (ns mylife.core (:require [aleph.http :as http] [clojure.java.io :as io] [jdbc-ring-session.cleaner :refer [start-cleaner stop-cleaner]] [mount.core :as mount] [mylife.activity :refer [generate-available-activity-email]] [mylife.config :refer [cfg-db env external-connect-url internal-server-url] :as cfg] [mylife.data :as d] [mylife.disk-safety :as s] [mylife.email :as email] [mylife.scheduler :as sched] [mylife.ssl :as ssl] [mylife.time :as time] [mylife.web :as w] [mylife.webmention :as wm] [taoensso.timbre :as log]) (:gen-class) (:import (java.io File) (java.util UUID) (io.netty.handler.ssl ApplicationProtocolConfig ApplicationProtocolConfig$Protocol ApplicationProtocolConfig$SelectedListenerFailureBehavior ApplicationProtocolConfig$SelectorFailureBehavior ApplicationProtocolNames SslContextBuilder))) (set! *warn-on-reflection* true) (Thread/setDefaultUncaughtExceptionHandler (reify Thread$UncaughtExceptionHandler (uncaughtException [_ thread ex] (log/error {:what :uncaught-exception :exception ex :where (str "Uncaught exception on" (.getName thread))})))) (defn- ssl-err [e keystore keystore-password-file] (log/error e "Unable to load certificates. Cleaning up possibly corrupt files. Will try again when you start the server again!") (doseq [^File f [keystore keystore-password-file]] (.delete f)) (System/exit 1)) (mount/defstate ^{:on-reload :noop} keystore-fn :start (when (env :server-enabled?) (let [cert-dir (doto (-> :cert-dir env io/file) s/guarantee-secure-dir!) keystore (io/file cert-dir ".keystore") keystore-password-file (io/file cert-dir ".keystore-password") keystore-path (.getAbsolutePath keystore) key-password-fn (fn [] (slurp keystore-password-file))] (if-not (and (.exists keystore) (.exists keystore-password-file)) (try (log/info "Generating self-signed SSL certificates") (let [new-password (str (UUID/randomUUID)) {:keys [private-key cert]} (ssl/self-signed-cert (env :domain))] (doseq [^File f [keystore keystore-password-file]] (s/new-secure-file! f)) (spit keystore-password-file new-password) (ssl/save-single-cert! keystore private-key cert new-password)) (catch Exception e (ssl-err e keystore keystore-password-file))) (try (ssl/validate-keystore keystore (key-password-fn)) (catch Exception e (ssl-err e keystore keystore-password-file)))) (fn [] {:key-manager-factory (ssl/load-key-manager-factory keystore-path (key-password-fn)) :keystore-path keystore-path :key-password-fn key-password-fn})))) (mount/defstate ^{:on-reload :noop} jdbc-session-cleaner :start (start-cleaner cfg-db) :stop (stop-cleaner jdbc-session-cleaner)) (declare stop-server) (mount/defstate ^{:on-reload :noop} http-server :start (when (env :server-enabled?) (let [handler (w/app cfg-db) {:keys [port ssl-port]} (env) ^ApplicationProtocolConfig$SelectorFailureBehavior selector-failure-behavior ApplicationProtocolConfig$SelectorFailureBehavior/NO_ADVERTISE ^ApplicationProtocolConfig$SelectedListenerFailureBehavior listener-failure-behavior ApplicationProtocolConfig$SelectedListenerFailureBehavior/ACCEPT {:keys [key-manager-factory]} (keystore-fn) protocol-config (ApplicationProtocolConfig. ApplicationProtocolConfig$Protocol/ALPN selector-failure-behavior listener-failure-behavior [ApplicationProtocolNames/HTTP_2 ApplicationProtocolNames/HTTP_1_1]) ssl-context (-> (SslContextBuilder/forServer key-manager-factory) (.applicationProtocolConfig protocol-config) (.build))] {:http (http/start-server handler {:port port}) :https (http/start-server handler {:port ssl-port :http-versions [:http2 :http1] :ssl-context ssl-context})})) :stop (stop-server)) (defn stop-server [] (let [{:keys [http https]} http-server] (when (and http https) (.close http) (.close https)))) (mount/defstate ^{:on-reload :noop} http-server-context :start (when (env :server-enabled?) (log/infof "You can connect to %s externally" (external-connect-url)) (log/infof "You can connect to %s for internal configuration" (internal-server-url)))) (defn- restart-web [] (log/info "Restarting web listener") (mount/stop #'mylife.core/keystore-fn #'mylife.core/http-server) (mount/start #'mylife.core/keystore-fn #'mylife.core/http-server) (log/info "Web listener restart complete")) (defn- check-acme-ssl [] (let [{:keys [keystore-path key-password-fn]} (keystore-fn)] (try (ssl/guarantee-acme-cert! (merge (select-keys (env) [:domain :cert-dir :acme-server-uri]) {:new-cert-fn (fn [private-key cert-chain] (log/infof (str "Saving new LetsEncrypt certificate chain to '%s'. It may " "take up to a minute for the new cert to be available") keystore-path) (ssl/save-cert-chain! keystore-path private-key cert-chain (key-password-fn)) (restart-web))})) (catch Throwable t (log/error t "Unable to generate SSL cert"))))) |
︙ | ︙ |
Changes to src/mylife/ssl.clj.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | (ns mylife.ssl (:require [clojure.java.io :as io] [mylife.disk-safety :as s] [taoensso.timbre :as log]) (:import (java.io File FileWriter FileReader FileInputStream) (java.security KeyPairGenerator Security SecureRandom KeyStore PrivateKey KeyPair) (java.security.cert X509Certificate CertificateFactory) (java.time Instant) (java.time.temporal ChronoUnit) (java.util Date Collection) (org.bouncycastle.asn1.x500 X500Name) (org.bouncycastle.operator.jcajce JcaContentSignerBuilder) (org.bouncycastle.jce.provider BouncyCastleProvider) (org.bouncycastle.cert.jcajce JcaX509v3CertificateBuilder JcaX509CertificateConverter) (org.shredzone.acme4j.util KeyPairUtils CSRBuilder) (org.shredzone.acme4j Session AccountBuilder Order Status Authorization) (org.shredzone.acme4j.challenge Http01Challenge) (org.shredzone.acme4j.exception AcmeRetryAfterException))) (Security/addProvider (BouncyCastleProvider.)) (def ^:private acme-token (atom nil)) (def ^:private acme-content (atom nil)) ;; "Borrowed" from (comment (str "https://github.com/neo4j/neo4j/blob/3.5/community/ssl/src/main/" "java/org/neo4j/ssl/PkiUtils.java#L94")) (defn self-signed-cert [domain] | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | (ns mylife.ssl (:require [clojure.java.io :as io] [mylife.disk-safety :as s] [taoensso.timbre :as log]) (:import (java.io File FileWriter FileReader FileInputStream) (java.security KeyPairGenerator Security SecureRandom KeyStore PrivateKey KeyPair) (java.security.cert X509Certificate CertificateFactory) (java.time Instant) (java.time.temporal ChronoUnit) (java.util Date Collection) (javax.net.ssl KeyManagerFactory) (org.bouncycastle.asn1.x500 X500Name) (org.bouncycastle.operator.jcajce JcaContentSignerBuilder) (org.bouncycastle.jce.provider BouncyCastleProvider) (org.bouncycastle.cert.jcajce JcaX509v3CertificateBuilder JcaX509CertificateConverter) (org.shredzone.acme4j.util KeyPairUtils CSRBuilder) (org.shredzone.acme4j Session AccountBuilder Order Status Authorization) (org.shredzone.acme4j.challenge Http01Challenge) (org.shredzone.acme4j.exception AcmeRetryAfterException))) (Security/addProvider (BouncyCastleProvider.)) (def ^:private acme-token (atom nil)) (def ^:private acme-content (atom nil)) (def ^:private keystore-type "JKS") ;; "Borrowed" from (comment (str "https://github.com/neo4j/neo4j/blob/3.5/community/ssl/src/main/" "java/org/neo4j/ssl/PkiUtils.java#L94")) (defn self-signed-cert [domain] |
︙ | ︙ | |||
247 248 249 250 251 252 253 | (defn acme-cert-route [] ["/.well-known/acme-challenge/:token" {:get {:handler get-acme-content}}]) (defn validate-keystore [^File keystore-file ^String password] (with-open [is (FileInputStream. keystore-file)] | | > | > > > > > > > > > > | 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 | (defn acme-cert-route [] ["/.well-known/acme-challenge/:token" {:get {:handler get-acme-content}}]) (defn validate-keystore [^File keystore-file ^String password] (with-open [is (FileInputStream. keystore-file)] (doto (KeyStore/getInstance keystore-type) (.load is (.toCharArray password))))) (defn save-cert-chain! [^String keystore-path ^PrivateKey private-key ^Collection certs ^String password] (let [key-store (doto (KeyStore/getInstance keystore-type) (.load nil nil)) password (.toCharArray password)] (.setKeyEntry key-store "certificate" private-key password (into-array X509Certificate certs)) (with-open [out (io/output-stream keystore-path)] (.store key-store out password)))) (defn save-single-cert! [^String keystore-path ^PrivateKey private-key ^X509Certificate cert ^String password] (save-cert-chain! keystore-path private-key [cert] password)) (defn load-key-manager-factory [keystore-path keystore-password] (with-open [keystore-is (io/input-stream keystore-path)] (let [keystore-password (.toCharArray keystore-password) keystore (doto (KeyStore/getInstance keystore-type) (.load keystore-is keystore-password))] (doto (KeyManagerFactory/getInstance "SunX509") (.init keystore keystore-password))))) |