Răsfoiți Sursa

Add procfile

Tienson Qin 5 ani în urmă
părinte
comite
b0c0b6ad40

+ 14 - 10
.gitignore

@@ -1,4 +1,17 @@
-node_modules/
+/target
+/classes
+/checkouts
+profiles.clj
+pom.xml
+pom.xml.asc
+*.jar
+*.class
+/.lein-*
+/.nrepl-port
+.hgignore
+.hg/
+
+web/node_modules/
 web/public/js/main.js
 
 /.cpcache
@@ -6,19 +19,10 @@ web/public/js/main.js
 /checkouts
 /src/gen
 
-pom.xml
-pom.xml.asc
 *.iml
-*.jar
 *.log
 .shadow-cljs
 .idea
 .lein-*
 .nrepl-*
 .DS_Store
-
-.hgignore
-.hg/
-
-.now
-.env

+ 0 - 0
backend/LICENSE → LICENSE


+ 0 - 12
backend/.gitignore

@@ -1,12 +0,0 @@
-/target
-/classes
-/checkouts
-profiles.clj
-pom.xml
-pom.xml.asc
-*.jar
-*.class
-/.lein-*
-/.nrepl-port
-.hgignore
-.hg/

+ 0 - 24
backend/CHANGELOG.md

@@ -1,24 +0,0 @@
-# Change Log
-All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/).
-
-## [Unreleased]
-### Changed
-- Add a new arity to `make-widget-async` to provide a different widget shape.
-
-## [0.1.1] - 2020-02-20
-### Changed
-- Documentation on how to make the widgets.
-
-### Removed
-- `make-widget-sync` - we're all async, all the time.
-
-### Fixed
-- Fixed widget maker to keep working when daylight savings switches over.
-
-## 0.1.0 - 2020-02-20
-### Added
-- Files from the new template.
-- Widget maker public API - `make-widget-sync`.
-
-[Unreleased]: https://github.com/your-name/backend/compare/0.1.1...HEAD
-[0.1.1]: https://github.com/your-name/backend/compare/0.1.0...0.1.1

+ 0 - 22
backend/README.md

@@ -1,22 +0,0 @@
-# backend
-
-A Clojure library designed to ... well, that part is up to you.
-
-## Usage
-
-FIXME
-
-## License
-
-Copyright © 2020 FIXME
-
-This program and the accompanying materials are made available under the
-terms of the Eclipse Public License 2.0 which is available at
-http://www.eclipse.org/legal/epl-2.0.
-
-This Source Code may also be made available under the following Secondary
-Licenses when the conditions for such availability set forth in the Eclipse
-Public License, v. 2.0 are satisfied: GNU General Public License as published by
-the Free Software Foundation, either version 2 of the License, or (at your
-option) any later version, with the GNU Classpath Exception which is available
-at https://www.gnu.org/software/classpath/license.html.

+ 0 - 3
backend/doc/intro.md

@@ -1,3 +0,0 @@
-# Introduction to backend
-
-TODO: write [great documentation](http://jacobian.org/writing/what-to-write/)

+ 0 - 36
backend/project.clj

@@ -1,36 +0,0 @@
-(defproject backend "0.1.0-SNAPSHOT"
-  :description "FIXME: write description"
-  :url "http://example.com/FIXME"
-  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
-            :url "https://www.eclipse.org/legal/epl-2.0/"}
-  :dependencies [[org.clojure/clojure "1.10.0"]
-                 [clj-social "0.1.6"]
-                 [org.postgresql/postgresql "42.2.8"]
-                 [org.clojure/java.jdbc "0.7.10"]
-                 [honeysql "0.9.8"]
-                 [hikari-cp "2.9.0"]
-                 [toucan "1.15.0"]
-                 [ragtime "0.8.0"]
-                 [com.taoensso/timbre "4.10.0"]
-                 [org.clojure/tools.namespace "0.3.1"]
-                 [buddy/buddy-sign "3.1.0"]
-                 [buddy/buddy-hashers "1.4.0"]
-                 [enlive "1.1.6"]
-                 [io.pedestal/pedestal.service "0.5.5"]
-                 [io.pedestal/pedestal.jetty "0.5.5"]
-                 [metosin/reitit-pedestal "0.4.2"]
-                 [metosin/reitit "0.4.2"]
-                 [metosin/jsonista "0.2.5"]
-                 [aero "1.1.6"]
-                 [com.stuartsierra/component "0.4.0"]
-                 [com.taoensso/nippy "2.14.0"]
-                 [hiccup "1.0.5"]]
-  ;; :main backend.core
-  :profiles {:repl {:dependencies [[io.pedestal/pedestal.service-tools "0.5.7"]]
-                    :source-paths ["src/backend" "dev"]}
-             :uberjar {:main backend.core
-                       :aot :all}}
-  :repl-options {:init-ns user}
-  :jvm-opts ["-Duser.timezone=UTC" "-Dclojure.spec.check-asserts=true"]
-  :aliases {"migrate"  ["run" "-m" "user/migrate"]
-            "rollback" ["run" "-m" "user/rollback"]})

+ 0 - 24
backend/resources/config.edn

@@ -1,24 +0,0 @@
-{:env #or [#env ENVIRONMENT "dev"]
- :port #or [#env PORT 3000]
- :oauth {:github {:app-key #env GITHUB_APP_KEY
-                  :app-secret #env GITHUB_APP_SECRET
-                  :redirect-uri #env GITHUB_REDIRECT_URI}}
- :jwt-secret #env JWT_SECRET
- :cookie-secret #env COOKIE_SECRET
- :log-path #or [#env LOG_PATH "/tmp/logseq"]
- :hikari-spec {:auto-commit        true
-               :read-only          false
-               :connection-timeout 30000
-               :validation-timeout 5000
-               :idle-timeout       600000
-               :max-lifetime       1800000
-               :minimum-idle       10
-               :maximum-pool-size  48
-               :pool-name          "logseq-clj-db-pool"
-               :adapter            "postgresql"
-               :username           #env PG_USERNAME
-               :password           #env PG_PASSWORD
-               :database-name      "logseq"
-               :server-name        "localhost"
-               :port-number        5432
-               :register-mbeans    false}}

+ 0 - 52
backend/resources/logback.xml

@@ -1,52 +0,0 @@
-<!-- Logback configuration. See http://logback.qos.ch/manual/index.html -->
-<!-- Scanning is currently turned on; This will impact performance! -->
-<configuration scan="true" scanPeriod="10 seconds">
-  <!-- Silence Logback's own status messages about config parsing
-  <statusListener class="ch.qos.logback.core.status.NopStatusListener" /> -->
-
-  <!-- Simple file output -->
-  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
-    <!-- encoder defaults to ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
-    <encoder>
-      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %X{io.pedestal} - %msg%n</pattern>
-    </encoder>
-
-    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
-      <!-- rollover daily -->
-      <fileNamePattern>logs/restream-api-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
-      <!-- or whenever the file size reaches 64 MB -->
-      <maxFileSize>64 MB</maxFileSize>
-    </rollingPolicy>
-
-    <!-- Safely log to the same file from multiple JVMs. Degrades performance! -->
-    <prudent>true</prudent>
-  </appender>
-
-
-  <!-- Console output -->
-  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
-    <!-- encoder defaults to ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
-    <encoder>
-      <pattern>%-5level %logger{36} %X{io.pedestal} - %msg%n</pattern>
-    </encoder>
-    <!-- Only log level INFO and above -->
-    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
-      <level>INFO</level>
-    </filter>
-  </appender>
-
-
-  <!-- Enable FILE and STDOUT appenders for all log messages.
-       By default, only log at level INFO and above. -->
-  <root level="INFO">
-    <appender-ref ref="FILE" />
-    <appender-ref ref="STDOUT" />
-  </root>
-
-  <!-- For loggers in the these namespaces, log at all levels. -->
-  <logger name="user" level="ALL" />
-  <!-- To log pedestal internals, enable this and change ThresholdFilter to DEBUG
-    <logger name="io.pedestal" level="ALL" />
-  -->
-
-</configuration>

+ 0 - 10
backend/resources/migrations/20200221043329_create_table_users.edn

@@ -1,10 +0,0 @@
-{:up ["create extension if not exists \"uuid-ossp\";
-CREATE TABLE users (
-  id uuid DEFAULT uuid_generate_v4() NOT NULL UNIQUE,
-  name text NOT NULL,
-  email text NOT NULL UNIQUE,
-  created_at timestamp with time zone DEFAULT timezone('UTC'::text, now()) NOT NULL,
-  CONSTRAINT created_at_chk CHECK ((date_part('timezone'::text, created_at) = '0'::double precision))
-);
-"]
- :down ["drop table users"]}

+ 0 - 9
backend/resources/migrations/20200221044628_create_table_repos.edn

@@ -1,9 +0,0 @@
-{:up ["CREATE TABLE repos (
-  id uuid DEFAULT uuid_generate_v4() NOT NULL UNIQUE,
-  user_id uuid NOT NULL,
-  url text NOT NULL,
-  created_at timestamp with time zone DEFAULT timezone('UTC'::text, now()) NOT NULL,
-  CONSTRAINT created_at_chk CHECK ((date_part('timezone'::text, created_at) = '0'::double precision))
-);"
-      "CREATE UNIQUE INDEX idx_repos_user_repo ON repos(user_id, url);"]
- :down ["drop table repos"]}

+ 0 - 9
backend/resources/migrations/20200221045345_create_table_refresh_tokens.edn

@@ -1,9 +0,0 @@
-{:up ["CREATE TABLE refresh_tokens (
-    user_id uuid NOT NULL UNIQUE,
-    token uuid NOT NULL UNIQUE
-)"
-      "ALTER TABLE refresh_tokens
-      ADD CONSTRAINT refresh_tokens_users_fkey FOREIGN KEY (user_id)
-      REFERENCES users (id)
-      ON UPDATE CASCADE ON DELETE CASCADE;"]
- :down ["drop table refresh_tokens"]}

+ 0 - 11
backend/resources/migrations/20200221072508_create_table_tokens.edn

@@ -1,11 +0,0 @@
-{:up ["CREATE TABLE tokens (
-  id uuid DEFAULT uuid_generate_v4() NOT NULL UNIQUE,
-  user_id uuid NOT NULL,
-  oauth_type text NOT NULL,
-  oauth_id text NOT NULL UNIQUE,
-  oauth_token text NOT NULL UNIQUE,
-  created_at timestamp with time zone DEFAULT timezone('UTC'::text, now()) NOT NULL,
-  CONSTRAINT created_at_chk CHECK ((date_part('timezone'::text, created_at) = '0'::double precision))
-);"
-      ]
- :down ["drop table tokens"]}

+ 0 - 1
backend/resources/public

@@ -1 +0,0 @@
-../../web/public

+ 0 - 35
backend/src/backend/auth.clj

@@ -1,35 +0,0 @@
-(ns backend.auth
-  (:require [taoensso.timbre :as timbre]
-            [clj-social.core :as social]
-            [backend.config :as config]
-            [backend.util :as util]
-            [backend.db.user :as u]
-            [backend.db.token :as token]
-            [backend.cookie :as cookie]
-            [clojure.java.jdbc :as j]))
-
-(defn github [data]
-  (let [{:keys [app-key app-secret redirect-uri]} (get-in config/config [:oauth :github])
-        instance (social/make-social :github app-key app-secret redirect-uri
-                                     :state (str (util/uuid))
-                                     :scope "user:email,repo")
-        access-token (social/getAccessToken instance (:code data))
-        info (social/getUserInfo instance access-token)
-        oauth-type "github"
-        oauth-id (str (:id info))
-        access-token (.getAccessToken access-token)]
-    (toucan.db/transaction
-      (if-let [token (token/get oauth-type oauth-id)]
-        ;; user already exists
-        (do
-          (token/update (:id token) access-token)
-          (let [token (assoc token :token access-token)]
-           (some-> (u/get (:user_id token))
-                   (assoc :token token))))
-        (when-let [user (u/insert {:name (:login info)
-                                   :email (:email info)})]
-          (let [token (token/create {:user_id (:id user)
-                                     :oauth_type oauth-type
-                                     :oauth_id oauth-id
-                                     :oauth_token access-token})]
-            (assoc user :token token)))))))

+ 0 - 24
backend/src/backend/components/hikari.clj

@@ -1,24 +0,0 @@
-(ns backend.components.hikari
-  (:require [com.stuartsierra.component :as component]
-            [hikari-cp.core :as hikari]
-            [clojure.java.jdbc :as j]
-            [toucan.db :as toucan]
-            [backend.db-migrate :as migrate]))
-
-(defrecord Hikari [db-spec datasource]
-  component/Lifecycle
-  (start [component]
-    (let [s (or datasource (hikari/make-datasource db-spec))]
-      ;; set time zone
-      (j/execute! {:datasource s} ["set time zone 'UTC'"])
-      ;; migrate
-      (migrate/migrate {:datasource s})
-      (toucan/set-default-db-connection! {:datasource s})
-      (assoc component :datasource s)))
-  (stop [component]
-    (when datasource
-      (hikari/close-datasource datasource))
-    (assoc component :datasource nil)))
-
-(defn new-hikari-cp [db-spec]
-  (map->Hikari {:db-spec db-spec}))

+ 0 - 26
backend/src/backend/components/http.clj

@@ -1,26 +0,0 @@
-(ns backend.components.http
-  (:require [com.stuartsierra.component :as component]
-            [io.pedestal.http :as http]))
-
-(defn test?
-  [service-map]
-  (= :test (:env service-map)))
-
-(defrecord Server [service-map service]
-  component/Lifecycle
-  (start [this]
-    (prn "service-map: " service-map)
-    (if service
-      this
-      (cond-> service-map
-        true                      http/create-server
-        (not (test? service-map)) http/start
-        true                      ((partial assoc this :service)))))
-  (stop [this]
-    (when (and service (not (test? service-map)))
-      (http/stop service))
-    (assoc this :service nil)))
-
-(defn new-server
-  []
-  (map->Server {}))

+ 0 - 12
backend/src/backend/config.clj

@@ -1,12 +0,0 @@
-(ns backend.config
-  (:require [aero.core :refer (read-config)]
-            [clojure.java.io :as io]))
-
-(def config (read-config (io/resource "config.edn")))
-
-(def production? (= "production" (:env config)))
-(def dev? (= "dev" (:env config)))
-(def test? (= "test" (:env config)))
-(def website-uri (if dev?
-                   "http://localhost:3000"
-                   "https://logseq.com"))

+ 0 - 58
backend/src/backend/cookie.clj

@@ -1,58 +0,0 @@
-(ns backend.cookie
-  (:require [buddy.sign.compact :as buddy]
-            [backend.util :as util]
-            [backend.config :as config]))
-
-(defn sign [token]
-  (buddy/sign token (:cookie-secret config/config)))
-
-(defn unsign [cookie]
-  (buddy/unsign cookie (:cookie-secret config/config)))
-
-;; domain path expires
-(defn token-cookie [value & {:keys [max-age path]
-                             :or {path "/"
-                                  max-age (* (* 3600 24) 30)}}]
-  (let [dev? config/dev?
-        xsrf-token (str (util/uuid))
-        domain (if-not dev?
-                 ".logseq.com"
-                 "")
-        secure (if-not dev?
-                 true
-                 false)]
-    {"x" (cond->
-           {:value   (sign value)
-            :max-age max-age
-            :http-only true
-            :path path
-            :secure secure}
-           domain
-           (assoc :domain domain))
-     "xsrf-token" (cond->
-                    {:value xsrf-token
-                     :max-age max-age
-                     :http-only true
-                     :path "/"
-                     :secure secure}
-                    domain
-                    (assoc :domain domain))}))
-
-(def delete-token
-  (let [domain (if-not config/dev?
-                 ".logseq.com"
-                 "")]
-    {"x" {:value ""
-          :path "/"
-          :expires "Thu, 01 Jan 1970 00:00:00 GMT"
-          :http-only true
-          :domain domain}
-     "xsrf-token" {:value ""
-                   :path "/"
-                   :expires "Thu, 01 Jan 1970 00:00:00 GMT"
-                   :http-only true
-                   :domain domain}}))
-
-(defn get-token [req]
-  (when-let [access-token (get-in req [:cookies "x" :value])]
-    (unsign access-token)))

+ 0 - 21
backend/src/backend/core.clj

@@ -1,21 +0,0 @@
-(ns backend.core
-  (:require [backend.config :as config]
-            [backend.system :as system]
-            [taoensso.timbre :as timbre]
-            [taoensso.timbre.appenders.core :as appenders]
-            [com.stuartsierra.component :as component]))
-
-(defn set-logger!
-  [log-path]
-  (timbre/merge-config! (cond->
-                          {:appenders {:spit (appenders/spit-appender {:fname log-path})}}
-                          config/production?
-                          (assoc :output-fn (partial timbre/default-output-fn {:stacktrace-fonts {}})))))
-
-(defn start []
-  (System/setProperty "https.protocols" "TLSv1.2,TLSv1.1,SSLv3")
-  (set-logger! (:log-path config/config))
-
-  (let [system (system/new-system config/config)]
-    (component/start system))
-  (println "server running in port 3000"))

+ 0 - 33
backend/src/backend/db/refresh_token.clj

@@ -1,33 +0,0 @@
-(ns backend.db.refresh-token
-  (:refer-clojure :exclude [get update])
-  (:require [toucan.db :as db]
-            [toucan.models :as model]
-            [backend.util :as util]))
-
-(model/defmodel RefreshToken :refresh_tokens
-  model/IModel
-  (primary-key [_] :user_id))
-
-(defn get-token
-  [user-id]
-  (db/select-one-field :token RefreshToken {:user_id user-id}))
-
-(defn token-exists?
-  [token]
-  (db/exists? RefreshToken {:token token}))
-
-(defn get-user-id-by-token
-  [token]
-  (db/select-one-field :user_id RefreshToken {:token token}))
-
-(defn create
-  [user-id]
-  (if-let [token (get-token user-id)]
-    token
-    (loop [token (util/uuid)]
-      (if (token-exists? token)
-        (recur (util/uuid))
-        (do
-          (db/insert! RefreshToken {:user_id user-id
-                                    :token token})
-          token)))))

+ 0 - 32
backend/src/backend/db/repo.clj

@@ -1,32 +0,0 @@
-(ns backend.db.repo
-  (:refer-clojure :exclude [get update])
-  (:require [toucan.db :as db]
-            [toucan.models :as model]
-            [ring.util.response :as resp]
-            [backend.config :as config]
-            [backend.cookie :as cookie]))
-
-(model/defmodel Repo :repos)
-
-(defn insert
-  [args]
-  (cond
-    (and
-     (:user_id args) (:url args)
-     (db/exists? Repo (select-keys args [:user_id :url])))
-    [:bad :user-repo-exists]
-
-    :else
-    [:ok (db/insert! Repo args)]))
-
-(defn get-user-repos
-  [user-id]
-  (db/select Repo {:user_id user-id}))
-
-(defn delete
-  [id]
-  (db/delete! Repo {:id id}))
-
-(defn update
-  [id url]
-  (db/update! Repo id {:url url}))

+ 0 - 36
backend/src/backend/db/token.clj

@@ -1,36 +0,0 @@
-(ns backend.db.token
-  (:refer-clojure :exclude [get update])
-  (:require [toucan.db :as db]
-            [toucan.models :as model]
-            [backend.util :as util]))
-
-(model/defmodel Token :tokens)
-
-(defn get
-  [oauth-type oauth-id]
-  (db/select-one Token {:oauth_type oauth-type
-                        :oauth_id oauth-id}))
-
-(defn get-user-tokens
-  [user-id]
-  (db/select Token {:user_id user-id}))
-
-(defn exists?
-  [oauth-type oauth-id]
-  (db/exists? Token {:oauth_type oauth-type
-                     :oauth_id oauth-id}))
-
-(defn delete
-  [oauth-type oauth-id]
-  (db/delete! Token {:oauth_type oauth-type
-                     :oauth_id oauth-id}))
-
-(defn create
-  [{:keys [oauth_type oauth_id] :as m}]
-  (if (exists? oauth_type oauth_id)
-    (delete oauth_type oauth_id))
-  (db/insert! Token m))
-
-(defn update
-  [id new-token]
-  (db/update! Token id {:oauth_token new-token}))

+ 0 - 45
backend/src/backend/db/user.clj

@@ -1,45 +0,0 @@
-(ns backend.db.user
-  (:refer-clojure :exclude [get update])
-  (:require [toucan.db :as db]
-            [toucan.models :as model]
-            [ring.util.response :as resp]
-            [backend.config :as config]
-            [backend.cookie :as cookie]
-            [backend.jwt :as jwt]
-            [backend.db.refresh-token :as refresh-token]))
-
-(model/defmodel User :users)
-
-;; move to handler
-(defn logout
-  []
-  (-> (resp/redirect config/website-uri)
-      (assoc :cookies cookie/delete-token)))
-
-(defn get
-  [id]
-  (db/select-one User :id id))
-
-(defn insert
-  [{:keys [name email] :as args}]
-  (when-not (db/exists? User {:email email})
-    (db/insert! User args)))
-
-(defn delete
-  [id]
-  (db/delete! User {:id id}))
-
-(defn update-email
-  [id email]
-  (cond
-    (db/exists? User {:email email})
-    [:bad :email-address-exists]
-
-    :else
-    [:ok (db/update! User id {:email email})]))
-
-(defn generate-tokens
-  [user-id]
-  (cookie/token-cookie
-   {:access-token  (jwt/sign {:id user-id})
-    :refresh-token (refresh-token/create user-id)}))

+ 0 - 16
backend/src/backend/db_migrate.clj

@@ -1,16 +0,0 @@
-(ns backend.db-migrate
-  (:require [ragtime.jdbc :as jdbc]
-            [ragtime.repl :as repl]))
-
-;; db migrations
-(defn load-config
-  [db]
-  {:datastore  (jdbc/sql-database db)
-   :migrations (jdbc/load-resources "migrations")})
-
-(defn migrate [db]
-  (prn "db: " db)
-  (repl/migrate (load-config db)))
-
-(defn rollback [db]
-  (repl/rollback (load-config db)))

+ 0 - 29
backend/src/backend/interceptors.clj

@@ -1,29 +0,0 @@
-(ns backend.interceptors
-  (:require [io.pedestal.interceptor :refer [interceptor]]
-            [backend.cookie :as cookie]
-            [backend.jwt :as jwt]
-            [backend.db.user :as u]
-            [backend.util :as util]))
-
-(def cookie-interceptor
-  {:name ::cookie-authenticate
-   :enter
-   (fn [{:keys [request] :as context}]
-     (let [tokens (cookie/get-token request)]
-       (if tokens
-         (let [{:keys [access-token refresh-token]} tokens]
-           (if access-token
-             (try
-               (let [user (jwt/unsign access-token)
-                     uid (some-> (:id user) util/->uuid)
-                     user (u/get uid)]
-                 (if (:id user)
-                   (-> context
-                       (assoc-in [:request :app-context :uid] uid)
-                       (assoc-in [:request :app-context :user] user))
-                   context))
-               (catch Exception e       ; token is expired
-                 (when (= (ex-data e)
-                          {:type :validation, :cause :exp})
-                   (assoc context :response (u/logout)))))))
-         context)))})

+ 0 - 23
backend/src/backend/jwt.clj

@@ -1,23 +0,0 @@
-(ns backend.jwt
-  (:require [buddy.sign.jwt :as jwt]
-            [clj-time.core :as time]
-            [backend.config :refer [config]]))
-
-(defonce secret (:jwt-secret config))
-
-(defn sign
-  "Serialize and sign a token with defined claims"
-  ([m]
-   (sign m (* 60 60 12)))
-  ([m expire-secs]
-   (let [claims (assoc m
-                       :exp (time/plus (time/now) (time/seconds expire-secs)))]
-     (jwt/sign claims secret))))
-
-(defn unsign
-  [token]
-  (jwt/unsign token secret))
-
-(defn unsign-skip-validation
-  [token]
-  (jwt/unsign token secret {:skip-validation true}))

+ 0 - 102
backend/src/backend/routes.clj

@@ -1,102 +0,0 @@
-(ns backend.routes
-  (:require [reitit.swagger :as swagger]
-            [clj-social.core :as social]
-            [backend.config :as config]
-            [backend.util :as util]
-            [backend.auth :as auth]
-            [backend.db.user :as u]
-            [backend.db.token :as token]
-            [backend.db.repo :as repo]
-            [ring.util.response :as resp]
-            [backend.views.home :as home]
-            [backend.interceptors :as interceptors]))
-
-;; TODO: spec validate, authorization (owner?)
-
-(def routes
-  [["/swagger.json"
-    {:get {:no-doc true
-           :swagger {:info {:title "logseq api"
-                            :description "with pedestal & reitit-http"}}
-           :handler (swagger/create-swagger-handler)}}]
-
-   ["/"
-    {:get {:no-doc true
-           :handler (fn [_req]
-                      {:status 200
-                       :body (home/home)})}}]
-
-   ["/login"
-    {:swagger {:tags ["Login"]}}
-
-    ["/github"
-     {:get {:summary "Login with github"
-            :handler
-            (fn [req]
-              (let [{:keys [app-key app-secret redirect-uri]} (get-in config/config [:oauth :github])
-                    social (social/make-social :github app-key app-secret
-                                               (str redirect-uri
-                                                    "?referer="
-                                                    (get-in req [:headers "referer"] ""))
-                                               :state (str (util/uuid))
-                                               :scope "user:email,repo")
-                    url (social/getAuthorizationUrl social)]
-                (resp/redirect url)))}}]]
-   ["/auth"
-    {:swagger {:tags ["Authenticate"]}}
-
-    ["/github"
-     {:get {:summary "Authenticate with github"
-            :handler
-            (fn [{:keys [params] :as req}]
-              (if (and (:code params)
-                       (:state params))
-                (if-let [user (auth/github params)]
-                  (-> (resp/redirect config/website-uri)
-                      (assoc :cookies (u/generate-tokens (:id user))))
-                  {:status 500
-                   :body "Internal Error"})
-                {:status 401
-                 :body "Invalid request"}))}}]]
-   ["/api/v1" {:interceptors [interceptors/cookie-interceptor]}
-    ["/me"
-     {:get {:summary "Get current user's information"
-            :handler
-            (fn [{:keys [app-context] :as req}]
-              (prn "request: " req)
-              (if-let [user (:user app-context)]
-                (let [user-id (:id user)]
-                  {:status 200
-                   :body {:user user
-                          :tokens (token/get-user-tokens user-id)
-                          :repos (repo/get-user-repos user-id)}})
-                {:status 404
-                 :body "not-found"}))}}]
-
-    ["/repos"
-     {:post {:summary "Add a repo"
-             :handler
-             (fn [{:keys [app-context body-params] :as req}]
-               (let [user (:user app-context)
-                     result (repo/insert {:user_id (:id user)
-                                          :url (:url body-params)})]
-                 {:status 201
-                  :body result}))}
-      }]
-
-    ["/repos/:id"
-     {:patch {:summary "Update a repo's url"
-              :handler
-              (fn [{:keys [app-context params body-params] :as req}]
-                (let [user (:user app-context)
-                      result (repo/update (:id params)
-                                          (:url body-params))]
-                  {:status 200
-                   :body result}))}
-      :delete {:summary "Delete a repo"
-               :handler
-               (fn [{:keys [app-context params] :as req}]
-                 (let [user (:user app-context)
-                       result (repo/delete (:id params))]
-                   {:status 200
-                    :body {:result true}}))}}]]])

+ 0 - 101
backend/src/backend/system.clj

@@ -1,101 +0,0 @@
-(ns backend.system
-  (:require [io.pedestal.http :as server]
-            [reitit.ring :as ring]
-            [reitit.http :as http]
-            [reitit.coercion.spec]
-            [reitit.swagger :as swagger]
-            [reitit.swagger-ui :as swagger-ui]
-            [reitit.http.coercion :as coercion]
-            [reitit.dev.pretty :as pretty]
-            [reitit.http.interceptors.parameters :as parameters]
-            [reitit.http.interceptors.muuntaja :as muuntaja]
-            [reitit.http.interceptors.exception :as exception]
-            [reitit.http.interceptors.multipart :as multipart]
-            [reitit.http.interceptors.dev :as dev]
-            [reitit.http.spec :as spec]
-            [spec-tools.spell :as spell]
-            [io.pedestal.http :as server]
-            [reitit.pedestal :as pedestal]
-            [clojure.core.async :as a]
-            [muuntaja.core :as m]
-            [com.stuartsierra.component :as component]
-            [backend.components.http :as component-http]
-            [backend.components.hikari :as hikari]
-            [backend.routes :as routes]
-            [backend.config :as config]
-            [io.pedestal.http.ring-middlewares :as ring-middlewares]))
-
-(def router
-  (pedestal/routing-interceptor
-   (http/router
-    routes/routes
-
-    {;:reitit.interceptor/transform dev/print-context-diffs ;; pretty context diffs
-     ;;:validate spec/validate ;; enable spec validation for route data
-     ;;:reitit.spec/wrap spell/closed ;; strict top-level validation
-     ;; :exception pretty/exception
-     :data {:coercion reitit.coercion.spec/coercion
-            :muuntaja m/instance
-            :interceptors [;; swagger feature
-                           swagger/swagger-feature
-                           ;; query-params & form-params
-                           (parameters/parameters-interceptor)
-                           ;; content-negotiation
-                           (muuntaja/format-negotiate-interceptor)
-                           ;; encoding response body
-                           (muuntaja/format-response-interceptor)
-                           ;; exception handling
-                           ;; (exception/exception-interceptor)
-                           ;; decoding request body
-                           (muuntaja/format-request-interceptor)
-                           ;; coercing response bodys
-                           (coercion/coerce-response-interceptor)
-                           ;; coercing request parameters
-                           (coercion/coerce-request-interceptor)
-                           ;; multipart
-                           (multipart/multipart-interceptor)]}})
-
-   ;; optional default ring handler (if no routes have matched)
-   (ring/routes
-    (swagger-ui/create-swagger-ui-handler
-     {:path "/swagger"
-      :config {:validatorUrl nil
-               :operationsSorter "alpha"}})
-    (ring/create-resource-handler)
-    (ring/create-default-handler))))
-
-(defn merge-interceptors-map
-  [system-map interceptors]
-  (update system-map :io.pedestal.http/interceptors
-          (fn [old]
-            (vec (concat interceptors old)))))
-
-(defn new-system
-  [{:keys [env port hikari-spec] :as config}]
-  (let [service-map (-> {:env env
-                         ::server/type :jetty
-                         ::server/port port
-                         ::server/join? false
-                         ;; no pedestal routes
-                         ::server/routes []
-                         ;; allow serving the swagger-ui styles & scripts from self
-                         ;; ::server/secure-headers {:content-security-policy-settings
-                         ;;                          {:default-src "'self'"
-                         ;;                           :style-src "'self' 'unsafe-inline'"
-                         ;;                           :script-src "'self' 'unsafe-inline'"}}
-                         ::server/secure-headers {:content-security-policy-settings {:object-src "'none'"}}
-                         ::server/resource-path "/public"}
-                        (server/default-interceptors)
-                        ;; use the reitit router
-                        (pedestal/replace-last-interceptor router))
-        service-map (merge-interceptors-map
-                     service-map
-                     [ring-middlewares/cookies
-                      server/html-body])
-        service-map (if config/dev? (server/dev-interceptors service-map) service-map)]
-    (component/system-map :service-map service-map
-                          :hikari (hikari/new-hikari-cp hikari-spec)
-                          :http
-                          (component/using
-                           (component-http/new-server)
-                           [:service-map]))))

+ 0 - 82
backend/src/backend/util.clj

@@ -1,82 +0,0 @@
-(ns backend.util
-  (:require [clojure.string :as str]
-            [clj-time
-             [coerce :as tc]
-             [core :as t]
-             [format :as tf]])
-  (:import  [java.util UUID]
-            [java.util TimerTask Timer]))
-(defn uuid
-  "Generate uuid."
-  []
-  (UUID/randomUUID))
-
-(defn ->uuid
-  [s]
-  (if (uuid? s)
-    s
-    (UUID/fromString s)))
-
-(defn update-if
-  "Update m if k exists."
-  [m k f]
-  (if-let [v (get m k)]
-    (assoc m k (f v))
-    m))
-
-(defn dissoc-in
-  "Dissociates an entry from a nested associative structure returning a new
-  nested structure. keys is a sequence of keys. Any empty maps that result
-  will not be present in the new structure."
-  [m [k & ks :as keys]]
-  (if ks
-    (if-let [nextmap (get m k)]
-      (let [newmap (dissoc-in nextmap ks)]
-        (if (seq newmap)
-          (assoc m k newmap)
-          (dissoc m k)))
-      m)
-    (dissoc m k)))
-
-(defmacro doseq-indexed
-  "loops over a set of values, binding index-sym to the 0-based index of each value"
-  ([[val-sym values index-sym] & code]
-   `(loop [vals# (seq ~values)
-           ~index-sym (long 0)]
-      (if vals#
-        (let [~val-sym (first vals#)]
-          ~@code
-          (recur (next vals#) (inc ~index-sym)))
-        nil))))
-
-(defn indexed [coll] (map-indexed vector coll))
-
-(defn set-timeout [f interval]
-  (let [task (proxy [TimerTask] []
-               (run [] (f)))
-        timer (new Timer)]
-    (.schedule timer task (long interval))
-    timer))
-
-;; http://yellerapp.com/posts/2014-12-11-14-race-condition-in-clojure-println.html
-(defn safe-println [& more]
-  (.write *out* (str (clojure.string/join " " more) "\n")))
-
-(defn safe->int
-  [s]
-  (if (string? s)
-    (Integer/parseInt s)
-    s))
-
-(defn remove-nils
-  [m]
-  (reduce (fn [acc [k v]] (if v (assoc acc k v)
-                              acc))
-          {} m))
-
-(defn deep-merge [& maps]
-  (apply merge-with (fn [& args]
-                      (if (every? map? args)
-                        (apply deep-merge args)
-                        (last args)))
-    maps))

+ 0 - 30
backend/src/backend/views/home.clj

@@ -1,30 +0,0 @@
-(ns backend.views.home
-  (:require [hiccup.page :as html]))
-
-(defn home
-  []
-  (html/html5
-   [:head
-    [:meta {:charset "utf-8"}]
-    [:meta
-     {:content
-      "minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no",
-      :name "viewport"}]
-    ;; [:link {:type "text/css", :href "css/tailwind.min.css", :rel "stylesheet"}]
-    [:link {:type "text/css", :href "https://cdn.jsdelivr.net/npm/@tailwindcss/ui@latest/dist/tailwind-ui.min.css", :rel "stylesheet"}]
-    [:link {:type "text/css", :href "css/org.css", :rel "stylesheet"}]
-    [:link {:type "text/css", :href "css/style.css", :rel "stylesheet"}]
-    [:link
-     {:href
-      "https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap",
-      :rel "stylesheet"}]
-    [:link {:href "css/highlight.css", :rel "stylesheet"}]
-    [:title "Logseq"]]
-   [:body
-    [:div#root]
-    [:script {:src "https://unpkg.com/@isomorphic-git/[email protected]/dist/lightning-fs.min.js"}]
-    [:script {:src "https://unpkg.com/[email protected]/dist/bundle.umd.min.js"}]
-    [:script
-     "window.fs = new LightningFS('logseq');git.plugins.set('fs', window.fs);window.pfs = window.fs.promises;"]
-    [:script {:src "/js/highlight.pack.js"}]
-    [:script {:src "/js/main.js"}]]))

+ 0 - 7
backend/test/backend/core_test.clj

@@ -1,7 +0,0 @@
-(ns backend.core-test
-  (:require [clojure.test :refer :all]
-            [backend.core :refer :all]))
-
-(deftest a-test
-  (testing "FIXME, I fail."
-    (is (= 0 1))))

+ 0 - 7
build.sh

@@ -1,7 +0,0 @@
-#!/bin/bash
-
-cd api && yarn release
-cd ../web
-yarn clean && yarn release
-cd ../
-now

+ 0 - 0
backend/dev/user.clj → dev/user.clj


+ 1 - 0
procfile

@@ -0,0 +1 @@
+web: java -Dclojure.main.report=stderr -cp target/uberjar/logseq.jar clojure.main -m backend.core