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

27 Commits
0.9 ... v1.0

Author SHA1 Message Date
Zack Scholl
96cc288fa1 makeBinaries added
Former-commit-id: bf42a5371f50bcc7369662ec4a6313222b920050 [formerly 73f53bf45fc21178ffac34e264622b156d4f6663] [formerly 1bad08f49c1e09dfd2566771b3425ce0954f9c18 [formerly 7264847fa2a17fa41e57dab60e08b22884e9bff1 [formerly 2c88af9acc]]]
Former-commit-id: 084c71e5f791c21701ddbda804f80d5b7937decd [formerly 8c3b9f15b8eb7b5fce75822a643f79187c7f0d7c]
Former-commit-id: 027ab2fc628c9a1d2e480d6695259e24493edf18
Former-commit-id: 518efbd77a
2016-03-15 08:56:35 -04:00
Zack Scholl
a4cd5d11dc Added locking. Bumped version
Former-commit-id: 09543cf9d15b734ed48d24eb2fe30ad2ecc96cbb [formerly 793f2ff37d1dea630bd254643372c25f64603ff5] [formerly b4d60e8cd53aad403d8d0d7bf6324fb95631ebbd [formerly 956d8877d4cfb47ae818e79914c729ec7bfc431a [formerly 6b526e4327]]]
Former-commit-id: f1d0cc3a5199cfccab35243e9b6d75de3935f9fa [formerly bd9c741701aecb0a99a23e64b9c5a34e199281fb]
Former-commit-id: 56d6dcb02e92b18d69c2a1fdbb404be01bfa3ee7
Former-commit-id: 0b2928468b
2016-03-14 21:39:09 -04:00
Zack Scholl
fbf6398148 Check if opened
Former-commit-id: a9ee6c19c060ff6323c9657cb90f2b166a4f7103 [formerly d75dba0b429e79ac8d417dcf044496f8d735d803] [formerly 9413872d5aecf982e873fcf43bf87cec2acf3e98 [formerly 09e2d40e0233d22758e799f07ddf22db91eedaaf [formerly 3ef10c9466]]]
Former-commit-id: 7513366547dbac6a0778dbb49f1446092fc59842 [formerly f1f5d9eb16be383430f83b397c40c65ae0551ee9]
Former-commit-id: bd73123413245683c3a90e6ae46884fa901f6e35
Former-commit-id: 17f04a167f
2016-03-14 15:36:01 -04:00
Zack Scholl
f3ec63e3a3 Open DB only when needed
Former-commit-id: 2809fdfbbe56c1697a8a4699a11608fdefc33ef1 [formerly f3c48188157385a09b4aa8377727808faea640fd] [formerly f2ce94925b7a89b53d4fae7d7bef000174a9b7d2 [formerly 35366b6ef6fd94e16068eb8b27168c36421f2eb4 [formerly 3dc0ea7524]]]
Former-commit-id: 6207c2b06c010c4056cdda511262ef5e66b938d7 [formerly 755e934bab151bb526062c0fd0f2378fe1100c90]
Former-commit-id: a97aaa6e4146f587e3446a93caa4a2ad2bdb9e0c
Former-commit-id: bb0f0845a5
2016-03-14 15:26:55 -04:00
Zack Scholl
b873121f10 Open db only when needed
Former-commit-id: df0d9f6391b8490cd61bb1c49ec8fb8331873156 [formerly 73f2ff826d1711d937619618c6075dca99b3c7a8] [formerly 72a58bdb3ac3708539dbd28ea5248776ab741567 [formerly 9675611275fc51ba1e74ddba2653a51487b589d6 [formerly b767dd0352]]]
Former-commit-id: 616ef7e0ec57636152e1e338ec8e8b4c29dab262 [formerly df0a13da1e51f144a12dfc51dbed1f42b3546912]
Former-commit-id: 6c3f76f8a4f4cfd361b2a87fe0a09c717db94422
Former-commit-id: 5721604187
2016-03-14 15:25:09 -04:00
Zack Scholl
38317b8b66 Version bump
Former-commit-id: 64a745492d4e3f567d9c816ee8d762960ca6a375 [formerly 64c9c7f77af4320944ef9ca35f548e505225b840] [formerly 32aff0da01d8279ded527df75c574f432aa07b72 [formerly 24520987c7560ca09c08aa645c6f603dbef44fc7 [formerly 5b423cf3a0]]]
Former-commit-id: edcf6400dc1582d68a4a21974d5c3ae7212d135b [formerly aa2d0ae35d8eedda83b4026c58da02e7f34dfc43]
Former-commit-id: 0a17460d95b721ddb51c2cabfc1ae454321e1f4b
Former-commit-id: f9689e2c93
2016-03-14 14:51:27 -04:00
Zack Scholl
4770798c34 Time edited doesn't show ns
Former-commit-id: 24fc61197903696b5f44d5866b62a7f3a242113b [formerly 66c03d4a77a41f0aa6013f416c956d14bd477b24] [formerly 4c452c8aa99b07ec5b7d91c7eca56785cf2a67c9 [formerly 304946b54165907299bc4c09233a621722e664a4 [formerly 8554b5f535]]]
Former-commit-id: 5be4040c111ab8bd75cf879f3ef02f4502985a90 [formerly 16a8376cd42450055d9cd4a7c68e1405413ffe23]
Former-commit-id: 0cf418584a58c5f0633c1026828d42220494c1a5
Former-commit-id: 195bd6c7a1
2016-03-14 14:50:29 -04:00
Zack Scholl
de86984076 About -> Help
Former-commit-id: b840e8dcbb029fbad7c7c1146b36b662c4ccef11 [formerly f8ba9e007be4e19a01c875626ad10d7e47760967] [formerly 71337169077e80cebb10d8a62967af31852c8eac [formerly 62bfb066a218a9505e9d5235734cceeaee50087a [formerly 0291c55181]]]
Former-commit-id: 0e7ebcb8fa7a9c31ae53dfbe7e49fe47d26da50c [formerly 9810182e654e15602e34486f94e0b070b9033b13]
Former-commit-id: c663d5d59f79728a9b4731471927fc8f471aa84d
Former-commit-id: a9bc263ba4
2016-03-14 14:44:43 -04:00
Zack Scholl
b824de9cfb Added noprompting for encryption
Former-commit-id: 3c1e99d8a999aab6b054bd0b8bbf7a43369ef971 [formerly ee8f5a6e2f793ed9e381ededcf1154da00022ad8] [formerly 55a0d258aa83ce2683aedfc9da60d0568d864945 [formerly aec11a9a4e4053e144094270914f7f28a18fa9bf [formerly a7589f1408]]]
Former-commit-id: e724a3bb346d7e3ebc487788b84ad276a3da21cf [formerly 250b7ea3ff87f0db61f3e4a4b8c54925c7d86285]
Former-commit-id: 5b2412d02eb7e2c1bb2d82d502a0eac2de4cc792
Former-commit-id: 4db31614ae
2016-03-14 14:31:28 -04:00
Zack Scholl
c5ff5a660d Fixed error
Former-commit-id: 326b4bb39ef6f2cac0f2472a6f7176098f5af68c [formerly 7039e826a99969dd557765dbb8fcaa1e16fb20bc] [formerly d5be716d560b9b59bc2b8164ac3b151e737709ab [formerly fda70a2428de11aab529d3eeaaaf6028e120268a [formerly 573f976ea3]]]
Former-commit-id: ab4e86a5fcbb3d8b547e9b26f2373bd7aba23e97 [formerly 4b6ae5b9b134720d46f597da71961f594f3f9cda]
Former-commit-id: 163dc66bc5329801c06ef94d650144e84e0be85b
Former-commit-id: 0b3f38922e
2016-03-14 14:18:31 -04:00
Zack Scholl
23fc464464 Pushed version
Former-commit-id: f2e18541549e55970280291c8ed576482126f32c [formerly 9fb35b2cfb263b681922129352d2810dae9ea7e5] [formerly a83ad53bae5802075f0a527ddb4a9d07d5f12033 [formerly d85485d2ed9fe4e745e024591e99daae43a47dbd [formerly ad59f31709]]]
Former-commit-id: 64323109d3e330ad392ca86a6b437b5c34b036cc [formerly b93e1e89db8129e5976a723917dce95a71b58b4f]
Former-commit-id: 25325dc1d1af84e25dfd00b41129ff467f9de211
Former-commit-id: 03e6691771
2016-03-14 11:44:33 -04:00
Zack Scholl
fe0db5759a Asks for password on page load (if encrypted)
Former-commit-id: d3359afc82912db24f9f58c1bdaedaf7da0a83f1 [formerly 1d0c7deccf184d53d6b7ee9a6941debc797eba27] [formerly 6c31542a665d32a10ae076685fbd323f6ba54f55 [formerly d7441a31522a70e092d3967032879be4610179a2 [formerly 00f28d7544]]]
Former-commit-id: 401b98d78bd410e167af3a3647ba40bb22067208 [formerly 71f8b0ffb3c38cdd036061fce3fd969a83653876]
Former-commit-id: 288985cbcaba05fefc8622ead8e939aa3abb564d
Former-commit-id: d4e9938296
2016-03-14 11:43:28 -04:00
Zack Scholl
e3327d1520 Compress text
Former-commit-id: 5f267e5b0ea1dfdee1237f52e5ca715b83b201ba [formerly 80e5c201ec5b5cf061b3a3788a01d83580b4d93a] [formerly b06e4e69fccfa114e1d09cabe79b428ebf5caa1b [formerly c7c49e03e26599da5d1983949adc8ca5dc3ceefa [formerly d4a48ea392]]]
Former-commit-id: e6047a83549de9bcc6fb94342708f383a13ce977 [formerly 144a3dfb722166333f84a4d704b4aeb246389696]
Former-commit-id: 7815960549e17e32ab8626236c8e54d34a045299
Former-commit-id: 0307b56724
2016-03-14 11:35:30 -04:00
Zack Scholl
325528a2ec Updated
Former-commit-id: f9d735e264523a6ffe0bf8244165c9c7135bd887 [formerly 0e5a3cb35a4b0c561a7d4c6175992f96612389bb] [formerly 39f294620a898be4236f0d857dfceb0fe29d634d [formerly 1b6bfa38bca2fb74388892d7fb6dbe1a452e2521 [formerly 2926bc623c]]]
Former-commit-id: 642f9bb7ae80811899a7424893171c97a42ef417 [formerly c919605caae0c76df1cb76e178e22a5c8cbc1011]
Former-commit-id: ce7ed1fbf7889d2fc8f2a13c3cfa7ffe527e1673
Former-commit-id: 8f383e072c
2016-03-14 11:26:23 -04:00
Zack
abed4141f8 Update README.md
Former-commit-id: 5ecd1f22f2b2fa8c8c5ded59cfd13e91eae6e280 [formerly ea790979cbb6058738d1f3c88b96baa90c84f490] [formerly c2d0b2a29cf088c711cf77ee46cb8c6846c84b3c [formerly 9ba6a0d165f5ce0f39c70f2fa254bfc5c8085cf1 [formerly c8a7b60b5f]]]
Former-commit-id: 456524ea66452868bd521391ade709bc1f78f481 [formerly 73e1f98bcae08112a236f05476033eca90adace1]
Former-commit-id: 78aa10f5cbc423451db8b66f6cb6fca23350c9b5
Former-commit-id: 26997b9b06
2016-03-14 11:02:24 -04:00
Zack Scholl
77e8a20552 dded some Gifs
Former-commit-id: 1b9881ea39ed9c2e5e2706b19b27b6f99001ace7 [formerly a02fb56618e12fd92840dfbccdec893bb61f061e] [formerly f3d971af1e4c76723ad8ef612bb9a9876f255331 [formerly 12b5d2a7e023fb72a3a6699110ee3144a57c0550 [formerly b56614fc5a]]]
Former-commit-id: fdf353f8e2ce644381e03d442d8d3cee5de28237 [formerly 9778b6d2f5f4d6ad3d8e6912b3e4f4b6076953be]
Former-commit-id: 0980fc9060af2bcb64b9ca50036f90de7a1d531d
Former-commit-id: 63c409928a
2016-03-14 11:01:21 -04:00
Zack Scholl
8bb5a6b42b Edited look of pgp
Former-commit-id: 497e2367bbb62faa997855dce5c8b0fdef2976b2 [formerly c8718b40c422a4b131649828d76532530b898a5c] [formerly 3d4a1649f17980c15521f1fce692f17ae5daf826 [formerly 741b226735451ce402fe96b7c6d58a2cece55f3c [formerly aeb783a118]]]
Former-commit-id: b30ebd06b281576bf167ecdc11a5c396582d265b [formerly 9090ca06f7ddfdd545885b7f4b2154baf73e248b]
Former-commit-id: 66c2fb2e55562bf935e2a6cf49be3232cb1b731e
Former-commit-id: d7174f0e8e
2016-03-14 09:56:12 -04:00
Zack Scholl
1f0cda0324 Added encryption/decryption
Former-commit-id: 4cd1a24aec45fbfeff04fab18abde8e2f9f01b16 [formerly f10b9db62ba2289af4b48b2086258b7995f16caf] [formerly d7020bfd45870f48485982fc05e671df8ffc9307 [formerly 33df65ef54db9744efc17ee10c197362835795e0 [formerly e97bfb05a9]]]
Former-commit-id: f6cdb6634f8329f8d87634a86d2ff8af1f5d4c3c [formerly 6ddc94157b82f131bcfd890ab27e8021931b65cd]
Former-commit-id: 2287c335a383ed5c85b97b76459fd49e62a65f8c
Former-commit-id: b99cb7ce70
2016-03-14 09:42:19 -04:00
Zack Scholl
5e03d0c5b9 Self-destruct alert
Former-commit-id: eb06130939eaa1752c6e5b2efb1561f87c05fca3 [formerly 37d7574092e8858228b3ef99379ffda95376505a] [formerly b86c359da7ae72a8ed41d49b26dae9b55fc49b3b [formerly 7b9db2afe7918c8ff1ec1c1f9e3096ed3e6b21ff [formerly 4509722cbf]]]
Former-commit-id: 08ea0f8c80fdc8fe9bf3c4c31a71e4a5c0180b44 [formerly 7d87539a749d43471d6ca017e42daf6152a61f48]
Former-commit-id: 2af4c5cae1a890228e5d59280ba1d0c949d45f61
Former-commit-id: 8645d3a3ac
2016-03-14 08:07:15 -04:00
Zack Scholl
e5cb53cf20 Bumped version to 0.91
Former-commit-id: 0a0d82ebdcfe25a260b8a5ee7627dcb35c3b4da1 [formerly cc3c56e55c8d693b71273f45d61d896ffa95c452] [formerly 9f0f4545ffafe52055b824bf19cad7e0a13e7737 [formerly 8315ce08728423d204b84b6f2ef014fae56e0bb7 [formerly 5ce388764a]]]
Former-commit-id: 2de411fcf47c6f180fdb00b5f0bbcc979e6f4b9f [formerly 116af09214ccc929ef1a164b9bf84429c88d34a1]
Former-commit-id: 7b50bab330b8f2e0320140631d947e292a2d78db
Former-commit-id: d86413147d
2016-03-13 22:07:56 -04:00
Zack Scholl
c542615b62 Updated readme and about page with self-destructing message
information


Former-commit-id: 9afef25b0304da842eb41cde6613ee2a56b14ef7 [formerly 2d26b64ed0752e3e1276fa8bf89c8d986b3837cf] [formerly 8785846c8b218b30b18451c2b46387cc505ca85b [formerly c382f978c2e794f2add70d2e89d574d072578a0c [formerly af04d05233]]]
Former-commit-id: 6e344f746285c5dbf984758f4373ebd5f0073bc5 [formerly 37fe3ff0c5c8287ca2acddde853b4424c17845bd]
Former-commit-id: ba1c83d4af161836f8ea13357dbf383d9b6105a8
Former-commit-id: 7eb62c03ee
2016-03-13 22:05:32 -04:00
Zack Scholl
fc908fb04d Added self-destructing messages https://github.com/schollz/awwkoala/issues/30
Former-commit-id: 85c1bb9080d38319a177c5a3280bf787646cdbb6 [formerly 031b62247892aeb88767ace1da5c46fdc956ff55] [formerly 33bfd5df1c8da72cb068ace2bdb6cae78b4410aa [formerly 0b8a2737fc0034de6cc9c6ec8a16e5f724aa25c0 [formerly 7fb8834284]]]
Former-commit-id: 90290f5a2f51a52ec71491baba14d4211462be0a [formerly 032a61675ff8bf3c6b61b26021ab71ac62ca6e3e]
Former-commit-id: 6897a2246cee59b67abf36999c300763341d2ae2
Former-commit-id: e0ec6ba810
2016-03-13 22:00:22 -04:00
Zack
32abcbcde7 Merge pull request #29 from schollz/dump
Added dump flag to dump all the pages to markdown files

Former-commit-id: 102c9d852456b64d1a365242c9b6521a729adf2a [formerly 921def6205cd6c9223d8c5a8502f7be40ce377d5] [formerly c2c0c0d87425d13583d7e148a017a3d5400a7c2f [formerly e3ec4d8b925d264b968c5c346fef9c3c8b959ae7 [formerly 3513926cf8]]]
Former-commit-id: 4df0fc273fa1c007c431eeba98fc1507dd07a3d8 [formerly 396764512eeb27e2cf179b150849803acf71af60]
Former-commit-id: e9e8a138a49277f1e5170bcb0e881c488620d0eb
Former-commit-id: 9eff989504
2016-03-11 14:13:52 -05:00
Zack Scholl
b6e3f2f440 Added dump flag
Former-commit-id: 1c5335ec40ce0b7ee52abb71af58bedd5559ee32 [formerly f6c2c21205c5859ae3972a43e6de8086268d9d88] [formerly f4dfec26467d1e2b994aab8305281364b3029986 [formerly 4d3affefbf993978d8e976d6e6ca6f55eff70094 [formerly ee5c701dd0]]]
Former-commit-id: 632facfd569da911dd20b0375b4fede77664e32e [formerly 3dc6dbad5fc4d30880af0961fae01c8fac5a113f]
Former-commit-id: 577ba1fedacb3cc7a0ecc9fca4cadd896398e8cf
Former-commit-id: 12476b03c2
2016-03-11 14:11:00 -05:00
Zack Scholl
4ac3a97d52 Slight modifification
Former-commit-id: c2932d76de569169b1a35defe80b1353e3ceec53 [formerly 05576c2cb9f3dbdd6eaa4199a7afa2868a29c10e] [formerly f5e0c7932dd4f27e3d246cae1ac2723594b330fb [formerly 00a61ce94c6f69cd9e03e542c6cca97006c66af7 [formerly 53827bf663]]]
Former-commit-id: 15092c428a21f95f2f2ec059fb2b92eebea0c5e8 [formerly 507937f96e5a5838491efdd621b36567d3da1110]
Former-commit-id: 505ebf3273ef23ed93e4ec6d2c0eab32875e691d
Former-commit-id: 1d102fcc3c
2016-02-17 11:54:34 -05:00
Zack Scholl
78389fda75 No more underline on hover
Former-commit-id: 2e242952e607d032a255797731ec7be40f73f2bf [formerly bfdb723eb1d4e2c360dad6586ba5c03a4af92424] [formerly 3018391d5a0b40e49fea8356c833f6d6a62bac30 [formerly e9e3dc3072d3d49b1bef45a9e6e5882c55302c21 [formerly b0020f9fc2]]]
Former-commit-id: d8250ad67b0c3e208aac7b681d564e8f73c52521 [formerly f7271c8d18eeeea190ae1bd11bcb2c8efe81811f]
Former-commit-id: 64863ce949bd8e0b3b45ea9030f792ef058f22a9
Former-commit-id: bc923b427b
2016-02-17 11:43:22 -05:00
Zack Scholl
f6ab9c8b5c Improved CSS
Former-commit-id: 48dc14c4a195d8de0cac0c4abe7887bea7c6d56e [formerly 75314301a6c8945e2f5fbcc22c76574d0f28b51d] [formerly f133a86489e34803f4edc6a81bc054a802898ca3 [formerly 2279c7f373e70755b2fd0822c16021c97f4b4463 [formerly 7547a5333a]]]
Former-commit-id: 27534afd0bbd06d9b9a00567eaae64599b22b5be [formerly 50b8892cec2355808f06b8f3435cc026ab5c5d7a]
Former-commit-id: d667e7065321654e28183fb1ac9136882d5d37dd
Former-commit-id: f21ae365ea
2016-02-17 11:40:39 -05:00
19 changed files with 769 additions and 113 deletions

View File

@@ -2,25 +2,52 @@
# AwwKoala - [Demo](http://awwkoala.com/)
## A Websocket Wiki and Kind Of A List Application
[![Version 0.9](https://img.shields.io/badge/version-0.9-brightgreen.svg)]() [![Go Report Card](https://goreportcard.com/badge/github.com/schollz/AwwKoala)](https://goreportcard.com/report/github.com/schollz/AwwKoala) [![Join the chat at https://gitter.im/schollz/AwwKoala](https://badges.gitter.im/schollz/AwwKoala.svg)](https://gitter.im/schollz/AwwKoala?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Version 0.94](https://img.shields.io/badge/version-0.94-brightgreen.svg)]() [![Go Report Card](https://goreportcard.com/badge/github.com/schollz/AwwKoala)](https://goreportcard.com/report/github.com/schollz/AwwKoala) [![Join the chat at https://gitter.im/schollz/AwwKoala](https://badges.gitter.im/schollz/AwwKoala.svg)](https://gitter.im/schollz/AwwKoala?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
This is a self-contained wiki webserver that makes sharing easy and _fast_. You can make any page you want, and any page is editable by anyone. Pages load instantly for editing, and have special rendering for whether you want to view as a web page or view as list.
This is a self-contained wiki 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, 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://AwwKoala.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://AwwKoala.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://AwwKoala.com/AnythingYouWant).
**Simplicity**. The philosophy here is to *just type*. To jot a note, simply load the page at [`/`](http://AwwKoala.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://AwwKoala.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://AwwKoala.com/AnythingYouWant). All pages can be rendered into HTML by adding `/view`. For example, the page [`/AnythingYouWant`](http://AwwKoala.com/AnythingYouWant) is rendered at [`/AnythingYouWant/view`](http://AwwKoala.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 `[[AnythingYouWnat]]`.
**Viewing**. All pages can be rendered into HTML by adding `/view`. For example, the page [`/AnythingYouWant`](http://AwwKoala.com/AnythingYouWant) is rendered at [`/AnythingYouWant/view`](http://AwwKoala.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 `[[AnythingYouWnat]]`. Math is supported with [Katex](https://github.com/Khan/KaTeX) using `$\frac{1}{2}$` for inline equations and `$$\frac{1}{2}$$` for regular equations.
![Simply type to edit.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main1.gif)
<br>
**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://AwwKoala.com/grocery), goto [`/grocery/list`](http://AwwKoala.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.
![Lists are easy to make.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main3.gif)
<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).
**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!). The automatic URL is an alliterative animal description - of which there are over 500,000 possibilities - so the URL is easy to remember and hard to guess.
![Versioning is easy.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main2.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`".
![Simply type to edit.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main4.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_).
![Simply type to edit.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main7.gif)
<br>
**Page locking**. You can apply a password to a page to allow further edits from being available. The whole version tree will still be available. _Note_: This is not available for list mode.
**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
First [install Go](https://golang.org/doc/install). Then continue.

23
db.go
View File

@@ -39,12 +39,15 @@ type WikiData struct {
CurrentText string
Diffs []string
Timestamps []string
Encrypted bool
Locked string
}
func hasPassword(title string) (bool, error) {
title = strings.ToLower(title)
if !open {
return false, fmt.Errorf("db must be opened before loading")
Open(RuntimeArgs.DatabaseLocation)
defer Close()
}
hasPassword := false
err := db.View(func(tx *bolt.Tx) error {
@@ -77,14 +80,18 @@ func hasPassword(title string) (bool, error) {
return hasPassword, nil
}
func getCurrentText(title string, version int) (string, []versionsInfo, bool, time.Duration) {
func getCurrentText(title string, version int) (string, []versionsInfo, bool, time.Duration, bool, string) {
Open(RuntimeArgs.DatabaseLocation)
defer Close()
title = strings.ToLower(title)
var vi []versionsInfo
totalTime := time.Now().Sub(time.Now())
isCurrent := true
currentText := ""
encrypted := false
locked := ""
if !open {
return currentText, vi, isCurrent, totalTime
return currentText, vi, isCurrent, totalTime, encrypted, locked
}
err := db.View(func(tx *bolt.Tx) error {
var err error
@@ -103,6 +110,8 @@ func getCurrentText(title string, version int) (string, []versionsInfo, bool, ti
return err
}
currentText = p.CurrentText
encrypted = p.Encrypted
locked = p.Locked
if version > -1 && version < len(p.Diffs) {
// get that version of text instead
currentText = rebuildTextsToDiffN(p, version)
@@ -115,13 +124,14 @@ func getCurrentText(title string, version int) (string, []versionsInfo, bool, ti
if err != nil {
fmt.Printf("Could not get WikiData: %s", err)
}
return currentText, vi, isCurrent, totalTime
return currentText, vi, isCurrent, totalTime, encrypted, locked
}
func (p *WikiData) load(title string) error {
title = strings.ToLower(title)
if !open {
return fmt.Errorf("db must be opened before loading!")
Open(RuntimeArgs.DatabaseLocation)
defer Close()
}
err := db.View(func(tx *bolt.Tx) error {
var err error
@@ -154,7 +164,8 @@ func (p *WikiData) load(title string) error {
func (p *WikiData) save(newText string) error {
if !open {
return fmt.Errorf("db must be opened before saving")
Open(RuntimeArgs.DatabaseLocation)
defer Close()
}
err := db.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists([]byte("datas"))

73
encryption.go Executable file
View File

@@ -0,0 +1,73 @@
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)
//
// }

18
main.go
View File

@@ -35,7 +35,7 @@ var RuntimeArgs struct {
var VersionNum string
func main() {
VersionNum = "0.9"
VersionNum = "0.94"
// _, executableFile, _, _ := runtime.Caller(0) // get full path of this file
cwd, _ := os.Getwd()
databaseFile := path.Join(cwd, "data.db")
@@ -45,6 +45,7 @@ func main() {
flag.StringVar(&RuntimeArgs.ServerCRT, "crt", "", "location of ssl crt")
flag.StringVar(&RuntimeArgs.ServerKey, "key", "", "location of ssl key")
flag.StringVar(&RuntimeArgs.WikiName, "w", "AwwKoala", "custom name for wiki")
dumpDataset := flag.Bool("dump", false, "flag to dump all data to 'dump' directory")
flag.CommandLine.Usage = func() {
fmt.Println(`AwwKoala (version ` + VersionNum + `): A Websocket Wiki and Kind Of A List Application
run this to start the server and then visit localhost at the port you specify
@@ -57,15 +58,22 @@ Options:`)
flag.CommandLine.PrintDefaults()
}
flag.Parse()
if *dumpDataset {
fmt.Println("Dumping data to 'dump' folder...")
dumpEverything()
os.Exit(1)
}
RuntimeArgs.ExternalIP = flag.Arg(0)
if RuntimeArgs.ExternalIP == "" {
RuntimeArgs.ExternalIP = GetLocalIP() + RuntimeArgs.Port
}
RuntimeArgs.SourcePath = cwd
Open(RuntimeArgs.DatabaseLocation)
defer Close()
// create programdata bucket
Open(RuntimeArgs.DatabaseLocation)
err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte("programdata"))
if err != nil {
@@ -76,10 +84,11 @@ Options:`)
if err != nil {
panic(err)
}
Close()
// Default page
aboutFile, _ := ioutil.ReadFile(path.Join(RuntimeArgs.SourcePath, "templates/aboutpage.md"))
p := WikiData{"about", "", []string{}, []string{}}
p := WikiData{"help", "", []string{}, []string{}, false, "zzz"}
p.save(string(aboutFile))
// var q WikiData
@@ -92,6 +101,7 @@ Options:`)
r.HEAD("/", func(c *gin.Context) { c.Status(200) })
r.GET("/:title", editNote)
r.GET("/:title/*option", everythingElse)
r.POST("/:title/*option", encryptionRoute)
r.DELETE("/listitem", deleteListItem)
r.DELETE("/deletepage", deletePage)
if RuntimeArgs.ServerCRT != "" && RuntimeArgs.ServerKey != "" {

59
makeBinaries.py Executable file
View File

@@ -0,0 +1,59 @@
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("../")

265
routes.go
View File

@@ -5,6 +5,7 @@ import (
"html/template"
"io/ioutil"
"net/http"
"os"
"path"
"regexp"
"strconv"
@@ -17,6 +18,128 @@ import (
"github.com/russross/blackfriday"
)
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")
fmt.Println(option, title)
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)
@@ -28,8 +151,8 @@ func editNote(c *gin.Context) {
wshandler(c.Writer, c.Request)
} else if title == "robots.txt" {
c.Data(200, "text/plain", []byte(robotsTxt))
} else if strings.ToLower(title) == "about" { //}&& strings.Contains(AllowedIPs, c.ClientIP()) != true {
c.Redirect(302, "/about/view")
} else if strings.ToLower(title) == "help" { //}&& strings.Contains(AllowedIPs, c.ClientIP()) != true {
c.Redirect(302, "/Help/view")
} else {
locked, _ := hasPassword(title)
if locked {
@@ -37,32 +160,29 @@ func editNote(c *gin.Context) {
} else {
version := c.DefaultQuery("version", "-1")
versionNum, _ := strconv.Atoi(version)
currentText, versions, currentVersion, totalTime := getCurrentText(title, versionNum)
numRows := len(strings.Split(currentText, "\n")) + 10
if currentVersion {
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"Title": title,
"WikiName": RuntimeArgs.WikiName,
"ExternalIP": RuntimeArgs.ExternalIP,
"CurrentText": currentText,
"NumRows": numRows,
"Versions": versions,
"TotalTime": totalTime,
"SocketType": RuntimeArgs.Socket,
})
} else {
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"Title": title,
"WikiName": RuntimeArgs.WikiName,
"ExternalIP": RuntimeArgs.ExternalIP,
"CurrentText": currentText,
"NumRows": numRows,
"Versions": versions,
"TotalTime": totalTime,
"SocketType": RuntimeArgs.Socket,
"NoEdit": true,
})
currentText, versions, currentVersion, totalTime, encrypted, locked := getCurrentText(title, versionNum)
if encrypted || len(locked) > 0 {
c.Redirect(302, "/"+title+"/view")
}
if strings.Contains(currentText, "self-destruct\n") || strings.Contains(currentText, "\nself-destruct") {
c.Redirect(302, "/"+title+"/view")
}
numRows := len(strings.Split(currentText, "\n")) + 10
totalTimeString := totalTime.String()
if totalTime.Seconds() < 1 {
totalTimeString = "< 1 s"
}
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"Title": title,
"WikiName": RuntimeArgs.WikiName,
"ExternalIP": RuntimeArgs.ExternalIP,
"CurrentText": currentText,
"NumRows": numRows,
"Versions": versions,
"TotalTime": totalTimeString,
"SocketType": RuntimeArgs.Socket,
"NoEdit": !currentVersion,
})
}
}
@@ -73,14 +193,21 @@ func everythingElse(c *gin.Context) {
title := c.Param("title")
if option == "/view" {
version := c.DefaultQuery("version", "-1")
noprompt := c.DefaultQuery("noprompt", "-1")
versionNum, _ := strconv.Atoi(version)
if strings.ToLower(title) == "about" {
if strings.ToLower(title) == "help" {
versionNum = -1
}
currentText, versions, _, totalTime := getCurrentText(title, versionNum)
renderMarkdown(c, currentText, title, versions, "", totalTime)
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)
} else if title == "ls" && option == "/"+RuntimeArgs.AdminKey && len(RuntimeArgs.AdminKey) > 1 {
renderMarkdown(c, listEverything(), "ls", nil, RuntimeArgs.AdminKey, time.Now().Sub(time.Now()))
renderMarkdown(c, listEverything(), "ls", nil, RuntimeArgs.AdminKey, time.Now().Sub(time.Now()), false, false, false)
} else if option == "/list" {
renderList(c, title)
} else if title == "static" {
@@ -99,7 +226,7 @@ func serveStaticFile(c *gin.Context, option string) {
}
}
func renderMarkdown(c *gin.Context, currentText string, title string, versions []versionsInfo, AdminKey string, totalTime time.Duration) {
func renderMarkdown(c *gin.Context, currentText string, title string, versions []versionsInfo, AdminKey string, totalTime time.Duration, encrypted bool, noprompt bool, locked bool) {
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)
@@ -128,25 +255,23 @@ func renderMarkdown(c *gin.Context, currentText string, title string, versions [
html2 = strings.Replace(html2, "&amp;#91;", "&#91;", -1)
html2 = strings.Replace(html2, "&amp;#93;", "&#93;", -1)
html2 = strings.Replace(html2, "&amp35;", "&#35;", -1)
if AdminKey == "" {
c.HTML(http.StatusOK, "view.tmpl", gin.H{
"Title": title,
"WikiName": RuntimeArgs.WikiName,
"Body": template.HTML([]byte(html2)),
"TotalTime": totalTime.String(),
"Versions": versions,
})
} else {
c.HTML(http.StatusOK, "view.tmpl", gin.H{
"Title": title,
"WikiName": RuntimeArgs.WikiName,
"Body": template.HTML([]byte(html2)),
"Versions": versions,
"TotalTime": totalTime.String(),
"AdminKey": AdminKey,
})
totalTimeString := totalTime.String()
if totalTime.Seconds() < 1 {
totalTimeString = "< 1 s"
}
c.HTML(http.StatusOK, "view.tmpl", gin.H{
"Title": title,
"WikiName": RuntimeArgs.WikiName,
"Body": template.HTML([]byte(html2)),
"Versions": versions,
"TotalTime": totalTimeString,
"AdminKey": AdminKey,
"Encrypted": encrypted,
"Locked": locked,
"Prompt": noprompt,
"LockedOrEncrypted": locked || encrypted,
})
}
func reorderList(text string) ([]template.HTML, []string) {
@@ -191,8 +316,8 @@ func reorderList(text string) ([]template.HTML, []string) {
}
func renderList(c *gin.Context, title string) {
if strings.ToLower(title) == "about" { //}&& strings.Contains(AllowedIPs, c.ClientIP()) != true {
c.Redirect(302, "/about/view")
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))
@@ -200,7 +325,14 @@ func renderList(c *gin.Context, title string) {
panic(err)
}
fmt.Println(p.CurrentText)
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")
@@ -262,8 +394,8 @@ func deletePage(c *gin.Context) {
fmt.Println(adminKey)
fmt.Println(deleteName)
// if adminKey == RuntimeArgs.AdminKey || true == true {
if strings.ToLower(deleteName) != "about" {
p := WikiData{strings.ToLower(deleteName), "", []string{}, []string{}}
if strings.ToLower(deleteName) != "help" {
p := WikiData{strings.ToLower(deleteName), "", []string{}, []string{}, false, ""}
p.save("")
}
// // remove from program data
@@ -307,6 +439,8 @@ func deletePage(c *gin.Context) {
}
func listEverything() string {
Open(RuntimeArgs.DatabaseLocation)
defer Close()
everything := `| Title | Current size | Changes | Total Size | |
| --------- |-------------| -----| ------------- | ------------- |
`
@@ -328,3 +462,24 @@ func listEverything() string {
})
return everything
}
func dumpEverything() {
Open(RuntimeArgs.DatabaseLocation)
defer Close()
err := os.MkdirAll("dump", 0777)
if err != nil {
fmt.Println("Already exists")
}
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))
fmt.Println(string(k), len(p.CurrentText))
ioutil.WriteFile(path.Join("dump", string(k)+".md"), []byte(p.CurrentText), 0644)
}
return nil
})
}

View File

@@ -8,7 +8,7 @@
.yue {
font: 400 18px/1.62 "Georgia", "Xin Gothic", "Hiragino Sans GB", "Droid Sans Fallback", "Microsoft YaHei", sans-serif;
color: #444443;
color: #333334;
}
.windows .yue {
@@ -31,7 +31,9 @@ background-color: rgba(0,0,0,0.2);
.yue h5,
.yue h6 {
font-family: "Georgia", "Xin Gothic", "Hiragino Sans GB", "Droid Sans Fallback", "Microsoft YaHei", "SimSun", sans-serif;
color: #222223;
color: #333333;
font-weight: 600;
letter-spacing: -0.02em;
}
.yue h1 {
@@ -89,6 +91,7 @@ text-decoration-color: rgba(0, 0, 0, 0.4);
color: #555;
-moz-text-decoration-color: rgba(0, 0, 0, 0.6);
text-decoration-color: rgba(0, 0, 0, 0.6);
text-decoration: none;
}
.yue h1 a,
@@ -255,7 +258,7 @@ box-sizing: border-box;
.yue td {
text-align: left;
padding: 4px 8px 4px 10px;
border: 1px solid #dadada;
border: 1px solid #c1c1bd;
}
.yue td {
@@ -263,7 +266,7 @@ vertical-align: top;
}
.yue tr:nth-child(even) {
background-color: #efefee;
background-color: #dadad8;
}
.yue iframe {
@@ -298,16 +301,16 @@ background: none;
body {
margin: 0;
padding: 0.4em 1em 6em;
background: #fff;
background: #fdfdfd;
}
.yue {
max-width: 800px;
max-width: 700px;
margin: 0 auto;
}
@media (min-width: 1200px) {
.container{
max-width: 800px;
max-width: 700px;
}
}

View File

@@ -0,0 +1 @@
1892f890ee1c6ae802ecaab5a49dcd8d030efd0e

View File

@@ -0,0 +1 @@
d80db96bd467d88ad89eef066efbaf80c3266a76

View File

@@ -0,0 +1 @@
391fc67f2278d89464f4fbd4d2e54af582abb5c0

View File

@@ -0,0 +1 @@
9199fc12a7b46b9f704513b9520fe0f2939fc481

View File

@@ -0,0 +1 @@
7242bf5cfc0ed67be7bb3177e6ba0b8eabdfd6ec

View File

@@ -0,0 +1 @@
60b3cdbea2c28d78a9f853d75aa99bd98d814df7

View File

@@ -0,0 +1 @@
2a331627a6afd0f05e00ea4eb56f58904a8168a7

View File

@@ -1,3 +1,4 @@
var selfDestruct = false;
$(document).ready(function() {
var isTyping = false;
var typingTimer; //timer identifier
@@ -30,6 +31,17 @@ $(document).ready(function() {
console.log("Done typing")
updateInterval = setInterval(updateText, pollToGetNewestCopyInterval);
document.title = "[SAVED] " + title_name;
if ($('#emit_data').val().indexOf("self-destruct\n") > -1 || $('#emit_data').val().indexOf("\nself-destruct") > -1) {
if (selfDestruct == false) {
selfDestruct = true;
swal({ title: "Info", text: "This page is primed to self-destruct.", timer: 1000, showConfirmButton: true });
}
} else {
if (selfDestruct == true) {
selfDestruct = false;
swal({ title: "Info", text: "This page is no longer primed to self-destruct.", timer: 1000, showConfirmButton: true });
}
}
}
function uhoh() {

View File

@@ -2,21 +2,50 @@
# AwwKoala
## A Websocket Wiki and Kind Of A List Application
![Version 0.9](https://img.shields.io/badge/version-0.9-brightgreen.svg)
![Version 0.94](https://img.shields.io/badge/version-0.94-brightgreen.svg)
This is a self-contained wiki webserver that makes sharing easy and _fast_. You can make any page you want, and any page is editable by anyone. Pages load instantly for editing, and have special rendering for whether you want to view as a web page or view as list. **AwwKoala** is also [Open Source](https://github.com/schollz/AwwKoala).
This is a self-contained wiki 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, and listifying. Read on to learn more about the features. **AwwKoala** is also [Open Source](https://github.com/schollz/AwwKoala).
## Features
**Simplicity**. The philosophy here is to *just type*. To jot a note, simply load the page at [`/`](/) 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 [`/`](/) 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`](/AnythingYouWant).
**Simplicity**. The philosophy here is to *just type*. To jot a note, simply load the page at [`/`](http://AwwKoala.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://AwwKoala.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://AwwKoala.com/AnythingYouWant). All pages can be rendered into HTML by adding `/view`. For example, the page [`/AnythingYouWant`](http://AwwKoala.com/AnythingYouWant) is rendered at [`/AnythingYouWant/view`](http://AwwKoala.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 `[[AnythingYouWnat]]`.
**Viewing**. All pages can be rendered into HTML by adding `/view`. For example, the page [`/AnythingYouWant`](/AnythingYouWant) is rendered at [`/AnythingYouWant/view`](/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 `&#91;&#91;AnythingYouWant&#93;&#93;`. Math is supported with [Katex](https://github.com/Khan/KaTeX) using `&#36;\frac{1}{2}&#36;` for inline equations and `&#36;&#36;\frac{1}{2}&#36;&#36;` for regular equations.
![Simply type to edit.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main1.gif)
**Listifying**. If you are writing a list and you want to tick off things really easily, just add `/list`. For example, after editing [`/grocery`](/grocery), goto [`/grocery/list`](/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.
<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!). The automatic URL is an alliterative animal description - of which there are over 500,000 possibilities - so the URL is easy to remember and hard to guess.
**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://AwwKoala.com/grocery), goto [`/grocery/list`](http://AwwKoala.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.
![Lists are easy to make.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main3.gif)
<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/awwkoala/master/static/img/Main2.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`".
![Simply type to edit.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main4.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.
![Simply type to edit.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main7.gif)
<br>
**Page locking**. You can apply a password to a page to allow further edits from being available. The whole version tree will still be available. _Note_: This is not available for list mode.
**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 `&#36;\frac{1}{2}&#36;` for inline equations and `&#36;&#36;\frac{1}{2}&#36;&#36;` for regular equations.
# Contact
Any other comments, questions or anything at all, just <a href="https://twitter.com/intent/tweet?screen_name=zack_118" class="twitter-mention-button" data-related="zack_118">tweet me @zack_118</a>

View File

@@ -9,7 +9,8 @@
<script src="/static/js/jquery.autogrowtextarea.min.js"></script>
{{if .NoEdit}} {{else}} <script src="/static/js/websockets.js"></script> {{end}}
{{if .NoEdit}} {{else}}
<script src="/static/js/websockets.js"></script> {{end}}
<script>
external_ip = '{{ .ExternalIP }}'
@@ -41,13 +42,11 @@
margin: 0 auto;
}
@media (min-width: 1200px) {
.container{
.container {
max-width: 800px;
}
}
}
</style>
<script src="/static/js/sweetalert-dev.js"></script>
<link rel="stylesheet" href="/static/css/sweetalert.css">
@@ -74,7 +73,7 @@
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="dropdown active">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" class="active"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit <span class="caret"></span></a>
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" class="active"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit <span class="caret"></span></a>
<ul class="dropdown-menu">
<li class="dropdown-header">Time edited: {{ .TotalTime }}</li>
<li role="separator" class="divider"></li>
@@ -85,13 +84,15 @@
{{ end }}
<li><a href="/{{ .Title }}">Current</a></li>
<li role="separator" class="divider"></li>
<li class="dropdown-header">Options</li>
<li><a href="#" id="{{ .Title }}" class="deleteable">Erase</a></li>
<li class="dropdown-header">Options</li>
<li><a href="#" class="postencrypt">Encrypt</a></li>
<li><a href="#" class="postlock">Lock</a></li>
<li><a href="#" id="{{ .Title }}" class="deleteable">Erase</a></li>
</ul>
</li>
<li><a href="/{{ .Title }}/view"><span class="glyphicon glyphicon-sunglasses" aria-hidden="true"></span> View</a></li>
<li><a href="/{{ .Title }}/list"><span class="glyphicon glyphicon-align-left" aria-hidden="true"></span> List</a></li>
<li><a href="/about/view"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> About</a></li>
<li><a href="/Help/view"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> Help</a></li>
</ul>
</div>
<!--/.nav-collapse -->
@@ -111,33 +112,158 @@
$("#emit_data").autoGrow();
});
$(document).keydown(function(e){
if( e.which === 90 && e.ctrlKey && e.shiftKey ){
console.log('control + shift + z');
window.location = "/{{ .Title }}/view";
$(document).keydown(function(e) {
if (e.which === 90 && e.ctrlKey && e.shiftKey) {
console.log('control + shift + z');
window.location = "/{{ .Title }}/view";
}
});
$(document).keydown(function(e){
if( e.which === 76 && e.ctrlKey && e.shiftKey ){
console.log('control + shift + l');
window.location = "/{{ .Title }}/list";
}
$(document).keydown(function(e) {
if (e.which === 76 && e.ctrlKey && e.shiftKey) {
console.log('control + shift + l');
window.location = "/{{ .Title }}/list";
}
});
$('.postencrypt').click(function(event) {
var pass1 = "";
var pass2 = "";
event.preventDefault();
swal({
title: "Encryption",
text: "Enter your passphrase:",
type: "input",
showCancelButton: true,
closeOnConfirm: false,
animation: "slide-from-top",
inputPlaceholder: "Write something"
}, function(inputValue) {
if (inputValue === false) return false;
if (inputValue === "") {
swal.showInputError("You need to write something!");
return false
}
pass1 = inputValue;
swal({
title: "Encryption",
text: "Enter your passphrase again:",
type: "input",
showCancelButton: true,
closeOnConfirm: false,
animation: "slide-from-top",
inputPlaceholder: "Write something"
}, function(inputValue) {
if (inputValue === false) return false;
if (inputValue === "") {
swal.showInputError("You need to write something!");
return false
}
pass2 = inputValue
if (pass1 == pass2) {
swal("Encryption", "Passwords match!", "success");
$.ajax({
type: "POST",
//the url where you want to sent the userName and password to
url: '/{{ .Title }}/encrypt',
dataType: 'json',
data: JSON.stringify({
text: $('#emit_data').val(),
password: pass1
}),
success: function (data) {
if (data['success'] == true) {
swal("Encryption", "Encrypted!", "success");
window.location.href = '/{{ .Title }}/view?noprompt=1';
} else {
swal("Encryption", "Something went wrong.", "error");
}
}
});
} else {
swal("Encryption", "Passwords do not match.", "error");
}
});
});
});
$('.postlock').click(function(event) {
var pass1 = "";
var pass2 = "";
event.preventDefault();
swal({
title: "Locking",
text: "Enter your passphrase:",
type: "input",
showCancelButton: true,
closeOnConfirm: false,
animation: "slide-from-top",
inputPlaceholder: "Write something"
}, function(inputValue) {
if (inputValue === false) return false;
if (inputValue === "") {
swal.showInputError("You need to write something!");
return false
}
pass1 = inputValue;
swal({
title: "Locking",
text: "Enter your passphrase again:",
type: "input",
showCancelButton: true,
closeOnConfirm: false,
animation: "slide-from-top",
inputPlaceholder: "Write something"
}, function(inputValue) {
if (inputValue === false) return false;
if (inputValue === "") {
swal.showInputError("You need to write something!");
return false
}
pass2 = inputValue
if (pass1 == pass2) {
swal("Locking", "Passwords match!", "success");
$.ajax({
type: "POST",
//the url where you want to sent the userName and password to
url: '/{{ .Title }}/lock',
dataType: 'json',
data: JSON.stringify({
text: $('#emit_data').val(),
password: pass1
}),
success: function (data) {
if (data['success'] == true) {
swal("Locking", "Page locked!", "success");
window.location.href = '/{{ .Title }}/view';
} else {
swal("Locking", "Something went wrong.", "error");
}
}
});
} else {
swal("Encryption", "Passwords do not match.", "error");
}
});
});
});
$('.deleteable').click(function(event) {
event.preventDefault();
var deleteName = $(this).attr('id')
var href = $(this).attr('href')
swal({
title: "Are you sure?",
text: "You will not be able to recover /{{ .Title }}!",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "Yes, delete it!",
closeOnConfirm: false
title: "Are you sure?",
text: "You will not be able to recover /{{ .Title }}!",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "Yes, delete it!",
closeOnConfirm: false
}, function() {
$.ajax({
url: "/deletepage" + '?' + $.param({
@@ -147,7 +273,9 @@
type: 'DELETE',
success: function() {
swal("Deleted!", "/{{ .Title }} has been deleted.", "success");
setTimeout(function(){ window.location.reload(true); }, 1000);
setTimeout(function() {
window.location.reload(true);
}, 1000);
}
});

View File

@@ -35,7 +35,7 @@
<li><a href="/{{ .Title }}"><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span> Edit</a></li>
<li><a href="/{{ .Title }}/view"><span class="glyphicon glyphicon-sunglasses" aria-hidden="true"></span> View</a></li>
<li class="active"><a href="/{{ .Title }}/list"><span class="glyphicon glyphicon-align-left" aria-hidden="true"></span> List</a></li>
<li><a href="/about/view"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> About</a></li>
<li><a href="/Help/view"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> Help</a></li>
</ul>
</div>
<!--/.nav-collapse -->

View File

@@ -15,6 +15,8 @@ a.deleteable {
cursor: pointer;
}
</style>
<script src="/static/js/sweetalert-dev.js"></script>
<link rel="stylesheet" href="/static/css/sweetalert.css">
</head>
<body>
@@ -44,10 +46,19 @@ a.deleteable {
<li><a href="/{{ $.Title }}/view?version={{ .VersionNum }}">{{ .VersionDate }}</a></li>
{{ end }}
<li><a href="/{{ .Title }}/view">Current</a></li>
{{ if .LockedOrEncrypted }}
<li class="dropdown-header">Options</li>
{{ if .Encrypted }}
<li><a href="#" class="postdecrypt">Decrypt</a></li>
{{end }}
{{ if .Locked }}
<li><a href="#" class="postunlock">Unlock</a></li>
{{end }}
{{ end }}
</ul>
</li>
<li><a href="/{{ .Title }}/list"><span class="glyphicon glyphicon-align-left" aria-hidden="true"></span> List</a></li>
<li><a href="/about/view"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> About</a></li>
<li><a href="/Help/view"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> Help</a></li>
</ul>
</div>
<!--/.nav-collapse -->
@@ -55,12 +66,140 @@ a.deleteable {
</nav>
<div class="yue">
{{ if .Encrypted }}
<pre>
{{ .Body }}
</pre>
{{ else }}
{{ .Body }}
</div>
{{ end }}
</div>
<script>
$( document ).ready(function() {
{{ if .Encrypted }}
{{ if .Prompt }}
var pass1 = "";
swal({
title: "Decryption",
text: "Enter your passphrase:",
type: "input",
showCancelButton: true,
closeOnConfirm: false,
animation: "slide-from-top",
inputPlaceholder: "Write something"
}, function(inputValue) {
if (inputValue === false) return false;
if (inputValue === "") {
swal.showInputError("You need to write something!");
return false
}
pass1 = inputValue;
$.ajax({
type: "POST",
//the url where you want to sent the userName and password to
url: '/{{ .Title }}/decrypt',
dataType: 'json',
data: JSON.stringify({
text: " ",
password: pass1
}),
success: function (data) {
if (data['success'] == true) {
swal("Decryption", "Decrypted!", "success");
window.location.href = '/{{ .Title }}/view';
} else {
swal("Decryption", data['status'], "error");
}
}
});
});
{{ end }}
$('.postdecrypt').click(function(event) {
var pass1 = "";
event.preventDefault();
swal({
title: "Decryption",
text: "Enter your passphrase:",
type: "input",
showCancelButton: true,
closeOnConfirm: false,
animation: "slide-from-top",
inputPlaceholder: "Write something"
}, function(inputValue) {
if (inputValue === false) return false;
if (inputValue === "") {
swal.showInputError("You need to write something!");
return false
}
pass1 = inputValue;
$.ajax({
type: "POST",
//the url where you want to sent the userName and password to
url: '/{{ .Title }}/decrypt',
dataType: 'json',
data: JSON.stringify({
text: " ",
password: pass1
}),
success: function (data) {
if (data['success'] == true) {
swal("Decryption", "Decrypted!", "success");
window.location.href = '/{{ .Title }}/view';
} else {
swal("Decryption", data['status'], "error");
}
}
});
});
});
{{ end }}
{{ if .Locked }}
$('.postunlock').click(function(event) {
var pass1 = "";
event.preventDefault();
swal({
title: "Unlock",
text: "Enter your passphrase:",
type: "input",
showCancelButton: true,
closeOnConfirm: false,
animation: "slide-from-top",
inputPlaceholder: "Write something"
}, function(inputValue) {
if (inputValue === false) return false;
if (inputValue === "") {
swal.showInputError("You need to write something!");
return false
}
pass1 = inputValue;
$.ajax({
type: "POST",
//the url where you want to sent the userName and password to
url: '/{{ .Title }}/unlock',
dataType: 'json',
data: JSON.stringify({
text: " ",
password: pass1
}),
success: function (data) {
if (data['success'] == true) {
swal("Unlock", "Unlocked!", "success");
window.location.href = '/{{ .Title }}';
} else {
swal("Unlock", data['status'], "error");
}
}
});
});
});
{{ end }}
var texi = document.getElementsByClassName("texi");
Array.prototype.forEach.call(texi, function(el) {
katex.render(el.getAttribute("data-expr"), el, { displayMode: false });
@@ -71,6 +210,8 @@ $( document ).ready(function() {
katex.render(el.getAttribute("data-expr"), el, { displayMode: true });
});
});
$(document).keydown(function(e){
@@ -88,6 +229,7 @@ $(document).keydown(function(e){
}
});
{{ if .AdminKey }}
$('.deleteable').click(function(event) {
event.preventDefault();