1
0
mirror of https://github.com/schollz/cowyo.git synced 2023-08-10 21:13:00 +03:00

Compare commits

...

277 Commits

Author SHA1 Message Date
Zack Scholl
ed996d9e09 update deps 2023-07-06 09:56:50 -07:00
Zack
a58f4ca346
Create FUNDING.yml 2020-05-14 10:17:15 -07:00
Zack Scholl
a318863eca fix dockerfile 2019-07-06 08:57:39 -06:00
Zack Scholl
d8d3200491 update dockerfile 2019-07-05 06:59:34 -07:00
Zack Scholl
f414ccc2f3 update readme 2019-07-05 06:55:56 -07:00
Zack Scholl
0a9b069152 update dockerfile 2019-07-05 06:54:48 -07:00
Zack Scholl
4954276644 update deps 2019-07-05 06:45:52 -07:00
Zack Scholl
156722d23e use go.mod 2019-07-05 06:42:41 -07:00
Zack Scholl
9accd685c0 update depndencies 2018-08-22 21:14:49 -06:00
Zack Scholl
126b13fcea update cookiestore 2018-08-21 19:36:24 -07:00
Zack Scholl
5a0587c3ce update cookie store 2018-08-21 19:36:10 -07:00
Zack Scholl
e5a1a88a02 update docker 2018-07-31 12:56:00 -07:00
Zack Scholl
d43cc80b53 update dependencies 2018-07-31 12:40:32 -07:00
Zack Scholl
ff84acd9d7 make sure to publish 2018-07-21 14:36:10 -07:00
Zack
1e46c40cc6
Merge pull request #137 from DanielHeath/toml
Configure via Toml
2018-07-21 14:32:44 -07:00
Daniel Heath
9e928f6b74 Fix bindata for build 2018-05-28 10:25:30 +10:00
Daniel Heath
3b91b699e3 Add 'tomlo', configure cowyo via TOML 2018-05-27 21:51:33 +10:00
Daniel Heath
f567f86bab Remove unused property 2018-05-27 21:49:55 +10:00
Daniel Heath
0e93250cc9 Remove dud log line 2018-05-27 21:49:43 +10:00
Daniel Heath
82d5ac908d Always name the cookie "_session" 2018-05-27 21:49:34 +10:00
Daniel Heath
7a61b16e7a Add TOML library 2018-05-27 21:49:05 +10:00
Daniel Heath
f86dc1095a Make the max document length configurable 2018-05-02 16:27:35 +10:00
Daniel Heath
a976007642 Fixup: Debug mode broken 2018-04-30 20:19:11 +10:00
Zack
180a28cda6
Merge pull request #133 from DanielHeath/master
Make cowyo separately mountable
2018-04-30 02:19:23 -07:00
Daniel Heath
08bbce1298 Move main back to root 2018-04-28 19:55:15 +10:00
Daniel Heath
e53123d8fa Add herdyo command for an example of a multisite 2018-04-28 19:54:44 +10:00
Daniel Heath
d48d1458a5 Make save mutex per-site 2018-04-28 19:32:48 +10:00
Daniel Heath
d9b8bfc95d Remove final global var 2018-04-28 19:29:35 +10:00
Daniel Heath
bc1a9ee86b Remove global logger 2018-04-28 12:30:13 +10:00
Daniel Heath
f4f5042245 Remove sitemapUpToDate global 2018-04-28 12:19:43 +10:00
Daniel Heath
d095915f83 One more global 2018-04-28 12:16:10 +10:00
Daniel Heath
c2cd54a12d Killing globals 2018-04-28 12:13:19 +10:00
Daniel Heath
2e80633cd4 Kill more globals 2018-04-28 12:12:10 +10:00
Daniel Heath
9cf0d0e129 Remove maxupload global 2018-04-28 12:10:47 +10:00
Daniel Heath
ede4d1fba3 Pass in a session store so they can be shared 2018-04-28 12:09:08 +10:00
Daniel Heath
ff5c100cf8 Fixup tests 2018-04-28 11:58:26 +10:00
Daniel Heath
465e9c8e93 Move serving and CSS reading out of Site 2018-04-28 11:56:07 +10:00
Daniel Heath
0d984da5b2 Move serve params into a struct 2018-04-28 11:42:51 +10:00
Daniel Heath
8944170646 Initial split (towards a separately mountable cowyo) 2018-04-28 11:32:08 +10:00
Zack Scholl
f21c89f7bd update dependencies 2018-04-22 05:51:19 -06:00
Zack Scholl
91178f4f29 bug fix: need to add custom funcs when using assets 2018-04-19 20:59:14 -07:00
Zack Scholl
8bc1123f1d display the filename when uploading 2018-04-19 20:55:08 -07:00
Zack Scholl
5d0523117b bump version 2018-04-18 00:10:38 -07:00
Zack Scholl
6e1d64b40e bump version 2018-04-18 00:09:28 -07:00
Zack
f9b8713404
Merge pull request #129 from DanielHeath/master
Fix sitemaps, make brute-forcing access codes harder
2018-04-18 00:05:06 -07:00
Daniel Heath
fc3030339f Add "/uploads" to list all uploaded files 2018-04-01 11:31:26 +10:00
Daniel Heath
df406ec71b Raw pages are always shown text/plain (fix #127) 2018-04-01 10:06:51 +10:00
Daniel Heath
2a43ebdb53 Upgrade dependencies 2018-03-31 15:30:04 +11:00
Daniel Heath
d9e622fa9d Sitemap includes the request host/port, fixes #125 2018-03-31 14:34:15 +11:00
Daniel Heath
446ba00fe9 Remove .DS_Store from bindata 2018-03-31 14:34:15 +11:00
Daniel Heath
f729aece51 Run gofmt when building assets 2018-03-31 14:34:14 +11:00
Daniel Heath
e5dfacb4bb Update to latest teeny-security: Makes brute-forcing access codes much harder 2018-03-31 14:34:14 +11:00
Zack
2373e339d0
Merge pull request #119 from katomaso/feature/lock-per-session
Lock page with respect to session
2018-03-26 23:19:53 -07:00
Tomas Peterka
c64e54a991 Lock page with respect to session 2018-03-11 23:46:29 +01:00
Zack
f350d0bbad
Update README.md 2018-02-20 07:30:13 -08:00
Zack
308244cfa4
Update README.md 2018-02-20 07:29:59 -08:00
Zack
50624e75e9
Merge pull request #117 from DanielHeath/implement-file-uploads
Implement file upload / download, fix #77
2018-02-20 05:54:09 -08:00
Daniel Heath
efd4cfb0d1 Implement file upload / download, fix #77 2018-02-20 21:04:33 +11:00
Zack Scholl
4ebf9e02ef Allow center 2018-02-18 12:04:23 -08:00
Zack Scholl
c1a78c2006 can not -> cannot 2018-02-18 11:59:46 -08:00
Zack Scholl
8a4ba13506 Change default stylesheet 2018-02-18 11:50:02 -08:00
Zack
c78dec28eb
Merge pull request #115 from DanielHeath/split-bindata
Remove unused cgo and split debug/non-debug asset builds
2018-02-18 11:34:39 -08:00
Zack
0f8e572d43
Merge pull request #116 from DanielHeath/run-gofmt
Run gofmt
2018-02-18 11:34:16 -08:00
Zack Scholl
1720711029 Self destruct with published messages works 2018-02-18 11:33:32 -08:00
Zack Scholl
3c53386e08 Debounce works 2018-02-18 11:33:23 -08:00
Zack
9d4fcae7b2
Merge pull request #118 from DanielHeath/tidy-logging-statement
Remove logging statement
2018-02-15 19:30:56 -08:00
Daniel Heath
db48f1291b Remove unused cgo and split debug/non-debug asset builds
It's now much harder to accidentally build a release with debug or old assets.
2018-02-16 10:54:41 +11:00
Daniel Heath
131a54a682 Gofmt 2018-02-16 10:53:59 +11:00
Daniel Heath
bb475bd924 Remove logging statement 2018-02-16 10:53:01 +11:00
Zack
dfd9aea863
Merge pull request #113 from DanielHeath/master
Make the 'view' link do something helpful
2018-02-09 15:13:08 -08:00
Daniel Heath
9982fb5175 Auth doesn't stop you publishing stuff 2018-02-07 15:09:01 +11:00
Daniel Heath
f5f0bdb3bb Prevent updates from running out-of-order 2018-02-07 14:48:10 +11:00
Daniel Heath
d65eccbe9d Whitespace 2018-01-31 22:38:40 +11:00
Daniel Heath
1a3e891dfd Fixup test 2018-01-31 21:44:15 +11:00
Daniel Heath
917d32f8a5 Support category pages 2018-01-31 16:47:12 +11:00
Daniel Heath
c207077877 Enable hot-template-reloading in development 2018-01-31 16:47:12 +11:00
Daniel Heath
5e4a317b10 Split inline CSS & javascript into their own files 2018-01-31 16:47:12 +11:00
Daniel Heath
da6d6e5d43 Avoid overwriting others work (fix #107) 2018-01-31 16:47:10 +11:00
Daniel Heath
d0bc74ec55 Only save the page if its contents have changed 2018-01-31 16:46:58 +11:00
Daniel Heath
b42750073c Whitespace 2018-01-31 16:46:58 +11:00
Daniel Heath
e1934b9797 Clicking "View" while editing takes you to the current view page 2018-01-31 16:46:58 +11:00
Daniel Heath
0badf719e0 Add mutex around Save, fixes #70 2018-01-31 16:46:57 +11:00
Zack
a0dcc9652f
Merge pull request #108 from DanielHeath/mutex-save
Add mutex around page.Save(), fixes #70
2018-01-27 07:56:39 -05:00
Zack
fb6405ba1b
Merge pull request #109 from DanielHeath/fix-comment
Fix comment: Upload the right binary
2018-01-27 07:56:16 -05:00
Daniel Heath
b18f40e336 Add mutex around Save, fixes #70 2018-01-27 20:58:20 +11:00
Daniel Heath
fa66472128 Upload the binary you built (ok, it's just a comment) 2018-01-27 20:27:33 +11:00
Zack
526688c7e3
Merge pull request #104 from DanielHeath/update-deps
Update all dependencies to latest
2018-01-23 18:41:25 -05:00
Zack
31153063f2
Merge pull request #103 from DanielHeath/fix-gitignore
Fixup gitignore
2018-01-23 18:40:49 -05:00
Daniel Heath
b4638476cc Update all dependencies to latest 2018-01-24 08:45:42 +11:00
Zack
ff420fb81d
Update Makefile 2018-01-23 09:28:57 -05:00
Daniel Heath
dec21a80c2 Fixup gitignore 2018-01-22 21:04:15 +11:00
Zack
f998015f0c
Merge pull request #99 from DanielHeath/master
Fade-out items while waiting for a delete to go through
2018-01-21 09:09:55 -05:00
Daniel Heath
fea5ef4647 Merge branch 'master' of github.com:danielheath/cowyo 2018-01-21 12:52:43 +11:00
Daniel Heath
ecf1d2ab10 Add password to access wiki 2018-01-21 12:09:16 +11:00
Daniel Heath
765c5788b3 Ignore dist 2018-01-21 12:03:55 +11:00
Daniel Heath
76ec1c1acb Allow setting session secret cookie code 2018-01-18 20:51:08 +11:00
Daniel Heath
89d58f5a22 Allow insecure markup (for private wikis amongst friends) 2018-01-18 20:50:55 +11:00
Daniel Heath
2f1c0e3cd2 Whitespace 2018-01-18 20:50:13 +11:00
Daniel Heath
10d45b0c76 Add my deploy instructions 2018-01-18 17:39:11 +11:00
Daniel Heath
038a895772 Sort 'ls' page by recent 2018-01-18 17:39:05 +11:00
Daniel Heath
ff2920965d Sort 'ls' page by recent 2018-01-18 17:12:03 +11:00
Daniel Heath
a36f8e318e Use a struct for related data instead of four separate arrays 2018-01-18 17:08:04 +11:00
Daniel Heath
c802fa06be Strip trailing whitespace 2018-01-18 17:04:44 +11:00
Daniel Heath
81f6b2d263 Remove random println 2018-01-03 22:47:27 +11:00
Daniel Heath
5803cbdc3f Fade-out items while waiting for a delete to go through 2018-01-03 22:47:13 +11:00
Daniel Heath
5a82e77738 Strip trailing whitespace 2018-01-03 22:38:39 +11:00
Zack Scholl
55368b1c1a Fix bug with one letter routes 2017-11-13 11:32:32 -07:00
Zack Scholl
7cc1eccb83 Add 32-bit version 2017-11-05 06:35:11 -07:00
Zack Scholl
45436a3762 Update the readme 2017-11-04 16:15:57 -06:00
Zack Scholl
684ff4e692 Add -diary for diary mode
New creates a new page with a timestamp
2017-11-04 16:09:10 -06:00
Zack Scholl
63bdbb5824 Add -debounce for setting the debounce time 2017-11-04 15:30:44 -06:00
Zack Scholl
85877602e0 /view -> /read when published
Fixes #97
2017-11-04 15:23:38 -06:00
Zack Scholl
6eacf90e18 Sitemap should goto /read and not /view 2017-11-04 12:57:04 -06:00
Zack Scholl
4a916984e6 Need to handle /page/ and /page the same
Fixes #88
2017-11-04 12:49:02 -06:00
Zack Scholl
840fbf73ec Don't need this line anymore 2017-11-04 18:45:10 +00:00
Zack Scholl
a2288cf129 Oops 2017-11-04 04:52:29 -06:00
Zack Scholl
8ccd40c105 Update README 2017-11-04 04:52:18 -06:00
Zack Scholl
1ef24a0347 Update dependencies 2017-11-04 04:45:08 -06:00
Zack
8b3e7b0605
Merge pull request #93 from schollz/v3
V3
2017-11-04 04:42:49 -06:00
Zack Scholl
de03d2b547 Add -lock for automatic locking
Fixes #90
2017-11-04 04:41:56 -06:00
Zack Scholl
d481145c5f Add default page Fixes #91 2017-11-04 04:21:51 -06:00
Zack Scholl
2040dbaaa5 Add -css option for custom CSS on the /read page 2017-11-04 04:18:05 -06:00
Zack Scholl
5ae5c91945 Trying to make a /read 2017-11-02 06:27:39 -06:00
Zack
0ef75a919c Update README.md 2017-10-15 09:00:21 -06:00
Zack
da30d15eb1 Merge pull request #84 from yamilurbina/adding_dockerfile
Added Dockerfile to generate a small docker image
2017-10-15 08:50:44 -06:00
Zack
9d22d5d41d Update README.md 2017-10-15 08:38:31 -06:00
Zack Scholl
c35aeca859 Generate sitemap in thread because it takes a long time 2017-10-15 08:18:48 -06:00
Zack Scholl
e52d0097a9 Adding new bindata 2017-10-15 07:59:07 -06:00
Zack Scholl
b2a01d0a6d Sitemap now working 2017-10-15 07:58:43 -06:00
Zack Scholl
026dd7c647 Remove twitter 2017-10-15 07:50:13 -06:00
Zack Scholl
107a0dc32b Add publishing, for sitemap.xml 2017-10-15 07:49:40 -06:00
Zack
683bcdea1c Update README.md 2017-10-12 17:11:36 -06:00
Zack
e33ddcfddb Update README.md 2017-10-12 17:10:45 -06:00
Zack Scholl
6baa87daa4 Update bindata with twitter 2017-10-12 17:08:38 -06:00
Zack
912bf83c59 Merge pull request #86 from benjaminmisell/master
Add twitter share icon
2017-10-12 13:53:14 -06:00
Benjamin Misell
ecba3aa0ea Add twitter share icon 2017-10-12 20:29:48 +01:00
Yamil Urbina
b77c32d646
Improvements for Dockerfile; docker-compose file added 2017-10-11 00:26:33 -04:00
Zack Scholl
bef20f3366 Vendoring 2017-10-03 14:43:55 -04:00
Zack
21047443d6 Merge pull request #85 from tgulacsi/master
Small tweaks
2017-10-03 06:45:27 -04:00
Tamás Gulácsi
b83e3fd52e precompile regexp 2017-10-02 13:58:39 +02:00
Tamás Gulácsi
8474b79cf1 migrate: return on error (esp. Save) 2017-10-02 13:55:48 +02:00
Zack Scholl
b3b5f31575 Bump version 2017-09-30 09:18:30 -04:00
Zack Scholl
90a8ea1706 Skip icons in the recent files 2017-09-29 12:21:10 -04:00
Zack Scholl
6d81fe5b19 Add recent pages to "view" 2017-09-29 08:48:32 -04:00
Zack Scholl
bb56db31f7 Update bindata 2017-09-28 06:15:32 -04:00
Zack Scholl
b286fae089 Goto "/view" when decrypting a self-destructing page
Fixes #81
2017-09-28 02:06:38 -06:00
Zack Scholl
ce2deee49f Update CSS 2017-09-11 12:11:22 -06:00
Zack Scholl
673e38a1c8 Update 2017-09-11 12:08:48 -06:00
Zack Scholl
2246747698 Update CSS 2017-09-11 12:04:09 -06:00
Zack Scholl
dad4492c17 Update readme 2017-08-18 08:21:45 -07:00
Zack Scholl
9d87359c62 Remove upx for now 2017-08-18 07:05:10 -07:00
Zack Scholl
76b054122a Add save 2017-08-17 07:44:18 -07:00
Zack Scholl
def0f9d669 Merge branch 'master' of github.com:schollz/cowyo 2017-08-17 07:36:16 -07:00
Zack Scholl
4821e7ae62 Use meta instead of storing in history 2017-08-17 07:36:07 -07:00
Zack Scholl
aa1b7a1afb Save is redundant 2017-08-16 06:49:10 -07:00
Zack Scholl
e10ff91f10 Save is redundant 2017-08-16 06:48:51 -07:00
Zack Scholl
bb9d95620c Bump version 2017-08-12 08:28:12 -06:00
Zack Scholl
c0b6365440 Allow saving filename in history for cowyodel 2017-08-12 08:05:01 -06:00
Zack Scholl
89d0f828b0 Default to editing, not vieweing 2017-08-08 11:37:22 -06:00
Zack
0b1c74695b Update with info about cowyodel 2017-07-09 09:01:30 -06:00
Zack Scholl
d3101da4ac Add /exists to check if page exists 2017-07-05 10:49:06 -06:00
Zack Scholl
d69e37d1bc Update README 2017-06-29 15:34:56 -06:00
Zack Scholl
57e28b85cd Update README 2017-06-29 15:25:13 -06:00
Zack Scholl
9dbb83e8af Moved the encryption to its own package 2017-06-29 15:24:34 -06:00
Zack Scholl
228b324149 Specify relinquish parameters 2017-06-28 10:10:32 -06:00
Zack Scholl
b7fc420f7e Added API for new CLI program 2017-06-27 21:32:38 -06:00
Zack Scholl
294d4611d2 Spelling 2017-06-23 13:02:56 -06:00
Zack
9b5376af30 Merge pull request #74 from zealotree/master
Added HTTPS session support
2017-06-23 13:02:06 -06:00
zealotree
40c5d8bee4 Mistakes were made 2017-06-23 10:32:46 -04:00
zealotree
2a7e98a8e2 Remove extra struff 2017-06-23 10:27:36 -04:00
Zealotree
956cef54df Added HTTPS session support 2017-06-23 10:26:13 -04:00
Zealotree
2101b706d3 Added HTTPS session support 2017-06-23 09:40:35 -04:00
Zack Scholl
68ecbdd595 Redirect to /page/view on /page/ request 2017-05-24 10:42:37 -06:00
root
ceaef5a1ae Add oulipo modus (for excising the fifth glyph) 2017-04-15 05:15:00 +00:00
Zack Scholl
9571592de0 Trim whitespace at bottom of entry 2017-04-11 07:53:59 -06:00
Zack Scholl
7d50c644e5 Include Markdown information in textarea
Former-commit-id: 346bfed7dc6f156b1687057d80db88706f1d2ebb
2017-03-28 10:37:06 -06:00
Zack Scholl
4af9b87519 Version bump
Former-commit-id: d640f0b7b9eec46a415858619558157db8d33353
2017-03-28 08:08:29 -06:00
Zack Scholl
23536ad927 New: Make host configurable, Fix #69
Former-commit-id: e087e6952610f7baeb212e12cef44eafcb06613d
2017-03-28 08:01:03 -06:00
Zack
86c86779dd Merge pull request #67 from Silvus/patch-1
Json files should not be executable

Former-commit-id: 4f74e31d1e3b575b25e302850dff7b9d56a7a469
2017-03-24 17:59:40 -06:00
Silvus
6e1c85e205 Json files should not be executable
Former-commit-id: 850733197b
2017-03-25 00:52:37 +01:00
Zack
9248892668 Update readme to elaborate #64 #66
Former-commit-id: 84c4951805
2017-03-24 08:41:16 -06:00
Zack Scholl
8d3a301497 Version bump
Former-commit-id: 3dcfacd49a
2017-03-24 08:05:17 -06:00
Zack Scholl
a3e1d6f6d1 Update README
Former-commit-id: 8c24712323
2017-03-24 08:00:21 -06:00
Zack Scholl
8318b5afd8 Add new bindata
Former-commit-id: e5b0471593
2017-03-24 07:58:55 -06:00
Zack Scholl
82b16ec121 Add route /ls for viewing directory
Former-commit-id: 53ace40488
2017-03-24 07:58:37 -06:00
Zack Scholl
8e6b798be5 Clean code
Former-commit-id: 446c9cf2b4
2017-03-24 07:24:36 -06:00
Zack Scholl
57cda99027 Benchmark for alliterative animal
Former-commit-id: b489e299fe
2017-03-24 07:18:20 -06:00
Zack Scholl
e8959d8923 Require random names to be smaller
Former-commit-id: ec99ac305c
2017-03-24 07:14:34 -06:00
Zack Scholl
9d9918966a History every 60 seconds instead of 1
Former-commit-id: 931a0a7c62
2017-03-24 07:12:09 -06:00
Zack Scholl
05f43196c5 Updated bindata
Former-commit-id: ed458d05d6
2017-03-24 07:10:31 -06:00
Zack Scholl
c35ab4eb4a Improve history UI and non-history route speed
Former-commit-id: 30f3956568
2017-03-24 07:09:41 -06:00
Zack Scholl
5fab2749f9 Add reverse list
Former-commit-id: f45b4f86ca
2017-03-24 06:25:59 -06:00
Zack
da2dcee3fd Version links to latest release
Former-commit-id: 7b096e6013
2017-03-23 21:16:18 -06:00
Zack Scholl
e11baae47c Updated README
Former-commit-id: ce166d8e56
2017-03-23 20:44:01 -06:00
Zack Scholl
035cd7b6d9 Change name
Former-commit-id: b0308589ec
2017-03-23 20:38:33 -06:00
Zack Scholl
7b1ce17506 Show page on menu
Former-commit-id: 3f109f7160
2017-03-23 20:23:06 -06:00
Zack Scholl
14f563572d Print server version on startup
Former-commit-id: 4717d5d066
2017-03-23 20:13:58 -06:00
Zack
5d273c037a Update .travis.yml
Former-commit-id: 25d99852c0
2017-03-23 19:30:24 -06:00
Zack Scholl
dbd1be938e Merge branch 'master' of github.com:schollz/cowyo
Former-commit-id: 25c30e4be6
2017-03-23 19:27:12 -06:00
Zack Scholl
3f98cb7e70 Updated README
Former-commit-id: 3114a70286
2017-03-23 19:26:35 -06:00
Zack
f09cf1b3e9 Update .travis.yml
Former-commit-id: 79c2a1dcbc
2017-03-23 19:26:10 -06:00
Zack Scholl
c9b9032dc9 Updated logo and README
Former-commit-id: 96eb431dfe
2017-03-23 19:23:33 -06:00
Zack
674b39b49a Create .travis.yml
Former-commit-id: b11c754b8e
2017-03-23 19:18:23 -06:00
Zack Scholl
e066ad23cc Allow raw to get locked copy
Former-commit-id: 685117ffd3
2017-03-23 17:47:41 -06:00
Zack Scholl
11b671423c -> Clear done
Former-commit-id: 3f70a86d65
2017-03-23 17:42:20 -06:00
Zack Scholl
82c24f2413 Remove static files that aren't used anymore
Former-commit-id: 1495b6d836c5ba5c1b87bb778bfb85b3073d9fd2 [formerly 438b5d6af9c80b85d036f1ccce5b593a33934dbf] [formerly 331f1f8bd4a19a0639c35e96298bc834fef406a2 [formerly f391d9132ccd146acea91387a2520bec8349c151]]
Former-commit-id: 19c3b9ec3bbe4056e8c4e99f1576d6fd30a27b16 [formerly 7929749a5807f4d866bd7fd4301aa4305b70d7d8]
Former-commit-id: 658645d15e6e200d1e4123eafde15d6bf5efe2cc
Former-commit-id: e182fbbe64
2017-03-23 16:38:29 -06:00
Zack Scholl
fa7ae60d59 Better UI for locking/encrypting
Former-commit-id: a4d999be67331667d085f4d21e76c36248d4de6d [formerly e0ea851e54280d1299e3c8285412b3df421682a7] [formerly 011d5bf0194d46ee5e2f07cb6ef16a28554235b5 [formerly 17f856fdde59ddf045a116cb85af6f7b79f9d4a5]]
Former-commit-id: b8a3b6ed199e3c06aeb6f365e4b05572a28bd852 [formerly 1998dce4d8b98ccd1586748b26d32935c9b3d78d]
Former-commit-id: 81aa00298e9fe22a5017c726b772a4a34dd53acf
Former-commit-id: c71c77fd5c
2017-03-23 16:28:19 -06:00
Zack Scholl
20dd8d4f20 Fix: filenames to lower case before base32
Former-commit-id: 75492d2819399d026bac979cdfdc9ca3b6cf1466 [formerly e32fd7161849a8e11d2b31f27deb04378f9dcbb4] [formerly da830c5c0d0b73548699f6a6341eb55f5afa130f [formerly 7a780609f7276ff3569323beb61cb12088e17447]]
Former-commit-id: a9a517909321225859867f0b031b8303d35102ae [formerly 59d8c184f6f6359f811b1413b259a01aaee93854]
Former-commit-id: f31efaed6d2b7175ea50f8205ae2fae034163518
Former-commit-id: fc6a38f047
2017-03-23 16:24:25 -06:00
Zack Scholl
bd3b9c96e1 Clean up UI
Former-commit-id: 15a1200308fe75f9c3163d400ff3c56287492dfb [formerly 79f3f90c3d0e620372f1ad338252a988135b302e] [formerly 3d62c398b3487e92160d35a200c08134fcf4eb32 [formerly 52370dde7adf6c4fe9899544567a2469c03b031a]]
Former-commit-id: bbe42ca94a0e072215e5fef3f163add591206ab2 [formerly df5785bc10f23dab6110d38900a55946d95f0fce]
Former-commit-id: 4c02db90005227a23605f2a46d6546ab3f698a99
Former-commit-id: a456b44b5a
2017-03-23 16:16:15 -06:00
Zack Scholl
a78468e086 Makefile works in parallel
Former-commit-id: 0e8b4a8544a37d5ba70b5105972af6f682121fe2 [formerly b27e6f754cf6267f4d9b24e4e16d719b1b58ac3a] [formerly 643edb8efb9bb54ffe343f6d8e09d70277ba8880 [formerly d064c67547da6da2f97431f2962dd6a6f81cb8ce]]
Former-commit-id: de0f0fadefb349b5fef99655c574515dde046c44 [formerly 471f13c019582eff335fef36b8602308e9bfd06d]
Former-commit-id: db630b02ebee07b2f95eff560f6ff0e74152413e
Former-commit-id: ebfdb06f25
2017-03-23 14:37:47 -06:00
Zack Scholl
97bfc15692 Remove goreleaser
Former-commit-id: 4917070ce632a6cedf0b5b3c4a2cf45aa4bbb271 [formerly 98fa8a9d297a2786d332f22ac77eb53e43b43f0c] [formerly f0bbeb242e7615e4784f3898b97b7555e134868b [formerly d1dc4804ad9e5a7e57e9f112c9bd7ab98564ae4f]]
Former-commit-id: de57b86dde4c6d5bde9eb211a6755966a95d64ee [formerly 395bc843211b4c46410c62f9525f9e43527e8dfd]
Former-commit-id: 04e6b16e8658bb4caeee788651bfad6da0e4e36c
Former-commit-id: f1abfbe858
2017-03-23 14:18:58 -06:00
Zack Scholl
61b8d275be Update goreleaser
Former-commit-id: 30888d69957a68dd8a3fe025bb90a671135c011a [formerly 64906e29fda1a6da209155a2572cecd3cd4ab639] [formerly d6d932dab5bd2bdb4e7a78b0229fc21c29dd76af [formerly a9b194fe32d668683d3099286d5079773fdff3e6]]
Former-commit-id: d8684df40761e41ba6a08e5caf025ee652bd7d8c [formerly f4827b3cfd5a561865df53ce8246e8c412c0a202]
Former-commit-id: dca71837f3bffdab7c9374a47536a16e72eb2013
Former-commit-id: 752902466e
2017-03-23 14:15:30 -06:00
Zack Scholl
3b7e35f9ef Add "Clear old" for deleting crossed list items
Former-commit-id: f43b6caae264ef5f8cd46f8a40143501a39d5948 [formerly 328a9724705837f8287a6d519578513fb868c962] [formerly b006fff403bdf2421495a4d4e8f9ef246e15adec [formerly fb4118be424a81a90072ad987c886af08923917a]]
Former-commit-id: da4974f66bfcea8602d9b08f129b73ba06d69ea8 [formerly 8215c6a461f8bd2a9baee02176e64ec99a401ec9]
Former-commit-id: 4e070f07eef46a9f140a778dca7dc6c20adc76d5
Former-commit-id: 2980f1e193
2017-03-23 13:31:09 -06:00
Zack Scholl
bbf55f539f Use monospace when editing a file
Files are determined if they have a period in the name.


Former-commit-id: 8ae103ccf40f20a2c0a385021fad70caf614ca51 [formerly 23e44beb2ececa617280feb256a94900342cbdc4] [formerly 4872f2c5eaa25e32514c01aabd63355e6cb1d5d4 [formerly 518ee34f5615b9c9a6e3b5b68322df2c8a221ae8]]
Former-commit-id: c646c3b38b5cf7d2552f9aac8d1802ef98e9170b [formerly 71bb4f2d64a0c87f9b881e612091ec98253721fa]
Former-commit-id: a60d4c8180b584dea243391a7a41abc175524afb
Former-commit-id: 5f42c319a2
2017-03-23 13:13:56 -06:00
Zack Scholl
28d70b6148 New: release with goreleaser
Former-commit-id: 0714de93b3df45312d65ed61340b066515d10bae [formerly 23a3da72fd9edd64da8b30b0ead05ca4c440c709] [formerly 01d2da8eb8e0b9e5fba12ed5cc92e50c29aa9be6 [formerly 838f599daae4e00ea0f2bace01ddd8521f28a157]]
Former-commit-id: 73bb360a0b18b0ad1b626b5a29a6e2e188b91257 [formerly 104088fcf4e6880a40436c7ff309747baea2640e]
Former-commit-id: ffbc185e61ad872f99713d15289fc07b14bbaf80
Former-commit-id: b69ddc7cdb
2017-03-23 11:59:55 -06:00
Zack Scholl
3b0169212c Update bin
Former-commit-id: 8383f8425cd42c2ccf34e992019a7c84c033e0c9 [formerly 0aed9dbed69ff4e95429f8638101252a0f962776] [formerly a25974ec2c0f768eab091fb2c64de4138834a110 [formerly 1a3fa54b60fd2a38cdc6833e2d48fcf5f2793046]]
Former-commit-id: 185dddc3245f701da416c1437d3bb59fa7d57e55 [formerly c4ea403af36b85707ebf2f30d22edde4e77d6e06]
Former-commit-id: ab8139e0438e552531a7f86e1116d40cda905752
Former-commit-id: caa85d22af
2017-03-23 10:57:31 -06:00
Zack Scholl
90faae9e85 Add link for view/raw/list
Former-commit-id: 288ff9cd595b174372f32c928082cea7d7983fe3 [formerly c40384353290006486b9c7f9c94f07f7b0378a6c] [formerly c79385490b081fa4878526f946dc6861b428829d [formerly c4906b78767ae66904d8dd1e3ee53351797d20ad]]
Former-commit-id: 9691087d6cf38d30829d0b9d5e9af9e2469607d1 [formerly 781213c951e51a1d3fe61421b0526329e84d4eb8]
Former-commit-id: f933ace45d149daed95cb00b19abf4f1f418ec87
Former-commit-id: 41666f1238
2017-03-23 10:57:14 -06:00
Zack Scholl
e9e513171c Create special folder for tests
Former-commit-id: ae51c6c248baaf996f899e77e760aba1f0c161cf [formerly 7bf4f38602dd6b24f7888d88f18ca6e9cac9b9a3] [formerly 0ef00a42149f4e7724fde45c2a252e9ec7c550f5 [formerly d20e40ca85e6a06d0aba698624e3433e7c5c612a]]
Former-commit-id: 69b97c02f08943de02a1ae58c8291dcae6c56db8 [formerly d229497f2b2159c144939054ad21f57ce380c47e]
Former-commit-id: 3951a8763debafee5f9ddf812fd022c9b5ce2d10
Former-commit-id: 4a1c502ee3
2017-03-23 10:56:53 -06:00
Zack Scholl
bd6ed8d3c0 Keep messaging system alive when locked/encrypted
Former-commit-id: becceb5fed276d2daa80eca4a41b9fa9d3eae9fd [formerly 4ac3d85f084df155b06e17eea289e34ea60faa50] [formerly 19561726e85e6c133c667d1ee948b0ff2978632d [formerly a3fe621f1947a2cf0b6af4ac6ccb01c3c744fc2e]]
Former-commit-id: 3b7092e62e255f90d4ddcea9a104f5e6db22da2e [formerly 0dd41c8f221df6a21fad9da2ff2287ebb98f3b9a]
Former-commit-id: 62a685d81f08d7b96596cbc236c540141cdd9e42
Former-commit-id: 4b75f66381
2017-03-23 10:39:14 -06:00
Zack Scholl
c1b9b71669 Remove menu options when locking/encrypting
Former-commit-id: 9020d91d5e3f4ab07cc5ec8e5abfc68d7f30cc05 [formerly c501c57e329676731abd211e6f26619806db2bc6] [formerly 8c7c7f37bb87845da3af2e1a40ccf5bf73852846 [formerly 13d370370f4f68203998d8eee7ebacec61a6a2a6]]
Former-commit-id: da6da64e8940ef34512f9abc8ed5480a9d132a64 [formerly 6902d14822e41218409d23c5e8669afa370e1fb9]
Former-commit-id: a02186b53b69eff7a51d8cb61e9af0470113e7df
Former-commit-id: 078801ce85
2017-03-23 10:26:40 -06:00
Zack
edcd2e7d09 Merge pull request #61 from schollz/frontend
Update frontend for locking page

Former-commit-id: 6c657ff4a43f33cb040185d259c03e793676f332 [formerly 72b937774f347af8f114d919bcc260c1c441ced7] [formerly 9429791652dc3e9a3e44d866dc346a45225e5560 [formerly c5fa54646d56b3eaebfef06aff2821b1839a39cc]]
Former-commit-id: 182ba5b59c9852b46a80f7215a7113a640e11ef7 [formerly 4a66a0a6bce4d892bcef1eb00d074ddcd025b425]
Former-commit-id: e6fdc1362ec4b3d22195a9c7b2874010dfda4a36
Former-commit-id: 695b838991
2017-03-23 10:03:45 -06:00
Zack Scholl
3fd886e625 Update frontend for locking page
Former-commit-id: e7a41b6d0ef9fc0814b4f6fa378370a2dc94749b [formerly 54b793d738e5deb2ebf2e8316a1f43e3450900aa] [formerly 842e120f0f69713229f4227bd90f8c33ce2932b3 [formerly 9c95fd350104dd20055c7564fc5e167e21409c38 [formerly b9860a5d2f]]]
Former-commit-id: 3d6c223179053689aa785e50ba1a6d7affd075aa [formerly 61ab106b098ea088e70597cb1a5a19e3a71eced9]
Former-commit-id: ab5ef985f31336a09d0a6d9dd7291967ea4da3f0
Former-commit-id: a7f6cd8a0e
2017-03-23 09:57:25 -06:00
Zack Scholl
4709654c4c Version bump
Former-commit-id: 9e36c6e26026b6da693e9c63665a359bd8f81132 [formerly 90d2930802e405e495c9d4f115fc96607240fa8d] [formerly 384133006e5622b83245917ca698c6678728be97 [formerly dcf6fd4e981e1eec2f77ba5e3de39ee40253d73f [formerly 59036904d8]]]
Former-commit-id: 73ead65f17e5bc8e72002d3db704f31e66d4bf37 [formerly 123a60c023445bb54818026f4ea8bd8683c4846a]
Former-commit-id: 8637b59aaedf6f665c581a630f8e076c2e38b735
Former-commit-id: ef0414a548
2017-03-23 07:55:36 -06:00
Zack Scholl
cc04599b6f Update bindata on Makefile
Former-commit-id: eb5f6d3dcc38157ddff43ab777962555c20d2b16 [formerly 4d02759fbb3a7f208a178c0cd797437300b9feb0] [formerly d68e70cde8da577d49b4ec1116c7948e792d80c0 [formerly 98d57a49c87b73fd59680fda968e16b2a4a94296 [formerly 8f76fbecf4]]]
Former-commit-id: 75cf27c8d4bb198da60f59548a3ed846c8529c7d [formerly e05a48d24612a7bd8994e7c51d68ee2ca46e8736]
Former-commit-id: 530053a77d420a205f65fd9ead96b656b523b766
Former-commit-id: 72b285671b
2017-03-23 07:40:47 -06:00
Zack Scholl
d247846934 Add ARM to makefile
Former-commit-id: a4ad6c9c132f48b5f679f973a0c80db923765156 [formerly 0635361632881f2e831e3ecea20134df27838d9e] [formerly 2d91ad2c4766984722e88d20275d0105218b1145 [formerly 2549aac8215b9213a2f8c7b9387f3081a5c1bc04 [formerly 2a2570835d]]]
Former-commit-id: d5459ec2b879c7b5ea69361643fdc6e1a5857114 [formerly 63abe22d8706168f9a7f035f53585d7d58b654f3]
Former-commit-id: 2326e71c3063d2bbee54839dadd8c57484a90835
Former-commit-id: cf04407182
2017-03-23 07:33:46 -06:00
Zack Scholl
79768ce9be Updated bindata
Former-commit-id: b54ce6344bd46fc6f82ff1e4fb676dc425d99680 [formerly 0fa45573d6b17c6e1566e7a1ab7f56f437759625] [formerly 9fe8f5b04b0f415c74ee52f171f1378c2834d7b5 [formerly 3c86cbfc68d699bf79fe05d513d0a0e0aa00ed7c [formerly 6440472b2d]]]
Former-commit-id: 4f471e841a3022e0dc3999b4ff450b82870859e5 [formerly 05232e825e9c05340760432ac53a29661d596380]
Former-commit-id: 3f607ad0c09546f775fed460ebcde58dfa3afa23
Former-commit-id: 88bfd0a1f7
2017-03-23 07:33:13 -06:00
Zack Scholl
a16fd6f8fb Show error message if route not understood
Former-commit-id: a8f0c8962f4bfc694d883a6c6162dac512dce40d [formerly a1a3744bd736baaf6af702e1635089a29a87ac8a] [formerly bd72fb7c08e2efda8ab97fc7d8a57b0d50ef43d2 [formerly c1b05ff1b8a28f4067785b8b2d0e8e68b325d0db [formerly 4193da80fc]]]
Former-commit-id: 8686c6cfb26ca2542efe1aeca186093320c12228 [formerly 6ffcbe6472aa192fd8f59faace2c87fd08225b3d]
Former-commit-id: e726d4a1d3b075389f1a652198b45c2c7ce8ab5a
Former-commit-id: 526a97affb
2017-03-23 07:33:00 -06:00
Zack Scholl
62c9e6ee1b Simplify routes, handle error route
Former-commit-id: a2be41df36132865f1f5ddee954a2c36cd15946a [formerly 952889d5fec4f3207cfd3a071bcf7278afeb09a8] [formerly 21c56db9a4a329ff6e70573734bbe7a33ee8b0a0 [formerly 8f4f07ce580467b241015b8b6291d7bce91bf66e [formerly 9becf07745]]]
Former-commit-id: 1ad7a875ff4738230e09a4d46bf58c7d03c43a90 [formerly 5263a10dd6b8057bcfa7ac65509c7a87d5889175]
Former-commit-id: 57c32446567ef6b8d219893ed93c6499cf0b0da8
Former-commit-id: 95afa389bc
2017-03-23 07:32:40 -06:00
Zack Scholl
3a265a0979 Update GetMajorSnapshots API
Former-commit-id: eafad64cf835e69fa4994dd2173287eb68a49975 [formerly 7c91b825f33c0deea8a4b26419c3395fe1d9ff69] [formerly 4fdb4a30274e2028f13bf89b37b678a208118b58 [formerly 6e7d202e7b38b8a6d14f7824971b3acd28807906 [formerly 3a29cc5adf]]]
Former-commit-id: f131365c5c22dc159510a1c837dd26e8c8b9e06f [formerly 10b24d4adb33a566fb1e1e76d19d5af677d7ad38]
Former-commit-id: 1b0096a9d5b26ea2f6492394e7b7cbee944e1fa1
Former-commit-id: 3781c6457e
2017-03-23 07:22:14 -06:00
Zack Scholl
39ebd6c125 Get major versions instead of all
Former-commit-id: 5cdd8a3b23c05604fa82d5ba3319ec8caf402313 [formerly d0acfb7a8b73c1549cb8a3814583dbacd0833b27] [formerly 60213b204673cd65c02b6945df1883ca0f348aa7 [formerly c2c93edebbabde3fd2ec773cc4fdf6ad0fba868c [formerly 80610690ea]]]
Former-commit-id: f783c78d0353eec3003fbe3fab6854a4ef5e761a [formerly 69e182e4f7a83e76eaa52d3a494760e715fe4abc]
Former-commit-id: d1c6e0fcf474c14ab15a5148fdd4531462ce6702
Former-commit-id: 3886f3d00e
2017-03-23 07:17:44 -06:00
Zack Scholl
bd85204eff Bump version in Makefile
Former-commit-id: 456a4137adadb4044e720f04ac8ec6ab7b3d80d6 [formerly 12de613c2e4c46aca255f15f04e9b2819e51f2a0] [formerly 64bc5f9527ec3babaeade20a5601cf43a6b239a4 [formerly 3168d427d4b6f85a2caedae527a2f78a13988db3 [formerly c8e237c3c5]]]
Former-commit-id: 70bc9dc28a3c99506768567cb4f2eb1a9479c393 [formerly 687f78214d9de740f9e4492cd2d383bcbd0da2c4]
Former-commit-id: dad5e45520f127757318cc867f2013492aeb3501
Former-commit-id: 7b0bfea196
2017-03-22 21:43:47 -06:00
Zack Scholl
322294b2ac Normalize filenames when writing
Former-commit-id: 84c13ae6f2f058709c59c3bc3cd3bce41cfc580c [formerly d0200cc718af544ea6d809207ee496b9234d76cb] [formerly 31012f0ebf56f4e7fd142b658aaa32645f743a72 [formerly 7d92efd3cd8ce550304eda03518515f57b99a376 [formerly a3d1b822e4]]]
Former-commit-id: ca2c13ab6848c86deb1a7b432e564c21c29e1007 [formerly af1fd98d479fac25cef74f522d2467f9ee582132]
Former-commit-id: e47d01d1ef3f59d7b4acaef5d74965d4e2e40adb
Former-commit-id: 0136d5d640
2017-03-22 21:22:26 -06:00
Zack Scholl
1686409cf7 Update README
Former-commit-id: 63039d0d361bb3af1243938b34024e3c7fb62c79 [formerly b833b75dccaf7b5c43945dc0e46eb0dbf9b747c5] [formerly 873059f5bca74f3d134969ffd8dd1f1abc17d058 [formerly aea5747e7a2eff250dc8d565b12e4c82fbfc61d7 [formerly f2b317be8b]]]
Former-commit-id: aa7719891c68c0e3c9432ad2053d3e3154768899 [formerly 8e005b32f106cd9870e771591310c40d89be56eb]
Former-commit-id: 8d01eea63586af137fbef2013331de8a915ce261
Former-commit-id: 192cb9f0bc
2017-03-22 21:11:20 -06:00
Zack Scholl
0109a94cbb Add logo
Former-commit-id: afb41ca8dd353e4ed932a958b4f734c74dd5b927 [formerly 5c23e3e9a78afcb30517cf5cb18c0063fb343cd6] [formerly 5a098bcf34598737f50ad25676b8f3869cd0a27c [formerly 2c14b97b390ec8a77eaad49f237d4ba0f2b7d433 [formerly 25c01cf1f6]]]
Former-commit-id: 71a7802cffe269f17d79a761ef6ecd9af696bc16 [formerly 2becefe0ced4277410e8a39d093a140387f9319c]
Former-commit-id: 52505e15a9f264a75166d9a66f8a5445bde7dd7e
Former-commit-id: 3221ee0cae
2017-03-22 21:09:32 -06:00
Zack Scholl
17bf3c3a4c Updated README
Former-commit-id: 4550d3d46e3fd08d1f62ff42b3c74a029e3ba7e3 [formerly 57b84131f2a4d6f2dbdd878f5af4da442e4995be] [formerly e893072497705cc6311192fa763fbc5353292bb3 [formerly 0b785d77b5362bcec7fba5281447850e5484b09e [formerly 405bc46958]]]
Former-commit-id: 83fa42e2070637493214e282c6de4cda688b850f [formerly a0149f30c14d82e1123f23587241a3e34843822a]
Former-commit-id: 373c765a77beb16837994661ecd613ecdc84190e
Former-commit-id: 9e7641df3c
2017-03-22 21:06:14 -06:00
Zack Scholl
cbede33317 Merge branch 'v2-master'
Former-commit-id: 18ae04b11d6cbb3927d58732515fc1db6959708f [formerly 7d140d3d7ca7225021516cd778f4b00a2f16eae6] [formerly fd6b7171287b7a55d486cf6ee4b014736c4cdaf0 [formerly 646c41cc7eb60cf2139b7e29562b20f951168bb4 [formerly 1c7524cc45]]]
Former-commit-id: 8146d635fb76f7d156a198877c4a3cd2f76334cf [formerly e541056fe056ca9cbf07fb2008ced91524f0ef6d]
Former-commit-id: b4f71c483d27fad2e8869e77149ca8b682baa085
Former-commit-id: 5540875d62
2017-03-22 20:56:47 -06:00
Zack Scholl
dbeea86d6a Start again from scratch
Former-commit-id: 02983a8b61d9fd5ff30de4967c9741ad2478a023 [formerly d55b11ab31697ca40d2d0d80f7261df3c76fc812] [formerly 5d236ead7feece2b73db56d195bdcbcc7623b081 [formerly 4433a9a3b172c82a283df36a601e6c5e581c8ff6 [formerly 52abf02847]]]
Former-commit-id: 1176ee8d4cbb8d093ba3b08046c7e93f6c0d835b [formerly f7a072e5000efd8e1b58cfb04336ae8687519d3a]
Former-commit-id: 8441f7eff180268e16a037abd2de7e07c1104926
Former-commit-id: 2640e02b72
2017-03-22 20:49:55 -06:00
Zack Scholl
accc3b5f62 Version bump
Former-commit-id: ae819a9a760577d7fe156251b19ef671373b9d6b [formerly 8147fb5032726aea60e5c6ea526fa0cd574acd4c] [formerly 24ed3c1b03a257c9340165acc7bbd2f0059d5b25 [formerly d1a85cfb9914f63ce12ab588d3ea1a26b74fdd72 [formerly c0d87c752c]]]
Former-commit-id: 9d45d4e7c0836884455676d485d4edd65903133c [formerly ef1d4c3c2115a88766b7c2f4aeb580126272c1e3]
Former-commit-id: ffc2a7c8c1148e78896d893a7697ea292f7176ac
Former-commit-id: 0ebb3ace3b
2017-03-22 20:41:38 -06:00
Zack Scholl
12d447ed74 Add makefile
Former-commit-id: dc66526762f6a8392e48838cad1460411c3fa643 [formerly 6fa00115b96b91eafdd2f7884c6bacac6b0368e4] [formerly f0e9be4c45c1367f6229e86653357dc5a47739f8 [formerly be99f99ab97119871abfbe954e612f534ad35b27 [formerly 37bf947b79]]]
Former-commit-id: 612c65f57f037d23df33578b7ff392d800183647 [formerly e26be98ff430eaa0af46147f554d2139884fa959]
Former-commit-id: 5320bdd630a3e5617f9c8e9db57263571599370c
Former-commit-id: 4f95637831
2017-03-22 17:13:19 -06:00
Zack Scholl
328e177b66 Changed app info
Former-commit-id: 468ca3f8f493737ea4f465b093a962f11ac374e3 [formerly db359ec78a0866482d3a98e476efd11e0b7a8422] [formerly 8186e0d7d6821753ae8f1c262020cd585d80e1e0 [formerly 11e4bb08b4fc047ad7a54f3e4f244eedf11c322e [formerly 0a3184b190]]]
Former-commit-id: d07891eab39c576e217b40d1207f6b9744be90e9 [formerly cefd9ba438e2643405475933f8dbe230fb7c40bd]
Former-commit-id: e117bd79abdfebe744ac26e6e12213770eaabb03
Former-commit-id: fd4b52cc05
2017-03-22 15:35:02 -06:00
Zack Scholl
38e0943df3 Bundle highlight assets
Former-commit-id: 4fc809225b291daaf71a41995c2b90f69192eb0e [formerly 6dd44249c158fce63dd521132b68ef2cd815c18f] [formerly 15f3d5fd2ed3887836ed3b561ee0919d3f032ee0 [formerly 94f0785d6f8eb46c486e5443363b3fed3da09d21 [formerly 0a14010081]]]
Former-commit-id: 14495f0654f9805c4fa83e7b721a08f820691a9e [formerly 84f8793320fbb21899b5812e4ef8d034515657f6]
Former-commit-id: 46c14a9f9c38194689716813bc3ae0c6a942a9f6
Former-commit-id: 1de1ceb05d
2017-03-22 14:29:53 -06:00
Zack Scholl
c66c312fdb Remove serve subcommand
Former-commit-id: d043e461b53d8525268889380e6d307bf1e91ffe [formerly 7148459fedf6847393039077ed1b61c3ad65392f] [formerly f32e700c6e421f094b2c7047fc277050e66d3c70 [formerly 1f6e70028a87b587f42ddd6dd3a240d60cd1e46c [formerly 375979fc19]]]
Former-commit-id: 690c8ae68f8f3f18dd5726d9f30557b251238c71 [formerly 07fe4a8721cd13e277cbb25cd743b1f8ff4c0e82]
Former-commit-id: 2d0761bcb0e6f3b856fb962155b8292e92a231da
Former-commit-id: 15debdf177
2017-03-22 14:14:47 -06:00
Zack Scholl
c3ba228288 Prevent locking encrypted
Former-commit-id: e986e1b8094f80c479c7f97bdefdcc216ba17ef4 [formerly e4eb6b192ca3af6951acb574d56f2ddd003d6d16] [formerly 142979758915d24210fbc4085270e49611047d59 [formerly fe0c2a45dc56e5b55f5c83f6586f4470a2fa6b1f [formerly 59af266a12]]]
Former-commit-id: f218c28663c6d35b1f9fddb4e618e9cf9f3d12d2 [formerly f665f59e08d5b84845baab64c27fd6a45d3d1dd9]
Former-commit-id: 9877bf4627091886f47a176b88373375e2dbe942
Former-commit-id: 1998538fe1
2017-03-22 13:42:51 -06:00
Zack Scholl
3a463449fb Prevent encrypting locked pages
Former-commit-id: 0154b3bbfe05a6a1520971a9e7019f50d2099005 [formerly 51ddb6c687c5e659293898765b6b1d3e023bcae4] [formerly d1032b8324e9d7433a4ee806c49cac43d32012a3 [formerly 66faab1fbd0b245475d19eb40bcf443d20b8ae73 [formerly 41cdfda98a]]]
Former-commit-id: 21cc2358168803e11bd33ea20b60d7c86907cd17 [formerly 054bae1e71259eaf0a9866cbf33cc5214120c1d1]
Former-commit-id: 2d7d6714588efd33381b80c3f4f2bf2e77b306c7
Former-commit-id: 590538d8ef
2017-03-22 13:37:59 -06:00
Zack Scholl
bca008f315 No case
Former-commit-id: eb4cb02a7cde861255c9b425619ea8bf3d2f90a1 [formerly d603bd8d9e765a82d954b48586357b7593bd873b] [formerly 7ed56a670228947c7b180a7aeba1353a5f74b7c8 [formerly 5c9b621bb564be87a11d35a268d152476e4048d8 [formerly b6a9c44bce]]]
Former-commit-id: ffa105847b5eeb409bd471f6fc2065d3ca0c14e4 [formerly e6c16a7b3c8b4d8371fc09f7c374d90af16b7bc1]
Former-commit-id: 6b70c68d2f8ac7b11dd0631b1133f3c8b16841e9
Former-commit-id: fe8022d128
2017-03-22 13:31:33 -06:00
Zack Scholl
6347038563 Cowyo works as a single binary now
Former-commit-id: e3c11510f61564a5f8c6a53d9d9bd768c5fa0f17 [formerly 8c3e635c3cfd2f00acbe4fe4c89b5c9921c3a4ba] [formerly ed287ec86da7ff501a972dcb68f9a0045dcb3798 [formerly b2ea068629093bbeebca0be37f463701117a5d1b [formerly 3f3ee6c9fa]]]
Former-commit-id: 049fac69946d4e4a7866d28afa0999bac8457840 [formerly 4451a7e8d278d70f1547f4ade77df8f9ef76b0d3]
Former-commit-id: 3a8a4d9ab222d9d23ebf6981e0553a28a123a08f
Former-commit-id: 70616e9d31
2017-03-22 13:19:00 -06:00
Zack Scholl
a203fcc32f Updated bindata
Former-commit-id: 39ad7cb6efbc76619f97934e37c5d95637d23e7f [formerly 38969bebc88d6fbf80a34b6b1c9d9faac6da84f8] [formerly f02d3546dea518e0c0a840dec8e57c4d4ed65dbd [formerly c124a03557453d9ca36c5298373a3267023eeba6 [formerly 2c687f79ca]]]
Former-commit-id: a6e13ec4ec75cf12468677bafbf850e70c67d335 [formerly fdc16f327c1cdf53e66c2e4773c0db92123c785c]
Former-commit-id: cae020f1e5645f62fb59bcc90b24f0712e72edd6
Former-commit-id: 1a670ce298
2017-03-22 13:04:38 -06:00
Zack Scholl
538fa425b0 Delete bloat
Former-commit-id: 366d6d4299f6b873937fbf4eb539e1837f053b3d [formerly aed4d69ccca3351ff0a6ad7e1a8459b2f9644041] [formerly 7a2d10add93e5f62b7cab38f4f6af8c2b30465ff [formerly 09e03f0ade3c55240981e9627669baecd82a3d1a [formerly a6da2b4a4d]]]
Former-commit-id: 8cc271f57f0b37196c6542bd3f4a6efe3170c2ba [formerly e1e606dcb6126e9048c2c5fa5185c65b8ea755a8]
Former-commit-id: 1f9fc804c56de040f946cd7506825e191e8aeba1
Former-commit-id: 5a3007d014
2017-03-22 13:03:00 -06:00
Zack Scholl
b4fecbfaa2 Add favicon, modify UI
Former-commit-id: 4dfac83c7384a0b61ee78c78f6e0263e5b2876b2 [formerly 9225888ef11e9920685918183afbcf76695a38aa] [formerly 62debde9de7916cc55f4f1aec0b690ecc6ddd70d [formerly 3192c8484b]]
Former-commit-id: fdd39f686cf3609ffe182d7d47d27033fbe13f85 [formerly fbf52ab1f9c8c215c9b7148c001ea0db02fc0cd3]
Former-commit-id: 4ee6195397465f0ad7b45609afdb9105719e1aa5
2017-03-22 11:34:06 -06:00
Zack Scholl
fb5ee8d09e List works
Former-commit-id: d123891bd72ab552a24bbccfd614270de340dac5 [formerly ce7c2d1b9dbdcaf5ef2d665e647833f065c18246] [formerly fc2e4872180a9f264e57e348e2ec4e5896781e74 [formerly 67d5d2f882]]
Former-commit-id: 1d9e245d81cab6cfe197bc52b516154c7429421b [formerly 1f22520a0e2509d0a509a592d1fed50002eccebc]
Former-commit-id: b60b97411ea8c9b54b26b8058aeda0f1f0466b64
2017-03-22 11:19:39 -06:00
Zack Scholl
522c4c6283 Allow [[link]] for internal links
Former-commit-id: aefb0d00cd7f493ec8706d890eec1eb1510b7092 [formerly 400d9b4158b49fdd6b6910f772f02fc4f82fd2f6] [formerly 99230db788cdfac4fe4fa7e1266fbe77a54f60bf [formerly 420f56843f]]
Former-commit-id: d780226ab14e3b022d497021ddc66c7d2e1be423 [formerly 6c38cf48fc1889ddef0dd1f9b7c36f0ed6525907]
Former-commit-id: 82e848729b0ea8b8deb5a310847490f5f22ad114
2017-03-22 10:59:48 -06:00
Zack Scholl
f42c0f411b Fit to mobile
Former-commit-id: 25818ef30bed76dc22b9e4ae477054f92c3d73ce [formerly e29a9f2666465ed0ba660e68a1aa52b74fcd0a19] [formerly 15a78d243212e9b4339c3d7074551b5990b761a7 [formerly cf0a8a7f30]]
Former-commit-id: 2952e3a0822a96c7dae375860c5adf11790d4ae6 [formerly 38224960971c35a288044c3fcd3478d6591306f7]
Former-commit-id: ad16df82d5572dbdabe0c95d6af2dc17e703f040
2017-03-22 08:50:45 -06:00
Zack Scholl
59284f2601 Fit to mobile
Former-commit-id: 01f88e62753d2a19d6a2c03fba76c26b473febfe [formerly 1810a3f470c9a4c1f1acb34dbef4092eebe15a54] [formerly 44cf7ff6313f92bc328107415b7d00ac9254e811 [formerly 515a27a9c6]]
Former-commit-id: b8c6bd7f1d3e1fcfd45bb1e2bec72be73f8ca3e2 [formerly 078e1d9356457cb6a057f73e09cb8c6e568fb808]
Former-commit-id: 6b65bc325a1cefa633c663acfc2deb90e9e3e93b
2017-03-22 08:49:51 -06:00
Zack Scholl
d46fe9cb6a New: Flag for port
Former-commit-id: 2d0a3148379cc650552887358558b05b8efd2545 [formerly b5bbb0e6df504588c406ffea0b7f68ff18b34ff9] [formerly 1cded1417754d12f2ea94246e1e22bc8a7c8631a [formerly d3e04ef2ed]]
Former-commit-id: be9e181ffd0c1d00666928618237f709760f8f72 [formerly f6f73dc9e043e6b96b190cbfe9011c3e1c7b83a3]
Former-commit-id: 1a016d36925db45aac5786e552ae7c9f7ccde722
2017-03-22 08:26:34 -06:00
Zack Scholl
64b3fdb0d4 Add raw
Former-commit-id: 4789ed45d9449fea9db6c01c171736d1c20044a2 [formerly 62d03a41e8978454b14e4ff156e5e4cbc6de6149] [formerly f68618c11a4e02f37b14215352a305d5ee9fb65c [formerly 29460a6c26]]
Former-commit-id: d108a21904309b99e95b1d485f48234f78865d0f [formerly b22903c4191a97d021858fb6bb26ca57b09bd571]
Former-commit-id: 4ad94bd008e7cc3532a68f472cfac29e133e7572
2017-03-22 08:21:49 -06:00
Zack Scholl
6031590d87 Show version dates
Former-commit-id: 222b35b37979d677f127aeb9734673f1cac8bfcd [formerly 40e7b691637165d97c9333f8f2f046f38682da7d] [formerly c88082dc6c55be884fb7c6a1c8129e185458cb0c [formerly dfa18bca5a]]
Former-commit-id: 89f8aa21a6e6d1a3ea492104917e96c9d149281b [formerly b51759ae32f00d306eb2b6b17eeeaf2f91a9de3b]
Former-commit-id: 4a57c6e7a79986b9bf55e8a2e2b2ac446a1f8b45
2017-03-22 08:16:16 -06:00
Zack Scholl
4d3385b3f1 Add highlighting
Former-commit-id: 7acb465444aca4f69527137829d652faf6abe671 [formerly ae84e91bd4f267daac6bb60d597a63bdaaedd586] [formerly 7c7022dc26ed2b999f217cb797c71a822e578cf5 [formerly 59c4a95e4d]]
Former-commit-id: 20d0ec0cb14f3b5fc873fc135e8584b5c2c5dbac [formerly 87039afd37157771fa11e4d249c52ce74fb3cfd8]
Former-commit-id: d28f7d56aca2547cf61b3cb51a5422d9555d1c07
2017-03-22 08:09:19 -06:00
Zack Scholl
3ba5537c1e Make sure is not encrypted before performing self destruct
Former-commit-id: e0e540845dd56b55a7f88cddab6f75293318784c [formerly 6304d826ec6c5d7ce7f5c1dc6e821f0ba3488c30] [formerly 50ff4ab9306b5a0f2371699ece8b0baa6e49672f [formerly ff49e21ecc]]
Former-commit-id: 7212147366f26318f0ce4363edbc977fd688da8e [formerly c8604e60d2eae61659622a1b0424cbe6e74bb927]
Former-commit-id: 3fb6fd7b4e514fcd5e9a9867efd3dcd6819cb893
2017-03-22 08:09:09 -06:00
Zack Scholl
8a7803250f Reorganize and add migration
Former-commit-id: 807d525f11b5306c2a4a2e7097a8d92d026fc9df [formerly 9c40fcf478e90dd062c9a173b4d563a4ba15b8bc] [formerly 0c56b23e0782fe14717c243d149983185fe9ad2d [formerly 7f4acdda9a]]
Former-commit-id: 71ae3d9712ec1b7203e6483ad586aeef319fba08 [formerly e633558e9a7491044e324ceeb2cb699b2130f1c8]
Former-commit-id: 8e483f94bf5c75d62d00219085d1e68c4e5683c7
2017-03-21 21:51:52 -06:00
Zack Scholl
97edbd73a8 Fix: Bug with decryption
Former-commit-id: 7fcbc62027b94ec7203cd3bc176db254a5ea4ed6 [formerly 141ae1267e555901984e9b170eafe924189f1d1e] [formerly 7361c4f5dd2331ec3dd9f86326ae43daa0f66afc [formerly 6012c92c96]]
Former-commit-id: 6d89689ca47ca7a1e7b3d1a3dbbee40c055c527d [formerly 6a9175f0309bf4727ed1f5bab0e428fa6652894c]
Former-commit-id: 4210e389bed5bdc229818dce1f1af023c7803ad7
2017-03-21 21:10:41 -06:00
Zack Scholl
79422d1fb3 Add static files, encryption
Former-commit-id: 80a53c01301cf96c28a22e1018efac2ceca7a381 [formerly af1c7ae599f506fcceebc85f87cc345e76329da3] [formerly 1bf3deab458c0e1f376da2296b03ac4fe8fcf8e9 [formerly 5a923ae11a]]
Former-commit-id: e826a5ffd431d22f8c37ac51f1be2193de1c270f [formerly 1deadc5e9c4fc7ff98b48bba18be31b773b8bde7]
Former-commit-id: 7fc964bb4c9d007010b1bb52665818212c0a2a8c
2017-03-21 21:06:35 -06:00
Zack Scholl
0be3c91823 Add static and template
Former-commit-id: 46c13d0c36c98e1c4cd7a0548c561dc70c0952ea [formerly 4a56a0bbffd2bb5b437c2c4e7497da0cd42dc306] [formerly c6e51179179dad88c1014b07d13e83f759ad6b6c [formerly 21cd7d092c]]
Former-commit-id: e17744bea8af56671b35709b358bea7d4c9aaff5 [formerly 40b9fe4727f897ee471af9ffce8cd7780f22a612]
Former-commit-id: 0d2322553b3a177f74d4110044e1f801391daaeb
2017-03-21 20:46:24 -06:00
Zack Scholl
04f1cc87e9 Things are working 2017-03-21 20:46:05 -06:00
Zack
706e4e6fdf Initial commit 2017-03-21 17:52:25 -06:00
Zack Scholl
5e1ac31d13 Updated packages, new fixed Bolt version
Former-commit-id: da2423082a10392910bbafcd7683c1c93d7aa556 [formerly 708ea8f31cde627a1b64a5a49c80b5faf571740e] [formerly f127dbccd73fbba2526cb7763c1fafccc926596c [formerly c8f595b2715bfd2ab8a3ad046d05f79e7b632694 [formerly 4558c48cdf]]]
Former-commit-id: c9d4a6c430c6c5f731e755f270bdf66f89eaa646 [formerly 291db3593d93e63d7234d85f49773ec34c867f1e]
Former-commit-id: d1a21b94fe3760e037c7c02a56ca933123a8cae7
Former-commit-id: 9ad5907b48
2017-02-02 14:32:55 -05:00
Zack Scholl
7d878a18b4 Updated readme to use Godep building
Former-commit-id: 592808cb7fd58931cf9bfd1cac398896490cec5a [formerly 53e317b59452a97d355244515316b4ccb3fc9ae4] [formerly 61b7bb6ec4c0186bd49b0117cf5b6f5881bb28c2 [formerly 8c02a9041f0311c4bf6c577756be07ccf8c3e101 [formerly fe7e8b345d]]]
Former-commit-id: 0782b12f11ee3aedc6a26fbd7e15740fa973dd90 [formerly 5707c85187052f77e396c5d5ad289f05ce11c090]
Former-commit-id: ac9d937aa04dd29ad21e93324036a41e046aafb9
Former-commit-id: 25e76fb242
2017-01-31 08:07:20 -05:00
Zack Scholl
95401434a2 Now vendored, with Bolt v1.2.0
Former-commit-id: 900e1a398fd82aa1cea4f319e89b8088dd81cf6c [formerly f172c22b12c49c0291e0d986dc4af94fcc91d192] [formerly 43c5da81442a5f0ca79a6eabb1dfdfbfb3f22680 [formerly 7a39ef2b52fc10e7c980d377dbd52f5340e4e53a [formerly 2694d0b183]]]
Former-commit-id: fa39d6a984adc4ca8f8c82c5df145c336885a53f [formerly 94543f8081bc18b1a39daf8500cfa7e0b1ba7393]
Former-commit-id: 809aae62e28a3f99a01854f71fcd5a85f89d2972
Former-commit-id: e8a4d30139
2017-01-31 08:05:27 -05:00
Zack
074b776984 Create keystore.js
Former-commit-id: 6e57c4fa824b56c208f0910bd462b7145b3a201d [formerly 6750759dea61eb28bc5cca842d5fb3659fd9795b] [formerly 69062d21361147bfc05d329f26e4ba5167a59565 [formerly 83dfac9923a9ce6869b5b62f34d5282927480cdf [formerly 169e4b41c9]]]
Former-commit-id: fed116cda64134c4b76f2665f283d08176697b1c [formerly 8654e5a8f910d7f5135572eab42c48511e62f38f]
Former-commit-id: 136a617fe456bdea15c6d4173f6abd9bc4bba819
Former-commit-id: ad4ed37898
2016-12-13 06:59:04 -08:00
Zack
4ac089780e Create keystore.go
Former-commit-id: bc371d744dca228255db92d05fa764fbaa157eb2 [formerly ed45c432823c880548e99b37f400a103dc4f91e8] [formerly dcb0bd1f93260429a86ed8abf1ef8f835b0680e7 [formerly 691e6cc496fe41a0699bba697ed6bcf617463a9b [formerly 12ca452230]]]
Former-commit-id: 00e79655127e4308e6f7665486f95a3c417e0025 [formerly 40bfa8fd615cf56c8890120d1f5bc92b40d2e6c9]
Former-commit-id: c47dfb60b3cffe45224be010e30328eb2330277b
Former-commit-id: 239341ee87
2016-12-13 06:58:42 -08:00
Zack Scholl
d0791f3b2e Remove access control
Former-commit-id: bec2c180ee17800de01a6a082d74fd9968e2b55c [formerly bb4d29806f89b26fb1413ec2e68f48f8cb2983f7] [formerly 86c852521132a70a2e83a1e1622b7844bdba20d6 [formerly 157ebfda79bebfa11483d85b9de18ab6d67505c9 [formerly 1432660990]]]
Former-commit-id: 3e2642f169712c3c4e8d18ade497bf7c2a107ea9 [formerly 929f8679ffddd6bb609b11bec10f3dd024ea6349]
Former-commit-id: 84a0347714c097f0b5ecba0541259be456183678
Former-commit-id: a7066dd68c
2016-12-12 19:58:18 -08:00
Zack Scholl
18d002abf4 Fix check origin
Former-commit-id: cceac08c2cb9abce1811910b1477880d10fc5f5f [formerly b221fde742c33a95464a32cd41e3b71449d0c065] [formerly eb4538558611ffa502486fd47cc71ddae32eae7a [formerly fea438080efb3ec93c3c3dd0b9511c6fa10b50b0 [formerly 483d698f3a]]]
Former-commit-id: a2c0d11edc96c26595566c80991a7dfd4e67bfc8 [formerly 3f8e10a9d26fad1a127e59ca94bcc3fb35aad5a0]
Former-commit-id: 8d19a2d69896d55a31d57563c9a323feb34f70ab
Former-commit-id: 7d5af5dd2e
2016-12-12 19:57:33 -08:00
Zack Scholl
4276e4bec3 Added access control header *
Former-commit-id: a426e27bcbc533a15c2a2b0459ed799fadc2a787 [formerly 800b5f52810f6ef875f96c754cb42185a50badc0] [formerly 01b72cb3f3ae23f101d6a6d8273b9b426ce0b7da [formerly 2cf48e3280c86a89c8a9d95b68ee8c727772bf23 [formerly 8c406982f3]]]
Former-commit-id: 95422abadd277c5c5b06257ff15ea35b1fc46740 [formerly 3a2861c4f90c1aa0794840f44af0a1c1a6ecfb49]
Former-commit-id: 7cce559cf40c44ea3695eba5ec9f8c8a2ef4376a
Former-commit-id: bbb33c1f72
2016-12-12 19:55:36 -08:00
Zack Scholl
eca269696f Version bump
Former-commit-id: 1c7bddbee39bcb5b3f44434bcc42b8ac7d1e2eb3 [formerly ec9df5ad73eb3385eb6b0d95b3146e3cc700790c] [formerly fb6f9fbd11856ab8621aa0f8371e3bdf4d63b08e [formerly 0d491e56bd7048276d6fe42e31552b8d20748fd2 [formerly bacee6570d]]]
Former-commit-id: 277132a108412adedd039bbd966e0473655148ff [formerly 5d0a4a9ae1818b9c3e63c4e12b4ba4d7bad88636]
Former-commit-id: c43d882672237b733b47e08f418d33d95c887763
Former-commit-id: 46b3b7b5a0
2016-09-10 07:59:02 -04:00
Zack Scholl
a3a881b65e Passwords stored as hashes instead of on the server
Former-commit-id: 8e42d0aad6b21a4378f2881c80a25786aaec3fca [formerly 1a169ed9ce9372babf129ee16e782cd5a7ebf960] [formerly 23767ee90357ba8a46cf2bcb24f43dabc951dbc7 [formerly 58d10f965428d7898ecae9af9788f34a1ddcab88 [formerly ccfe66e973]]]
Former-commit-id: 2f1dcbda58dec1ff16dd783711ea8401932d1e37 [formerly 6420a2b52256cfec0edf10b478f99483b49b76bb]
Former-commit-id: 7b8c830459c262958272bcb5c9661f7d5a57a86d
Former-commit-id: c7e37649d2
2016-09-10 07:45:36 -04:00
Zack Scholl
09b09f3dbc Fix #57
Former-commit-id: 8a967397a0af8dedcc36f56bb9adca904f77705f [formerly 84097c863b5c0b6036fb4d4757b0b56bdbd41192] [formerly adc17b2404fa552efaebf485f6f30a412ae90f9a [formerly a43305f34b1c5a4a1f29b198604d2dbe1b127bab [formerly 87e5439854]]]
Former-commit-id: bd94b8dacfc04010499567541d5313dae6dc89fa [formerly 874671aa7538960f6ac236a11f4fdff8d2d3a994]
Former-commit-id: 517229eb395c1cda4d125ad2014240112d9fff64
Former-commit-id: 0a4549d3a7
2016-09-10 07:06:20 -04:00
Zack
dba80e26c7 Update README.md
Former-commit-id: 5b8f052dfa442d6c3324bf2bd8e6449cc94c72ac [formerly e6e9c01f402b0793cb57196cd5476a5c7cc195d8] [formerly 0e8afff061cb9d24ac9e06ab221d7ccee4425b60 [formerly debc9dc6e445d426e00cc83a5032ba5eb7852d1e [formerly 4d089ec756]]]
Former-commit-id: 6188a590130df95a855d4e74b47321d239687777 [formerly 6ec294629d6aadc188afd6c305c869e7d54c5896]
Former-commit-id: a99ce73839437542d757a8dbc49d1b95867cf141
Former-commit-id: a25a71e7a2
2016-09-09 16:04:17 -04:00
Zack
7d6cb4910c Update README.md
Former-commit-id: 70eb72785e899e76d32d6733e13d30b003bc5b17 [formerly 68465e3068258198cca447a9fc9c67944ec46bed] [formerly ebbf68ccfe679552a22832fb934e6c0d9a7a8199 [formerly 10cd4b19e27044d732583e60286849ef287d5a57 [formerly 634a321c86]]]
Former-commit-id: fdea4332e61fb3f8839c1a6e9ce5ad8378df10bc [formerly 2e072f20a513f8fadbd273cb74e6541a4962018d]
Former-commit-id: 768fc47c985046b25c750a84da7b8634a995a795
Former-commit-id: a9e89d5230
2016-09-09 15:56:28 -04:00
290 changed files with 19407 additions and 24306 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,3 @@
# These are supported funding model platforms
github: schollz

5
.gitignore vendored
View File

@ -22,3 +22,8 @@ _testmain.go
*.exe
*.test
*.prof
data/*
cowyo
dist

8
.travis.yml Normal file
View File

@ -0,0 +1,8 @@
language: go
go:
- tip
before_install:
- go get -u github.com/schollz/cowyo
- go get -u github.com/jteeuwen/go-bindata/...

View File

@ -1,32 +1,13 @@
# sudo docker build -t cowyo .
# sudo docker run -it -p 8003:8003 -v `pwd`/data:/data cowyo bash
FROM ubuntu:16.04
FROM golang:1.12-alpine as builder
RUN apk add --no-cache git make
RUN go get -v github.com/jteeuwen/go-bindata/go-bindata
WORKDIR /go/cowyo
COPY . .
RUN make build
# Get basics
RUN apt-get update
RUN apt-get -y upgrade
RUN apt-get install -y golang git wget curl vim
RUN mkdir /usr/local/work
ENV GOPATH /usr/local/work
# Install cowyo
WORKDIR "/root"
RUN go get github.com/schollz/cowyo
RUN git clone https://github.com/schollz/cowyo.git
WORKDIR "/root/cowyo"
RUN git pull
RUN go build
# Setup supervisor
RUN apt-get update && apt-get install -y supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
# Add Tini
ENV TINI_VERSION v0.9.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]
# Startup
CMD ["/usr/bin/supervisord"]
FROM alpine:latest
VOLUME /data
EXPOSE 8050
COPY --from=builder /go/cowyo/cowyo /cowyo
ENTRYPOINT ["/cowyo"]
CMD ["--data","/data","--allow-file-uploads","--max-upload-mb","10","--host","0.0.0.0"]

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
MIT License
Copyright (c) 2016 Zack
Copyright (c) 2017 Zack
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

101
Makefile
View File

@ -1,47 +1,54 @@
SOURCEDIR=.
SOURCES := $(shell find $(SOURCEDIR) -name '*.go')
BINARY=cowyo
VERSION=1.1.0
BUILD_TIME=`date +%FT%T%z`
BUILD=`git rev-parse HEAD`
LDFLAGS=-ldflags "-X main.VersionNum=${VERSION} -X main.Build=${BUILD} -X main.BuildTime=${BUILD_TIME}"
.DEFAULT_GOAL: $(BINARY)
$(BINARY): $(SOURCES)
go get github.com/boltdb/bolt
go get github.com/gin-gonic/contrib/sessions
go get github.com/gin-gonic/gin
go get github.com/gorilla/websocket
go get github.com/microcosm-cc/bluemonday
go get github.com/russross/blackfriday
go get github.com/sergi/go-diff/diffmatchpatch
go get github.com/jcelliott/lumber
go build ${LDFLAGS} -o ${BINARY} ${SOURCES}
.PHONY: clean
clean:
if [ -f ${BINARY} ] ; then rm ${BINARY} ; fi
rm -rf binaries
.PHONY: binaries
binaries:
rm -rf binaries
rm -f cowyo
mkdir binaries
env GOOS=linux GOARCH=amd64 go build ${LDFLAGS} -o ${BINARY} ${SOURCES}
zip -9 -r cowyo-linux-64bit.zip cowyo static/* templates/*
rm -f cowyo
env GOOS=windows GOARCH=amd64 go build ${LDFLAGS} -o ${BINARY}.exe ${SOURCES}
zip -9 -r cowyo-windows-64bit.zip cowyo.exe static/* templates/*
rm -f cowyo.exe
env GOOS=linux GOARCH=arm go build ${LDFLAGS} -o ${BINARY} ${SOURCES}
zip -9 -r cowyo-raspberrypi.zip cowyo static/* templates/*
rm -f cowyo
env GOOS=darwin GOARCH=amd64 go build ${LDFLAGS} -o ${BINARY} ${SOURCES}
zip -9 -r cowyo-macosx-64bit.zip cowyo static/* templates/*
rm -f cowyo
mv *.zip binaries/
# Make a release with
# make -j4 release
VERSION=$(shell git describe)
LDFLAGS=-ldflags "-X main.version=${VERSION}"
.PHONY: build
build: server/bindata.go
go build ${LDFLAGS}
STATICFILES := $(wildcard static/*)
TEMPLATES := $(wildcard templates/*)
server/bindata.go: $(STATICFILES) $(TEMPLATES)
go-bindata -pkg server -tags '!debug' -o server/bindata.go static/... templates/...
go fmt
server/bindata-debug.go: $(STATICFILES) $(TEMPLATES)
go-bindata -pkg server -tags 'debug' -o server/bindata-debug.go -debug static/... templates/...
go fmt
.PHONY: devel
devel: server/bindata-debug.go
go build -tags debug
.PHONY: quick
quick: server/bindata.go
go build
.PHONY: linuxarm
linuxarm: server/bindata.go
env GOOS=linux GOARCH=arm go build ${LDFLAGS} -o dist/cowyo_linux_arm
#cd dist && upx --brute cowyo_linux_arm
.PHONY: linux32
linux32: server/bindata.go
env GOOS=linux GOARCH=386 go build ${LDFLAGS} -o dist/cowyo_linux_32bit
#cd dist && upx --brute cowyo_linux_32bit
.PHONY: linux64
linux64: server/bindata.go
env GOOS=linux GOARCH=amd64 go build ${LDFLAGS} -o dist/cowyo_linux_amd64
.PHONY: windows
windows: server/bindata.go
env GOOS=windows GOARCH=amd64 go build ${LDFLAGS} -o dist/cowyo_windows_amd64.exe
#cd dist && upx --brute cowyo_windows_amd64.exe
.PHONY: osx
osx: server/bindata.go
env GOOS=darwin GOARCH=amd64 go build ${LDFLAGS} -o dist/cowyo_osx_amd64
#cd dist && upx --brute cowyo_osx_amd64
.PHONY: release
release: osx windows linux64 linux32 linuxarm

214
README.md
View File

@ -1,122 +1,140 @@
![Logo](/static/img/cowyo.png)
# [cowyo.com](http://cowyo.com/)
[![Version 1.1.0](https://img.shields.io/badge/version-1.1.0-brightgreen.svg)]() [![Go Report Card](https://goreportcard.com/badge/github.com/schollz/cowyo)](https://goreportcard.com/report/github.com/schollz/cowyo) [![Join the chat at https://gitter.im/schollz/cowyo](https://badges.gitter.im/schollz/cowyo.svg)](https://gitter.im/schollz/cowyo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
This is a self-contained notepad webserver that makes sharing easy and _fast_. The most important feature here is _simplicity_. There are many other features as well including versioning, page locking, self-destructing messages, encryption, math support, syntax highlighting, command line support, content-delivery, and listifying. Read on to learn more about the features.
# Features
**Simplicity**. The philosophy here is to _just type_. To jot a note, simply load the page at [`/`](http://cowyo.com/) and just start typing. No need to press edit, the browser will already be focused on the text. No need to press save - it will automatically save when you stop writing. The URL at [`/`](http://cowyo.com/) will redirect to an easy-to-remember name that you can use to reload the page at anytime, anywhere. But, you can also use any URL you want, e.g. [`/AnythingYouWant`](http://cowyo.com/AnythingYouWant). All pages can be rendered into HTML by adding `/view`. For example, the page [`/AnythingYouWant`](http://cowyo.com/AnythingYouWant) is rendered at [`/AnythingYouWant/view`](http://cowyo.com/AnythingYouWant/view). You can write in HTML or [Markdown](https://daringfireball.net/projects/markdown/) for page rendering. To quickly link to `/view` pages, just use `[[AnythingYouWant]]`.
![Simply type to edit.](https://raw.githubusercontent.com/schollz/cowyo/master/static/img/help1.gif)
<p align="center">
<img
src="/static/img/logo.png"
width="260" height="80" border="0" alt="linkcrawler">
<br>
<a href="https://travis-ci.org/schollz/cowyo"><img
src="https://img.shields.io/travis/schollz/cowyo.svg?style=flat-square"
alt="Build Status"></a> <a
href="https://github.com/schollz/cowyo/releases/latest"><img
src="https://img.shields.io/badge/version-2.11.0-brightgreen.svg?style=flat-square"
alt="Version"></a> </p>
**Listifying**. If you are writing a list and you want to tick off things really easily, just add `/list`. For example, after editing [`/grocery`](http://cowyo.com/grocery), goto [`/grocery/list`](http://cowyo.com/grocery/list). In this page, whatever you click on will be struck through and moved to the end. This is helpful if you write a grocery list and then want to easily delete things from it.
<p align="center">A feature-rich wiki for minimalists</a></p>
![Lists are easy to make.](https://raw.githubusercontent.com/schollz/cowyo/master/static/img/help2.gif)
*cowyo* is a self-contained wiki server that makes jotting notes easy and _fast_. The most important feature here is _simplicity_. Other features include versioning, page locking, self-destructing messages, encryption, and listifying. You can [download *cowyo* as a single executable](https://github.com/schollz/cowyo/releases/latest) or install it with Go. Try it out at https://cowyo.com.
<br>
There is now [a command-line tool, *cowyodel*](https://github.com/schollz/cowyodel) to interact with *cowyo* and transfer information between computers with only a code phrase: [schollz/cowyodel](https://github.com/schollz/cowyodel).
**Page locking**. Pages can be locked by providing a password to prevent further editing. The whole version tree will still be available. _Note_: This is not available for list mode.
Getting Started
===============
![Locking is easy.](https://raw.githubusercontent.com/schollz/cowyo/master/static/img/help3.gif)
## Install
<br>
**Automatic versioning**. All previous versions of all notes are stored and can be accessed by adding `?version=X` onto `/view` or `/edit`. If you are on the `/view` or `/edit` pages the menu below will show the most substantial changes in the history. Note, only the _current_ version can be edited (no branching allowed, yet).
![Versioning is easy.](https://raw.githubusercontent.com/schollz/cowyo/master/static/img/help4.gif)
<br>
**Self-destructing messages**. You can write a message that will delete itself when a user loads it (in any view). Useful for transmitting sensitive information. To use, simply add a line somewhere that says only "`self-destruct`".
![Mission impossible style self-destruction.](https://raw.githubusercontent.com/schollz/cowyo/master/static/img/help5.gif)
<br>
**Security**. HTTPS support is provided and everything is sanitized to prevent XSS attacks. Though all URLs are publicly accessible, you are free to obfuscate your website by using an obscure/random address (read: the site is still publicly accessible, just hard to find!). In addition to TLS support, you can PGP-encrypt your messages using a passphrase (_Note: This will delete the version tree_).
![Security and encryption baked in.](https://raw.githubusercontent.com/schollz/cowyo/master/static/img/help6.gif)
<br>
**Syntax highlighting**. If you use a coding extension (e.g. .py, .md, .txt, .js, ...) then you'll automatically see syntax highlighting and line numbers.
![Coding syntax is provided if you use an extension](https://raw.githubusercontent.com/schollz/cowyo/master/static/img/help7.gif)
<br>
**CLI tools**. Want to upload/download from the command line? Its super easy. Upload/download files using `curl` with a simple command:
```bash
$ echo "Hello, world!" > hi.txt
$ curl -L --upload-file hi.txt cowyo.com
File uploaded to http://cowyo.com/hi.txt
$ curl -L cowyo.com/test.txt
Hello, world!
```
or just skip the file-creation step and let `cowyo` figure out a name for you:
```bash
$ echo "Wow, so easy" | curl -L --upload-file "-" cowyo.com
File uploaded to http://cowyo.com/CautiousCommonLoon
$ curl -L cowyo.com/CautiousCommonLoon
Wow, so easy
```
<br>
**Content Delivery**. Want use a script on your own domain? Just use the extension `/raw` with optional versioning (e.g. `/raw?version=1`). `cowyo` will serve these files with a wildcard Access-Control-Allow-Origin so they will work anywhere. Check out a live example [on this JSFiddle](https://jsfiddle.net/9mm3afao/).
**Keyboard Shortcuts**. Quickly transition between Edit/View/List by using `Ctl+Shift+E` to Edit, `Ctl+Shift+Z` to View, and `Ctl+Shift+L` to Listify.
**Admin controls**. The Admin can view/delete all the documents by setting the `-a YourAdminKey` when starting the program. Then the admin has access to the `/ls/YourAdminKey` to view and delete any of the pages.
**Math support**. Math is supported with [Katex](https://github.com/Khan/KaTeX) using `$\frac{1}{2}$` for inline equations and `$$\frac{1}{2}$$` for regular equations.
# Install
If you haven't done so, first [install Go](https://golang.org/doc/install).
If you have go
```
$ git clone https://github.com/schollz/cowyo.git
$ cd cowyo
$ go get ./...
$ go build
$ ./cowyo
--------------------------
cowyo (version Y) is up and running on http://SOMEADDRESS:8003
Admin key: VRSgtuVpkrwtWrfphsooOYeErzdbVHHqecMcqcbkLFNFNPwakH
--------------------------
go get -u github.com/schollz/cowyo/...
```
Now open your browser to `http://SOMEADDRESS:8003` to see your cowyo! For more information type `./cowyo --help`.
or just [download the latest release](https://github.com/schollz/cowyo/releases/latest).
# Contact
## Run
If you'd like help or you find a bug, please submit [an issue](https://github.com/schollz/cowyo/issues). Any other comments, questions or anything at all, just [tweet me @zack_118](https://twitter.com/intent/tweet?screen_name=zack_118)
To run just double click or from the command line:
# Acknowledgements
```
cowyo
```
Thanks to [tscholl2](https://github.com/tscholl2) and [sjsafranek](https://github.com/sjsafranek).
and it will start a server listening on `0.0.0.0:8050`. To view it, just go to http://localhost:8050 (the server prints out the local IP for your info if you want to do LAN networking). You can change the port with `-port X`, and you can listen *only* on localhost using `-host localhost`.
Icons made by [Freepik](http://www.freepik.com) from [www.flaticon.com](http://www.flaticon.com), licensed by [CC 3.0 BY](http://creativecommons.org/licenses/by/3.0/ "Creative Commons BY 3.0").
**Running with TLS**
File uploading from [transfer.sh](https://github.com/dutchcoders/transfer.sh/blob/98399c91dd86682077cf9542badbf1658fd9a8c1/transfersh-server/handlers.go#L293-L369), licensed by [MIT license](https://github.com/dutchcoders/transfer.sh/blob/40c9bf7675fb84e78d9a011052b9d0900ec7dde1/LICENSE).
Specify a matching pair of SSL Certificate and Key to run cowyo using https. *cowyo* will now run in a secure session.
# License
*N.B. Let's Encrypt is a CA that signs free and signed certificates.*
The MIT License (MIT)
```
cowyo --cert "/path/to/server.crt" --key "/p/t/server.key"
```
Copyright (c) 2016 Zack
**Running with Docker**
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:
You can easily get started with Docker. First pull the latest image and create the volume with:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
```
docker run -d -v /directory/to/data:/data -p 8050:8050 schollz/cowyo
```
Then you can stop it with `docker stop cowyo` and start it again with `docker start cowyo`.
## Server customization
There are a couple of command-line flags that you can use to make *cowyo* your own micro-CMS.
```
cowyo -lock 123 -default-page index.html -css mystyle.css -diary
```
The `-lock` flag will automatically lock every page with the passphrase "123". Also, the default behavior will be to redirect `/` to `/index.html`. Also, every page that is published will automatically redirect to `/mypage/read` which will show the custom CSS file if it is supplied with `-css`. The `-diary` flag allows you to generate a time-stamped page instead of a random named page when you select "New".
## Usage
*cowyo* is straightforward to use. Here are some of the basic features:
### Publishing
If you hover the the top left button (the name of the page) you will see the option "Publish". Publishing will add the page to the `sitemap.xml` for crawlers to find. It will also default that page to go to the `/read` route so it can be easily viewed as a single page.
### View all the pages
To view the current list of all the pages goto to `/ls`.
### Editing
When you open a document you'll be directed to an alliterative animal (which is supposed to be easy to remember). You can write in Markdown. Saving is performed as soon as you stop writing. You can easily link pages using [[PageName]] as you edit.
![Editing](http://i.imgur.com/vEs2U8z.gif)
### History
You can easily see previous versions of your documents.
![History](http://i.imgur.com/CxhRkyo.gif)
### Lists
You can easily make lists and check them off.
![Lists](http://i.imgur.com/7xbauy8.gif)
### Locking
Locking prevents other users from editing your pages without a passphrase.
![Locking](http://i.imgur.com/xwUFV8b.gif)
### Encryption
Encryption is performed using AES-256.
![Encryption](http://i.imgur.com/rWoqoLB.gif)
### Self-destructing pages
Just like in mission impossible.
![Self-destructing](http://i.imgur.com/upMxFQh.gif)
## Development
You can run the tests using
```
$ cd $GOPATH/src/github.com/schollz/cowyo
$ go test ./...
```
Any contributions are welcome.
## Thanks
...to [DanielHeath](https://github.com/DanielHeath) who has introduced some stellar improvements into cowyo including supporting category pages, hot-template reloading, preventing out-of-order updates, added password access, fade-out on deleting list items, and image upload support!
## License
MIT
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.

View File

@ -1,9 +0,0 @@
# Release 1.0
New features:
- New route /raw to use as a content delivery system
- Coding support provided through [CodeMirror](https://codemirror.net/)
- CLI support for uploading/downloading files with `curl`
- New route to force WSS sockets (for use with Caddy)
- Changed name, improved documentation

40
cmd/herdyo/herdyo.go Normal file
View File

@ -0,0 +1,40 @@
package main
import (
"net/http"
"strings"
"github.com/gin-contrib/sessions"
"github.com/schollz/cowyo/server"
)
func main() {
store := sessions.NewStore([]byte("secret"))
first := server.Site{
PathToData: "site1",
Debounce: 500,
SessionStore: store,
AllowInsecure: true,
Fileuploads: true,
MaxUploadSize: 2,
}.Router()
second := server.Site{
PathToData: "site2",
Debounce: 500,
SessionStore: store,
AllowInsecure: true,
Fileuploads: true,
MaxUploadSize: 2,
}.Router()
panic(http.ListenAndServe("localhost:8000", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.Host, "first") {
first.ServeHTTP(rw, r)
} else if strings.HasPrefix(r.Host, "second") {
second.ServeHTTP(rw, r)
} else {
http.NotFound(rw, r)
}
})))
}

14
cmd/tomlo/tomlo.go Normal file
View File

@ -0,0 +1,14 @@
package main
import (
"github.com/schollz/cowyo/config"
)
func main() {
c, err := config.ParseFile("multisite_sample.toml")
if err != nil {
panic(err)
}
panic(c.ListenAndServe())
}

55
config/config.go Normal file
View File

@ -0,0 +1,55 @@
package config
import (
"github.com/BurntSushi/toml"
)
func ParseFile(path string) (Config, error) {
c := Config{}
if _, err := toml.DecodeFile("multisite_sample.toml", &c); err != nil {
// handle error
return c, err
}
c.SetDefaults()
c.Validate()
return c, nil
}
type Config struct {
Default SiteConfig
Sites []SiteConfig
}
type SiteConfig struct {
Host *string
Port *int
DataDir *string
DefaultPage *string
AllowInsecureMarkup *bool
Lock *string
DebounceSave *int
Diary *bool
AccessCode *string
FileUploadsAllowed *bool
MaxFileUploadMb *uint
MaxDocumentLength *uint
TLS *TLSConfig
CookieKeys []CookieKey
}
type TLSConfig struct {
CertPath string
KeyPath string
Port int
}
type CookieKey struct {
AuthenticateBase64 string
EncryptBase64 string
}
func (c Config) Validate() {
for _, v := range c.Sites {
v.sessionStore()
}
}

102
config/defaults.go Normal file
View File

@ -0,0 +1,102 @@
package config
import (
"encoding/base64"
"crypto/rand"
)
var DefaultSiteConfig SiteConfig
func makeAuthKey() string {
secret := make([]byte, 32)
_, err := rand.Read(secret)
if err != nil {
panic(err)
}
return base64.StdEncoding.EncodeToString(secret)
}
func init() {
host := "*"
port := 8050
debounce := 500
dataDir := "data"
empty := ""
zer := uint(0)
lots := uint(100000000)
fal := false
ck := CookieKey{
AuthenticateBase64: "",
EncryptBase64: "",
}
DefaultSiteConfig = SiteConfig{
Host:&host,
Port:&port,
DataDir:&dataDir,
DebounceSave:&debounce,
CookieKeys: []CookieKey{ck},
DefaultPage:&empty,
AllowInsecureMarkup:&fal,
Lock:&empty,
Diary:&fal,
AccessCode:&empty,
FileUploadsAllowed:&fal,
MaxFileUploadMb:&zer,
MaxDocumentLength:&lots,
}
}
func copyDefaults(base, defaults *SiteConfig) {
if base.Host == nil {
base.Host = defaults.Host
}
if base.Port == nil {
base.Port = defaults.Port
}
if base.DataDir == nil {
base.DataDir = defaults.DataDir
}
if base.DefaultPage == nil {
base.DefaultPage = defaults.DefaultPage
}
if base.AllowInsecureMarkup == nil {
base.AllowInsecureMarkup = defaults.AllowInsecureMarkup
}
if base.Lock == nil {
base.Lock = defaults.Lock
}
if base.DebounceSave == nil {
base.DebounceSave = defaults.DebounceSave
}
if base.Diary == nil {
base.Diary = defaults.Diary
}
if base.AccessCode == nil {
base.AccessCode = defaults.AccessCode
}
if base.FileUploadsAllowed == nil {
base.FileUploadsAllowed = defaults.FileUploadsAllowed
}
if base.MaxFileUploadMb == nil {
base.MaxFileUploadMb = defaults.MaxFileUploadMb
}
if base.MaxDocumentLength == nil {
base.MaxDocumentLength = defaults.MaxDocumentLength
}
if base.TLS == nil {
base.TLS = defaults.TLS
}
if base.CookieKeys == nil {
base.CookieKeys = defaults.CookieKeys
}
}
func (c *Config) SetDefaults() {
copyDefaults(&c.Default, &DefaultSiteConfig)
for i := range c.Sites {
copyDefaults(&c.Sites[i], &c.Default)
}
}

105
config/http.go Normal file
View File

@ -0,0 +1,105 @@
package config
import (
"fmt"
"log"
"encoding/base64"
"net/http"
"github.com/gin-contrib/sessions"
"github.com/jcelliott/lumber"
"github.com/schollz/cowyo/server"
"strings"
)
func (c Config) ListenAndServe() error {
insecurePorts := map[int]bool{}
securePorts := map[int]bool{}
err := make(chan error)
for _, s := range c.Sites {
if !insecurePorts[*s.Port] {
insecurePorts[*s.Port] = true
go func(s SiteConfig) {
err <- http.ListenAndServe(fmt.Sprintf("localhost:%d", *s.Port), c)
}(s)
}
if s.TLS != nil && !securePorts[s.TLS.Port] {
securePorts[s.TLS.Port] = true
go func(s SiteConfig) {
err <- http.ListenAndServeTLS(
fmt.Sprintf("localhost:%d", s.TLS.Port),
s.TLS.CertPath,
s.TLS.KeyPath,
c,
)
}(s)
}
}
for {
return <- err
}
return nil
}
func (c Config) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
for i := range c.Sites {
if c.Sites[i].MatchesRequest(r) {
c.Sites[i].Handle(rw, r)
return
}
}
http.NotFound(rw, r)
}
func (s SiteConfig) MatchesRequest(r *http.Request) bool {
sh := *s.Host
if strings.HasPrefix(sh, "*") {
return strings.HasSuffix(r.Host, sh[1:])
}
return sh == r.Host
}
func (s SiteConfig) sessionStore() sessions.Store {
keys := [][]byte{}
for _, k := range s.CookieKeys {
key, err := base64.StdEncoding.DecodeString(k.AuthenticateBase64)
if err != nil {
panic(err)
}
if len(key) != 32 {
log.Panicf("AuthenticateBase64 key %s must be 32 bytes; suggest %s", k.AuthenticateBase64, makeAuthKey())
}
keys = append(keys, key)
key, err = base64.StdEncoding.DecodeString(k.EncryptBase64)
if err != nil {
panic(err)
}
if len(key) != 32 {
log.Panicf("EncryptBase64 key %s must be 32 bytes, suggest %s", k.EncryptBase64, makeAuthKey())
}
keys = append(keys, key)
}
return sessions.NewStore(keys...)
}
func (s SiteConfig) Handle(rw http.ResponseWriter, r *http.Request) {
dataDir := strings.Replace(*s.DataDir, "${HOST}", r.Host, -1)
router := server.Site{
PathToData: dataDir,
Css: []byte{},
DefaultPage: *s.DefaultPage,
DefaultPassword: *s.Lock,
Debounce: *s.DebounceSave,
Diary: *s.Diary,
SessionStore: s.sessionStore(),
SecretCode: *s.AccessCode,
AllowInsecure: *s.AllowInsecureMarkup,
Fileuploads: *s.MaxFileUploadMb > 0,
MaxUploadSize: *s.MaxFileUploadMb,
Logger: lumber.NewConsoleLogger(server.LogLevel),
MaxDocumentSize: *s.MaxDocumentLength,
}.Router()
router.ServeHTTP(rw, r)
}

186
db.go
View File

@ -1,186 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"log"
"strings"
"time"
"github.com/boltdb/bolt"
"github.com/sergi/go-diff/diffmatchpatch"
)
var db *bolt.DB
var open bool
// Open to create the database and open
func Open(filename string) error {
var err error
config := &bolt.Options{Timeout: 30 * time.Second}
db, err = bolt.Open(filename, 0600, config)
if err != nil {
fmt.Println("Opening BoltDB timed out")
log.Fatal(err)
}
open = true
return nil
}
// Close database
func Close() {
open = false
db.Close()
}
// WikiData is data for storing in DB
type WikiData struct {
Title string
CurrentText string
Diffs []string
Timestamps []string
Encrypted bool
Locked string
}
func getCurrentText(title string, version int) (string, []versionsInfo, bool, time.Duration, bool, string, int) {
Open(RuntimeArgs.DatabaseLocation)
defer Close()
title = strings.ToLower(title)
var vi []versionsInfo
totalTime := time.Now().Sub(time.Now())
isCurrent := true
currentText := ""
encrypted := false
locked := ""
currentVersionNum := -1
if !open {
return currentText, vi, isCurrent, totalTime, encrypted, locked, currentVersionNum
}
err := db.View(func(tx *bolt.Tx) error {
var err error
b := tx.Bucket([]byte("datas"))
if b == nil {
return fmt.Errorf("db must be opened before loading")
}
k := []byte(title)
val := b.Get(k)
if val == nil {
return nil
}
var p WikiData
err = p.decode(val)
if err != nil {
return err
}
currentText = p.CurrentText
encrypted = p.Encrypted
locked = p.Locked
currentVersionNum = len(p.Diffs) - 1
if version > -1 && version < len(p.Diffs) {
// get that version of text instead
currentText = rebuildTextsToDiffN(p, version)
isCurrent = false
}
vi, totalTime = getImportantVersions(p)
return nil
})
if err != nil {
fmt.Printf("Could not get WikiData: %s", err)
}
return currentText, vi, isCurrent, totalTime, encrypted, locked, currentVersionNum
}
func (p *WikiData) load(title string) error {
title = strings.ToLower(title)
if !open {
Open(RuntimeArgs.DatabaseLocation)
defer Close()
}
err := db.View(func(tx *bolt.Tx) error {
var err error
b := tx.Bucket([]byte("datas"))
if b == nil {
return nil
}
k := []byte(title)
val := b.Get(k)
if val == nil {
// make new one
p.Title = title
p.CurrentText = ""
p.Diffs = []string{}
p.Timestamps = []string{}
return nil
}
err = p.decode(val)
if err != nil {
return err
}
return nil
})
if err != nil {
fmt.Printf("Could not get WikiData: %s", err)
return err
}
return nil
}
func (p *WikiData) save(newText string) error {
if !open {
Open(RuntimeArgs.DatabaseLocation)
defer Close()
}
err := db.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists([]byte("datas"))
if err != nil {
return fmt.Errorf("create bucket: %s", err)
}
// find diffs
dmp := diffmatchpatch.New()
diffs := dmp.DiffMain(p.CurrentText, newText, true)
delta := dmp.DiffToDelta(diffs)
p.CurrentText = newText
p.Timestamps = append(p.Timestamps, time.Now().Format(time.ANSIC))
p.Diffs = append(p.Diffs, delta)
enc, err := p.encode()
if err != nil {
return fmt.Errorf("could not encode WikiData: %s", err)
}
p.Title = strings.ToLower(p.Title)
err = bucket.Put([]byte(p.Title), enc)
if err != nil {
return fmt.Errorf("could add to bucket: %s", err)
}
return err
})
// // Add the new name to the programdata so its not randomly generated
// if err == nil && len(p.Timestamps) > 0 && len(p.CurrentText) > 0 {
// err2 := db.Update(func(tx *bolt.Tx) error {
// b := tx.Bucket([]byte("programdata"))
// id, _ := b.NextSequence()
// idInt := int(id)
// return b.Put(itob(idInt), []byte(p.Title))
// })
// if err2 != nil {
// return fmt.Errorf("could not add to programdata: %s", err)
// }
// }
return err
}
func (p *WikiData) encode() ([]byte, error) {
enc, err := json.Marshal(p)
if err != nil {
return nil, err
}
return enc, nil
}
func (p *WikiData) decode(data []byte) error {
err := json.Unmarshal(data, &p)
if err != nil {
return err
}
return nil
}

9
docker-compose.yml Normal file
View File

@ -0,0 +1,9 @@
version: "2"
services:
cowyo:
build: .
ports:
- 8050:8050
volumes:
- ./data:/data

28
encrypt/encrypt.go Normal file
View File

@ -0,0 +1,28 @@
package encrypt
import (
"crypto/sha256"
"encoding/hex"
"github.com/schollz/cryptopasta"
)
func EncryptString(toEncrypt string, password string) (string, error) {
key := sha256.Sum256([]byte(password))
encrypted, err := cryptopasta.Encrypt([]byte(toEncrypt), &key)
if err != nil {
return "", err
}
return hex.EncodeToString(encrypted), nil
}
func DecryptString(toDecrypt string, password string) (string, error) {
key := sha256.Sum256([]byte(password))
contentData, err := hex.DecodeString(toDecrypt)
if err != nil {
return "", err
}
bDecrypted, err := cryptopasta.Decrypt(contentData, &key)
return string(bDecrypted), err
}

22
encrypt/encrypt_test.go Normal file
View File

@ -0,0 +1,22 @@
package encrypt
import "testing"
func TestEncryption(t *testing.T) {
s, err := EncryptString("some string", "some password")
if err != nil {
t.Errorf("What")
}
d, err := DecryptString(s, "some wrong password")
if err == nil {
t.Errorf("Should throw error for bad password")
}
d, err = DecryptString(s, "some password")
if err != nil {
t.Errorf("Should not throw password")
}
if d != "some string" {
t.Errorf("Problem decoding")
}
}

View File

@ -1,73 +0,0 @@
package main
import (
"bytes"
"errors"
"io/ioutil"
"log"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/armor"
)
var encryptionType string
func init() {
encryptionType = "PGP SIGNATURE"
}
func encryptString(encryptionText string, encryptionPassphraseString string) string {
encryptionPassphrase := []byte(encryptionPassphraseString)
encbuf := bytes.NewBuffer(nil)
w, err := armor.Encode(encbuf, encryptionType, nil)
if err != nil {
log.Fatal(err)
}
plaintext, err := openpgp.SymmetricallyEncrypt(w, encryptionPassphrase, nil, nil)
if err != nil {
log.Fatal(err)
}
message := []byte(encryptionText)
_, err = plaintext.Write(message)
plaintext.Close()
w.Close()
return encbuf.String()
}
func decryptString(decryptionString string, encryptionPassphraseString string) (string, error) {
encryptionPassphrase := []byte(encryptionPassphraseString)
decbuf := bytes.NewBuffer([]byte(decryptionString))
result, err := armor.Decode(decbuf)
if err != nil {
return "", err
}
alreadyPrompted := false
md, err := openpgp.ReadMessage(result.Body, nil, func(keys []openpgp.Key, symmetric bool) ([]byte, error) {
if alreadyPrompted {
return nil, errors.New("Could not decrypt using passphrase")
} else {
alreadyPrompted = true
}
return encryptionPassphrase, nil
}, nil)
if err != nil {
return "", err
}
bytes, err := ioutil.ReadAll(md.UnverifiedBody)
if err != nil {
return "", err
}
return string(bytes), nil
}
// func main() {
// test := encryptString("This is some string", "golang")
// fmt.Println(test)
// testD := decryptString(test, "golang")
// fmt.Println(testD)
//
// }

35
go.mod Normal file
View File

@ -0,0 +1,35 @@
module github.com/schollz/cowyo
go 1.12
require (
github.com/BurntSushi/toml v1.3.2
github.com/bytedance/sonic v1.9.2 // indirect
github.com/danielheath/gin-teeny-security v0.0.0-20180331042316-bb11804dd0e2
github.com/gin-contrib/multitemplate v0.0.0-20230212012517-45920c92c271
github.com/gin-contrib/sessions v0.0.5
github.com/gin-gonic/gin v1.9.1
github.com/go-playground/validator/v10 v10.14.1 // indirect
github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/microcosm-cc/bluemonday v1.0.24
github.com/russross/blackfriday v1.6.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0
github.com/schollz/cryptopasta v0.0.0-20170217152710-dcd61c7d42a1
github.com/schollz/versionedtext v0.0.0-20180523061923-d8ce0957c254
github.com/sergi/go-diff v1.3.1 // indirect
github.com/shurcooL/github_flavored_markdown v0.0.0-20210228213109-c3a9aa474629
github.com/shurcooL/go v0.0.0-20190704215121-7189cc372560 // indirect
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041 // indirect
github.com/shurcooL/highlight_diff v0.0.0-20181222201841-111da2e7d480 // indirect
github.com/shurcooL/highlight_go v0.0.0-20191220051317-782971ddf21b // indirect
github.com/shurcooL/octicon v0.0.0-20230705024016-66bff059edb8 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d // indirect
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e // indirect
golang.org/x/arch v0.4.0 // indirect
golang.org/x/crypto v0.11.0
golang.org/x/net v0.12.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/urfave/cli.v1 v1.20.0
)

405
go.sum Normal file
View File

@ -0,0 +1,405 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/antonlindstrom/pgstore v0.0.0-20200229204646-b08ebf1105e0/go.mod h1:2Ti6VUHVxpC0VSmTZzEvpzysnaGAfGBOoMIz5ykPyyw=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw=
github.com/bos-hieu/mongostore v0.0.2/go.mod h1:8AbbVmDEb0yqJsBrWxZIAZOxIfv/tsP8CDtdHduZHGg=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bradleypeabody/gorilla-sessions-memcache v0.0.0-20181103040241-659414f458e1/go.mod h1:dkChI7Tbtx7H1Tj7TqGSZMOeGpMP5gLHtjroHd4agiI=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/bytedance/sonic v1.9.2 h1:GDaNjuWSGu09guE9Oql0MSTNhNCLlWwO8y/xM5BzcbM=
github.com/bytedance/sonic v1.9.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/danielheath/gin-teeny-security v0.0.0-20180331042316-bb11804dd0e2 h1:OU8xbewlvG+K/mPYGshgkSDJZugXeV4cvlI8r02a58o=
github.com/danielheath/gin-teeny-security v0.0.0-20180331042316-bb11804dd0e2/go.mod h1:iufTPweOVe3TKbMOYF0OyJ5iM4pdK/D9F9dIQoQC4IE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gin-contrib/multitemplate v0.0.0-20230212012517-45920c92c271 h1:s+boMV47gwTyff2PL+k6V33edJpp+K5y3QPzZlRhno8=
github.com/gin-contrib/multitemplate v0.0.0-20230212012517-45920c92c271/go.mod h1:XLLtIXoP9+9zGcEDc7gAGV3AksGPO+vzv4kXHMJSdU0=
github.com/gin-contrib/sessions v0.0.5 h1:CATtfHmLMQrMNpJRgzjWXD7worTh7g7ritsQfmF+0jE=
github.com/gin-contrib/sessions v0.0.5/go.mod h1:vYAuaUPqie3WUSsft6HUlCjlwwoJQs97miaG2+7neKY=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k=
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.0.7/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
github.com/jackc/pgtype v1.6.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
github.com/jackc/pgx/v4 v4.10.1/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25 h1:EFT6MH3igZK/dIVqgGbTqWVvkZ7wJ5iGN03SVtvvdd8=
github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25/go.mod h1:sWkGw/wsaHtRsT9zGQ/WyJCotGWG/Anow/9hsAcBWRw=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kidstuff/mongostore v0.0.0-20181113001930-e650cd85ee4b/go.mod h1:g2nVr8KZVXJSS97Jo8pJ0jgq29P6H7dG0oplUA86MQw=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/memcachier/mc v2.0.1+incompatible/go.mod h1:7bkvFE61leUBvXz+yxsOnGBQSZpBSPIMUQSmmSHvuXc=
github.com/microcosm-cc/bluemonday v1.0.24 h1:NGQoPtwGVcbGkKfvyYk1yRqknzBuoMiUrO6R7uFTPlw=
github.com/microcosm-cc/bluemonday v1.0.24/go.mod h1:ArQySAMps0790cHSkdPEJ7bGkF2VePWH773hsJNSHf8=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTepVu7uJBYgZ0SdWHQlIas582j6cn2jgk4DDdlg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/schollz/cryptopasta v0.0.0-20170217152710-dcd61c7d42a1 h1:CAVM5ALs/TKIa2ri7WMqge+m5wz/ItuiU6CFUPjZTjA=
github.com/schollz/cryptopasta v0.0.0-20170217152710-dcd61c7d42a1/go.mod h1:sM7qObCXSAwGYckYHG4m0hP3PSCBcHmC7/w/kBwcwgM=
github.com/schollz/versionedtext v0.0.0-20180523061923-d8ce0957c254 h1:/EgihFrDLhb/x7NLm8cWB7QTquw5gatR+y/jv2gLWsY=
github.com/schollz/versionedtext v0.0.0-20180523061923-d8ce0957c254/go.mod h1:116sjYSGDGoVSTUCdO34dA1Yg1ZGbN2jk/aYThLfK60=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/github_flavored_markdown v0.0.0-20210228213109-c3a9aa474629 h1:86e54L0i3pH3dAIA8OxBbfLrVyhoGpnNk1iJCigAWYs=
github.com/shurcooL/github_flavored_markdown v0.0.0-20210228213109-c3a9aa474629/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
github.com/shurcooL/go v0.0.0-20190704215121-7189cc372560 h1:SpaoQDTgpo2YZkvmr2mtgloFFfPTjtLMlZkQtNAPQik=
github.com/shurcooL/go v0.0.0-20190704215121-7189cc372560/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041 h1:llrF3Fs4018ePo4+G/HV/uQUqEI1HMDjCeOf2V6puPc=
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
github.com/shurcooL/highlight_diff v0.0.0-20181222201841-111da2e7d480 h1:KaKXZldeYH73dpQL+Nr38j1r5BgpAYQjYvENOUpIZDQ=
github.com/shurcooL/highlight_diff v0.0.0-20181222201841-111da2e7d480/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
github.com/shurcooL/highlight_go v0.0.0-20191220051317-782971ddf21b h1:rBIwpb5ggtqf0uZZY5BPs1sL7njUMM7I8qD2jiou70E=
github.com/shurcooL/highlight_go v0.0.0-20191220051317-782971ddf21b/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
github.com/shurcooL/octicon v0.0.0-20230705024016-66bff059edb8 h1:W5meM/5DP0Igf+pS3Se363Y2DoDv9LUuZgQ24uG9LNY=
github.com/shurcooL/octicon v0.0.0-20230705024016-66bff059edb8/go.mod h1:hWBWTvIJ918VxbNOk2hxQg1/5j1M9yQI1Kp8d9qrOq8=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d h1:yKm7XZV6j9Ev6lojP2XaIshpT4ymkqhMeSghO5Ps00E=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e h1:qpG93cPwA5f7s/ZPBJnGOYQNK/vKsaDaseuKT5Asee8=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/wader/gormstore/v2 v2.0.0/go.mod h1:3BgNKFxRdVo2E4pq3e/eiim8qRDZzaveaIcIvu2T8r0=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.mongodb.org/mongo-driver v1.9.0/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc=
golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.0.4/go.mod h1:MEgp8tk2n60cSBCq5iTcPDw3ns8Gs+zOva9EUhkknTs=
gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg=
gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw=
gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View File

@ -1,101 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: awwkoala
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start daemon at boot time
# Description: Enable service provided by daemon.
### END INIT INFO
# This should be placed in /etc/init.d directory
# start with
# sudo /etc/init.d/awwkoala start
# stop with
# sudo /etc/init.d/awwkoala start
name="cowyo"
dir="CUR_DIR"
user="USERCUR"
cmd="./$name -p :PORT EXT_ADDRESS"
pid_file="/var/run/$name.pid"
stdout_log="/var/log/$name.log"
stderr_log="/var/log/$name.err"
get_pid() {
cat "$pid_file"
}
is_running() {
[ -f "$pid_file" ] && ps `get_pid` > /dev/null 2>&1
}
case "$1" in
start)
if is_running; then
echo "Already started"
else
echo "Starting $name"
cd "$dir"
sudo -u "$user" $cmd >> "$stdout_log" 2>> "$stderr_log" &
echo $! > "$pid_file"
if ! is_running; then
echo "Unable to start, see $stdout_log and $stderr_log"
exit 1
fi
fi
;;
stop)
if is_running; then
echo -n "Stopping $name.."
kill `get_pid`
for i in {1..10}
do
if ! is_running; then
break
fi
echo -n "."
sleep 1
done
echo
if is_running; then
echo "Not stopped; may still be shutting down or shutdown may have failed"
exit 1
else
echo "Stopped"
if [ -f "$pid_file" ]; then
rm "$pid_file"
fi
fi
else
echo "Not running"
fi
;;
restart)
$0 stop
if is_running; then
echo "Unable to stop, will not attempt to start"
exit 1
fi
$0 start
;;
status)
if is_running; then
echo "Running"
else
echo "Stopped"
exit 1
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0

View File

@ -1,41 +0,0 @@
server {
# SERVER BLOCK FOR cowyo
listen 80; ## listen for ipv4; this line is default and implied
access_log /etc/nginx/logs/access-cowyo.log;
error_log /etc/nginx/logs/error-cowyo.log info;
root CUR_DIR;
server_name ADDRESS;
# Media: images, icons, video, audio, HTC
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
}
# CSS and Javascript
location ~* \.(?:css|js)$ {
expires 1y;
access_log off;
add_header Cache-Control "public";
}
location ^~ /static {
try_files $uri $uri/ =404;
}
location ~ ^/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://127.0.0.1:PORT;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

View File

@ -1,31 +0,0 @@
ADDRESS = yourserver.com
PORT = 8003
CUR_DIR = $(shell bash -c 'pwd')
USERCUR = $(shell bash -c 'whoami')
make:
go build
install:
rm -rf jinstall
mkdir jinstall
cp cowyo.ssl.nginx cowyo.ssl.nginx.temp
sed -i 's/PORT/$(PORT)/g' cowyo.ssl.nginx.temp
sed -i 's/ADDRESS/$(ADDRESS)/g' cowyo.ssl.nginx.temp
sed -i 's^CUR_DIR^$(CUR_DIR)^g' cowyo.ssl.nginx.temp
cp cowyo.init cowyo.init.temp
sed -i 's/EXT_ADDRESS/$(ADDRESS)/g' cowyo.init.temp
sed -i 's^CUR_DIR^$(CUR_DIR)^g' cowyo.init.temp
sed -i 's^USERCUR^$(USERCUR)^g' cowyo.init.temp
sed -i 's^PORT^$(PORT)^g' cowyo.init.temp
cp cowyo.init.temp /etc/init.d/cowyo.init
chmod +x /etc/init.d/cowyo.init
cp cowyo.ssl.nginx.temp /etc/nginx/sites-available/cowyo.nginx
ln -fs /etc/nginx/sites-available/cowyo.nginx /etc/nginx/sites-enabled/cowyo.nginx
/etc/init.d/nginx reload
/etc/init.d/nginx restart
/etc/init.d/cowyo.init restart
rm -rf *.temp
.PHONY: install

View File

@ -1,17 +0,0 @@
First install the NGINX block in this directory. (There is an experimental Makefile that will do this, just try `sudo make install`.
To use letsencrypt follow these steps:
```
git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt
sudo service nginx stop
sudo ./letsencrypt-auto certonly --standalone --email youremail@somewhere.com -d yourserver.com
sudo service nginx start
```
Then startup `cowyo` with
```bash
sudo ./cowyo -p :8001 -key /etc/letsencrypt/live/yourserver.com/privkey.pem -crt /etc/letsencrypt/live/yourserver.com/cert.pem yourserver.com
```

View File

@ -1,101 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: cowyo
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start daemon at boot time
# Description: Enable service provided by daemon.
### END INIT INFO
# This should be placed in /etc/init.d directory
# start with
# sudo /etc/init.d/cowyo start
# stop with
# sudo /etc/init.d/cowyo start
dir="CUR_DIR"
user="USERCUR"
cmd="./cowyo -p :PORT -key /etc/letsencrypt/live/EXT_ADDRESS/privkey.pem -crt /etc/letsencrypt/live/EXT_ADDRESS/cert.pem yourserver.com./cowyo EXT_ADDRESS"
name="cowyo"
pid_file="/var/run/$name.pid"
stdout_log="/var/log/$name.log"
stderr_log="/var/log/$name.err"
get_pid() {
cat "$pid_file"
}
is_running() {
[ -f "$pid_file" ] && ps `get_pid` > /dev/null 2>&1
}
case "$1" in
start)
if is_running; then
echo "Already started"
else
echo "Starting $name"
cd "$dir"
sudo -u "$user" $cmd >> "$stdout_log" 2>> "$stderr_log" &
echo $! > "$pid_file"
if ! is_running; then
echo "Unable to start, see $stdout_log and $stderr_log"
exit 1
fi
fi
;;
stop)
if is_running; then
echo -n "Stopping $name.."
kill `get_pid`
for i in {1..10}
do
if ! is_running; then
break
fi
echo -n "."
sleep 1
done
echo
if is_running; then
echo "Not stopped; may still be shutting down or shutdown may have failed"
exit 1
else
echo "Stopped"
if [ -f "$pid_file" ]; then
rm "$pid_file"
fi
fi
else
echo "Not running"
fi
;;
restart)
$0 stop
if is_running; then
echo "Unable to stop, will not attempt to start"
exit 1
fi
$0 start
;;
status)
if is_running; then
echo "Running"
else
echo "Stopped"
exit 1
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0

View File

@ -1,50 +0,0 @@
server {
listen 80;
server_name ADDRESS;
rewrite ^ https://$server_name$request_uri? permanent;
}
server {
# SERVER BLOCK FOR ADDRESS
listen 443 ssl;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_certificate /etc/letsencrypt/live/ADDRESS/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ADDRESS/privkey.pem;
access_log /etc/nginx/logs/access-ADDRESS.log;
error_log /etc/nginx/logs/error-ADDRESS.log info;
root CUR_DIR;
server_name ADDRESS;
# Media: images, icons, video, audio, HTC
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
}
# CSS and Javascript
location ~* \.(?:css|js)$ {
expires 1y;
access_log off;
add_header Cache-Control "public";
}
location ^~ /static {
try_files $uri $uri/ =404;
}
location ~ ^/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_pass https://127.0.0.1:PORT;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

354
main.go Normal file → Executable file
View File

@ -1,181 +1,209 @@
package main
import (
"bytes"
"flag"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"path"
"path/filepath"
"time"
"github.com/boltdb/bolt"
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
"github.com/jcelliott/lumber"
"github.com/schollz/cowyo/server"
cli "gopkg.in/urfave/cli.v1"
)
// AllowedIPs is a white/black list of
// IP addresses allowed to access cowyo
var AllowedIPs = map[string]bool{
"192.168.1.13": true,
"192.168.1.12": true,
"192.168.1.2": true,
}
// RuntimeArgs contains all runtime
// arguments available
var RuntimeArgs struct {
WikiName string
ExternalIP string
Port string
DatabaseLocation string
ServerCRT string
ServerKey string
SourcePath string
AdminKey string
Socket string
ForceWss bool
DumpDataset string
RestoreDataset string
Debug bool
}
var VersionNum string
func init() {
gin.SetMode(gin.ReleaseMode)
}
var version string
var pathToData string
func main() {
// _, executableFile, _, _ := runtime.Caller(0) // get full path of this file
cwd, _ := os.Getwd()
databaseFile := path.Join(cwd, "data.db")
flag.StringVar(&RuntimeArgs.Port, "p", ":8003", "port to bind")
flag.StringVar(&RuntimeArgs.DatabaseLocation, "db", databaseFile, "location of database file")
flag.StringVar(&RuntimeArgs.AdminKey, "a", "", "key to access admin privaleges")
flag.StringVar(&RuntimeArgs.ServerCRT, "crt", "", "location of SSL certificate")
flag.StringVar(&RuntimeArgs.ServerKey, "key", "", "location of SSL key")
flag.StringVar(&RuntimeArgs.WikiName, "w", "cowyo", "custom name for wiki")
flag.BoolVar(&RuntimeArgs.ForceWss, "e", false, "force encrypted sockets (use if using Caddy auto HTTPS)")
flag.BoolVar(&RuntimeArgs.Debug, "d", false, "debugging mode")
flag.StringVar(&RuntimeArgs.DumpDataset, "dump", "", "directory to dump all data to")
flag.StringVar(&RuntimeArgs.RestoreDataset, "restore", "", "directory to restore all data from")
flag.CommandLine.Usage = func() {
fmt.Println(`cowyo (version ` + VersionNum + `)
Usage: cowyo [options] [address]
If address is not provided then cowyo
will determine the best internal IP address.
Example: 'cowyo'
Example: 'cowyo yourserver.com'
Example: 'cowyo -p :8080 localhost:8080'
Example: 'cowyo -p :8080 -crt ssl/server.crt -key ssl/server.key localhost:8080'
Options:`)
flag.CommandLine.PrintDefaults()
}
flag.Parse()
// Set the log level
if RuntimeArgs.Debug == false {
logger.Level(2)
} else {
logger.Level(0)
}
if len(RuntimeArgs.DumpDataset) > 0 {
fmt.Println("Dumping data to '" + RuntimeArgs.DumpDataset + "' folder...")
dumpEverything(RuntimeArgs.DumpDataset)
os.Exit(1)
}
RuntimeArgs.ExternalIP = flag.Arg(0)
if RuntimeArgs.ExternalIP == "" {
logger.Debug("Getting external ip...")
RuntimeArgs.ExternalIP = GetLocalIP() + RuntimeArgs.Port
logger.Debug("Using ip: %s and port %s", GetLocalIP(), RuntimeArgs.Port)
}
RuntimeArgs.SourcePath = cwd
if len(RuntimeArgs.AdminKey) == 0 {
RuntimeArgs.AdminKey = RandStringBytesMaskImprSrc(50)
}
// create programdata bucket
Open(RuntimeArgs.DatabaseLocation)
err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte("programdata"))
if err != nil {
return fmt.Errorf("create bucket: %s", err)
app := cli.NewApp()
app.Name = "cowyo"
app.Usage = "a simple wiki"
app.Version = version
app.Compiled = time.Now()
app.Action = func(c *cli.Context) error {
pathToData = c.GlobalString("data")
os.MkdirAll(pathToData, 0755)
host := c.GlobalString("host")
crt_f := c.GlobalString("cert") // crt flag
key_f := c.GlobalString("key") // key flag
if host == "" {
host = GetLocalIP()
}
return err
})
TLS := false
if crt_f != "" && key_f != "" {
TLS = true
}
if TLS {
fmt.Printf("\nRunning cowyo server (version %s) at https://%s:%s\n\n", version, host, c.GlobalString("port"))
} else {
fmt.Printf("\nRunning cowyo server (version %s) at http://%s:%s\n\n", version, host, c.GlobalString("port"))
}
server.Serve(
pathToData,
c.GlobalString("host"),
c.GlobalString("port"),
c.GlobalString("cert"),
c.GlobalString("key"),
TLS,
c.GlobalString("css"),
c.GlobalString("default-page"),
c.GlobalString("lock"),
c.GlobalInt("debounce"),
c.GlobalBool("diary"),
c.GlobalString("cookie-secret"),
c.GlobalString("access-code"),
c.GlobalBool("allow-insecure-markup"),
c.GlobalBool("allow-file-uploads"),
c.GlobalUint("max-upload-mb"),
c.GlobalUint("max-document-length"),
logger(c.GlobalBool("debug")),
)
return nil
}
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "data",
Value: "data",
Usage: "data folder to use",
},
cli.StringFlag{
Name: "olddata",
Value: "",
Usage: "data folder for migrating",
},
cli.StringFlag{
Name: "host",
Value: "",
Usage: "host to use",
},
cli.StringFlag{
Name: "port,p",
Value: "8050",
Usage: "port to use",
},
cli.StringFlag{
Name: "cert",
Value: "",
Usage: "absolute path to SSL public sertificate",
},
cli.StringFlag{
Name: "key",
Value: "",
Usage: "absolute path to SSL private key",
},
cli.StringFlag{
Name: "css",
Value: "",
Usage: "use a custom CSS file",
},
cli.StringFlag{
Name: "default-page",
Value: "",
Usage: "show default-page/read instead of editing (default: show random editing)",
},
cli.BoolFlag{
Name: "allow-insecure-markup",
Usage: "Skip HTML sanitization",
},
cli.StringFlag{
Name: "lock",
Value: "",
Usage: "password to lock editing all files (default: all pages unlocked)",
},
cli.IntFlag{
Name: "debounce",
Value: 500,
Usage: "debounce time for saving data, in milliseconds",
},
cli.BoolFlag{
Name: "debug, d",
Usage: "turn on debugging",
},
cli.BoolFlag{
Name: "diary",
Usage: "turn diary mode (doing New will give a timestamped page)",
},
cli.StringFlag{
Name: "access-code",
Value: "",
Usage: "Secret code to login with before accessing any wiki stuff",
},
cli.StringFlag{
Name: "cookie-secret",
Value: "secret",
Usage: "random data to use for cookies; changing it will invalidate all sessions",
},
cli.BoolFlag{
Name: "allow-file-uploads",
Usage: "Enable file uploads",
},
cli.UintFlag{
Name: "max-upload-mb",
Value: 2,
Usage: "Largest file upload (in mb) allowed",
},
cli.UintFlag{
Name: "max-document-length",
Value: 100000000,
Usage: "Largest wiki page (in characters) allowed",
},
}
app.Commands = []cli.Command{
{
Name: "migrate",
Aliases: []string{"m"},
Usage: "migrate from the old cowyo",
Action: func(c *cli.Context) error {
pathToData = c.GlobalString("data")
pathToOldData := c.GlobalString("olddata")
if len(pathToOldData) == 0 {
fmt.Printf("You need to specify folder with -olddata")
return nil
}
os.MkdirAll(pathToData, 0755)
if !exists(pathToOldData) {
fmt.Printf("Can not find '%s', does it exist?", pathToOldData)
return nil
}
server.Migrate(pathToOldData, pathToData, logger(c.GlobalBool("debug")))
return nil
},
},
}
app.Run(os.Args)
}
// GetLocalIP returns the local ip address
func GetLocalIP() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
panic(err)
return ""
}
Close()
// Default page
defaultPage, _ := ioutil.ReadFile(path.Join(RuntimeArgs.SourcePath, "templates/aboutpage.md"))
p := WikiData{"help", "", []string{}, []string{}, false, "zzz"}
p.save(string(defaultPage))
defaultPage, _ = ioutil.ReadFile(path.Join(RuntimeArgs.SourcePath, "templates/privacypolicy.md"))
p = WikiData{"privacypolicy", "", []string{}, []string{}, false, "zzz"}
p.save(string(defaultPage))
if len(RuntimeArgs.RestoreDataset) > 0 {
fmt.Println("Restoring data from '" + RuntimeArgs.RestoreDataset + "' folder...")
filepath.Walk(RuntimeArgs.RestoreDataset, restoreFile)
os.Exit(1)
}
// var q WikiData
// q.load("about")
// fmt.Println(getImportantVersions(q))
r := gin.Default()
r.LoadHTMLGlob(path.Join(RuntimeArgs.SourcePath, "templates/*"))
store := sessions.NewCookieStore([]byte("secret"))
r.Use(sessions.Sessions("mysession", store))
r.GET("/", newNote)
r.HEAD("/", func(c *gin.Context) { c.Status(200) })
r.GET("/:title", editNote)
r.PUT("/:title", putFile)
r.PUT("/", putFile)
r.GET("/:title/*option", everythingElse)
r.POST("/:title/*option", encryptionRoute)
r.DELETE("/listitem", deleteListItem)
r.DELETE("/deletepage", deletePage)
if RuntimeArgs.ServerCRT != "" && RuntimeArgs.ServerKey != "" {
RuntimeArgs.Socket = "wss"
fmt.Println("--------------------------")
fmt.Println("cowyo (version " + VersionNum + ") is up and running on https://" + RuntimeArgs.ExternalIP)
fmt.Println("Admin key: " + RuntimeArgs.AdminKey)
fmt.Println("--------------------------")
r.RunTLS(RuntimeArgs.Port, RuntimeArgs.ServerCRT, RuntimeArgs.ServerKey)
} else {
RuntimeArgs.Socket = "ws"
if RuntimeArgs.ForceWss {
RuntimeArgs.Socket = "wss"
bestIP := ""
for _, address := range addrs {
// check the address type and if it is not a loopback the display it
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return ipnet.IP.String()
}
}
fmt.Println("--------------------------")
fmt.Println("cowyo (version " + VersionNum + ") is up and running on http://" + RuntimeArgs.ExternalIP)
fmt.Println("Admin key: " + RuntimeArgs.AdminKey)
fmt.Println("--------------------------")
r.Run(RuntimeArgs.Port)
}
return bestIP
}
func restoreFile(path string, f os.FileInfo, err error) error {
fName := filepath.Base(path)
buf := bytes.NewBuffer(nil)
fOpen, _ := os.Open(path) // Error handling elided for brevity.
io.Copy(buf, fOpen) // Error handling elided for brevity.
fOpen.Close()
s := string(buf.Bytes())
fmt.Println(fName)
p := WikiData{fName, "", []string{}, []string{}, false, ""}
p.save(s)
return nil
// exists returns whether the given file or directory exists or not
func exists(path string) bool {
_, err := os.Stat(path)
return !os.IsNotExist(err)
}
func logger(debug bool) *lumber.ConsoleLogger {
if !debug {
return lumber.NewConsoleLogger(lumber.WARN)
}
return lumber.NewConsoleLogger(lumber.TRACE)
}

View File

@ -1,59 +0,0 @@
import os
"""DEFUNCT
darwin arm
darwin arm64
dragonfly amd64
freebsd 386
freebsd amd64
freebsd arm
linux 386
linux arm64
linux ppc64le
netbsd 386
netbsd amd64
netbsd arm
openbsd 386
openbsd amd64
openbsd arm
plan9 386
plan9 amd64
solaris amd64
windows 386
darwin 386
darwin amd64
linux arm
linux ppc64
windows amd64"""
arches = """linux amd64
windows amd64
linux arm
darwin amd64"""
arches = arches.split("\n")
version = "1.0"
programName = "awwkoala"
try:
os.system("rm -rf builds")
except:
pass
os.mkdir("builds")
for arch in arches:
goos = arch.split()[0]
goarch = arch.split()[1]
exe = ""
if "windows" in goos:
exe = ".exe"
cmd1 = 'env GOOS=%(goos)s GOARCH=%(goarch)s go build -o builds/%(programName)s%(exe)s' % {'goos':goos,'goarch':goarch,'exe':exe,'programName':programName}
cmd2 = 'zip -r %(programName)s-%(version)s-%(goos)s-%(goarch)s.zip %(programName)s%(exe)s ../templates ../static' % {'goos':goos,'goarch':goarch,'exe':exe,'version':version,'programName':programName}
print(cmd1)
os.system(cmd1)
os.chdir("builds")
print(cmd2)
os.system(cmd2)
cmd3 = 'rm %(programName)s%(exe)s' % {'exe':exe,'programName':programName}
print(cmd3)
os.system(cmd3)
os.chdir("../")

40
multisite_sample.toml Normal file
View File

@ -0,0 +1,40 @@
[default]
dataDir = "root_data/${HOST}"
maxDocumentLength = 100000000
# Specify multiple times to change keys without expiring sessions
[[default.CookieKeys]]
authenticateBase64 = "RpW4LjGCPNOx75G8DrywmzlEHLB/ISXCAAayZ47Ifkc="
encryptBase64 = "ofCKkrfosQb5T4cvz7R5IMP4BQUDHOPsLSMZZy2CUOA="
[[sites]]
host = "nerdy.party"
dataDir = "somewhere else"
# theme = "custom.css" # TODO: Theme support. Would prefer to move to a complete directory replacement.
defaultPage = "welcome"
allowInsecureMarkup = true
lock = "1234"
debounceSave = 600
diary = true
accessCode = "correct horse battery staple"
fileUploadsAllowed = true
maxFileUploadMb = 6
port = 8090
#[sites.TLS]
# TODO: ACME support eg letsencrypt
#certPath = "path.crt"
#keyPath = "path.key"
#port = 8443
[[sites]]
host = "cowyo.com"
allowInsecureMarkup = false
fileUploadsAllowed = false
port = 8090
# Catchall config
[[sites]]
host = "*"
port = 8100
cookieSecret = "ASADFGKLJSH+4t4cC2X3f7GzsLZ+wtST67qoLuErpugJz06ZIpdDHEjcMxR+XOLM"

View File

@ -1,25 +0,0 @@
package main
import "time"
var bannedIPs []string
func init() {
go clearBannedIPs()
}
func clearBannedIPs() {
for {
bannedIPs = []string{}
time.Sleep(3 * time.Minute)
}
}
func isIPBanned(ip string) bool {
if stringInSlice(ip, bannedIPs) {
return true
} else {
bannedIPs = append(bannedIPs, ip)
return false
}
}

633
routes.go
View File

@ -1,633 +0,0 @@
package main
import (
"bytes"
"fmt"
"html/template"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"path"
"regexp"
"strconv"
"strings"
"time"
"github.com/boltdb/bolt"
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
"github.com/microcosm-cc/bluemonday"
"github.com/russross/blackfriday"
)
const _24K = (1 << 20) * 24
func putFile(c *gin.Context) {
if isIPBanned(c.ClientIP()) {
c.Data(200, "text/plain", []byte("You are rate limited to 20 requests/hour."))
return
}
filename := c.Param("title")
if len(filename) == 0 {
filename = randomAlliterateCombo()
}
contentLength := c.Request.ContentLength
var reader io.Reader
reader = c.Request.Body
if contentLength == -1 {
// queue file to disk, because s3 needs content length
var err error
var f io.Reader
f = reader
var b bytes.Buffer
n, err := io.CopyN(&b, f, _24K+1)
if err != nil && err != io.EOF {
log.Printf("%s", err.Error())
}
if n > _24K {
file, err := ioutil.TempFile("./", "transfer-")
if err != nil {
log.Printf("%s", err.Error())
}
defer file.Close()
n, err = io.Copy(file, io.MultiReader(&b, f))
if err != nil {
os.Remove(file.Name())
log.Printf("%s", err.Error())
}
reader, err = os.Open(file.Name())
} else {
reader = bytes.NewReader(b.Bytes())
}
contentLength = n
}
buf := new(bytes.Buffer)
buf.ReadFrom(reader)
// p := WikiData{filename, "", []string{}, []string{}, false, ""}
// p.save(buf.String())
var p WikiData
p.load(strings.ToLower(filename))
p.save(buf.String())
c.Data(200, "text/plain", []byte("File uploaded to http://"+RuntimeArgs.ExternalIP+"/"+filename))
}
type EncryptionPost struct {
Text string `form:"text" json:"text" binding:"required"`
Password string `form:"password" json:"password" binding:"required"`
}
func encryptionRoute(c *gin.Context) {
title := c.Param("title")
option := c.Param("option")
var jsonLoad EncryptionPost
if option == "/decrypt" {
if c.BindJSON(&jsonLoad) == nil {
var err error
currentText, _, _, _, encrypted, _, _ := getCurrentText(title, -1)
if encrypted == true {
currentText, err = decryptString(currentText, jsonLoad.Password)
if err != nil {
c.JSON(200, gin.H{
"status": "Inorrect passphrase.",
"title": title,
"option": option,
"success": false,
})
} else {
p := WikiData{strings.ToLower(title), "", []string{}, []string{}, false, ""}
p.save(currentText)
c.JSON(200, gin.H{
"status": "posted",
"title": title,
"option": option,
"success": true,
})
}
}
} else {
c.JSON(200, gin.H{
"status": "Could not bind",
"title": title,
"option": option,
"success": false,
})
}
}
if option == "/encrypt" {
if c.BindJSON(&jsonLoad) == nil {
p := WikiData{strings.ToLower(title), "", []string{}, []string{}, true, ""}
p.save(encryptString(jsonLoad.Text, jsonLoad.Password))
c.JSON(200, gin.H{
"status": "posted",
"title": title,
"option": option,
"success": true,
})
} else {
c.JSON(200, gin.H{
"status": "posted",
"title": title,
"option": option,
"success": false,
})
}
}
if option == "/lock" {
if c.BindJSON(&jsonLoad) == nil {
var p WikiData
err := p.load(strings.ToLower(title))
if err != nil {
panic(err)
}
p.Locked = jsonLoad.Password
p.save(p.CurrentText)
c.JSON(200, gin.H{
"status": "posted",
"title": title,
"option": option,
"success": true,
})
} else {
c.JSON(200, gin.H{
"status": "posted",
"title": title,
"option": option,
"success": false,
})
}
}
if option == "/unlock" {
if c.BindJSON(&jsonLoad) == nil {
var p WikiData
err := p.load(strings.ToLower(title))
if err != nil {
panic(err)
}
if len(p.Locked) > 0 && p.Locked == jsonLoad.Password {
p.Locked = ""
p.save(p.CurrentText)
c.JSON(200, gin.H{
"status": "Unlocked!",
"title": title,
"option": option,
"success": true,
})
} else {
c.JSON(200, gin.H{
"status": "Incorrect password!",
"title": title,
"option": option,
"success": false,
})
}
} else {
c.JSON(200, gin.H{
"status": "posted",
"title": title,
"option": option,
"success": false,
})
}
}
}
func newNote(c *gin.Context) {
title := randomAlliterateCombo()
c.Redirect(302, "/"+title)
}
func getCodeType(title string) string {
if strings.Contains(title, ".js") {
return "javascript"
} else if strings.Contains(title, ".py") {
return "python"
} else if strings.Contains(title, ".go") {
return "go"
} else if strings.Contains(title, ".html") {
return "htmlmixed"
} else if strings.Contains(title, ".md") {
return "markdown"
} else if strings.Contains(title, ".sh") {
return "shell"
} else if strings.Contains(title, ".css") {
return "css"
}
return ""
}
func getRecentlyEdited(title string, c *gin.Context) []string {
session := sessions.Default(c)
var recentlyEdited string
v := session.Get("recentlyEdited")
editedThings := []string{}
if v == nil {
recentlyEdited = title
} else {
editedThings = strings.Split(v.(string), "|||")
if !stringInSlice(title, editedThings) {
recentlyEdited = v.(string) + "|||" + title
} else {
recentlyEdited = v.(string)
}
}
session.Set("recentlyEdited", recentlyEdited)
session.Save()
return editedThings
}
func editNote(c *gin.Context) {
title := c.Param("title")
if title == "ws" {
wshandler(c.Writer, c.Request)
} else if title == "robots.txt" {
robotsTxtFile, _ := ioutil.ReadFile(path.Join(RuntimeArgs.SourcePath, "static/text/robots.txt"))
c.Data(200, "text/plain", robotsTxtFile)
} else if title == "sitemap.xml" {
robotsTxtFile, _ := ioutil.ReadFile(path.Join(RuntimeArgs.SourcePath, "static/text/sitemap.xml"))
c.Data(200, "text/plain", robotsTxtFile)
} else if strings.ToLower(title) == "help" { //}&& strings.Contains(AllowedIPs, c.ClientIP()) != true {
c.Redirect(302, "/Help/view")
} else {
version := c.DefaultQuery("version", "-1")
versionNum, _ := strconv.Atoi(version)
currentText, versions, currentVersion, totalTime, encrypted, locked, currentVersionNum := getCurrentText(title, versionNum)
if strings.Contains(c.Request.Header.Get("User-Agent"), "curl/") {
c.Data(200, "text/plain", []byte(currentText))
return
}
if encrypted || len(locked) > 0 {
c.Redirect(302, "/"+title+"/view")
return
}
if strings.Contains(currentText, "self-destruct\n") || strings.Contains(currentText, "\nself-destruct") {
c.Redirect(302, "/"+title+"/view")
return
}
numRows := len(strings.Split(currentText, "\n")) + 10
totalTimeString := totalTime.String()
if totalTime.Seconds() < 1 {
totalTimeString = "< 1 s"
}
splitStrings := strings.Split(title, ".")
suffix := splitStrings[len(splitStrings)-1]
CodeType := getCodeType(title)
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"Title": title,
"WikiName": RuntimeArgs.WikiName,
"ExternalIP": RuntimeArgs.ExternalIP,
"CurrentText": currentText,
"CurrentVersionNum": currentVersionNum,
"NumRows": numRows,
"Versions": versions,
"TotalTime": totalTimeString,
"SocketType": RuntimeArgs.Socket,
"NoEdit": !currentVersion,
"Coding": len(CodeType) > 0,
"CodeType": CodeType,
"Suffix": suffix,
"RecentlyEdited": getRecentlyEdited(title, c),
})
}
}
func everythingElse(c *gin.Context) {
option := c.Param("option")
title := c.Param("title")
if option == "/view" || option == "/v" {
version := c.DefaultQuery("version", "-1")
noprompt := c.DefaultQuery("noprompt", "-1")
versionNum, _ := strconv.Atoi(version)
if strings.ToLower(title) == "help" {
versionNum = -1
}
currentText, versions, _, totalTime, encrypted, locked, _ := getCurrentText(title, versionNum)
if (strings.Contains(currentText, "self-destruct\n") || strings.Contains(currentText, "\nself-destruct")) && strings.ToLower(title) != "help" {
currentText = strings.Replace(currentText, "self-destruct\n", `> *This page has been deleted, you cannot return after closing.*`+"\n", 1)
currentText = strings.Replace(currentText, "\nself-destruct", "\n"+`> *This page has been deleted, you cannot return after closing.*`, 1)
p := WikiData{strings.ToLower(title), "", []string{}, []string{}, false, ""}
p.save("")
}
renderMarkdown(c, currentText, title, versions, "", totalTime, encrypted, noprompt == "-1", len(locked) > 0, getRecentlyEdited(title, c))
} else if option == "/raw" {
version := c.DefaultQuery("version", "-1")
versionNum, _ := strconv.Atoi(version)
if strings.ToLower(title) == "help" {
versionNum = -1
}
currentText, _, _, _, _, _, _ := getCurrentText(title, versionNum)
c.Writer.Header().Set("Content-Type", contentType(title))
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Max-Age", "86400")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, X-Max")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Data(200, contentType(title), []byte(currentText))
} else if title == "ls" && option == "/"+RuntimeArgs.AdminKey && len(RuntimeArgs.AdminKey) > 1 {
renderMarkdown(c, listEverything(), "ls", nil, RuntimeArgs.AdminKey, time.Now().Sub(time.Now()), false, false, false, []string{})
} else if option == "/list" || option == "/l" {
renderList(c, title)
} else if title == "static" {
serveStaticFile(c, option)
} else {
c.Redirect(302, "/"+title)
}
}
func serveStaticFile(c *gin.Context, option string) {
staticFile, err := ioutil.ReadFile(path.Join(RuntimeArgs.SourcePath, "static") + option)
if err != nil {
c.AbortWithStatus(404)
} else {
c.Data(200, contentType(option), []byte(staticFile))
}
}
func renderMarkdown(c *gin.Context, currentText string, title string, versions []versionsInfo, AdminKey string, totalTime time.Duration, encrypted bool, noprompt bool, locked bool, recentlyEdited []string) {
originalText := currentText
CodeType := getCodeType(title)
if CodeType == "markdown" {
CodeType = ""
}
r, _ := regexp.Compile("\\[\\[(.*?)\\]\\]")
for _, s := range r.FindAllString(currentText, -1) {
currentText = strings.Replace(currentText, s, "["+s[2:len(s)-2]+"](/"+s[2:len(s)-2]+"/view)", 1)
}
unsafe := blackfriday.MarkdownCommon([]byte(currentText))
pClean := bluemonday.UGCPolicy()
pClean.AllowElements("img")
pClean.AllowAttrs("alt").OnElements("img")
pClean.AllowAttrs("src").OnElements("img")
pClean.AllowAttrs("class").OnElements("a")
pClean.AllowAttrs("href").OnElements("a")
pClean.AllowAttrs("id").OnElements("a")
pClean.AllowDataURIImages()
html := pClean.SanitizeBytes(unsafe)
html2 := string(html)
r, _ = regexp.Compile("\\$\\$(.*?)\\$\\$")
for _, s := range r.FindAllString(html2, -1) {
html2 = strings.Replace(html2, s, "<span class='texp' data-expr='"+s[2:len(s)-2]+"'></span>", 1)
}
r, _ = regexp.Compile("\\$(.*?)\\$")
for _, s := range r.FindAllString(html2, -1) {
html2 = strings.Replace(html2, s, "<span class='texi' data-expr='"+s[1:len(s)-1]+"'></span>", 1)
}
html2 = strings.Replace(html2, "&amp;#36;", "&#36;", -1)
html2 = strings.Replace(html2, "&amp;#91;", "&#91;", -1)
html2 = strings.Replace(html2, "&amp;#93;", "&#93;", -1)
html2 = strings.Replace(html2, "&amp35;", "&#35;", -1)
totalTimeString := totalTime.String()
if totalTime.Seconds() < 1 {
totalTimeString = "< 1 s"
}
if encrypted {
CodeType = "asciiarmor"
}
c.HTML(http.StatusOK, "view.tmpl", gin.H{
"Title": title,
"WikiName": RuntimeArgs.WikiName,
"Body": template.HTML([]byte(html2)),
"CurrentText": originalText,
"Versions": versions,
"TotalTime": totalTimeString,
"AdminKey": AdminKey,
"Encrypted": encrypted,
"Locked": locked,
"Prompt": noprompt,
"LockedOrEncrypted": locked || encrypted,
"Coding": len(CodeType) > 0,
"CodeType": CodeType,
"RecentlyEdited": recentlyEdited,
})
}
func reorderList(text string) ([]template.HTML, []string) {
listItemsString := ""
for _, lineString := range strings.Split(text, "\n") {
if len(lineString) > 1 {
if string(lineString[0]) != "-" {
listItemsString += "- " + lineString + "\n"
} else {
listItemsString += lineString + "\n"
}
}
}
// get ordering of template.HTML for rendering
renderedListString := string(blackfriday.MarkdownCommon([]byte(listItemsString)))
listItems := []template.HTML{}
endItems := []template.HTML{}
for _, lineString := range strings.Split(renderedListString, "\n") {
if len(lineString) > 1 {
if strings.Contains(lineString, "<del>") || strings.Contains(lineString, "</ul>") {
endItems = append(endItems, template.HTML(lineString))
} else {
listItems = append(listItems, template.HTML(lineString))
}
}
}
// get ordering of strings for deleting
listItemsStringArray := []string{}
endItemsStringArray := []string{}
for _, lineString := range strings.Split(listItemsString, "\n") {
if len(lineString) > 1 {
if strings.Contains(lineString, "~~") {
endItemsStringArray = append(endItemsStringArray, lineString)
} else {
listItemsStringArray = append(listItemsStringArray, lineString)
}
}
}
return append(listItems, endItems...), append(listItemsStringArray, endItemsStringArray...)
}
func renderList(c *gin.Context, title string) {
if strings.ToLower(title) == "help" { //}&& strings.Contains(AllowedIPs, c.ClientIP()) != true {
c.Redirect(302, "/Help/view")
}
var p WikiData
err := p.load(strings.ToLower(title))
if err != nil {
panic(err)
}
currentText := p.CurrentText
if strings.Contains(currentText, "self-destruct\n") || strings.Contains(currentText, "\nself-destruct") {
c.Redirect(302, "/"+title+"/view")
}
if p.Encrypted || len(p.Locked) > 0 {
c.Redirect(302, "/"+title+"/view")
}
pClean := bluemonday.UGCPolicy()
pClean.AllowElements("img")
pClean.AllowAttrs("alt").OnElements("img")
pClean.AllowAttrs("src").OnElements("img")
pClean.AllowAttrs("class").OnElements("a")
pClean.AllowAttrs("href").OnElements("a")
pClean.AllowAttrs("id").OnElements("a")
pClean.AllowDataURIImages()
text := pClean.SanitizeBytes([]byte(p.CurrentText))
listItems, _ := reorderList(string(text))
for i := range listItems {
newHTML := strings.Replace(string(listItems[i]), "</a>", "</a>"+`<span id="`+strconv.Itoa(i)+`" class="deletable">`, -1)
newHTML = strings.Replace(newHTML, "<a href=", "</span><a href=", -1)
newHTML = strings.Replace(newHTML, "<li>", "<li>"+`<span id="`+strconv.Itoa(i)+`" class="deletable">`, -1)
newHTML = strings.Replace(newHTML, "</li>", "</span></li>", -1)
newHTML = strings.Replace(newHTML, "<li>"+`<span id="`+strconv.Itoa(i)+`" class="deletable"><del>`, "<li><del>"+`<span id="`+strconv.Itoa(i)+`" class="deletable">`, -1)
newHTML = strings.Replace(newHTML, "</del></span></li>", "</span></del></li>", -1)
listItems[i] = template.HTML([]byte(newHTML))
}
c.HTML(http.StatusOK, "list.tmpl", gin.H{
"Title": title,
"WikiName": RuntimeArgs.WikiName,
"ListItems": listItems,
"RecentlyEdited": getRecentlyEdited(title, c),
})
}
func deleteListItem(c *gin.Context) {
lineNum, err := strconv.Atoi(c.DefaultQuery("lineNum", "None"))
title := c.Query("title") // shortcut for c.Request.URL.Query().Get("lastname")
if err == nil {
var p WikiData
err := p.load(strings.ToLower(title))
if err != nil {
panic(err)
}
_, listItems := reorderList(p.CurrentText)
newText := p.CurrentText
for i, lineString := range listItems {
// fmt.Println(i, lineString, lineNum)
if i+1 == lineNum {
// fmt.Println("MATCHED")
if strings.Contains(lineString, "~~") == false {
// fmt.Println(p.Text, "("+lineString[2:]+"\n"+")", "~~"+lineString[2:]+"~~"+"\n")
newText = strings.Replace(newText+"\n", lineString[2:]+"\n", "~~"+strings.TrimSpace(lineString[2:])+"~~"+"\n", 1)
} else {
newText = strings.Replace(newText+"\n", lineString[2:]+"\n", lineString[4:len(lineString)-2]+"\n", 1)
}
p.save(newText)
break
}
}
c.JSON(200, gin.H{
"message": "Done.",
})
} else {
c.JSON(404, gin.H{
"message": "?",
})
}
}
func deletePage(c *gin.Context) {
deleteName := c.DefaultQuery("DeleteName", "None")
// if adminKey == RuntimeArgs.AdminKey || true == true {
if strings.ToLower(deleteName) != "help" {
p := WikiData{strings.ToLower(deleteName), "", []string{}, []string{}, false, ""}
p.save("")
}
// // remove from program data
// var deleteKey []byte
// foundKey := false
// err := db.View(func(tx *bolt.Tx) error {
// b := tx.Bucket([]byte("programdata"))
// c := b.Cursor()
// for k, v := c.First(); k != nil; k, v = c.Next() {
// if strings.ToLower(string(v)) == strings.ToLower(deleteName) {
// fmt.Println("FOUND " + string(v))
// deleteKey = k
// foundKey = true
// break
// }
// }
// return nil
// })
// if err != nil {
// panic(err)
// }
// if foundKey == true {
// fmt.Println(len([]string{}))
// fmt.Println(deleteKey)
// db.View(func(tx *bolt.Tx) error {
// b := tx.Bucket([]byte("programdata"))
// err := b.Delete(deleteKey)
// return err
// })
// }
// return OKAY
c.JSON(200, gin.H{
"message": "Done.",
})
// } else {
// c.JSON(404, gin.H{
// "message": "?",
// })
// }
}
func listEverything() string {
Open(RuntimeArgs.DatabaseLocation)
defer Close()
everything := `| Title | Current size | Changes | Total Size | |
| --------- |-------------| -----| ------------- | ------------- |
`
db.View(func(tx *bolt.Tx) error {
// Assume bucket exists and has keys
b := tx.Bucket([]byte("datas"))
c := b.Cursor()
for k, v := c.First(); k != nil; k, v = c.Next() {
var p WikiData
p.load(string(k))
if len(p.CurrentText) > 1 {
contentSize := strconv.Itoa(len(p.CurrentText))
numChanges := strconv.Itoa(len(p.Diffs))
totalSize := strconv.Itoa(len(v))
everything += "| [" + p.Title + "](/" + p.Title + "/view) | " + contentSize + " | " + numChanges + " | " + totalSize + ` | <a class="deleteable" id="` + p.Title + `">Delete</a> | ` + "\n"
}
}
return nil
})
return everything
}
func dumpEverything(folderpath string) {
Open(RuntimeArgs.DatabaseLocation)
defer Close()
err := os.MkdirAll(folderpath, 0777)
if err != nil {
fmt.Printf("%s folder already exists.", folderpath)
}
db.View(func(tx *bolt.Tx) error {
// Assume bucket exists and has keys
b := tx.Bucket([]byte("datas"))
c := b.Cursor()
for k, _ := c.First(); k != nil; k, _ = c.Next() {
var p WikiData
p.load(string(k))
if len(p.CurrentText) > 0 {
ioutil.WriteFile(path.Join(folderpath, string(k)), []byte(p.CurrentText), 0644)
}
}
return nil
})
}

1174
server/bindata-debug.go Normal file

File diff suppressed because it is too large Load Diff

1309
server/bindata.go Normal file

File diff suppressed because one or more lines are too long

10
server/debug.go Normal file
View File

@ -0,0 +1,10 @@
// +build debug
package server
import "github.com/jcelliott/lumber"
func init() {
hotTemplateReloading = true
LogLevel = lumber.TRACE
}

889
server/handlers.go Executable file
View File

@ -0,0 +1,889 @@
package server
import (
"crypto/sha256"
"fmt"
"html/template"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path"
"strconv"
"strings"
"sync"
"time"
secretRequired "github.com/danielheath/gin-teeny-security"
"github.com/gin-contrib/multitemplate"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
"github.com/jcelliott/lumber"
"github.com/schollz/cowyo/encrypt"
)
const minutesToUnlock = 10.0
type Site struct {
PathToData string
Css []byte
DefaultPage string
DefaultPassword string
Debounce int
Diary bool
SessionStore cookie.Store
SecretCode string
AllowInsecure bool
Fileuploads bool
MaxUploadSize uint
Logger *lumber.ConsoleLogger
MaxDocumentSize uint // in runes; about a 10mb limit by default
saveMut sync.Mutex
sitemapUpToDate bool // TODO this makes everything use a pointer
}
func (s *Site) defaultLock() string {
if s.DefaultPassword == "" {
return ""
}
return HashPassword(s.DefaultPassword)
}
var hotTemplateReloading bool
var LogLevel int = lumber.WARN
func Serve(
filepathToData,
host,
port,
crt_path,
key_path string,
TLS bool,
cssFile string,
defaultPage string,
defaultPassword string,
debounce int,
diary bool,
secret string,
secretCode string,
allowInsecure bool,
fileuploads bool,
maxUploadSize uint,
maxDocumentSize uint,
logger *lumber.ConsoleLogger,
) {
var customCSS []byte
// collect custom CSS
if len(cssFile) > 0 {
var errRead error
customCSS, errRead = ioutil.ReadFile(cssFile)
if errRead != nil {
fmt.Println(errRead)
return
}
fmt.Printf("Loaded CSS file, %d bytes\n", len(customCSS))
}
router := Site{
PathToData: filepathToData,
Css: customCSS,
DefaultPage: defaultPage,
DefaultPassword: defaultPassword,
Debounce: debounce,
Diary: diary,
SessionStore: cookie.NewStore([]byte(secret)),
SecretCode: secretCode,
AllowInsecure: allowInsecure,
Fileuploads: fileuploads,
MaxUploadSize: maxUploadSize,
Logger: logger,
MaxDocumentSize: maxDocumentSize,
}.Router()
if TLS {
http.ListenAndServeTLS(host+":"+port, crt_path, key_path, router)
} else {
panic(router.Run(host + ":" + port))
}
}
func (s Site) Router() *gin.Engine {
if s.Logger == nil {
s.Logger = lumber.NewConsoleLogger(lumber.TRACE)
}
if hotTemplateReloading {
gin.SetMode(gin.DebugMode)
} else {
gin.SetMode(gin.ReleaseMode)
}
router := gin.Default()
router.SetFuncMap(template.FuncMap{
"sniffContentType": s.sniffContentType,
})
if hotTemplateReloading {
router.LoadHTMLGlob("templates/*.tmpl")
} else {
router.HTMLRender = s.loadTemplates("index.tmpl")
}
router.Use(sessions.Sessions("_session", s.SessionStore))
if s.SecretCode != "" {
cfg := &secretRequired.Config{
Secret: s.SecretCode,
Path: "/login/",
RequireAuth: func(c *gin.Context) bool {
page := c.Param("page")
cmd := c.Param("command")
if page == "sitemap.xml" || page == "favicon.ico" || page == "static" || page == "uploads" {
return false // no auth for these
}
if page != "" && cmd == "/read" {
p := s.Open(page)
if p != nil && p.IsPublished {
return false // Published pages don't require auth.
}
}
return true
},
}
router.Use(cfg.Middleware)
}
// router.Use(static.Serve("/static/", static.LocalFile("./static", true)))
router.GET("/", func(c *gin.Context) {
if s.DefaultPage != "" {
c.Redirect(302, "/"+s.DefaultPage+"/read")
} else {
c.Redirect(302, "/"+randomAlliterateCombo())
}
})
router.POST("/uploads", s.handleUpload)
router.GET("/:page", func(c *gin.Context) {
page := c.Param("page")
c.Redirect(302, "/"+page+"/")
})
router.GET("/:page/*command", s.handlePageRequest)
router.POST("/update", s.handlePageUpdate)
router.POST("/relinquish", s.handlePageRelinquish) // relinquish returns the page no matter what (and destroys if nessecary)
router.POST("/exists", s.handlePageExists)
router.POST("/prime", s.handlePrime)
router.POST("/lock", s.handleLock)
router.POST("/publish", s.handlePublish)
router.POST("/encrypt", s.handleEncrypt)
router.DELETE("/oldlist", s.handleClearOldListItems)
router.DELETE("/listitem", s.deleteListItem)
// start long-processes as threads
go s.thread_SiteMap()
// Allow iframe/scripts in markup?
allowInsecureHtml = s.AllowInsecure
return router
}
func (s *Site) loadTemplates(list ...string) multitemplate.Render {
r := multitemplate.New()
for _, x := range list {
templateString, err := Asset("templates/" + x)
if err != nil {
panic(err)
}
tmplMessage, err := template.New(x).Funcs(template.FuncMap{
"sniffContentType": s.sniffContentType,
}).Parse(string(templateString))
if err != nil {
panic(err)
}
r.Add(x, tmplMessage)
}
return r
}
func pageIsLocked(p *Page, c *gin.Context) bool {
// it is easier to reason about when the page is actually unlocked
var unlocked = !p.IsLocked ||
(p.IsLocked && p.UnlockedFor == getSetSessionID(c))
return !unlocked
}
func (s *Site) handlePageRelinquish(c *gin.Context) {
type QueryJSON struct {
Page string `json:"page"`
}
var json QueryJSON
err := c.BindJSON(&json)
if err != nil {
s.Logger.Trace(err.Error())
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Wrong JSON"})
return
}
if len(json.Page) == 0 {
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Must specify `page`"})
return
}
message := "Relinquished"
p := s.Open(json.Page)
name := p.Meta
if name == "" {
name = json.Page
}
text := p.Text.GetCurrent()
isLocked := pageIsLocked(p, c)
isEncrypted := p.IsEncrypted
destroyed := p.IsPrimedForSelfDestruct
if !isLocked && p.IsPrimedForSelfDestruct {
p.Erase()
message = "Relinquished and erased"
}
c.JSON(http.StatusOK, gin.H{"success": true,
"name": name,
"message": message,
"text": text,
"locked": isLocked,
"encrypted": isEncrypted,
"destroyed": destroyed})
}
func getSetSessionID(c *gin.Context) (sid string) {
var (
session = sessions.Default(c)
v = session.Get("sid")
)
if v != nil {
sid = v.(string)
}
if v == nil || sid == "" {
sid = RandStringBytesMaskImprSrc(8)
session.Set("sid", sid)
session.Save()
}
return sid
}
func (s *Site) thread_SiteMap() {
for {
if !s.sitemapUpToDate {
s.Logger.Info("Generating sitemap...")
s.sitemapUpToDate = true
ioutil.WriteFile(path.Join(s.PathToData, "sitemap.xml"), []byte(s.generateSiteMap()), 0644)
s.Logger.Info("..finished generating sitemap")
}
time.Sleep(time.Second)
}
}
func (s *Site) generateSiteMap() (sitemap string) {
files, _ := ioutil.ReadDir(s.PathToData)
lastEdited := make([]string, len(files))
names := make([]string, len(files))
i := 0
for _, f := range files {
names[i] = DecodeFileName(f.Name())
p := s.Open(names[i])
if p.IsPublished {
lastEdited[i] = time.Unix(p.Text.LastEditTime()/1000000000, 0).Format("2006-01-02")
i++
}
}
names = names[:i]
lastEdited = lastEdited[:i]
sitemap = ""
for i := range names {
sitemap += fmt.Sprintf(`
<url>
<loc>{{ .Request.Host }}/%s/read</loc>
<lastmod>%s</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
`, names[i], lastEdited[i])
}
sitemap += "</urlset>"
return
}
func (s *Site) handlePageRequest(c *gin.Context) {
page := c.Param("page")
command := c.Param("command")
if page == "sitemap.xml" {
siteMap, err := ioutil.ReadFile(path.Join(s.PathToData, "sitemap.xml"))
if err != nil {
c.Data(http.StatusInternalServerError, contentType("sitemap.xml"), []byte(""))
} else {
fmt.Fprintln(c.Writer, `<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">`)
template.Must(template.New("sitemap").Parse(string(siteMap))).Execute(c.Writer, c)
}
return
} else if page == "favicon.ico" {
data, _ := Asset("/static/img/cowyo/favicon.ico")
c.Data(http.StatusOK, contentType("/static/img/cowyo/favicon.ico"), data)
return
} else if page == "static" {
filename := page + command
var data []byte
if filename == "static/css/custom.css" {
data = s.Css
} else {
var errAssset error
data, errAssset = Asset(filename)
if errAssset != nil {
c.String(http.StatusInternalServerError, "Could not find data")
}
}
c.Data(http.StatusOK, contentType(filename), data)
return
} else if page == "uploads" {
if len(command) == 0 || command == "/" || command == "/edit" {
if !s.Fileuploads {
c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("Uploads are disabled on this server"))
return
}
} else {
command = command[1:]
if !strings.HasSuffix(command, ".upload") {
command = command + ".upload"
}
pathname := path.Join(s.PathToData, command)
if allowInsecureHtml {
c.Header(
"Content-Disposition",
`inline; filename="`+c.DefaultQuery("filename", "upload")+`"`,
)
} else {
// Prevent malicious html uploads by forcing type to plaintext and 'download-instead-of-view'
c.Header("Content-Type", "text/plain")
c.Header(
"Content-Disposition",
`attachment; filename="`+c.DefaultQuery("filename", "upload")+`"`,
)
}
c.File(pathname)
return
}
}
p := s.Open(page)
if len(command) < 2 {
if p.IsPublished {
c.Redirect(302, "/"+page+"/read")
} else {
c.Redirect(302, "/"+page+"/edit")
}
return
}
// use the default lock
if s.defaultLock() != "" && p.IsNew() {
p.IsLocked = true
p.PassphraseToUnlock = s.defaultLock()
}
version := c.DefaultQuery("version", "ajksldfjl")
isLocked := pageIsLocked(p, c)
// Disallow anything but viewing locked/encrypted pages
if (p.IsEncrypted || isLocked) &&
(command[0:2] != "/v" && command[0:2] != "/r") {
c.Redirect(302, "/"+page+"/view")
return
}
// Destroy page if it is opened and primed
if p.IsPrimedForSelfDestruct && !isLocked && !p.IsEncrypted {
p.Update("<center><em>This page has self-destructed. You cannot return to it.</em></center>\n\n" + p.Text.GetCurrent())
p.Erase()
if p.IsPublished {
command = "/read"
} else {
command = "/view"
}
}
if command == "/erase" {
if !isLocked && !p.IsEncrypted {
p.Erase()
c.Redirect(302, "/"+page+"/edit")
} else {
c.Redirect(302, "/"+page+"/view")
}
return
}
rawText := p.Text.GetCurrent()
rawHTML := p.RenderedPage
// Check to see if an old version is requested
versionInt, versionErr := strconv.Atoi(version)
if versionErr == nil && versionInt > 0 {
versionText, err := p.Text.GetPreviousByTimestamp(int64(versionInt))
if err == nil {
rawText = versionText
rawHTML = GithubMarkdownToHTML(rawText)
}
}
// Get history
var versionsInt64 []int64
var versionsChangeSums []int
var versionsText []string
if command[0:2] == "/h" {
versionsInt64, versionsChangeSums = p.Text.GetMajorSnapshotsAndChangeSums(60) // get snapshots 60 seconds apart
versionsText = make([]string, len(versionsInt64))
for i, v := range versionsInt64 {
versionsText[i] = time.Unix(v/1000000000, 0).Format("Mon Jan 2 15:04:05 MST 2006")
}
versionsText = reverseSliceString(versionsText)
versionsInt64 = reverseSliceInt64(versionsInt64)
versionsChangeSums = reverseSliceInt(versionsChangeSums)
}
if len(command) > 3 && command[0:3] == "/ra" {
c.Writer.Header().Set("Content-Type", "text/plain")
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Max-Age", "86400")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, X-Max")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Data(200, contentType(p.Name), []byte(rawText))
return
}
var DirectoryEntries []os.FileInfo
if page == "ls" {
command = "/view"
DirectoryEntries = s.DirectoryList()
}
if page == "uploads" {
command = "/view"
var err error
DirectoryEntries, err = s.UploadList()
if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}
}
// swap out /view for /read if it is published
if p.IsPublished {
rawHTML = strings.Replace(rawHTML, "/view", "/read", -1)
}
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"EditPage": command[0:2] == "/e", // /edit
"ViewPage": command[0:2] == "/v", // /view
"ListPage": command[0:2] == "/l", // /list
"HistoryPage": command[0:2] == "/h", // /history
"ReadPage": command[0:2] == "/r", // /history
"DontKnowPage": command[0:2] != "/e" &&
command[0:2] != "/v" &&
command[0:2] != "/l" &&
command[0:2] != "/r" &&
command[0:2] != "/h",
"DirectoryPage": page == "ls" || page == "uploads",
"UploadPage": page == "uploads",
"DirectoryEntries": DirectoryEntries,
"Page": page,
"RenderedPage": template.HTML([]byte(rawHTML)),
"RawPage": rawText,
"Versions": versionsInt64,
"VersionsText": versionsText,
"VersionsChangeSums": versionsChangeSums,
"IsLocked": isLocked,
"IsEncrypted": p.IsEncrypted,
"ListItems": renderList(rawText),
"Route": "/" + page + command,
"HasDotInName": strings.Contains(page, "."),
"RecentlyEdited": getRecentlyEdited(page, c),
"IsPublished": p.IsPublished,
"CustomCSS": len(s.Css) > 0,
"Debounce": s.Debounce,
"DiaryMode": s.Diary,
"Date": time.Now().Format("2006-01-02"),
"UnixTime": time.Now().Unix(),
"ChildPageNames": p.ChildPageNames(),
"AllowFileUploads": s.Fileuploads,
"MaxUploadMB": s.MaxUploadSize,
})
}
func getRecentlyEdited(title string, c *gin.Context) []string {
session := sessions.Default(c)
var recentlyEdited string
v := session.Get("recentlyEdited")
editedThings := []string{}
if v == nil {
recentlyEdited = title
} else {
editedThings = strings.Split(v.(string), "|||")
if !stringInSlice(title, editedThings) {
recentlyEdited = v.(string) + "|||" + title
} else {
recentlyEdited = v.(string)
}
}
session.Set("recentlyEdited", recentlyEdited)
session.Save()
editedThingsWithoutCurrent := make([]string, len(editedThings))
i := 0
for _, thing := range editedThings {
if thing == title {
continue
}
if strings.Contains(thing, "icon-") {
continue
}
editedThingsWithoutCurrent[i] = thing
i++
}
return editedThingsWithoutCurrent[:i]
}
func (s *Site) handlePageExists(c *gin.Context) {
type QueryJSON struct {
Page string `json:"page"`
}
var json QueryJSON
err := c.BindJSON(&json)
if err != nil {
s.Logger.Trace(err.Error())
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Wrong JSON", "exists": false})
return
}
p := s.Open(json.Page)
if len(p.Text.GetCurrent()) > 0 {
c.JSON(http.StatusOK, gin.H{"success": true, "message": json.Page + " found", "exists": true})
} else {
c.JSON(http.StatusOK, gin.H{"success": true, "message": json.Page + " not found", "exists": false})
}
}
func (s *Site) handlePageUpdate(c *gin.Context) {
type QueryJSON struct {
Page string `json:"page"`
NewText string `json:"new_text"`
FetchedAt int64 `json:"fetched_at"`
IsEncrypted bool `json:"is_encrypted"`
IsPrimed bool `json:"is_primed"`
Meta string `json:"meta"`
}
var json QueryJSON
err := c.BindJSON(&json)
if err != nil {
s.Logger.Trace(err.Error())
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Wrong JSON"})
return
}
if uint(len(json.NewText)) > s.MaxDocumentSize {
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Too much"})
return
}
if len(json.Page) == 0 {
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Must specify `page`"})
return
}
s.Logger.Trace("Update: %v", json)
p := s.Open(json.Page)
var (
message string
sinceLastEdit = time.Since(p.LastEditTime())
)
success := false
if pageIsLocked(p, c) {
if sinceLastEdit < minutesToUnlock {
message = "This page is being edited by someone else"
} else {
// here what might have happened is that two people unlock without
// editing thus they both suceeds but only one is able to edit
message = "Locked, must unlock first"
}
} else if p.IsEncrypted {
message = "Encrypted, must decrypt first"
} else if json.FetchedAt > 0 && p.LastEditUnixTime() > json.FetchedAt {
message = "Refusing to overwrite others work"
} else {
p.Meta = json.Meta
p.Update(json.NewText)
if json.IsEncrypted {
p.IsEncrypted = true
}
if json.IsPrimed {
p.IsPrimedForSelfDestruct = true
}
p.Save()
message = "Saved"
if p.IsPublished {
s.sitemapUpToDate = false
}
success = true
}
c.JSON(http.StatusOK, gin.H{"success": success, "message": message, "unix_time": time.Now().Unix()})
}
func (s *Site) handlePrime(c *gin.Context) {
type QueryJSON struct {
Page string `json:"page"`
}
var json QueryJSON
if c.BindJSON(&json) != nil {
c.String(http.StatusBadRequest, "Problem binding keys")
return
}
s.Logger.Trace("Update: %v", json)
p := s.Open(json.Page)
if pageIsLocked(p, c) {
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Locked"})
return
} else if p.IsEncrypted {
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Encrypted"})
return
}
p.IsPrimedForSelfDestruct = true
p.Save()
c.JSON(http.StatusOK, gin.H{"success": true, "message": "Primed"})
}
func (s *Site) handleLock(c *gin.Context) {
type QueryJSON struct {
Page string `json:"page"`
Passphrase string `json:"passphrase"`
}
var json QueryJSON
if c.BindJSON(&json) != nil {
c.String(http.StatusBadRequest, "Problem binding keys")
return
}
p := s.Open(json.Page)
if s.defaultLock() != "" && p.IsNew() {
p.IsLocked = true // IsLocked was replaced by variable wrt Context
p.PassphraseToUnlock = s.defaultLock()
}
if p.IsEncrypted {
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Encrypted"})
return
}
var (
message string
sessionID = getSetSessionID(c)
sinceLastEdit = time.Since(p.LastEditTime())
)
// both lock/unlock ends here on locked&timeout combination
if p.IsLocked &&
p.UnlockedFor != sessionID &&
p.UnlockedFor != "" &&
sinceLastEdit.Minutes() < minutesToUnlock {
c.JSON(http.StatusOK, gin.H{
"success": false,
"message": fmt.Sprintf("This page is being edited by someone else! Will unlock automatically %2.0f minutes after the last change.", minutesToUnlock-sinceLastEdit.Minutes()),
})
return
}
if !pageIsLocked(p, c) {
p.IsLocked = true
p.PassphraseToUnlock = HashPassword(json.Passphrase)
p.UnlockedFor = ""
message = "Locked"
} else {
err2 := CheckPasswordHash(json.Passphrase, p.PassphraseToUnlock)
if err2 != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Can't unlock"})
return
}
p.UnlockedFor = sessionID
message = "Unlocked only for you"
}
p.Save()
c.JSON(http.StatusOK, gin.H{"success": true, "message": message})
}
func (s *Site) handlePublish(c *gin.Context) {
type QueryJSON struct {
Page string `json:"page"`
Publish bool `json:"publish"`
}
var json QueryJSON
if c.BindJSON(&json) != nil {
c.String(http.StatusBadRequest, "Problem binding keys")
return
}
p := s.Open(json.Page)
p.IsPublished = json.Publish
p.Save()
message := "Published"
if !p.IsPublished {
message = "Unpublished"
}
s.sitemapUpToDate = false
c.JSON(http.StatusOK, gin.H{"success": true, "message": message})
}
func (s *Site) handleUpload(c *gin.Context) {
if !s.Fileuploads {
c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("Uploads are disabled on this server"))
return
}
file, info, err := c.Request.FormFile("file")
defer file.Close()
if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}
h := sha256.New()
if _, err := io.Copy(h, file); err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}
newName := "sha256-" + encodeBytesToBase32(h.Sum(nil))
// Replaces any existing version, but sha256 collisions are rare as anything.
outfile, err := os.Create(path.Join(s.PathToData, newName+".upload"))
if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}
file.Seek(0, io.SeekStart)
_, err = io.Copy(outfile, file)
if err != nil {
c.AbortWithError(http.StatusInternalServerError, err)
return
}
c.Header("Location", "/uploads/"+newName+"?filename="+url.QueryEscape(info.Filename))
return
}
func (s *Site) handleEncrypt(c *gin.Context) {
type QueryJSON struct {
Page string `json:"page"`
Passphrase string `json:"passphrase"`
}
var json QueryJSON
if c.BindJSON(&json) != nil {
c.String(http.StatusBadRequest, "Problem binding keys")
return
}
p := s.Open(json.Page)
if pageIsLocked(p, c) {
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Locked"})
return
}
q := s.Open(json.Page)
var message string
if p.IsEncrypted {
decrypted, err2 := encrypt.DecryptString(p.Text.GetCurrent(), json.Passphrase)
if err2 != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Wrong password"})
return
}
q.Erase()
q = s.Open(json.Page)
q.Update(decrypted)
q.IsEncrypted = false
q.IsLocked = p.IsLocked
q.IsPrimedForSelfDestruct = p.IsPrimedForSelfDestruct
message = "Decrypted"
} else {
currentText := p.Text.GetCurrent()
encrypted, _ := encrypt.EncryptString(currentText, json.Passphrase)
q.Erase()
q = s.Open(json.Page)
q.Update(encrypted)
q.IsEncrypted = true
q.IsLocked = p.IsLocked
q.IsPrimedForSelfDestruct = p.IsPrimedForSelfDestruct
message = "Encrypted"
}
q.Save()
c.JSON(http.StatusOK, gin.H{"success": true, "message": message})
}
func (s *Site) deleteListItem(c *gin.Context) {
lineNum, err := strconv.Atoi(c.DefaultQuery("lineNum", "None"))
page := c.Query("page") // shortcut for c.Request.URL.Query().Get("lastname")
if err == nil {
p := s.Open(page)
_, listItems := reorderList(p.Text.GetCurrent())
newText := p.Text.GetCurrent()
for i, lineString := range listItems {
// fmt.Println(i, lineString, lineNum)
if i+1 == lineNum {
// fmt.Println("MATCHED")
if strings.Contains(lineString, "~~") == false {
// fmt.Println(p.Text, "("+lineString[2:]+"\n"+")", "~~"+lineString[2:]+"~~"+"\n")
newText = strings.Replace(newText+"\n", lineString[2:]+"\n", "~~"+strings.TrimSpace(lineString[2:])+"~~"+"\n", 1)
} else {
newText = strings.Replace(newText+"\n", lineString[2:]+"\n", lineString[4:len(lineString)-2]+"\n", 1)
}
p.Update(newText)
break
}
}
c.JSON(200, gin.H{
"success": true,
"message": "Done.",
})
} else {
c.JSON(200, gin.H{
"success": false,
"message": err.Error(),
})
}
}
func (s *Site) handleClearOldListItems(c *gin.Context) {
type QueryJSON struct {
Page string `json:"page"`
}
var json QueryJSON
if c.BindJSON(&json) != nil {
c.String(http.StatusBadRequest, "Problem binding keys")
return
}
p := s.Open(json.Page)
if p.IsEncrypted {
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Encrypted"})
return
}
if pageIsLocked(p, c) {
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Locked"})
return
}
lines := strings.Split(p.Text.GetCurrent(), "\n")
newLines := make([]string, len(lines))
newLinesI := 0
for _, line := range lines {
if strings.Count(line, "~~") != 2 {
newLines[newLinesI] = line
newLinesI++
}
}
p.Update(strings.Join(newLines[0:newLinesI], "\n"))
p.Save()
c.JSON(http.StatusOK, gin.H{"success": true, "message": "Cleared"})
}

62
server/listify.go Normal file
View File

@ -0,0 +1,62 @@
package server
import (
"html/template"
"strconv"
"strings"
)
func reorderList(text string) ([]template.HTML, []string) {
listItemsString := ""
for _, lineString := range strings.Split(text, "\n") {
if len(lineString) > 1 {
if string(lineString[0]) != "-" {
listItemsString += "- " + lineString + "\n"
} else {
listItemsString += lineString + "\n"
}
}
}
// get ordering of template.HTML for rendering
renderedListString := MarkdownToHtml(listItemsString)
listItems := []template.HTML{}
endItems := []template.HTML{}
for _, lineString := range strings.Split(renderedListString, "\n") {
if len(lineString) > 1 {
if strings.Contains(lineString, "<del>") || strings.Contains(lineString, "</ul>") {
endItems = append(endItems, template.HTML(lineString))
} else {
listItems = append(listItems, template.HTML(lineString))
}
}
}
// get ordering of strings for deleting
listItemsStringArray := []string{}
endItemsStringArray := []string{}
for _, lineString := range strings.Split(listItemsString, "\n") {
if len(lineString) > 1 {
if strings.Contains(lineString, "~~") {
endItemsStringArray = append(endItemsStringArray, lineString)
} else {
listItemsStringArray = append(listItemsStringArray, lineString)
}
}
}
return append(listItems, endItems...), append(listItemsStringArray, endItemsStringArray...)
}
func renderList(currentRawText string) []template.HTML {
listItems, _ := reorderList(currentRawText)
for i := range listItems {
newHTML := strings.Replace(string(listItems[i]), "</a>", "</a>"+`<span id="`+strconv.Itoa(i)+`" class="deletable">`, -1)
newHTML = strings.Replace(newHTML, "<a href=", "</span><a href=", -1)
newHTML = strings.Replace(newHTML, "<li>", "<li>"+`<span id="`+strconv.Itoa(i)+`" class="deletable">`, -1)
newHTML = strings.Replace(newHTML, "</li>", "</span></li>", -1)
newHTML = strings.Replace(newHTML, "<li>"+`<span id="`+strconv.Itoa(i)+`" class="deletable"><del>`, "<li><del>"+`<span id="`+strconv.Itoa(i)+`" class="deletable">`, -1)
newHTML = strings.Replace(newHTML, "</del></span></li>", "</span></del></li>", -1)
listItems[i] = template.HTML([]byte(newHTML))
}
return listItems
}

36
server/migrate.go Executable file
View File

@ -0,0 +1,36 @@
package server
import (
"fmt"
"io/ioutil"
"path"
"github.com/jcelliott/lumber"
)
func Migrate(pathToOldData, pathToData string, logger *lumber.ConsoleLogger) error {
files, err := ioutil.ReadDir(pathToOldData)
if len(files) == 0 {
return err
}
s := Site{PathToData: pathToData, Logger: lumber.NewConsoleLogger(lumber.TRACE)}
for _, f := range files {
if f.Mode().IsDir() {
continue
}
fmt.Printf("Migrating %s", f.Name())
p := s.Open(f.Name())
bData, err := ioutil.ReadFile(path.Join(pathToOldData, f.Name()))
if err != nil {
return err
}
err = p.Update(string(bData))
if err != nil {
return err
}
if err = p.Save(); err != nil {
return err
}
}
return nil
}

207
server/page.go Executable file
View File

@ -0,0 +1,207 @@
package server
import (
"encoding/json"
"io/ioutil"
"os"
"path"
"path/filepath"
"regexp"
"sort"
"strings"
"time"
"github.com/schollz/versionedtext"
)
// Page is the basic struct
type Page struct {
Site *Site
Name string
Text versionedtext.VersionedText
Meta string
RenderedPage string
IsLocked bool
PassphraseToUnlock string
IsEncrypted bool
IsPrimedForSelfDestruct bool
IsPublished bool
UnlockedFor string
}
func (p Page) LastEditTime() time.Time {
return time.Unix(p.LastEditUnixTime(), 0)
}
func (p Page) LastEditUnixTime() int64 {
return p.Text.LastEditTime() / 1000000000
}
func (s *Site) Open(name string) (p *Page) {
p = new(Page)
p.Site = s
p.Name = name
p.Text = versionedtext.NewVersionedText("")
p.Render()
bJSON, err := ioutil.ReadFile(path.Join(s.PathToData, encodeToBase32(strings.ToLower(name))+".json"))
if err != nil {
return
}
err = json.Unmarshal(bJSON, &p)
if err != nil {
p = new(Page)
}
return p
}
type DirectoryEntry struct {
Path string
Length int
Numchanges int
LastEdited time.Time
}
func (d DirectoryEntry) LastEditTime() string {
return d.LastEdited.Format("Mon Jan 2 15:04:05 MST 2006")
}
func (d DirectoryEntry) Name() string {
return d.Path
}
func (d DirectoryEntry) Size() int64 {
return int64(d.Length)
}
func (d DirectoryEntry) Mode() os.FileMode {
return os.ModePerm
}
func (d DirectoryEntry) ModTime() time.Time {
return d.LastEdited
}
func (d DirectoryEntry) IsDir() bool {
return false
}
func (d DirectoryEntry) Sys() interface{} {
return nil
}
func (s *Site) DirectoryList() []os.FileInfo {
files, _ := ioutil.ReadDir(s.PathToData)
entries := make([]os.FileInfo, len(files))
for i, f := range files {
name := DecodeFileName(f.Name())
p := s.Open(name)
entries[i] = DirectoryEntry{
Path: name,
Length: len(p.Text.GetCurrent()),
Numchanges: p.Text.NumEdits(),
LastEdited: time.Unix(p.Text.LastEditTime()/1000000000, 0),
}
}
sort.Slice(entries, func(i, j int) bool { return entries[i].ModTime().After(entries[j].ModTime()) })
return entries
}
type UploadEntry struct {
os.FileInfo
}
func (s *Site) UploadList() ([]os.FileInfo, error) {
paths, err := filepath.Glob(path.Join(s.PathToData, "sha256*"))
if err != nil {
return nil, err
}
result := make([]os.FileInfo, len(paths))
for i := range paths {
result[i], err = os.Stat(paths[i])
if err != nil {
return result, err
}
}
return result, nil
}
func DecodeFileName(s string) string {
s2, _ := decodeFromBase32(strings.Split(s, ".")[0])
return s2
}
// Update cleans the text and updates the versioned text
// and generates a new render
func (p *Page) Update(newText string) error {
// Trim space from end
newText = strings.TrimRight(newText, "\n\t ")
// Update the versioned text
p.Text.Update(newText)
// Render the new page
p.Render()
return p.Save()
}
var rBracketPage = regexp.MustCompile(`\[\[(.*?)\]\]`)
func (p *Page) Render() {
if p.IsEncrypted {
p.RenderedPage = "<code>" + p.Text.GetCurrent() + "</code>"
return
}
// Convert [[page]] to [page](/page/view)
currentText := p.Text.GetCurrent()
for _, s := range rBracketPage.FindAllString(currentText, -1) {
currentText = strings.Replace(currentText, s, "["+s[2:len(s)-2]+"](/"+s[2:len(s)-2]+"/view)", 1)
}
p.Text.Update(currentText)
p.RenderedPage = MarkdownToHtml(p.Text.GetCurrent())
}
func (p *Page) Save() error {
p.Site.saveMut.Lock()
defer p.Site.saveMut.Unlock()
bJSON, err := json.MarshalIndent(p, "", " ")
if err != nil {
return err
}
return ioutil.WriteFile(path.Join(p.Site.PathToData, encodeToBase32(strings.ToLower(p.Name))+".json"), bJSON, 0644)
}
func (p *Page) ChildPageNames() []string {
prefix := strings.ToLower(p.Name + ": ")
files, err := filepath.Glob(path.Join(p.Site.PathToData, "*"))
if err != nil {
panic("Filepath pattern cannot be malformed")
}
result := []string{}
for i := range files {
basename := filepath.Base(files[i])
if strings.HasSuffix(basename, ".json") {
cname, err := decodeFromBase32(basename[:len(basename)-len(".json")])
if err == nil && strings.HasPrefix(strings.ToLower(cname), prefix) {
result = append(result, cname)
}
}
}
return result
}
func (p *Page) IsNew() bool {
return !exists(path.Join(p.Site.PathToData, encodeToBase32(strings.ToLower(p.Name))+".json"))
}
func (p *Page) Erase() error {
p.Site.Logger.Trace("Erasing " + p.Name)
return os.Remove(path.Join(p.Site.PathToData, encodeToBase32(strings.ToLower(p.Name))+".json"))
}
func (p *Page) Published() bool {
return p.IsPublished
}

74
server/page_test.go Executable file
View File

@ -0,0 +1,74 @@
package server
import (
"os"
"strings"
"testing"
)
func TestListFiles(t *testing.T) {
pathToData = "testdata"
os.MkdirAll(pathToData, 0755)
defer os.RemoveAll(pathToData)
s := Site{PathToData: pathToData}
p := s.Open("testpage")
p.Update("Some data")
p = s.Open("testpage2")
p.Update("A different bunch of data")
p = s.Open("testpage3")
p.Update("Not much else")
n := s.DirectoryList()
if len(n) != 3 {
t.Error("Expected three directory entries")
t.FailNow()
}
if n[0].Name() != "testpage" {
t.Error("Expected testpage to be first")
}
if n[1].Name() != "testpage2" {
t.Error("Expected testpage2 to be second")
}
if n[2].Name() != "testpage3" {
t.Error("Expected testpage3 to be last")
}
}
func TestGeneral(t *testing.T) {
pathToData = "testdata"
os.MkdirAll(pathToData, 0755)
defer os.RemoveAll(pathToData)
s := Site{PathToData: pathToData}
p := s.Open("testpage")
err := p.Update("**bold**")
if err != nil {
t.Error(err)
}
if strings.TrimSpace(p.RenderedPage) != "<p><strong>bold</strong></p>" {
t.Errorf("Did not render: '%s'", p.RenderedPage)
}
err = p.Update("**bold** and *italic*")
if err != nil {
t.Error(err)
}
p.Save()
p2 := s.Open("testpage")
if strings.TrimSpace(p2.RenderedPage) != "<p><strong>bold</strong> and <em>italic</em></p>" {
t.Errorf("Did not render: '%s'", p2.RenderedPage)
}
p3 := s.Open("testpage: childpage")
err = p3.Update("**child content**")
if err != nil {
t.Error(err)
}
children := p.ChildPageNames()
if len(children) != 1 {
t.Errorf("Expected 1 child page to be found, got %d", len(children))
return
}
if children[0] != "testpage: childpage" {
t.Errorf("Expected child page %s to be found (got %s)", "testpage: childpage", children[0])
}
}

215
server/utils.go Normal file
View File

@ -0,0 +1,215 @@
package server
import (
"encoding/base32"
"encoding/binary"
"encoding/hex"
"math/rand"
"net/http"
"os"
"path"
"strings"
"time"
"github.com/microcosm-cc/bluemonday"
"github.com/russross/blackfriday/v2"
"github.com/shurcooL/github_flavored_markdown"
"golang.org/x/crypto/bcrypt"
)
var animals []string
var adjectives []string
var aboutPageText string
var allowInsecureHtml bool
func init() {
rand.Seed(time.Now().Unix())
animalsText, _ := Asset("static/text/animals")
animals = strings.Split(string(animalsText), ",")
adjectivesText, _ := Asset("static/text/adjectives")
adjectives = strings.Split(string(adjectivesText), "\n")
}
func randomAnimal() string {
return strings.Replace(strings.Title(animals[rand.Intn(len(animals)-1)]), " ", "", -1)
}
func randomAdjective() string {
return strings.Replace(strings.Title(adjectives[rand.Intn(len(adjectives)-1)]), " ", "", -1)
}
func randomAlliterateCombo() (combo string) {
combo = ""
// generate random alliteration thats not been used
for {
animal := randomAnimal()
adjective := randomAdjective()
if animal[0] == adjective[0] && len(animal)+len(adjective) < 18 { //&& stringInSlice(strings.ToLower(adjective+animal), takenNames) == false {
combo = adjective + animal
break
}
}
return
}
// is there a string in a slice?
func stringInSlice(s string, strings []string) bool {
for _, k := range strings {
if s == k {
return true
}
}
return false
}
// itob returns an 8-byte big endian representation of v.
func itob(v int) []byte {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, uint64(v))
return b
}
func contentType(filename string) string {
switch {
case strings.Contains(filename, ".css"):
return "text/css"
case strings.Contains(filename, ".jpg"):
return "image/jpeg"
case strings.Contains(filename, ".png"):
return "image/png"
case strings.Contains(filename, ".js"):
return "application/javascript"
case strings.Contains(filename, ".xml"):
return "application/xml"
}
return "text/html"
}
func (s *Site) sniffContentType(name string) (string, error) {
file, err := os.Open(path.Join(s.PathToData, name))
if err != nil {
return "", err
}
defer file.Close()
// Only the first 512 bytes are used to sniff the content type.
buffer := make([]byte, 512)
_, err = file.Read(buffer)
if err != nil {
return "", err
}
// Always returns a valid content-type and "application/octet-stream" if no others seemed to match.
return http.DetectContentType(buffer), nil
}
var src = rand.NewSource(time.Now().UnixNano())
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
// RandStringBytesMaskImprSrc prints a random string
func RandStringBytesMaskImprSrc(n int) string {
b := make([]byte, n)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return string(b)
}
// HashPassword generates a bcrypt hash of the password using work factor 14.
// https://github.com/gtank/cryptopasta/blob/master/hash.go
func HashPassword(password string) string {
hash, _ := bcrypt.GenerateFromPassword([]byte(password), 14)
return hex.EncodeToString(hash)
}
// CheckPassword securely compares a bcrypt hashed password with its possible
// plaintext equivalent. Returns nil on success, or an error on failure.
// https://github.com/gtank/cryptopasta/blob/master/hash.go
func CheckPasswordHash(password, hashedString string) error {
hash, err := hex.DecodeString(hashedString)
if err != nil {
return err
}
return bcrypt.CompareHashAndPassword(hash, []byte(password))
}
// exists returns whether the given file or directory exists or not
func exists(path string) bool {
_, err := os.Stat(path)
return !os.IsNotExist(err)
}
func MarkdownToHtml(s string) string {
unsafe := blackfriday.Run([]byte(s))
if allowInsecureHtml {
return string(unsafe)
}
pClean := bluemonday.UGCPolicy()
pClean.AllowElements("img")
pClean.AllowElements("center")
pClean.AllowAttrs("alt").OnElements("img")
pClean.AllowAttrs("src").OnElements("img")
pClean.AllowAttrs("class").OnElements("a")
pClean.AllowAttrs("href").OnElements("a")
pClean.AllowAttrs("id").OnElements("a")
pClean.AllowDataURIImages()
html := pClean.SanitizeBytes(unsafe)
return string(html)
}
func GithubMarkdownToHTML(s string) string {
return string(github_flavored_markdown.Markdown([]byte(s)))
}
func encodeToBase32(s string) string {
return encodeBytesToBase32([]byte(s))
}
func encodeBytesToBase32(s []byte) string {
return base32.StdEncoding.EncodeToString(s)
}
func decodeFromBase32(s string) (s2 string, err error) {
bString, err := base32.StdEncoding.DecodeString(s)
s2 = string(bString)
return
}
func reverseSliceInt64(s []int64) []int64 {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
return s
}
func reverseSliceString(s []string) []string {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
return s
}
func reverseSliceInt(s []int) []int {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
return s
}

34
server/utils_test.go Executable file
View File

@ -0,0 +1,34 @@
package server
import (
"testing"
)
func BenchmarkAlliterativeAnimal(b *testing.B) {
for i := 0; i < b.N; i++ {
randomAlliterateCombo()
}
}
func TestReverseList(t *testing.T) {
s := []int64{1, 10, 2, 20}
if reverseSliceInt64(s)[0] != 20 {
t.Errorf("Could not reverse: %v", s)
}
s2 := []string{"a", "b", "d", "c"}
if reverseSliceString(s2)[0] != "c" {
t.Errorf("Could not reverse: %v", s2)
}
}
func TestHashing(t *testing.T) {
p := HashPassword("1234")
err := CheckPasswordHash("1234", p)
if err != nil {
t.Errorf("Should be correct password")
}
err = CheckPasswordHash("1234lkjklj", p)
if err == nil {
t.Errorf("Should NOT be correct password")
}
}

11
static/css/base-min.css vendored Normal file
View File

@ -0,0 +1,11 @@
/*!
Pure v0.6.2
Copyright 2013 Yahoo!
Licensed under the BSD License.
https://github.com/yahoo/pure/blob/master/LICENSE.md
*/
/*!
normalize.css v^3.0 | MIT License | git.io/normalize
Copyright (c) Nicolas Gallagher and Jonathan Neal
*/
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */img,legend{border:0}legend,td,th{padding:0}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,optgroup,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre,textarea{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}table{border-collapse:collapse;border-spacing:0}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}

View File

@ -1,596 +0,0 @@
/*!
* Bootstrap v3.3.5 (http://getbootstrap.com)
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
/*!
* Generated using the Bootstrap Customizer (https://getbootstrap.com/customize/?id=1d3a1642fef02a0b2ddb4ede6cb2cb3c)
* Config saved to config.json and https://gist.github.com/1d3a1642fef02a0b2ddb4ede6cb2cb3c
*/
/*!
* Bootstrap v3.3.6 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
.btn-default,
.btn-primary,
.btn-success,
.btn-info,
.btn-warning,
.btn-danger {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075);
}
.btn-default:active,
.btn-primary:active,
.btn-success:active,
.btn-info:active,
.btn-warning:active,
.btn-danger:active,
.btn-default.active,
.btn-primary.active,
.btn-success.active,
.btn-info.active,
.btn-warning.active,
.btn-danger.active {
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
.btn-default.disabled,
.btn-primary.disabled,
.btn-success.disabled,
.btn-info.disabled,
.btn-warning.disabled,
.btn-danger.disabled,
.btn-default[disabled],
.btn-primary[disabled],
.btn-success[disabled],
.btn-info[disabled],
.btn-warning[disabled],
.btn-danger[disabled],
fieldset[disabled] .btn-default,
fieldset[disabled] .btn-primary,
fieldset[disabled] .btn-success,
fieldset[disabled] .btn-info,
fieldset[disabled] .btn-warning,
fieldset[disabled] .btn-danger {
-webkit-box-shadow: none;
box-shadow: none;
}
.btn-default .badge,
.btn-primary .badge,
.btn-success .badge,
.btn-info .badge,
.btn-warning .badge,
.btn-danger .badge {
text-shadow: none;
}
.btn:active,
.btn.active {
background-image: none;
}
.btn-default {
background-image: -webkit-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);
background-image: -o-linear-gradient(top, #ffffff 0%, #e0e0e0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#e0e0e0));
background-image: linear-gradient(to bottom, #ffffff 0%, #e0e0e0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #dbdbdb;
text-shadow: 0 1px 0 #fff;
border-color: #ccc;
}
.btn-default:hover,
.btn-default:focus {
background-color: #e0e0e0;
background-position: 0 -15px;
}
.btn-default:active,
.btn-default.active {
background-color: #e0e0e0;
border-color: #dbdbdb;
}
.btn-default.disabled,
.btn-default[disabled],
fieldset[disabled] .btn-default,
.btn-default.disabled:hover,
.btn-default[disabled]:hover,
fieldset[disabled] .btn-default:hover,
.btn-default.disabled:focus,
.btn-default[disabled]:focus,
fieldset[disabled] .btn-default:focus,
.btn-default.disabled.focus,
.btn-default[disabled].focus,
fieldset[disabled] .btn-default.focus,
.btn-default.disabled:active,
.btn-default[disabled]:active,
fieldset[disabled] .btn-default:active,
.btn-default.disabled.active,
.btn-default[disabled].active,
fieldset[disabled] .btn-default.active {
background-color: #e0e0e0;
background-image: none;
}
.btn-primary {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #245580;
}
.btn-primary:hover,
.btn-primary:focus {
background-color: #265a88;
background-position: 0 -15px;
}
.btn-primary:active,
.btn-primary.active {
background-color: #265a88;
border-color: #245580;
}
.btn-primary.disabled,
.btn-primary[disabled],
fieldset[disabled] .btn-primary,
.btn-primary.disabled:hover,
.btn-primary[disabled]:hover,
fieldset[disabled] .btn-primary:hover,
.btn-primary.disabled:focus,
.btn-primary[disabled]:focus,
fieldset[disabled] .btn-primary:focus,
.btn-primary.disabled.focus,
.btn-primary[disabled].focus,
fieldset[disabled] .btn-primary.focus,
.btn-primary.disabled:active,
.btn-primary[disabled]:active,
fieldset[disabled] .btn-primary:active,
.btn-primary.disabled.active,
.btn-primary[disabled].active,
fieldset[disabled] .btn-primary.active {
background-color: #265a88;
background-image: none;
}
.btn-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #3e8f3e;
}
.btn-success:hover,
.btn-success:focus {
background-color: #419641;
background-position: 0 -15px;
}
.btn-success:active,
.btn-success.active {
background-color: #419641;
border-color: #3e8f3e;
}
.btn-success.disabled,
.btn-success[disabled],
fieldset[disabled] .btn-success,
.btn-success.disabled:hover,
.btn-success[disabled]:hover,
fieldset[disabled] .btn-success:hover,
.btn-success.disabled:focus,
.btn-success[disabled]:focus,
fieldset[disabled] .btn-success:focus,
.btn-success.disabled.focus,
.btn-success[disabled].focus,
fieldset[disabled] .btn-success.focus,
.btn-success.disabled:active,
.btn-success[disabled]:active,
fieldset[disabled] .btn-success:active,
.btn-success.disabled.active,
.btn-success[disabled].active,
fieldset[disabled] .btn-success.active {
background-color: #419641;
background-image: none;
}
.btn-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #28a4c9;
}
.btn-info:hover,
.btn-info:focus {
background-color: #2aabd2;
background-position: 0 -15px;
}
.btn-info:active,
.btn-info.active {
background-color: #2aabd2;
border-color: #28a4c9;
}
.btn-info.disabled,
.btn-info[disabled],
fieldset[disabled] .btn-info,
.btn-info.disabled:hover,
.btn-info[disabled]:hover,
fieldset[disabled] .btn-info:hover,
.btn-info.disabled:focus,
.btn-info[disabled]:focus,
fieldset[disabled] .btn-info:focus,
.btn-info.disabled.focus,
.btn-info[disabled].focus,
fieldset[disabled] .btn-info.focus,
.btn-info.disabled:active,
.btn-info[disabled]:active,
fieldset[disabled] .btn-info:active,
.btn-info.disabled.active,
.btn-info[disabled].active,
fieldset[disabled] .btn-info.active {
background-color: #2aabd2;
background-image: none;
}
.btn-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #e38d13;
}
.btn-warning:hover,
.btn-warning:focus {
background-color: #eb9316;
background-position: 0 -15px;
}
.btn-warning:active,
.btn-warning.active {
background-color: #eb9316;
border-color: #e38d13;
}
.btn-warning.disabled,
.btn-warning[disabled],
fieldset[disabled] .btn-warning,
.btn-warning.disabled:hover,
.btn-warning[disabled]:hover,
fieldset[disabled] .btn-warning:hover,
.btn-warning.disabled:focus,
.btn-warning[disabled]:focus,
fieldset[disabled] .btn-warning:focus,
.btn-warning.disabled.focus,
.btn-warning[disabled].focus,
fieldset[disabled] .btn-warning.focus,
.btn-warning.disabled:active,
.btn-warning[disabled]:active,
fieldset[disabled] .btn-warning:active,
.btn-warning.disabled.active,
.btn-warning[disabled].active,
fieldset[disabled] .btn-warning.active {
background-color: #eb9316;
background-image: none;
}
.btn-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #b92c28;
}
.btn-danger:hover,
.btn-danger:focus {
background-color: #c12e2a;
background-position: 0 -15px;
}
.btn-danger:active,
.btn-danger.active {
background-color: #c12e2a;
border-color: #b92c28;
}
.btn-danger.disabled,
.btn-danger[disabled],
fieldset[disabled] .btn-danger,
.btn-danger.disabled:hover,
.btn-danger[disabled]:hover,
fieldset[disabled] .btn-danger:hover,
.btn-danger.disabled:focus,
.btn-danger[disabled]:focus,
fieldset[disabled] .btn-danger:focus,
.btn-danger.disabled.focus,
.btn-danger[disabled].focus,
fieldset[disabled] .btn-danger.focus,
.btn-danger.disabled:active,
.btn-danger[disabled]:active,
fieldset[disabled] .btn-danger:active,
.btn-danger.disabled.active,
.btn-danger[disabled].active,
fieldset[disabled] .btn-danger.active {
background-color: #c12e2a;
background-image: none;
}
.thumbnail,
.img-thumbnail {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
}
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-color: #e8e8e8;
}
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-color: #2e6da4;
}
.navbar-default {
background-image: -webkit-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
background-image: -o-linear-gradient(top, #ffffff 0%, #f8f8f8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#f8f8f8));
background-image: linear-gradient(to bottom, #ffffff 0%, #f8f8f8 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 5px rgba(0, 0, 0, 0.075);
}
.navbar-default .navbar-nav > .open > a,
.navbar-default .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.075);
}
.navbar-brand,
.navbar-nav > li > a {
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.25);
}
.navbar-inverse {
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%);
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222222 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222222));
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222222 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
border-radius: 4px;
}
.navbar-inverse .navbar-nav > .open > a,
.navbar-inverse .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, 0.25);
}
.navbar-inverse .navbar-brand,
.navbar-inverse .navbar-nav > li > a {
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.navbar-static-top,
.navbar-fixed-top,
.navbar-fixed-bottom {
border-radius: 0;
}
@media (max-width: 318px) {
.navbar .navbar-nav .open .dropdown-menu > .active > a,
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
color: #fff;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
}
}
.alert {
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 1px 2px rgba(0, 0, 0, 0.05);
}
.alert-success {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
border-color: #b2dba1;
}
.alert-info {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
border-color: #9acfea;
}
.alert-warning {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
border-color: #f5e79e;
}
.alert-danger {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
border-color: #dca7a7;
}
.progress {
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
}
.progress-bar {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
}
.progress-bar-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
}
.progress-bar-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
}
.progress-bar-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
}
.progress-bar-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
}
.progress-bar-striped {
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}
.list-group {
border-radius: 4px;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075);
}
.list-group-item.active,
.list-group-item.active:hover,
.list-group-item.active:focus {
text-shadow: 0 -1px 0 #286090;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
border-color: #2b669a;
}
.list-group-item.active .badge,
.list-group-item.active:hover .badge,
.list-group-item.active:focus .badge {
text-shadow: none;
}
.panel {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.panel-default > .panel-heading {
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
}
.panel-primary > .panel-heading {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
}
.panel-success > .panel-heading {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
}
.panel-info > .panel-heading {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
}
.panel-warning > .panel-heading {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
}
.panel-danger > .panel-heading {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
}
.well {
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
border-color: #dcdcdc;
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(255, 255, 255, 0.1);
}

File diff suppressed because one or more lines are too long

6762
static/css/bootstrap.css vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,338 +0,0 @@
/* BASICS */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 300px;
color: black;
}
/* PADDING */
.CodeMirror-lines {
padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre {
padding: 0 4px; /* Horizontal padding of content */
}
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: white; /* The little square between H and V scrollbars */
}
/* GUTTER */
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
white-space: nowrap;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
white-space: nowrap;
}
.CodeMirror-guttermarker { color: black; }
.CodeMirror-guttermarker-subtle { color: #999; }
/* CURSOR */
.CodeMirror-cursor {
border-left: 1px solid black;
border-right: none;
width: 0;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
.cm-fat-cursor .CodeMirror-cursor {
width: auto;
border: 0;
background: #7e7;
}
.cm-fat-cursor div.CodeMirror-cursors {
z-index: 1;
}
.cm-animate-fat-cursor {
width: auto;
border: 0;
-webkit-animation: blink 1.06s steps(1) infinite;
-moz-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
background-color: #7e7;
}
@-moz-keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
@-webkit-keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
@keyframes blink {
0% {}
50% { background-color: transparent; }
100% {}
}
/* Can style cursor different in overwrite (non-insert) mode */
.CodeMirror-overwrite .CodeMirror-cursor {}
.cm-tab { display: inline-block; text-decoration: inherit; }
.CodeMirror-ruler {
border-left: 1px solid #ccc;
position: absolute;
}
/* DEFAULT THEME */
.cm-s-default .cm-header {color: blue;}
.cm-s-default .cm-quote {color: #090;}
.cm-negative {color: #d44;}
.cm-positive {color: #292;}
.cm-header, .cm-strong {font-weight: bold;}
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
.cm-strikethrough {text-decoration: line-through;}
.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable,
.cm-s-default .cm-punctuation,
.cm-s-default .cm-property,
.cm-s-default .cm-operator {}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3 {color: #085;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color: #170;}
.cm-s-default .cm-attribute {color: #00c;}
.cm-s-default .cm-hr {color: #999;}
.cm-s-default .cm-link {color: #00c;}
.cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;}
.CodeMirror-composing { border-bottom: 2px solid; }
/* Default styles for common addons */
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
.CodeMirror-activeline-background {background: #e8f2ff;}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
.CodeMirror {
position: relative;
overflow: hidden;
background: white;
}
.CodeMirror-scroll {
overflow: scroll !important; /* Things will break if this is overridden */
/* 30px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror */
margin-bottom: -30px; margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none; /* Prevent dragging from highlighting the element */
position: relative;
}
.CodeMirror-sizer {
position: relative;
border-right: 30px solid transparent;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actual scrolling happens, thus preventing shaking and
flickering artifacts. */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
}
.CodeMirror-vscrollbar {
right: 0; top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
.CodeMirror-hscrollbar {
bottom: 0; left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
.CodeMirror-scrollbar-filler {
right: 0; bottom: 0;
}
.CodeMirror-gutter-filler {
left: 0; bottom: 0;
}
.CodeMirror-gutters {
position: absolute; left: 0; top: 0;
min-height: 100%;
z-index: 3;
}
.CodeMirror-gutter {
white-space: normal;
height: 100%;
display: inline-block;
vertical-align: top;
margin-bottom: -30px;
/* Hack to make IE7 behave */
*zoom:1;
*display:inline;
}
.CodeMirror-gutter-wrapper {
position: absolute;
z-index: 4;
background: none !important;
border: none !important;
}
.CodeMirror-gutter-background {
position: absolute;
top: 0; bottom: 0;
z-index: 4;
}
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
.CodeMirror-gutter-wrapper {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
.CodeMirror-lines {
cursor: text;
min-height: 1px; /* prevents collapsing before first draw */
}
.CodeMirror pre {
/* Reset some styles that the rest of the page might have set */
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
-webkit-tap-highlight-color: transparent;
-webkit-font-variant-ligatures: none;
font-variant-ligatures: none;
}
.CodeMirror-wrap pre {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
.CodeMirror-linebackground {
position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
z-index: 0;
}
.CodeMirror-linewidget {
position: relative;
z-index: 2;
overflow: auto;
}
.CodeMirror-widget {}
.CodeMirror-code {
outline: none;
}
/* Force content-box sizing for the elements where we expect it */
.CodeMirror-scroll,
.CodeMirror-sizer,
.CodeMirror-gutter,
.CodeMirror-gutters,
.CodeMirror-linenumber {
-moz-box-sizing: content-box;
box-sizing: content-box;
}
.CodeMirror-measure {
position: absolute;
width: 100%;
height: 0;
overflow: hidden;
visibility: hidden;
}
.CodeMirror-cursor { position: absolute; }
.CodeMirror-measure pre { position: static; }
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
z-index: 3;
}
div.CodeMirror-dragcursors {
visibility: visible;
}
.CodeMirror-focused div.CodeMirror-cursors {
visibility: visible;
}
.CodeMirror-selected { background: #d9d9d9; }
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
.CodeMirror-crosshair { cursor: crosshair; }
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
.cm-searching {
background: #ffa;
background: rgba(255, 255, 0, .4);
}
/* IE7 hack to prevent it from returning funny offsetTops on the spans */
.CodeMirror span { *vertical-align: text-bottom; }
/* Used to force a border model for a node */
.cm-force-border { padding-right: .1px; }
@media print {
/* Hide the cursor when printing */
.CodeMirror div.CodeMirror-cursors {
visibility: hidden;
}
}
/* See issue #2901 */
.cm-tab-wrap-hack:after { content: ''; }
/* Help users use markselection to safely style text background */
span.CodeMirror-selectedtext { background: none; }

128
static/css/default.css Normal file
View File

@ -0,0 +1,128 @@
body.ListPage span {
cursor: pointer;
}
body {
background: #fff;
}
.success {
color: #5cb85c;
font-weight: bold;
}
.failure {
color: #d9534f;
font-weight: bold;
}
.pure-menu a {
color: #777;
}
.deleting {
opacity: 0.5;
}
#wrap {
position: absolute;
top: 50px;
left: 0px;
right: 0px;
bottom: 0px;
}
#pad {
height:100%;
}
body.EditPage {
overflow:hidden;
}
body#pad textarea {
width: 100%;
height: 100%;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
position: absolute;
left: 0;
right: 0;
bottom: 0;
top: 0;
border: 0;
border: none;
outline: none;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
resize: none;
font-size: 1.0em;
font-family: 'Open Sans','Segoe UI',Tahoma,Arial,sans-serif;
}
body#pad.HasDotInName textarea {
font-family: "Lucida Console", Monaco, monospace;
}
.markdown-body ul, .markdown-body ol {
padding-left: 0em;
}
@media (min-width: 5em) {
div#menu, div#rendered, .ChildPageNames, body#pad textarea {
padding-left: 2%;
padding-right: 2%;
}
.pure-menu .pure-menu-horizontal {
max-width: 300px;
}
.pure-menu-disabled, .pure-menu-heading, .pure-menu-link {
padding-left:1.2em;
padding-right:em;
}
.ChildPageNames ul {
grid-template-columns: repeat(1, 1fr);
}
}
@media (min-width: 50em) {
div#menu, div#rendered, .ChildPageNames, body#pad textarea {
padding-left: 10%;
padding-right: 10%;
}
.pure-menu-disabled, .pure-menu-heading, .pure-menu-link {
padding: .5em 1em;
}
.ChildPageNames ul {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 70em) {
div#menu, div#rendered, .ChildPageNames, body#pad textarea {
padding-left: 15%;
padding-right: 15%;
}
.ChildPageNames ul {
grid-template-columns: repeat(3, 1fr);
}
}
@media (min-width: 100em) {
div#menu, div#rendered, .ChildPageNames, body#pad textarea {
padding-left: 20%;
padding-right: 20%;
}
.ChildPageNames ul {
grid-template-columns: repeat(4, 1fr);
}
}
.ChildPageNames ul {
margin: 0.3em 0 0 1.6em;
padding: 0;
display: grid;
grid-gap: 0.5rem;
}
.ChildPageNames li {
margin: 0;
}
.ChildPageNames a {
color: #0645ad;
background: none;
}

388
static/css/dropzone.css Normal file
View File

@ -0,0 +1,388 @@
/*
* The MIT License
* Copyright (c) 2012 Matias Meno <m@tias.me>
*/
@-webkit-keyframes passing-through {
0% {
opacity: 0;
-webkit-transform: translateY(40px);
-moz-transform: translateY(40px);
-ms-transform: translateY(40px);
-o-transform: translateY(40px);
transform: translateY(40px); }
30%, 70% {
opacity: 1;
-webkit-transform: translateY(0px);
-moz-transform: translateY(0px);
-ms-transform: translateY(0px);
-o-transform: translateY(0px);
transform: translateY(0px); }
100% {
opacity: 0;
-webkit-transform: translateY(-40px);
-moz-transform: translateY(-40px);
-ms-transform: translateY(-40px);
-o-transform: translateY(-40px);
transform: translateY(-40px); } }
@-moz-keyframes passing-through {
0% {
opacity: 0;
-webkit-transform: translateY(40px);
-moz-transform: translateY(40px);
-ms-transform: translateY(40px);
-o-transform: translateY(40px);
transform: translateY(40px); }
30%, 70% {
opacity: 1;
-webkit-transform: translateY(0px);
-moz-transform: translateY(0px);
-ms-transform: translateY(0px);
-o-transform: translateY(0px);
transform: translateY(0px); }
100% {
opacity: 0;
-webkit-transform: translateY(-40px);
-moz-transform: translateY(-40px);
-ms-transform: translateY(-40px);
-o-transform: translateY(-40px);
transform: translateY(-40px); } }
@keyframes passing-through {
0% {
opacity: 0;
-webkit-transform: translateY(40px);
-moz-transform: translateY(40px);
-ms-transform: translateY(40px);
-o-transform: translateY(40px);
transform: translateY(40px); }
30%, 70% {
opacity: 1;
-webkit-transform: translateY(0px);
-moz-transform: translateY(0px);
-ms-transform: translateY(0px);
-o-transform: translateY(0px);
transform: translateY(0px); }
100% {
opacity: 0;
-webkit-transform: translateY(-40px);
-moz-transform: translateY(-40px);
-ms-transform: translateY(-40px);
-o-transform: translateY(-40px);
transform: translateY(-40px); } }
@-webkit-keyframes slide-in {
0% {
opacity: 0;
-webkit-transform: translateY(40px);
-moz-transform: translateY(40px);
-ms-transform: translateY(40px);
-o-transform: translateY(40px);
transform: translateY(40px); }
30% {
opacity: 1;
-webkit-transform: translateY(0px);
-moz-transform: translateY(0px);
-ms-transform: translateY(0px);
-o-transform: translateY(0px);
transform: translateY(0px); } }
@-moz-keyframes slide-in {
0% {
opacity: 0;
-webkit-transform: translateY(40px);
-moz-transform: translateY(40px);
-ms-transform: translateY(40px);
-o-transform: translateY(40px);
transform: translateY(40px); }
30% {
opacity: 1;
-webkit-transform: translateY(0px);
-moz-transform: translateY(0px);
-ms-transform: translateY(0px);
-o-transform: translateY(0px);
transform: translateY(0px); } }
@keyframes slide-in {
0% {
opacity: 0;
-webkit-transform: translateY(40px);
-moz-transform: translateY(40px);
-ms-transform: translateY(40px);
-o-transform: translateY(40px);
transform: translateY(40px); }
30% {
opacity: 1;
-webkit-transform: translateY(0px);
-moz-transform: translateY(0px);
-ms-transform: translateY(0px);
-o-transform: translateY(0px);
transform: translateY(0px); } }
@-webkit-keyframes pulse {
0% {
-webkit-transform: scale(1);
-moz-transform: scale(1);
-ms-transform: scale(1);
-o-transform: scale(1);
transform: scale(1); }
10% {
-webkit-transform: scale(1.1);
-moz-transform: scale(1.1);
-ms-transform: scale(1.1);
-o-transform: scale(1.1);
transform: scale(1.1); }
20% {
-webkit-transform: scale(1);
-moz-transform: scale(1);
-ms-transform: scale(1);
-o-transform: scale(1);
transform: scale(1); } }
@-moz-keyframes pulse {
0% {
-webkit-transform: scale(1);
-moz-transform: scale(1);
-ms-transform: scale(1);
-o-transform: scale(1);
transform: scale(1); }
10% {
-webkit-transform: scale(1.1);
-moz-transform: scale(1.1);
-ms-transform: scale(1.1);
-o-transform: scale(1.1);
transform: scale(1.1); }
20% {
-webkit-transform: scale(1);
-moz-transform: scale(1);
-ms-transform: scale(1);
-o-transform: scale(1);
transform: scale(1); } }
@keyframes pulse {
0% {
-webkit-transform: scale(1);
-moz-transform: scale(1);
-ms-transform: scale(1);
-o-transform: scale(1);
transform: scale(1); }
10% {
-webkit-transform: scale(1.1);
-moz-transform: scale(1.1);
-ms-transform: scale(1.1);
-o-transform: scale(1.1);
transform: scale(1.1); }
20% {
-webkit-transform: scale(1);
-moz-transform: scale(1);
-ms-transform: scale(1);
-o-transform: scale(1);
transform: scale(1); } }
.dropzone, .dropzone * {
box-sizing: border-box; }
.dropzone {
min-height: 150px;
border: 2px solid rgba(0, 0, 0, 0.3);
background: white;
padding: 20px 20px; }
.dropzone.dz-clickable {
cursor: pointer; }
.dropzone.dz-clickable * {
cursor: default; }
.dropzone.dz-clickable .dz-message, .dropzone.dz-clickable .dz-message * {
cursor: pointer; }
.dropzone.dz-started .dz-message {
display: none; }
.dropzone.dz-drag-hover {
border-style: solid; }
.dropzone.dz-drag-hover .dz-message {
opacity: 0.5; }
.dropzone .dz-message {
text-align: center;
margin: 2em 0; }
.dropzone .dz-preview {
position: relative;
display: inline-block;
vertical-align: top;
margin: 16px;
min-height: 100px; }
.dropzone .dz-preview:hover {
z-index: 1000; }
.dropzone .dz-preview:hover .dz-details {
opacity: 1; }
.dropzone .dz-preview.dz-file-preview .dz-image {
border-radius: 20px;
background: #999;
background: linear-gradient(to bottom, #eee, #ddd); }
.dropzone .dz-preview.dz-file-preview .dz-details {
opacity: 1; }
.dropzone .dz-preview.dz-image-preview {
background: white; }
.dropzone .dz-preview.dz-image-preview .dz-details {
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-ms-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear; }
.dropzone .dz-preview .dz-remove {
font-size: 14px;
text-align: center;
display: block;
cursor: pointer;
border: none; }
.dropzone .dz-preview .dz-remove:hover {
text-decoration: underline; }
.dropzone .dz-preview:hover .dz-details {
opacity: 1; }
.dropzone .dz-preview .dz-details {
z-index: 20;
position: absolute;
top: 0;
left: 0;
opacity: 0;
font-size: 13px;
min-width: 100%;
max-width: 100%;
padding: 2em 1em;
text-align: center;
color: rgba(0, 0, 0, 0.9);
line-height: 150%; }
.dropzone .dz-preview .dz-details .dz-size {
margin-bottom: 1em;
font-size: 16px; }
.dropzone .dz-preview .dz-details .dz-filename {
white-space: nowrap; }
.dropzone .dz-preview .dz-details .dz-filename:hover span {
border: 1px solid rgba(200, 200, 200, 0.8);
background-color: rgba(255, 255, 255, 0.8); }
.dropzone .dz-preview .dz-details .dz-filename:not(:hover) {
overflow: hidden;
text-overflow: ellipsis; }
.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span {
border: 1px solid transparent; }
.dropzone .dz-preview .dz-details .dz-filename span, .dropzone .dz-preview .dz-details .dz-size span {
background-color: rgba(255, 255, 255, 0.4);
padding: 0 0.4em;
border-radius: 3px; }
.dropzone .dz-preview:hover .dz-image img {
-webkit-transform: scale(1.05, 1.05);
-moz-transform: scale(1.05, 1.05);
-ms-transform: scale(1.05, 1.05);
-o-transform: scale(1.05, 1.05);
transform: scale(1.05, 1.05);
-webkit-filter: blur(8px);
filter: blur(8px); }
.dropzone .dz-preview .dz-image {
border-radius: 20px;
overflow: hidden;
width: 120px;
height: 120px;
position: relative;
display: block;
z-index: 10; }
.dropzone .dz-preview .dz-image img {
display: block; }
.dropzone .dz-preview.dz-success .dz-success-mark {
-webkit-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
-moz-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
-ms-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
-o-animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1);
animation: passing-through 3s cubic-bezier(0.77, 0, 0.175, 1); }
.dropzone .dz-preview.dz-error .dz-error-mark {
opacity: 1;
-webkit-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
-moz-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
-ms-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
-o-animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1);
animation: slide-in 3s cubic-bezier(0.77, 0, 0.175, 1); }
.dropzone .dz-preview .dz-success-mark, .dropzone .dz-preview .dz-error-mark {
pointer-events: none;
opacity: 0;
z-index: 500;
position: absolute;
display: block;
top: 50%;
left: 50%;
margin-left: -27px;
margin-top: -27px; }
.dropzone .dz-preview .dz-success-mark svg, .dropzone .dz-preview .dz-error-mark svg {
display: block;
width: 54px;
height: 54px; }
.dropzone .dz-preview.dz-processing .dz-progress {
opacity: 1;
-webkit-transition: all 0.2s linear;
-moz-transition: all 0.2s linear;
-ms-transition: all 0.2s linear;
-o-transition: all 0.2s linear;
transition: all 0.2s linear; }
.dropzone .dz-preview.dz-complete .dz-progress {
opacity: 0;
-webkit-transition: opacity 0.4s ease-in;
-moz-transition: opacity 0.4s ease-in;
-ms-transition: opacity 0.4s ease-in;
-o-transition: opacity 0.4s ease-in;
transition: opacity 0.4s ease-in; }
.dropzone .dz-preview:not(.dz-processing) .dz-progress {
-webkit-animation: pulse 6s ease infinite;
-moz-animation: pulse 6s ease infinite;
-ms-animation: pulse 6s ease infinite;
-o-animation: pulse 6s ease infinite;
animation: pulse 6s ease infinite; }
.dropzone .dz-preview .dz-progress {
opacity: 1;
z-index: 1000;
pointer-events: none;
position: absolute;
height: 16px;
left: 50%;
top: 50%;
margin-top: -8px;
width: 80px;
margin-left: -40px;
background: rgba(255, 255, 255, 0.9);
-webkit-transform: scale(1);
border-radius: 8px;
overflow: hidden; }
.dropzone .dz-preview .dz-progress .dz-upload {
background: #333;
background: linear-gradient(to bottom, #666, #444);
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 0;
-webkit-transition: width 300ms ease-in-out;
-moz-transition: width 300ms ease-in-out;
-ms-transition: width 300ms ease-in-out;
-o-transition: width 300ms ease-in-out;
transition: width 300ms ease-in-out; }
.dropzone .dz-preview.dz-error .dz-error-message {
display: block; }
.dropzone .dz-preview.dz-error:hover .dz-error-message {
opacity: 1;
pointer-events: auto; }
.dropzone .dz-preview .dz-error-message {
pointer-events: none;
z-index: 1000;
position: absolute;
display: block;
display: none;
opacity: 0;
-webkit-transition: opacity 0.3s ease;
-moz-transition: opacity 0.3s ease;
-ms-transition: opacity 0.3s ease;
-o-transition: opacity 0.3s ease;
transition: opacity 0.3s ease;
border-radius: 8px;
font-size: 13px;
top: 130px;
left: -10px;
width: 140px;
background: #be2626;
background: linear-gradient(to bottom, #be2626, #a92222);
padding: 0.5em 1.2em;
color: white; }
.dropzone .dz-preview .dz-error-message:after {
content: '';
position: absolute;
top: -6px;
left: 64px;
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid #be2626; }

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More