envs as placeholders, ftp code

master
Josha von Gizycki 6 years ago
parent fa8f588492
commit c8c831ddf2

@ -5,6 +5,6 @@
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.10.0"]
[me.raynes/fs "1.4.6"]
[com.velisco/clj-ftp "0.3.12"]
[markdown-clj "1.0.5"]]
[markdown-clj "1.0.5"]
[commons-net "3.6"]]
:eval-in-leiningen true)

@ -4,7 +4,7 @@
<meta charset="UTF-8">
<!-- generated at &:generated-at -->
<title>&:title - joshavg</title>
<base href="file:///home/josha/projekte/equilibrium/target/page/">
<base href="&:env:EQUILIBRIUM_BASE_HREF">
<link rel="stylesheet" href="resources/style.css">
</head>
<body>

@ -0,0 +1,249 @@
;; Apache Commons Net API:
;; http://commons.apache.org/proper/commons-net/javadocs/api-3.3/index.html
;; Uses Apache Commons Net 3.3. Does not support SFTP, but does support FTPS.
;; FTP is considered insecure. Data and passwords are sent in the
;; clear so someone could sniff packets on your network and discover
;; your password. Nevertheless, FTP access is useful for dealing with anonymous
;; FTP servers and situations where security is not an issue.
(ns equilibrium.miner.ftp
(:import [org.apache.commons.net.ftp FTP FTPClient FTPSClient FTPFile FTPReply]
[java.net URI URL]
[java.io File IOException FileOutputStream OutputStream FileInputStream InputStream])
(:require [me.raynes.fs :as fs]
[clojure.string :as str]
[clojure.java.io :as io]))
(defn as-uri ^URI [url]
(cond (instance? URL url) (.toURI ^URL url)
(instance? URI url) url
:else (URI. url)))
(defn open
([url] (open url "UTF-8" {}))
([url control-encoding] (open url control-encoding {}))
([url control-encoding
{:keys [security-mode
data-timeout-ms
connect-timeout-ms
default-timeout-ms
control-keep-alive-timeout-sec
control-keep-alive-reply-timeout-ms]
:or {security-mode :explicit
data-timeout-ms -1
connect-timeout-ms 30000
control-keep-alive-timeout-sec 300
control-keep-alive-reply-timeout-ms 1000}}]
(let [implicit? (not= :explicit security-mode)
^URI uri (as-uri url)
^FTPClient client (case (.getScheme uri)
"ftp" (FTPClient.)
"ftps" (FTPSClient. implicit?)
(throw (Exception. (str "unexpected protocol " (.getScheme uri) " in FTP url, need \"ftp\" or \"ftps\""))))]
;; (.setAutodetectUTF8 client true)
(when default-timeout-ms (.setDefaultTimeout client default-timeout-ms))
(.setControlEncoding client control-encoding)
(.setConnectTimeout client connect-timeout-ms)
(.setDataTimeout client data-timeout-ms)
(.setControlKeepAliveTimeout client control-keep-alive-timeout-sec)
(.setControlKeepAliveReplyTimeout client control-keep-alive-reply-timeout-ms)
(.connect client
(.getHost uri)
(if (= -1 (.getPort uri)) (int 21) (.getPort uri)))
(let [reply (.getReplyCode client)]
(when-not (FTPReply/isPositiveCompletion reply)
(.disconnect client)
(throw (ex-info "Connection failed" {:reply-code reply
:reply-string (.getReplyString client)}))))
client)))
(defn guess-file-type [file-name]
"Best guess about the file type to use when transferring a given file based on the extension.
Returns either :binary or :ascii (the default). If you don't know what you're dealing with,
this might help, but don't bet the server farm on it. See also `client-set-file-type`."
(case (str/lower-case (fs/extension file-name))
(".jpg" ".jpeg" ".zip" ".mov" ".bin" ".exe" ".pdf" ".gz" ".tar" ".dmg" ".jar" ".tgz" ".war"
".lz" ".mp3" ".mp4" ".sit" ".z" ".dat" ".o" ".app" ".png" ".gif" ".class" ".avi" ".m4v"
".mpg" ".mpeg" ".swf" ".wmv" ".ogg") :binary
:ascii))
(defn client-set-file-type [^FTPClient client filetype]
"Set the file type for transfers to either :binary or :ascii (the default)"
(if (= filetype :binary)
(.setFileType client FTP/BINARY_FILE_TYPE)
(.setFileType client FTP/ASCII_FILE_TYPE))
filetype)
(defmacro with-ftp
"Establish an FTP connection, bound to client, for the FTP url, and execute the body with
access to that client connection. Closes connection at end of body. Keyword
options can follow the url in the binding vector. By default, uses a passive local data
connection mode and ASCII file type.
Use [client url :local-data-connection-mode :active
:file-type :binary
:security-mode :explicit] to override.
Allows to override the following timeouts:
- `connect-timeout-ms` - The timeout used when opening a socket. Default 30000
- `data-timeout-ms` - the underlying socket timeout. Default - infinite (< 1).
- `control-keep-alive-timeout-sec` - control channel keep alive message
timeout. Default 300 seconds.
- `control-keep-alive-reply-timeout-ms` - how long to wait for the control
channel keep alive replies. Default 1000 ms.
- `control-encoding` - The new character encoding for the control connection. Default - UTF-8"
[[client url & {:keys [local-data-connection-mode file-type
control-encoding
ftp-user ftp-pass]
:as params
:or {control-encoding "UTF-8"}}] & body]
`(let [local-mode# ~local-data-connection-mode
u# (as-uri ~url)
~client ^FTPClient (open u# ~control-encoding ~params)
file-type# ~file-type]
(try
(when-not (.login ~client ~ftp-user ~ftp-pass)
(throw (ex-info (format "Unable to login")
{:url u#
:user ~ftp-user})))
(let [path# (.getPath u#)]
(when-not (or (str/blank? path#) (= path# "/"))
(.changeWorkingDirectory ~client (subs path# 1))))
(client-set-file-type ~client file-type#)
;; by default (when nil) use passive mode
(if (= local-mode# :active)
(.enterLocalActiveMode ~client)
(.enterLocalPassiveMode ~client))
~@body
(catch IOException e# (println (.getMessage e#)) (throw e#))
(finally (when (.isConnected ~client)
(try
(.disconnect ~client)
(catch IOException e2# nil)))))))
(defn client-FTPFiles-all [^FTPClient client]
(vec (.listFiles client)))
(defn client-FTPFiles [^FTPClient client]
(filterv (fn [f] (and f (.isFile ^FTPFile f))) (.listFiles client)))
(defn client-FTPFile-directories [^FTPClient client]
(vec (.listDirectories client)))
(defn client-all-names [^FTPClient client]
(vec (.listNames client)))
(defn client-file-names [^FTPClient client]
(mapv #(.getName ^FTPFile %) (client-FTPFiles client)))
(defn client-directory-names [^FTPClient client]
(mapv #(.getName ^FTPFile %) (client-FTPFile-directories client)))
(defn client-complete-pending-command
"Complete the previous command and check the reply code. Throw an exception if
reply code is not a positive completion"
[^FTPClient client]
(.completePendingCommand client)
(let [reply-code (.getReplyCode client)]
(when-not (FTPReply/isPositiveCompletion reply-code)
(throw (ex-info "Not a Positive completion of last command" {:reply-code reply-code
:reply-string (.getReplyString client)})))))
(defn client-get
"Get a file and write to local file-system (must be within a with-ftp)"
([client fname] (client-get client fname (fs/base-name fname)))
([client fname local-name]
(with-open [outstream (FileOutputStream. (io/as-file local-name))]
(.retrieveFile ^FTPClient client ^String fname ^OutputStream outstream))))
(defn client-get-stream
"Get a file and return InputStream (must be within a with-ftp). Note that it's necessary to complete
this command with a call to `client-complete-pending-command` after using the stream."
^InputStream [client fname]
(.retrieveFileStream ^FTPClient client ^String fname))
(defn client-put
"Put a file (must be within a with-ftp)"
([client fname] (client-put client fname (fs/base-name fname)))
([client fname remote] (with-open [instream (FileInputStream. (io/as-file fname))]
(.storeFile ^FTPClient client ^String remote ^InputStream instream))))
(defn client-put-stream
"Put an InputStream (must be within a with-ftp)"
[client instream remote]
(.storeFile ^FTPClient client ^String remote ^InputStream instream))
(defn client-cd [client dir]
(.changeWorkingDirectory ^FTPClient client ^String dir))
(defn- strip-double-quotes [^String s]
(let [len (count s)]
(cond (<= len 2) s
(and (= (.charAt s 0) \")
(= (.charAt s (dec len)) \")) (subs s 1 (dec len))
:else s)))
(defn client-pwd [client]
(strip-double-quotes (.printWorkingDirectory ^FTPClient client)))
(defn client-mkdir [client subdir]
(.makeDirectory ^FTPClient client ^String subdir))
;; Regular mkdir can only make one level at a time; mkdirs makes nested paths in the correct order
(defn client-mkdirs [client subpath]
(doseq [d (reductions (fn [path item] (str path File/separator item)) (fs/split subpath))]
(client-mkdir client d)))
(defn client-delete [client fname]
"Delete a file (must be within a with-ftp)"
(.deleteFile ^FTPClient client ^String fname))
(defn client-rename [client from to]
"Rename a remote file (must be within a with-ftp"
(.rename ^FTPClient client ^String from ^String to))
(defn client-send-site-command [client sitecmd ]
"Send Site Command must be within with-ftp"
(.sendSiteCommand ^FTPClient client ^String sitecmd))
;; convenience methods for one-shot results
(defn rename-file [url from to]
(with-ftp [client url]
(client-rename client from to)))
(defn retrieve-file
([url fname] (retrieve-file url fname (fs/base-name fname)))
([url fname local-file]
(with-ftp [client url]
(client-get client fname (io/as-file local-file)))))
(defn list-all [url]
(with-ftp [client url]
(seq (client-all-names client))))
(defn list-files [url]
(with-ftp [client url]
(seq (client-file-names client))))
(defn list-directories [url]
(with-ftp [client url]
(seq (client-directory-names client))))
;; this method encrypts the channel when you are using ftps.
;; to avoid error :
;; 425-Server requires protected data connection.
;; 425 Can't open data connection.
;; you must call this before doing a transfer
(defn encrypt-channel [client ]
(do (.execPBSZ ^FTPSClient client 0)
(.execPROT ^FTPSClient client "P")))

@ -108,6 +108,9 @@
(defn last-blog-sites-in-content [content]
(re-seq #"&:last-blog-sites:([^\s<]+)" content))
(defn envs-in-content [content]
(re-seq #"&:env:([a-zA-Z_-]+)" content))
(comment
(particles-in-content "asd &particle:hullu"))
@ -161,6 +164,7 @@
(string/replace #"&:generated-at" (now-str)))
particles (particles-in-content simple)
last-blog-sites (last-blog-sites-in-content simple)
envs (envs-in-content simple)
particle-fn (fn [result particle]
(string/replace
result
@ -170,10 +174,15 @@
(string/replace
result
match-str
(blog-sites-preview blog-name)))]
(blog-sites-preview blog-name)))
envs-fn (fn [result [match-str env-name]]
(string/replace result
match-str
(System/getenv env-name)))]
(as-> simple $
(reduce particle-fn $ particles)
(reduce blog-sites-fn $ last-blog-sites))))
(reduce blog-sites-fn $ last-blog-sites)
(reduce envs-fn $ envs))))
(comment
(fill-in-placeholders {:navcode "hhh"}
@ -181,8 +190,7 @@
{:content "dinge"})
(fill-in-placeholders {:navcode "hhh"}
"&:nav &:content &:particle:aside\n &:generated-at"
{:content "dinge"})
)
{:content "dinge"}))
(defn write-sites [template sites]
(doseq [site sites

@ -1,10 +1,28 @@
(ns leiningen.equilibrium
(:require [equilibrium.render :as render]
[leiningen.core.main :refer [info warn]]))
[equilibrium.miner.ftp :as ftp]
[leiningen.core.main :refer [info warn]]
[clojure.java.io :as io]))
(defn deploy
"Deploys the rendered page under target/page to the configured ftp target"
[project])
[project]
(ftp/with-ftp [client (System/getenv "EQUILIBRIUM_URL")
:ftp-user (System/getenv "EQUILIBRIUM_USER")
:ftp-pass (System/getenv "EQUILIBRIUM_PASS")]
(doseq [file (file-seq (io/file "target/page"))
:let [fname (.toString file)
relpath (subs fname (count "target/page"))
dir? (.isDirectory file)]
:when (> (count relpath) 0)]
(let [dest (subs relpath 1)]
(if dir?
(do
(println "creating dir" dest)
(ftp/client-mkdir client dest))
(do
(println fname "to" dest)
(ftp/client-put client fname dest)))))))
(defn invalid-input [project]
(warn "refer to 'lein help equilibrium' for available tasks"))

Loading…
Cancel
Save