├── resources ├── public │ ├── favicon.ico │ ├── style.css │ └── index.html ├── log4j.properties ├── server-config.edn └── templates │ ├── argument.html │ ├── topiqs.html │ └── tool.html ├── doc └── intro.md ├── .gitignore ├── test └── topiq │ └── core_test.clj ├── src └── topiq │ ├── plugins.cljs │ ├── db.cljs │ ├── view.cljs │ ├── core.clj │ └── core.cljs ├── project.clj └── README.md /resources/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/replikativ/topiq/HEAD/resources/public/favicon.ico -------------------------------------------------------------------------------- /doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to collective 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/great-documentation/what-to-write/) 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | /resources/public/static 11 | /resources/public/js 12 | /out 13 | /.repl 14 | -------------------------------------------------------------------------------- /test/topiq/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns topiq.core-test 2 | (:require [clojure.test :refer :all] 3 | [topiq.core :refer :all])) 4 | 5 | #_(deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | -------------------------------------------------------------------------------- /resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=WARN, A1 2 | log4j.logger.topiq.core=DEBUG 3 | log4j.logger.clojure.core=DEBUG 4 | log4j.logger.user=DEBUG 5 | log4j.appender.A1=org.apache.log4j.ConsoleAppender 6 | log4j.appender.A1.layout=org.apache.log4j.PatternLayout 7 | log4j.appender.A1.layout.ConversionPattern=%d %-5p %c: %m%n 8 | -------------------------------------------------------------------------------- /resources/public/style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Ubuntu', sans-serif; 3 | } 4 | 5 | .selected-entry { 6 | border-left: 4px solid steelblue; 7 | } 8 | 9 | #post-btn,#argument-btn { 10 | height: 100%; 11 | line-height: 100%; 12 | padding-top: 0; 13 | padding-bottom: 0; 14 | padding-right: 20; 15 | padding-left: 20; 16 | } 17 | 18 | #general-input-btn{ 19 | height: 100%; 20 | line-height: 100%; 21 | } 22 | 23 | .glyphicon-chevron-up, .glyphicon-chevron-down { 24 | cursor: pointer; 25 | } 26 | -------------------------------------------------------------------------------- /resources/server-config.edn: -------------------------------------------------------------------------------- 1 | {:build :dev 2 | :behind-proxy false 3 | :proto "http" 4 | :port 8080 5 | :host "localhost" ;; adjust hostname 6 | :user "mail:eve@topiq.es" 7 | ;; only do this for CDVCS' you control and do it on one peer 8 | ;; to avoid conflict management! 9 | :hooks {[#".*" 10 | #uuid "26558dfe-59bb-4de4-95c3-4028c56eb5b5"] 11 | [["mail:eve@topiq.es" 12 | #uuid "26558dfe-59bb-4de4-95c3-4028c56eb5b5"]]} 13 | :connect [#_"wss://topiq.es/replikativ/ws" #_"ws://topiq.polyc0l0r.net:8080/replikativ/ws"] 14 | :mail-config {:host "smtp.topiq.es"} 15 | :trusted-hosts #{"topiq.es" "78.47.61.129" "topiq.polyc0l0r.net" "91.250.113.223" "127.0.0.1"} 16 | } 17 | -------------------------------------------------------------------------------- /src/topiq/plugins.cljs: -------------------------------------------------------------------------------- 1 | (ns topiq.plugins 2 | (:require [topiq.db :refer [hashtag-regexp]] 3 | [markdown.core :as md] 4 | [clojure.string :as str])) 5 | 6 | 7 | ;; static pipline for now, will be factored out for js and runtime config later 8 | 9 | 10 | (def pre-markdown-plugins identity) 11 | 12 | 13 | (defn special-chars [s] 14 | (clojure.string/replace s #"\'" "’")) 15 | 16 | (defn replace-hashtags 17 | "Replace hashtags in string with html references" 18 | [s] 19 | (str/replace s hashtag-regexp "$1$2")) 20 | 21 | (defn img-responsive [s] 22 | (str/replace s " s 31 | replace-hashtags 32 | img-responsive 33 | new-tab-link)) 34 | 35 | (defn render-content [s] 36 | (-> s 37 | pre-markdown-plugins 38 | md/md->html 39 | post-markdown-plugins)) 40 | -------------------------------------------------------------------------------- /resources/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | topiq 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 32 | 33 | Fork me on GitHub 34 | 35 | 36 | 37 |
38 |

Loading, please hold on.

39 | You do not need to refresh, this web page reconnects automatically and tracks all state offline. 40 | If you find any bugs, please report them here.
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject topiq "0.1.0-SNAPSHOT" 2 | 3 | :description "An app to collectively link and share data." 4 | 5 | :url "https://github.com/kordano/topiq" 6 | 7 | :license {:name "Eclipse Public License" 8 | :url "http://www.eclipse.org/legal/epl-v10.html"} 9 | 10 | :source-paths ["src"] 11 | :dependencies [[org.clojure/clojure "1.8.0"] 12 | [org.clojure/clojurescript "1.8.51"] 13 | [org.clojure/core.cache "0.6.5"] 14 | ;; implicitly needed? 15 | [org.clojure/core.memoize "0.5.9" :exclusions [org.clojure/core.cache]] 16 | 17 | [com.fzakaria/slf4j-timbre "0.3.2"] 18 | 19 | [ring "1.5.0"] ;; implicitly needed? 20 | [ring-cors "0.1.8"] 21 | [enlive "1.1.6"] 22 | [compojure "1.5.1"] 23 | 24 | [domina "1.0.3"] 25 | [datascript "0.15.4"] 26 | [org.omcljs/om "0.9.0"] 27 | [kioo "0.4.2"] 28 | 29 | [com.cognitect/transit-cljs "0.8.239" :scope "provided"] 30 | [io.replikativ/replikativ "0.2.4"] 31 | #_[io.replikativ/kabel-auth "0.1.0-SNAPSHOT"] 32 | [com.draines/postal "2.0.1"] 33 | [markdown-clj "0.9.90"]] 34 | 35 | 36 | :profiles {:dev {:dependencies [[com.cemerick/piggieback "0.2.1"]] 37 | :figwheel {:nrepl-port 7888 38 | #_:nrepl-middleware #_["cider.nrepl/cider-middleware" 39 | "cemerick.piggieback/wrap-cljs-repl"]} 40 | :plugins [[lein-figwheel "0.5.8"]]} 41 | :uberjar {:aot :all}} 42 | 43 | :plugins [[lein-cljsbuild "1.1.4"]] 44 | 45 | :main topiq.core 46 | 47 | :prep-tasks ["compile" ["cljsbuild" "once" "prod"]] 48 | 49 | :uberjar-name "topiq-standalone.jar" 50 | 51 | 52 | :figwheel {:http-server-root "public" 53 | :css-dirs ["resources/public/css"]} 54 | 55 | :clean-targets ^{:protect false} ["target" "resources/public/js"] 56 | 57 | :cljsbuild 58 | {:builds 59 | {:figwheel {:source-paths ["src/"] 60 | :figwheel true 61 | :compiler 62 | {:main topiq.core 63 | :asset-path "js/out" 64 | :output-to "resources/public/js/main.js" 65 | :output-dir "resources/public/js/out" 66 | :optimizations :none 67 | :pretty-print true}} 68 | :dev {:source-paths ["src"] 69 | :compiler 70 | {:main topiq.core 71 | :output-to "resources/public/js/dev/main.js" 72 | :output-dir "resources/public/js/dev/out" 73 | :optimizations :none 74 | :pretty-print true}} 75 | :prod {:source-paths ["src"] 76 | :compiler 77 | {:main topiq.core 78 | :output-to "resources/public/js/prod/main.js" 79 | :output-dir "resources/public/js/prod/out" 80 | :optimizations :advanced}}}}) 81 | -------------------------------------------------------------------------------- /resources/templates/argument.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 83 | 84 | 85 | 86 |
87 | 88 | 89 | 92 | 93 |
94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /resources/templates/topiqs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 18 | 19 |
20 | 21 | 22 | 23 |
24 | 25 |
88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /resources/templates/tool.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 33 | 34 | 35 | 36 | 54 | 55 | 56 | 57 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # topiq 2 | 3 | A social network application based on [P2P replication with CRDTs](https://github.com/replikativ/replikativ). 4 | 5 | Test p2p network at: 6 | - 7 | 8 | ## Usage 9 | 10 | You can use the app in your browser all state changes are 11 | automatically synchronized and topiq reconnects automatically after 12 | you have been offline. Sometimes conflicts can happen and are resolved 13 | automatically atm. This will probably change at some point depending 14 | on the future feature set. 15 | 16 | ## Deployment Gitter 17 | 18 | We will be more than happy if you join the test network! Just drop into 19 | the replikativ channel of gitter and say hello :) 20 | 21 | Atm. you need an SMTP server which can send messages to notify users 22 | about authentication requests. 23 | 24 | Edit the config settings: 25 | 26 | ~~~clojure 27 | {:build :dev ;; or :prod 28 | :behind-proxy false 29 | :proto "http" 30 | :port 8080 31 | :host "localhost" ;; adjust hostname 32 | :user "mail:eve@your-isp.com" 33 | ;; only do this for CDVCS' you control and do it on one peer 34 | ;; to avoid server-side conflict management! 35 | :hooks {[#".*" 36 | #uuid "26558dfe-59bb-4de4-95c3-4028c56eb5b5"] 37 | [["mail:eve@your-isp.com" 38 | #uuid "26558dfe-59bb-4de4-95c3-4028c56eb5b5"]]} 39 | :connect ["wss://topiq.es/replikativ/ws"] 40 | :mail-config {:host "smtp.your-isp.com"} 41 | :trusted-hosts #{"topiq.es" "78.47.61.129"} 42 | } 43 | ~~~ 44 | 45 | Build an `AOT`-compiled jar file: 46 | 47 | ~~~bash 48 | git clone https://github.com/whilo/topiq 49 | lein uberjar # also compiles advanced cljs 50 | java -jar target/topiq-standalone.jar resources/server-config.edn 51 | ~~~ 52 | 53 | You probably also have to add the letsencrypt tool chain to your JDK 54 | with keytool. You can see this when you get connection errors with ssl 55 | for topiq.es. 56 | 57 | ~~~bash 58 | wget https://letsencrypt.org/certs/isrgrootx1.pem 59 | wget https://letsencrypt.org/certs/letsencryptauthorityx1.der 60 | sudo keytool -trustcacerts -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -noprompt -importcert -alias isrgrootx1 -file ~/isrgrootx1.pem 61 | sudo keytool -trustcacerts -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -noprompt -importcert -alias letsencryptauthorityx1 -file ~/letsencryptauthorityx1.der 62 | ~~~ 63 | 64 | ## Roadmap 65 | 66 | ## 0.1.0 (first release) 67 | - build test network 68 | - fix HTML 69 | - Use dialogs for authentication 70 | - improve datascript queries and understanding, e.g. rank & sort in datascript 71 | - escape topiq text 72 | - Allow time travel 73 | - show only recent entries (how?), dynamically load more content 74 | 75 | ## 0.2.0 76 | - partition state space to allow embedding in different contexts 77 | - make layout embeddable as comment stream 78 | - better editor for markdown: ProseMirror? 79 | 80 | ## TODO 81 | 82 | - remove navbar 83 | - use try for connect and then build up CDVCS directly instead of embedded store 84 | - organized in a discourse/conversation (branch?), e.g. private 85 | conversation "discourse" for each friend as "messaging" 86 | - support search by hash-tag, user 87 | - plugins to add data and structure to comments / social apps 88 | - individual up to collective help to summarize for new topic 89 | 90 | ## The MIT License (MIT) 91 | 92 | Copyright © 2014-2016 Christian Weilbach, Konrad Kühne 93 | 94 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 95 | 96 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 97 | 98 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 99 | -------------------------------------------------------------------------------- /src/topiq/db.cljs: -------------------------------------------------------------------------------- 1 | (ns topiq.db 2 | (:require [datascript.core :as d] 3 | [om.core :as om :include-macros true] ;; TODO avoid in db? 4 | [hasch.core :refer [uuid]] 5 | [replikativ.crdt.cdvcs.stage :as s] 6 | [cljs.core.async :refer [put! chan ! alts! timeout close!] :as async] 7 | [superv.async :refer [S]]) 8 | (:require-macros [superv.async :refer [<> text 102 | (re-seq url-regexp) 103 | (map first))] 104 | (go-try S (> text 134 | (re-seq url-regexp) 135 | (map first))] 136 | (go-try S ( substitute 6 | listen prepend append html remove-class add-class]] 7 | [kioo.core :refer [handle-wrapper]] 8 | [om.core :as om :include-macros true] 9 | [domina :as dom]) 10 | (:require-macros [kioo.om :refer [defsnippet deftemplate]])) 11 | 12 | (enable-console-print!) 13 | 14 | 15 | (defn compute-time-diff 16 | [timestamp] 17 | (let [time-diff (- (.getTime (js/Date.)) timestamp)] 18 | (cond 19 | (>= time-diff 172800000) (str (Math/round (/ time-diff 86400000)) " days ago") 20 | (< 86399999 time-diff 172800000) "yesterday" 21 | (< 7199999 time-diff 86400000) (str (Math/round (/ time-diff 3600000)) " hours ago") 22 | (< 119999 time-diff 7200000 ) (str (Math/round (/ time-diff 60000)) " minutes ago") 23 | (< time-diff 120000) "now" 24 | :else "NaN"))) 25 | 26 | 27 | ;; --- navbar templates and functions --- 28 | 29 | (defn handle-text-change [e owner text] 30 | (om/set-state! owner text (.. e -target -value))) 31 | 32 | 33 | (defn set-navbar-user 34 | "Set user name in user nav field and reset login text" 35 | [owner name] 36 | (do 37 | (om/set-state! owner :current-user name) 38 | (om/set-state! owner :login-user-text ""))) 39 | 40 | 41 | (defn commit [owner] 42 | (let [username (.-innerHTML (dom/by-id "nav-current-user")) 43 | stage (om/get-state owner :stage) 44 | selected-topiq (om/get-state owner :selected-topiq)] 45 | (if (= "Not logged in" username) 46 | (js/alert "Please login or register.") 47 | (try 48 | (let [text (dom/value (dom/by-id "general-input-form"))] 49 | (if (> (count text) 10) 50 | (if selected-topiq 51 | (add-argument stage username selected-topiq text) 52 | (add-topiq stage username text)) 53 | (js/alert "Not enough input."))) 54 | (dom/set-value! (dom/by-id "general-input-form") "") 55 | (catch js/Object e 56 | (js/alert e)))))) 57 | 58 | 59 | (deftemplate navbar "templates/tool.html" 60 | [owner {:keys [current-user search-text login-user-text search-placeholder login-fn]}] 61 | {#_[:#nav-input-field] #_(do-> (set-attr :placeholder search-placeholder) 62 | (content search-text) 63 | (listen :on-change #(handle-text-change % owner :search-text))) 64 | [:#nav-current-user] (do-> (content current-user) 65 | (listen :on-change #(.log js/console (.. % -target -value)))) 66 | [:#login-user-input] (do-> (set-attr :value login-user-text) 67 | (listen :on-change #(handle-text-change % owner :login-user-text))) 68 | [:#login-user-password] (set-attr :disabled true) 69 | [:#modal-login-btn] (listen :on-click (fn [e] 70 | (set-navbar-user owner (str "mail:" login-user-text)) 71 | (login-fn (str "mail:" login-user-text)))) 72 | [:#register-user-input] (set-attr :disabled true) 73 | [:#register-user-password] (set-attr :disabled true) 74 | [:#forgot-user-input] (set-attr :disabled true)}) 75 | 76 | 77 | (defn expose-links [text] 78 | (let [free-url #"([\(]?)((https?|ftp)://[a-z0-9\u00a1-\uffff-]+(\.[a-z0-9\u00a1-\uffff-]+)+(:\d{2,5})?(/\S+)?)[\)]?" 79 | links (re-seq free-url text)] 80 | (reduce (fn [text [_ par url]] 81 | (if (empty? par) 82 | (.replace text url (str "[" url "](" url ")")) 83 | text)) text links))) 84 | 85 | ;; --- user post templates --- 86 | (defsnippet topiq-argument "templates/argument.html" [:.argument] 87 | [argument] 88 | {[:.argument-text] (-> (:content argument) 89 | expose-links 90 | render-content 91 | html-content) 92 | [:.argument-author] (content (.replace (:author argument) "mail:" "")) 93 | [:.argument-ts] (content (compute-time-diff (:ts argument)))}) 94 | 95 | (defsnippet topiq-vote-group "templates/topiqs.html" [:.topiq-vote-group] 96 | [stage app topiq voter] 97 | {[:.glyphicon-chevron-up] (listen :on-click #(add-vote stage (:id topiq) voter :up)) 98 | [:.vote-counter] (content (:vote-count topiq)) 99 | [:.glyphicon-chevron-down] (listen :on-click #(add-vote stage (:id topiq) voter :down))}) 100 | 101 | 102 | 103 | (defsnippet topiq "templates/topiqs.html" [:.topiq] 104 | [topiq app owner] 105 | {[:.topiq] (set-attr "id" (str "topiq-" (:id topiq))) 106 | [:.argument-counter] (content (-> (get-arguments (:id topiq) app) count)) 107 | [:.argument-ref] (do-> 108 | (set-attr :href (str "#" (:id topiq))) 109 | (listen :on-click #(om/set-state! owner :selected-topiq (:id topiq)))) 110 | ;; XSS attack possible? 111 | [:.topiq-text] (html-content 112 | (let [text (replace-hashtags (:title topiq))] 113 | (reduce 114 | #(clojure.string/replace %1 %2 (str " link ")) 115 | text 116 | (map first (re-seq url-regexp text))))) 117 | [:.topiq-author] (content (.replace (:author topiq) "mail:" "")) 118 | [:.topiq-ts] (content (compute-time-diff (:ts topiq))) 119 | [:.topiq-vote-group] (substitute (topiq-vote-group (om/get-state owner :stage) 120 | app 121 | topiq 122 | (.-innerHTML (dom/by-id "nav-current-user"))))}) 123 | 124 | ;; WARNING: pure "arguments" name clashes with compiler 125 | (deftemplate topiq-arguments "templates/argument.html" 126 | [app owner] 127 | {[:.topiq-text] (html-content 128 | (let [topiq (get-topiq (om/get-state owner :selected-topiq) app) 129 | text (replace-hashtags (:title topiq))] 130 | (reduce 131 | #(clojure.string/replace %1 %2 (str " link ")) 132 | text 133 | (map first (re-seq url-regexp text))))) 134 | [:.topiq-author] (content (.replace (:author (get-topiq (om/get-state owner :selected-topiq) app)) 135 | "mail:" "")) 136 | [:.topiq-ts] (content (compute-time-diff (:ts (get-topiq (om/get-state owner :selected-topiq) app)))) 137 | [:#back-btn] (listen :on-click #(om/set-state! owner :selected-topiq nil)) 138 | [:.arguments] (content (map topiq-argument (get-arguments (om/get-state owner :selected-topiq) app))) 139 | [:#general-input-form] (listen :on-key-down #(if (= (.-keyCode %) 10) 140 | (commit owner) 141 | (when (= (.-which %) 13) 142 | (when (.-ctrlKey %) 143 | (commit owner))))) 144 | [:#argument-btn] (listen :on-click (fn [e] (commit owner)))}) 145 | 146 | 147 | (deftemplate topiqs "templates/topiqs.html" 148 | [app owner] 149 | {[:#general-input-form] (listen :on-key-down #(if (= (.-keyCode %) 10) 150 | (commit owner) 151 | (when (= (.-which %) 13) 152 | (when (.-ctrlKey %) 153 | (commit owner))))) 154 | [:#post-btn] (listen :on-click (fn [e] (commit owner))) 155 | [:.list-group] (content (for [t (get-topiqs app)] (topiq t app owner)))}) 156 | -------------------------------------------------------------------------------- /src/topiq/core.clj: -------------------------------------------------------------------------------- 1 | (ns topiq.core 2 | (:gen-class :main true) 3 | (:require [clojure.core.async :refer [timeout sub chan !! >! 4 | go go-loop put! close!] :as async] 5 | [clojure.edn :as edn] 6 | [clojure.java.io :as io] 7 | [kabel.platform-log :refer [info warn error debug]] 8 | [ring.util.response :as resp] 9 | [compojure.core :refer [GET POST routes]] 10 | [compojure.handler :refer [site api]] 11 | [compojure.route :refer [resources]] 12 | [kabel.middleware.block-detector :refer [block-detector]] 13 | #_[kabel-auth.core :refer [auth inbox-auth register-external-token external-tokens]] 14 | [kabel.peer :as peer] 15 | [konserve.core :as k] 16 | [konserve.filestore :refer [new-fs-store]] 17 | [konserve.memory :refer [new-mem-store]] 18 | [net.cgrand.enlive-html :refer [deftemplate template set-attr substitute html] :as enlive] 19 | [org.httpkit.server :refer [run-server]] 20 | [postal.core :refer [send-message]] 21 | [replikativ.p2p.fetch :refer [fetch]] 22 | [replikativ.p2p.hooks :refer [hook]] 23 | [replikativ.peer :refer [server-peer client-peer]] 24 | [replikativ.stage :as s] 25 | [ring.middleware.cors :refer [wrap-cors]] 26 | [superv.async :refer [ (routes 83 | (resources "/") 84 | (GET "/replikativ/ws" [] (-> @peer :volatile :handler)) 85 | #_(GET "/auth/:token" [token] (auth-token (java.util.UUID/fromString token))) 86 | (GET "/*" [] 87 | (template (io/resource "public/index.html") [_] 88 | [:#init-js] (substitute 89 | (html [:script 90 | {:type "text/javascript"} 91 | ;; direct pass in config flags to the client atm. 92 | (str "topiq.core.main(\"" user "\")")]))))) 93 | #_(wrap-cors :access-control-allow-origin [#"http://localhost:3449"] 94 | #_[#"http://localhost:3449/.*" #".*"] 95 | :access-control-allow-methods [:get :put :post :delete 96 | :upgrade])) 97 | stage ( (or path "resources/server-config.edn") 119 | slurp 120 | read-string)] 121 | (info (str "Starting server @ port " port " with config " path)) 122 | (defonce state (start config)))) 123 | 124 | (comment 125 | (-main "resources/server-config.edn") 126 | (stop state) 127 | 128 | (require '[replikativ.crdt.cdvcs.stage :as cs]) 129 | 130 | (:config state) 131 | 132 | (get-in @(:stage state) ["mail:eve@topiq.es" 133 | #uuid "26558dfe-59bb-4de4-95c3-4028c56eb5b5" :state :heads]) 134 | 135 | (! alts! timeout close!] :as async] 3 | [cljs.reader :refer [read-string] :as read] 4 | [clojure.data :refer [diff]] 5 | [clojure.set :as set] 6 | [datascript.core :as d] 7 | [hasch.core :refer [uuid]] 8 | [kabel.middleware.block-detector :refer [block-detector]] 9 | #_[kabel-auth.core :refer [auth]] 10 | [kioo.core :refer [handle-wrapper]] 11 | [kioo.om :refer [content set-attr do-> substitute listen]] 12 | [konserve.core :as k] 13 | [konserve.memory :refer [new-mem-store]] 14 | [om.core :as om :include-macros true] 15 | [om.dom :as omdom] 16 | [replikativ.crdt.cdvcs.realize :refer [stream-into-identity!]] 17 | [replikativ.crdt.cdvcs.stage :as sc] 18 | [replikativ.crdt.materialize :refer [key->crdt]] 19 | [replikativ.crdt.cdvcs.realize :refer [head-value]] 20 | [replikativ.crdt :refer [map->CDVCS]] 21 | [replikativ.p2p.fetch :refer [fetch]] 22 | [replikativ.p2p.hooks :refer [hook default-integrity-fn]] 23 | [replikativ.peer :refer [client-peer]] 24 | [replikativ.protocols :refer [-downstream]] 25 | [replikativ.stage :as s] 26 | [topiq.view :refer [topiqs navbar topiq-arguments]] 27 | [superv.async :refer [S]] 28 | [taoensso.timbre :as timbre]) 29 | (:require-macros [cljs.core.async.macros :refer [go go-loop]] 30 | [superv.async :refer [<?]])) 31 | 32 | (enable-console-print!) 33 | 34 | (def uri (goog.Uri. js/location.href)) 35 | 36 | (def cdvcs-id #uuid "26558dfe-59bb-4de4-95c3-4028c56eb5b5") 37 | 38 | (def ssl? (= (.getScheme uri) "https")) 39 | 40 | (def ^:dynamic eval-fn {'(fn [S old params] (d/db-with old params)) 41 | (fn [S old params] 42 | (when-not @old 43 | (let [schema {:identity/id {:db/unique :db.unique/identity}} 44 | conn (d/create-conn schema)] 45 | (reset! old @conn))) 46 | (swap! old d/db-with params) 47 | old) 48 | ;; keep old pre 0.2.4 schema 49 | '(fn [old params] (d/db-with old params)) 50 | (fn [S old params] 51 | (when-not @old 52 | (let [schema {:identity/id {:db/unique :db.unique/identity}} 53 | conn (d/create-conn schema)] 54 | (reset! old @conn))) 55 | (swap! old d/db-with params) 56 | old)}) 57 | 58 | 59 | (defn navbar-view 60 | "Builds navbar with search, user menu and user-related modals" 61 | [login-fn app owner] 62 | (reify 63 | om/IInitState 64 | (init-state [_] 65 | {:current-user "Not logged in" 66 | :search-placeholder "Search..." 67 | :search-text "" 68 | :login-user-text "" 69 | :login-fn login-fn}) 70 | om/IRenderState 71 | (render-state [this {:keys [current-user search-text login-user-text search-placeholder] :as state}] 72 | (navbar 73 | owner 74 | state)))) 75 | 76 | 77 | (defn topiqs-view 78 | "Builds topiqs list with topiq head and related argument list, resolves conflicts" 79 | [stage app owner] 80 | (reify 81 | om/IInitState 82 | (init-state [_] 83 | {:selected-topiq false 84 | :stage stage 85 | ;; refresh time calculations 86 | :refresh-loop (go-loop [] 87 | (CDVCS) 113 | 114 | 115 | (defonce state (atom {})) 116 | 117 | (defn ^:export main [full-user & args] 118 | (go-try S 119 | (let [[[_ _ track-user]] (re-seq #"(.+):(.+)" full-user) 120 | val-atom (atom nil) 121 | login-fn (fn [stage hooks stream new-user] 122 | (go-try S 123 | ;; NOTE: always track eve to ensure convergence 124 | (swap! stream (fn [s-ch] (close! (:close-ch s-ch)) 125 | (stream-into-identity! stage [new-user cdvcs-id] eval-fn val-atom))) 126 | (swap! hooks assoc ["mail:eve@topiq.es" cdvcs-id] 127 | [[new-user cdvcs-id] 128 | default-integrity-fn true]) 129 | (swap! hooks assoc [full-user cdvcs-id] 130 | [[new-user cdvcs-id] 131 | default-integrity-fn true]) 132 | (swap! stage assoc-in [:config :user] new-user) 133 | ( @stage :volatile :peer deref :volatile :store :state deref keys)) 269 | 270 | 271 | --------------------------------------------------------------------------------