Set up a http server, and other system boilerplate
This commit is contained in:
parent
c8b0d4e686
commit
39fe19f7a3
17 changed files with 5770 additions and 8 deletions
8
README.md
Normal file
8
README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Souk - ActivityPub in Clojure
|
||||
|
||||
A work-in-progress implementation of the ActivityPub standard in Clojure.
|
||||
|
||||
This repo should primarily be understood as a byproduct of the live streams
|
||||
which explore this topic, it is not meant to be generally consumable, either as
|
||||
a library or an application.
|
||||
|
26
deps.edn
26
deps.edn
|
@ -1,6 +1,28 @@
|
|||
{:deps {hato/hato {:mvn/version "0.9.0"}
|
||||
{:paths ["src" "resources"]
|
||||
:deps {org.clojure/clojure {:mvn/version "1.11.1"}
|
||||
|
||||
;; Application setup
|
||||
com.lambdaisland/webbing {:local/root "/home/arne/github/lambdaisland/webbing"}
|
||||
;; {:mvn/version "0.4.20-alpha"}
|
||||
;; Incoming HTTP
|
||||
ring/ring-core {:mvn/version "1.9.6"}
|
||||
ring/ring-jetty-adapter {:mvn/version "1.9.6"}
|
||||
ring/ring-mock {:mvn/version "0.4.0"}
|
||||
metosin/malli {:mvn/version "0.9.2"}
|
||||
metosin/muuntaja {:mvn/version "0.6.8"}
|
||||
metosin/reitit {:mvn/version "0.5.18"}
|
||||
|
||||
;; Outgoing HTTP
|
||||
hato/hato {:mvn/version "0.9.0"}
|
||||
|
||||
;; Formats
|
||||
cheshire/cheshire {:mvn/version "5.11.0"}
|
||||
|
||||
;; Database
|
||||
seancorfield/next.jdbc {:mvn/version "1.2.659"}
|
||||
com.impossibl.pgjdbc-ng/pgjdbc-ng {:mvn/version "0.8.9"}
|
||||
}
|
||||
|
||||
}}
|
||||
:aliases
|
||||
{:dev {:extra-paths ["dev"]}
|
||||
:souk {:main-opts ["-m" "lambdaisland.souk"]}}}
|
||||
|
|
3
dev/user.clj
Normal file
3
dev/user.clj
Normal file
|
@ -0,0 +1,3 @@
|
|||
(ns user)
|
||||
|
||||
(set! *print-namespace-maps* false)
|
BIN
preview-image.png
Normal file
BIN
preview-image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 502 KiB |
5507
preview-image.svg
Normal file
5507
preview-image.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 434 KiB |
|
@ -2,7 +2,6 @@
|
|||
(:require [lambdaisland.souk.json-ld :as ld]
|
||||
[lambdaisland.souk.activitypub :refer :all]))
|
||||
|
||||
|
||||
(ld/expand (:body (ld/json-get "https://raw.githubusercontent.com/gobengo/activitystreams2-spec-scraped/master/data/activitystreams-vocabulary/1528589057.json")))
|
||||
|
||||
(let [{:owl/keys [imports]} (GET "https://raw.githubusercontent.com/gobengo/activitystreams2-spec-scraped/master/data/activitystreams-vocabulary/1528589057.json")]
|
||||
|
|
8
resources/lambdaisland/souk/config.edn
Normal file
8
resources/lambdaisland/souk/config.edn
Normal file
|
@ -0,0 +1,8 @@
|
|||
{:http/router
|
||||
{:gx/component lambdaisland.souk.components.router/component
|
||||
:gx/props {:dev-router? #setting :dev/reload-routes?}}
|
||||
|
||||
:http/server
|
||||
{:gx/component lambdaisland.souk.components.jetty/component
|
||||
:gx/props {:jetty-options {:port #setting :port}
|
||||
:router (gx/ref :http/router)}}}
|
1
resources/lambdaisland/souk/settings-dev.edn
Normal file
1
resources/lambdaisland/souk/settings-dev.edn
Normal file
|
@ -0,0 +1 @@
|
|||
{:dev/reload-routes? true}
|
1
resources/lambdaisland/souk/settings-prod.edn
Normal file
1
resources/lambdaisland/souk/settings-prod.edn
Normal file
|
@ -0,0 +1 @@
|
|||
{:port 80}
|
2
resources/lambdaisland/souk/settings.edn
Normal file
2
resources/lambdaisland/souk/settings.edn
Normal file
|
@ -0,0 +1,2 @@
|
|||
{:port 3444
|
||||
:dev/reload-routes? false}
|
6
src/lambdaisland/souk.clj
Normal file
6
src/lambdaisland/souk.clj
Normal file
|
@ -0,0 +1,6 @@
|
|||
(ns lambdaisland.souk
|
||||
(:require [lambdaisland.webbing.prod :as prod]
|
||||
[lambdaisland.souk.setup :as setup]))
|
||||
|
||||
(defn -main [& _]
|
||||
(prod/go (setup/prod-setup)))
|
|
@ -1,8 +1,7 @@
|
|||
(ns lambdaisland.souk.activitypub
|
||||
"Interact with ActivityPub instances"
|
||||
(:require [lambdaisland.souk.json-ld :as ld]))
|
||||
|
||||
(set! *print-namespace-maps* false)
|
||||
|
||||
(def common-prefixes
|
||||
{"dcterms" "http://purl.org/dc/terms/"
|
||||
"ldp" "http://www.w3.org/ns/ldp#"
|
||||
|
|
93
src/lambdaisland/souk/components/jetty.clj
Normal file
93
src/lambdaisland/souk/components/jetty.clj
Normal file
|
@ -0,0 +1,93 @@
|
|||
(ns lambdaisland.souk.components.jetty
|
||||
(:require [ring.adapter.jetty :as ring.jetty]
|
||||
[reitit.ring :as reitit-ring])
|
||||
(:import (org.eclipse.jetty.server Server)))
|
||||
|
||||
(def defaults {:join? false})
|
||||
|
||||
(def ?JettyOptions
|
||||
"Start a Jetty webserver to serve the given handler according to the
|
||||
supplied options:
|
||||
:configurator - a function called with the Jetty Server instance
|
||||
:async? - if true, treat the handler as asynchronous
|
||||
:async-timeout - async context timeout in ms
|
||||
(defaults to 0, no timeout)
|
||||
:async-timeout-handler - an async handler to handle an async context timeout
|
||||
:port - the port to listen on (defaults to 80)
|
||||
:host - the hostname to listen on
|
||||
:join? - blocks the thread until server ends
|
||||
(defaults to true)
|
||||
:daemon? - use daemon threads (defaults to false)
|
||||
:http? - listen on :port for HTTP traffic (defaults to true)
|
||||
:ssl? - allow connections over HTTPS
|
||||
:ssl-port - the SSL port to listen on (defaults to 443, implies)
|
||||
:ssl? is true
|
||||
:ssl-context - an optional SSLContext to use for SSL connections
|
||||
:exclude-ciphers - when :ssl? is true, additionally exclude these
|
||||
cipher suites
|
||||
:exclude-protocols - when :ssl? is true, additionally exclude these
|
||||
protocols
|
||||
:replace-exclude-ciphers? - when true, :exclude-ciphers will replace rather
|
||||
than add to the cipher exclusion list (defaults)
|
||||
to false
|
||||
:replace-exclude-protocols? - when true, :exclude-protocols will replace
|
||||
rather than add to the protocols exclusion list
|
||||
(defaults to false)
|
||||
:keystore - the keystore to use for SSL connections
|
||||
:keystore-type - the keystore type (default jks)
|
||||
:key-password - the password to the keystore
|
||||
:keystore-scan-interval - if not nil, the interval in seconds to scan for an
|
||||
updated keystore
|
||||
:thread-pool - custom thread pool instance for Jetty to use
|
||||
:truststore - a truststore to use for SSL connections
|
||||
:trust-password - the password to the truststore
|
||||
:max-threads - the maximum number of threads to use (default 50)
|
||||
:min-threads - the minimum number of threads to use (default 8)
|
||||
:max-queued-requests - the maximum number of requests to be queued
|
||||
:thread-idle-timeout - Set the maximum thread idle time. Threads that are
|
||||
idle for longer than this period may be stopped
|
||||
(default 60000)
|
||||
:max-idle-time - the maximum idle time in milliseconds for a
|
||||
connection (default 200000)
|
||||
:client-auth - SSL client certificate authenticate, may be set to
|
||||
:need,:want or :none (defaults to :none)
|
||||
:send-date-header? - add a date header to the response (default true)
|
||||
:output-buffer-size - the response body buffer size (default 32768)
|
||||
:request-header-size - the maximum size of a request header (default 8192)
|
||||
:response-header-size - the maximum size of a response header (default 8192)
|
||||
:send-server-version? - add Server header to HTTP response (default true)"
|
||||
[:map
|
||||
[:port pos-int?]
|
||||
[:host {:optional true} string?]
|
||||
[:max-threads {:optional true} pos-int?]
|
||||
[:min-threads {:optional true} pos-int?]
|
||||
[:max-queued-requests {:optional true} pos-int?]])
|
||||
|
||||
(def ?PropsSchema
|
||||
[:map
|
||||
[:jetty-options ?JettyOptions]])
|
||||
|
||||
(defn ring-handler [reitit-router]
|
||||
(reitit-ring/ring-handler
|
||||
reitit-router
|
||||
(reitit-ring/routes
|
||||
(reitit-ring/create-default-handler))))
|
||||
|
||||
(defn http-start [{:keys [props]}]
|
||||
(let [{:keys [router jetty-options]} props
|
||||
jetty-options (merge defaults jetty-options)]
|
||||
(ring.jetty/run-jetty (ring-handler router) jetty-options)))
|
||||
|
||||
(defn http-stop [{^Server this :value}]
|
||||
(.stop this))
|
||||
|
||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||
(def component
|
||||
{:gx/start {:gx/processor http-start
|
||||
:gx/props-schema ?PropsSchema}
|
||||
:gx/stop {:gx/processor http-stop}})
|
||||
|
||||
(comment
|
||||
{:gx/component k16.gx.contrib.jetty/component
|
||||
:gx/props {:jetty-options {}
|
||||
:handler '(gx/ref :handler)}})
|
48
src/lambdaisland/souk/components/router.clj
Normal file
48
src/lambdaisland/souk/components/router.clj
Normal file
|
@ -0,0 +1,48 @@
|
|||
(ns lambdaisland.souk.components.router
|
||||
"Reitit routes and router"
|
||||
(:require
|
||||
[lambdaisland.souk.dev-router :as dev-router]
|
||||
[muuntaja.core :as muuntaja]
|
||||
[reitit.dev.pretty :as pretty]
|
||||
[reitit.ring :as reitit-ring]
|
||||
[reitit.ring.coercion :as reitit-coercion]
|
||||
[reitit.ring.middleware.muuntaja :as reitit-muuntaja]
|
||||
[reitit.ring.middleware.parameters :as reitit-parameters]))
|
||||
|
||||
(defn routes [opts]
|
||||
[["/"
|
||||
{:get
|
||||
{:handler
|
||||
(fn [req]
|
||||
{:status 200
|
||||
:body "OK!"})}}]])
|
||||
|
||||
(defn wrap-request-context [handler ctx]
|
||||
(fn [req]
|
||||
(handler
|
||||
(assoc req :souk/ctx ctx))))
|
||||
|
||||
(defn create-router
|
||||
[props]
|
||||
(reitit.ring/router
|
||||
(into [] (remove nil? (routes props)))
|
||||
{:exception pretty/exception
|
||||
:data {:muuntaja muuntaja/instance
|
||||
:middleware [;; ↓↓↓ request passes through middleware top-to-bottom ↓↓↓
|
||||
reitit-parameters/parameters-middleware
|
||||
reitit-muuntaja/format-negotiate-middleware
|
||||
reitit-muuntaja/format-response-middleware
|
||||
reitit-muuntaja/format-request-middleware
|
||||
reitit-coercion/coerce-response-middleware
|
||||
reitit-coercion/coerce-request-middleware
|
||||
[wrap-request-context props]
|
||||
;; ↑↑↑ response passes through middleware bottom-to-top ↑↑↑
|
||||
]}}))
|
||||
|
||||
(defn start! [{:keys [props]}]
|
||||
(if (:dev-router? props)
|
||||
(dev-router/dev-router #(create-router props))
|
||||
(create-router props)))
|
||||
|
||||
(def component
|
||||
{:gx/start {:gx/processor start!}})
|
19
src/lambdaisland/souk/dev_router.clj
Normal file
19
src/lambdaisland/souk/dev_router.clj
Normal file
|
@ -0,0 +1,19 @@
|
|||
(ns lambdaisland.souk.dev-router
|
||||
"Reitit router wrapper that auto-rebuilds, for REPL workflows."
|
||||
(:require [reitit.core :as reitit]))
|
||||
|
||||
(defn dev-router
|
||||
"Given a function which builds a reitit router, returns a router which rebuilds
|
||||
the router on every call. This makes sure redefining routes in a REPL works as
|
||||
expected. Should only every be used in development mode, since it completely
|
||||
undoes all of reitit's great performance."
|
||||
[new-router]
|
||||
(reify reitit/Router
|
||||
(router-name [_] (reitit/router-name (new-router)))
|
||||
(routes [_] (reitit/routes (new-router)))
|
||||
(compiled-routes [_] (reitit/compiled-routes (new-router)))
|
||||
(options [_] (reitit/options (new-router)))
|
||||
(route-names [_] (reitit/route-names (new-router)))
|
||||
(match-by-path [_ path] (reitit/match-by-path (new-router) path))
|
||||
(match-by-name [_ name] (reitit/match-by-name (new-router) name))
|
||||
(match-by-name [_ name path-params] (reitit/match-by-name (new-router) name path-params))))
|
|
@ -1,4 +1,6 @@
|
|||
(ns lambdaisland.souk.json-ld
|
||||
"Interfacing with JSON-LD endpoints, and converting to and from idiomatic
|
||||
Clojure data."
|
||||
(:require [hato.client :as hato]
|
||||
[clojure.string :as str]
|
||||
[clojure.walk :as walk]))
|
||||
|
|
44
src/lambdaisland/souk/setup.clj
Normal file
44
src/lambdaisland/souk/setup.clj
Normal file
|
@ -0,0 +1,44 @@
|
|||
(ns lambdaisland.souk.setup
|
||||
(:require [lambdaisland.webbing.config :as config]
|
||||
[clojure.java.io :as io]))
|
||||
|
||||
(def project 'lambdaisland/souk)
|
||||
(def start-keys #{:http/server})
|
||||
|
||||
(def schemas
|
||||
{:settings [[:port int?]
|
||||
[:dev/reload-routes? boolean?]]
|
||||
:secrets []})
|
||||
|
||||
(defn proj-resource [path]
|
||||
(io/resource (str project "/" path)))
|
||||
|
||||
(defn prod-setup []
|
||||
{:schemas schemas
|
||||
:keys start-keys
|
||||
:sources {:config [(proj-resource "config.edn")]
|
||||
:secrets [(config/cli-args)
|
||||
(config/env)]
|
||||
:settings [(config/cli-args)
|
||||
(config/env)
|
||||
(config/default-value)
|
||||
(proj-resource "settings-prod.edn")
|
||||
(proj-resource "settings.edn")]}})
|
||||
|
||||
(defn dev-setup []
|
||||
(let [local-file (io/file "config.local.edn")
|
||||
local-config (when (.exists local-file)
|
||||
(read-string (slurp local-file)))]
|
||||
{:schemas schemas
|
||||
:keys (:dev/start-keys local-config start-keys)
|
||||
:sources {:config [(proj-resource "config.edn")
|
||||
(proj-resource "config-development.edn")
|
||||
(dissoc local-config :dev/start-keys)]
|
||||
:secrets [(config/dotenv)
|
||||
(config/env)
|
||||
(config/default-value)]
|
||||
:settings [(config/dotenv)
|
||||
(config/env)
|
||||
(config/default-value)
|
||||
(proj-resource "settings-dev.edn")
|
||||
(proj-resource "settings.edn")]}}))
|
Loading…
Reference in a new issue