-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathcompact.cirru
More file actions
369 lines (368 loc) · 16.7 KB
/
compact.cirru
File metadata and controls
369 lines (368 loc) · 16.7 KB
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
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
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
{} (:package |app)
:configs $ {} (:init-fn |app.main/main!) (:reload-fn |app.main/reload!) (:version |0.1.10)
:modules $ [] |respo.calcit/ |lilac/ |memof/ |respo-ui.calcit/ |respo-markdown.calcit/ |reel.calcit/ |skir/
:entries $ {}
:server $ {} (:init-fn |app.server/main!) (:reload-fn |app.server/reload!) (:storage-key |calcit.cirru)
:modules $ [] |lilac/ |memof/ |skir/ |respo.calcit/ |respo-ui.calcit/
:files $ {}
|app.comp.container $ {}
:defs $ {}
|comp-container $ quote
defcomp comp-container (reel)
let
store $ :store reel
states $ :states store
div
{} $ :style
merge ui/global ui/column $ {} (:padding 16) (:font-size 16)
div
{} $ :style
merge ui/row $ {} (:font-size 16)
:color $ hsl 0 0 50
:font-weight 500
a
{} (:href "\"/files")
:style $ {} (:font-family ui/font-fancy)
<> "\"Browse files"
=< nil 16
div
{} $ :style
merge ui/center $ {} (:padding 8) (:position :relative)
:background-color $ hsl 240 80 70
:height 120
<> "\"Select files to upload" $ {} (:color :white) (:font-size 24) (:font-family ui/font-fancy)
input $ {} (:type "\"file") (:multiple true) (:on-change on-file-selected!)
:style $ {} (:position :absolute) (:top 0) (:bottom 0) (:left 0) (:right 0) (:width "\"100%") (:opacity 0)
=< nil 16
list->
{} $ :style ({})
-> store :logs
or $ []
map-indexed $ fn (idx log)
[] idx $ div
{} $ :style
{}
:color $ hsl 0 0 60
:font-family ui/font-fancy
:line-height "\"24px"
<> log
div
{} $ :style
merge ui/row $ {} (:font-size 16)
:color $ hsl 0 0 50
:font-weight 500
<> $ str "\"Connected to "
=< 8 nil
a
{} $ :style
{}
:color $ hsl 240 80 60
:font-family ui/font-fancy
<> $ .-host js/location
when dev? $ comp-reel (>> states :reel) (assoc reel :display? false)
{} $ :width "\"100%"
|on-file-selected! $ quote
defn on-file-selected! (e d!)
let
event $ :event e
target $ .-target event
files $ js/Array.from (-> target .-files)
set! (-> event .-target .-value) nil
d! :clear-logs nil
.forEach files $ fn (file i ? d)
if
> (.-size file) (* 4 1024 1024 1024)
d! :log "\"File larger than 4G, which is not supported."
let
form $ new js/FormData
.append form "\"file" file
d! :log $ str "\"sending " (.-name file)
->
.!request axios $ js-object
:url $ str (.-protocol js/location) "\"//" (.-host js/location) "\"/upload"
:data form
:method "\"post"
:onUploadProgress $ fn (ratio)
d! :log $ str "\"Uploading "
format-percent (.-loaded ratio) (.-total ratio)
.!then $ fn (response)
d! :log $ str (.-name file) "\" uploaded!"
.!catch $ fn (error) (.log js/console error)
d! :log $ str error
:ns $ quote
ns app.comp.container $ :require
[] respo-ui.core :refer $ [] hsl
[] respo-ui.core :as ui
[] respo.core :refer $ [] defcomp >> <> list-> div button textarea input span a
[] respo.comp.space :refer $ [] =<
[] reel.comp.reel :refer $ [] comp-reel
[] respo-md.comp.md :refer $ [] comp-md
[] app.config :refer $ [] dev?
[] "\"axios" :default axios
[] app.util :refer $ [] format-percent
|app.config $ {}
:defs $ {}
|dev? $ quote
def dev? $ = "\"env" (get-env "\"mode")
|site $ quote
def site $ {} (:storage "\"file-sucker") (:dev-ui "\"http://localhost:8100/main.css") (:release-ui "\"http://cdn.tiye.me/favored-fonts/main.css") (:cdn-url "\"http://cdn.tiye.me/file-sucker/") (:title "\"File Sucker") (:icon "\"http://cdn.tiye.me/logo/mvc-works.png")
:ns $ quote
ns app.config $ :require
[] app.util :refer $ [] get-env!
|app.main $ {}
:defs $ {}
|*reel $ quote
defatom *reel $ -> reel-schema/reel (assoc :base schema/store) (assoc :store schema/store)
|dispatch! $ quote
defn dispatch! (op op-data) (println |Dispatch: op op-data)
reset! *reel $ reel-updater updater @*reel op op-data
|main! $ quote
defn main! ()
if config/dev? $ load-console-formatter!
render-app!
add-watch *reel :changes $ fn (r p) (render-app!)
listen-devtools! |k dispatch!
js/window.addEventListener |beforeunload persist-storage!
js/setInterval persist-storage! $ * 1000 60
; let
raw $ js/localStorage.getItem (:storage config/site)
when (some? raw)
dispatch! :hydrate-storage $ parse-cirru-edn raw
println "|App started."
|mount-target $ quote
def mount-target $ js/document.querySelector |.app
|persist-storage! $ quote
defn persist-storage! (? e)
js/localStorage.setItem (:storage config/site)
format-cirru-edn $ :store @*reel
|reload! $ quote
defn reload! () $ if (nil? build-errors)
do (remove-watch *reel :changes) (clear-cache!)
add-watch *reel :changes $ fn (reel prev) (render-app!)
reset! *reel $ refresh-reel @*reel schema/store updater
hud! "\"ok~" "\"Ok"
hud! "\"error" build-errors
|render-app! $ quote
defn render-app! () $ render! mount-target (comp-container @*reel) dispatch!
:ns $ quote
ns app.main $ :require
[] respo.core :refer $ [] render! clear-cache!
[] app.comp.container :refer $ [] comp-container
[] app.updater :refer $ [] updater
[] app.schema :as schema
[] reel.util :refer $ [] listen-devtools!
[] reel.core :refer $ [] reel-updater refresh-reel
[] reel.schema :as reel-schema
[] app.config :as config
"\"./calcit.build-errors" :default build-errors
"\"bottom-tip" :default hud!
|app.schema $ {}
:defs $ {}
|store $ quote
def store $ {}
:states $ {}
:log $ []
:ns $ quote (ns app.schema)
|app.server $ {}
:defs $ {}
|check-version! $ quote
defn check-version! () (hint-fn async)
let
pkg $ js/JSON.parse
fs/readFileSync $ path/join dirname "\"../package.json"
version $ .-version pkg
npm-version $ js-await
latest-version $ .-name pkg
if (= npm-version version) (println "\"Running latest version" version)
println $ .!yellow chalk (str "\"New version " npm-version "\" available, current one is " version "\" . Please upgrade!" "\"\n\nyarn global add file-sucker\n")
|dirname $ quote
def dirname $ path/dirname
.!fileURLToPath url $ new js/URL "\"" js/import.meta.url
|load-stats! $ quote
defn load-stats! (xs) (hint-fn async)
let
tasks $ js-array
&doseq (x xs)
.!push tasks $ new js/Promise
fn (resolve reject)
fs/stat x $ fn (err stat)
resolve $ {} (:name x)
:size $ .-size stat
:created-time $ .-ctime stat
js/Promise.all tasks
|main! $ quote
defn main! () $ let
port $ or js/process.env.PORT js/process.env.port 4000
skir/create-server! (\ on-request! % %2)
{} (:port port)
:after-start $ fn (options)
.!forEach addresses $ fn (ip-address _idx _a)
let
address $ str &newline "\"http://" ip-address "\":" port &newline
println "\"Open page on your phone and send file:" &newline address
.!generate qrcode address
js-object $ :small true
, js/console.log
if (not= js/process.env.NO_VERSION_CHECK "\"false") (check-version!) (println "\"[INFO] skipped version checking.")
|on-download! $ quote
defn on-download! (req res)
set! (.-url req)
.replace (.-url req) "\"/files/" "\"/"
println "\"url" $ .-url req
serve-files! req res $ finalhandler req res
, :effect
|on-file-indexed! $ quote
defn on-file-indexed! (req res) (hint-fn async)
let
filenames $ filter
to-calcit-data $ fs/readdirSync "\"."
fn (filename)
.isFile $ fs/lstatSync filename
files-info $ js-await (load-stats! filenames)
result $ make-string
html ({})
head ({})
create-element :meta $ {} (:content "\"width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no") (:name "\"viewport")
create-element :meta $ {} (:charset "\"utf8")
link $ {} (:rel "\"stylesheet") (:href "\"http://cdn.tiye.me/favored-fonts/josefin-sans.css")
body ({})
div ({})
if (empty? filenames)
div
{} $ :style
merge ui/center $ {} (:padding 80)
<> "\"No files" $ {} (:font-family ui/font-fancy)
:color $ hsl 0 0 80
:font-size 40
:font-weight 300
list->
{} $ :style
{} $ :padding "\"24px 8px"
-> (turn-list files-info)
.sort-by $ fn (x)
negate $ :created-time x
map-indexed $ fn (idx file)
[] idx $ div
{} $ :style
merge ui/row $ {} (:line-height "\"40px")
a $ {}
:href $ str "\"/files/" (:name file)
:inner-text $ :name file
:style $ {} (:text-decoration :none) (:font-family ui/font-fancy) (:font-size 16) (:max-width "\"50vw") (:overflow :hidden) (:text-overflow :ellipsis)
=< 16 nil
<>
prettysize $ :size file
{} (:font-family ui/font-fancy)
:color $ hsl 0 0 70
:font-size 12
=< 16 nil
<>
-> (:created-time file) dayjs $ .format "\"MM-DD HH:mm"
{} (:font-family ui/font-fancy)
:color $ hsl 0 0 70
:font-size 12
{} (:code 200)
:headers $ {} ("\"Content-Type" "\"text/html")
:body result
|on-page! $ quote
defn on-page! (req res)
serve req res $ finalhandler req res
, :effect
|on-request! $ quote
defn on-request! (req-edn res)
let
req $ :original-request req-edn
cond
= "\"/upload" $ .-url req
on-upload! req res
(or (= (.-url req) "\"/files") (= (.-url req) "\"/files/"))
on-file-indexed! req res
(.starts-with? (.-url req) "\"/files/")
on-download! req res
true $ on-page! req res
|on-upload! $ quote
defn on-upload! (req res)
if-let
origin $ -> req .-headers .-origin
.!setHeader res "\"Access-Control-Allow-Origin" origin
.!setHeader res "\"Access-Control-Allow-Methods" "\"POST,GET,OPTIONS"
case-default (.-method req)
{} (:code 404) (:body "\"method not supported")
"\"POST" $ fn (send!)
let
form $ new (.-IncomingForm formidable)
size-limit $ * 4 1024 1024 1024
println "\"New request of file transferring..."
set! (.-maxFieldsSize form) size-limit
set! (.-maxFileSize form) size-limit
.!parse form req $ fn (error fields files)
when (some? error) (raise error)
let
file $ .-file files
println "\"Received file:" $ .-originalFilename file
fs/rename (.-filepath file)
path/join (-> js/process .-env .-PWD) (.-originalFilename file)
fn (rename-error)
when (some? rename-error) (raise rename-error)
send! $ {} (:code 200)
:headers $ {}
:message "\"Uploaded"
"\"GET" $ {} (:code 200) (:body "\"use POST")
"\"OPTIONS" $ {} (:code 200) (:body "\"ok")
|reload! $ quote
defn reload! () $ println "\"reloaded!"
|serve $ quote
def serve $ serve-static (path/join dirname "\"../dist")
js-object $ "\"index" (js-array "\"index.html")
|serve-files! $ quote
def serve-files! $ serve-static (.-PWD js/process.env)
js-object $ "\"index" (js-array)
|turn-list $ quote
defn turn-list (arr)
apply-args
[]
, arr
fn (xs as)
if
= 0 $ .-length as
, xs $ recur
conj xs $ .-0 as
.!slice as 1
:ns $ quote
ns app.server $ :require ([] "\"formidable" :default formidable) ([] "\"serve-static" :default serve-static) ([] "\"path" :as path) ([] "\"finalhandler" :default finalhandler) ([] "\"fs" :as fs) ([] "\"ip" :as ip) ([] "\"qrcode-terminal" :default qrcode) ([] "\"dayjs" :default dayjs) ([] "\"prettysize" :default prettysize) ([] "\"latest-version" :default latest-version) ([] "\"chalk" :default chalk)
[] respo.render.html :refer $ [] make-string
[] respo.core :refer $ [] div html head body list-> <> span a style link create-element
[] respo.comp.space :refer $ [] comp-space
[] respo-ui.core :as ui
[] respo-ui.core :refer $ hsl
[] skir.core :as skir
[] respo.comp.space :refer $ [] =<
"\"../entry/address.mjs" :refer $ addresses
"\"url" :default url
|app.updater $ {}
:defs $ {}
|updater $ quote
defn updater (store op op-data op-id op-time)
case-default op
do (println "\"Unknown op:" op) store
:states $ update-states store op-data
:hydrate-storage op-data
:log $ update store :logs
fn (logs) (prepend logs op-data)
:clear-logs $ assoc store :logs ([])
:ns $ quote
ns app.updater $ :require
[] respo.cursor :refer $ [] update-states
|app.util $ {}
:defs $ {}
|format-percent $ quote
defn format-percent (part total)
str
.!toFixed
* 100 $ / part total
, 1
, "\"%"
|get-env! $ quote
defn get-env! (property)
aget (.-env js/process) property
:ns $ quote (ns app.util)