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

Upgrade dependencies

This commit is contained in:
Daniel Heath 2018-03-31 14:42:54 +11:00
parent d9e622fa9d
commit 2a43ebdb53
44 changed files with 295 additions and 34489 deletions

52
Gopkg.lock generated
View File

@ -3,9 +3,9 @@
[[projects]]
branch = "master"
name = "dmitri.shuralyov.com/kebabcase"
name = "dmitri.shuralyov.com/text/kebabcase"
packages = ["."]
revision = "bf160e40a7918fbe9dc3cc841a023d87242bd2eb"
revision = "40e40b42552a9cb37d6e98f4ad31f63ae53ea43a"
[[projects]]
name = "github.com/boj/redistore"
@ -29,11 +29,14 @@
branch = "master"
name = "github.com/danielheath/gin-teeny-security"
packages = ["."]
revision = "b9ad6bd1a94e8e68fd1256221b3e055c8af5f81a"
revision = "bb11804dd0e27b2d5a489af15d6c966f2e954079"
[[projects]]
name = "github.com/garyburd/redigo"
packages = ["internal","redis"]
packages = [
"internal",
"redis"
]
revision = "d1ed5c67e5794de818ea85e6b522fda02623a484"
version = "v1.4.0"
@ -57,7 +60,11 @@
[[projects]]
name = "github.com/gin-gonic/gin"
packages = [".","binding","render"]
packages = [
".",
"binding",
"render"
]
revision = "d459835d2b077e44f7c9b453505ee29881d5d12d"
version = "v1.2"
@ -115,12 +122,6 @@
revision = "4048872b16cc0fc2c5fd9eacf0ed2c2fedaa0c8c"
version = "v1.5"
[[projects]]
name = "github.com/schollz/cowyo"
packages = ["encrypt"]
revision = "684ff4e692da7ce06781b2fecb47b34fc100faf4"
version = "v2.8.0"
[[projects]]
branch = "master"
name = "github.com/schollz/cryptopasta"
@ -148,7 +149,12 @@
[[projects]]
branch = "master"
name = "github.com/shurcooL/go"
packages = ["parserutil","printerutil","reflectfind","reflectsource"]
packages = [
"parserutil",
"printerutil",
"reflectfind",
"reflectsource"
]
revision = "004faa6b0118cf52635363b72b51cdcc297800a2"
[[projects]]
@ -179,7 +185,7 @@
branch = "master"
name = "github.com/shurcooL/octiconssvg"
packages = ["."]
revision = "38b02129bb6460858e11f90798a3832da1e502bd"
revision = "91d14858bf8188ba5143d67a3a677ea559000b0a"
[[projects]]
branch = "master"
@ -208,13 +214,19 @@
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = ["bcrypt","blowfish"]
packages = [
"bcrypt",
"blowfish"
]
revision = "39efaea5da11abd5e2b90a435b1f338cdb94619c"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = ["html","html/atom"]
packages = [
"html",
"html/atom"
]
revision = "5ccada7d0a7ba9aeb5d3aca8d3501b4c2a509fec"
[[projects]]
@ -232,7 +244,13 @@
[[projects]]
branch = "v2"
name = "gopkg.in/mgo.v2"
packages = [".","bson","internal/json","internal/sasl","internal/scram"]
packages = [
".",
"bson",
"internal/json",
"internal/sasl",
"internal/scram"
]
revision = "3f83fa5005286a7fe593b055f0d7771a7dce4655"
[[projects]]
@ -250,6 +268,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "78100cdb84da20268b3f0840cd94ee575c61b892b6474f3744499256026625af"
inputs-digest = "aad079402a0a87842f679dbee25dcf75b91057be588d8a27dc7bc35eb996014c"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -1,6 +1,5 @@
// Code generated by go-bindata.
// sources:
// static/.DS_Store
// static/css/base-min.css
// static/css/default.css
// static/css/dropzone.css
@ -76,27 +75,9 @@ type asset struct {
info os.FileInfo
}
// staticDs_store reads file data from disk. It returns an error on failure.
func staticDs_store() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/.DS_Store"
name := "static/.DS_Store"
bytes, err := bindataRead(path, name)
if err != nil {
return nil, err
}
fi, err := os.Stat(path)
if err != nil {
err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err)
}
a := &asset{bytes: bytes, info: fi}
return a, err
}
// staticCssBaseMinCss reads file data from disk. It returns an error on failure.
func staticCssBaseMinCss() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/css/base-min.css"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/css/base-min.css"
name := "static/css/base-min.css"
bytes, err := bindataRead(path, name)
if err != nil {
@ -114,7 +95,7 @@ func staticCssBaseMinCss() (*asset, error) {
// staticCssDefaultCss reads file data from disk. It returns an error on failure.
func staticCssDefaultCss() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/css/default.css"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/css/default.css"
name := "static/css/default.css"
bytes, err := bindataRead(path, name)
if err != nil {
@ -132,7 +113,7 @@ func staticCssDefaultCss() (*asset, error) {
// staticCssDropzoneCss reads file data from disk. It returns an error on failure.
func staticCssDropzoneCss() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/css/dropzone.css"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/css/dropzone.css"
name := "static/css/dropzone.css"
bytes, err := bindataRead(path, name)
if err != nil {
@ -150,7 +131,7 @@ func staticCssDropzoneCss() (*asset, error) {
// staticCssGithubMarkdownCss reads file data from disk. It returns an error on failure.
func staticCssGithubMarkdownCss() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/css/github-markdown.css"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/css/github-markdown.css"
name := "static/css/github-markdown.css"
bytes, err := bindataRead(path, name)
if err != nil {
@ -168,7 +149,7 @@ func staticCssGithubMarkdownCss() (*asset, error) {
// staticCssHighlightCss reads file data from disk. It returns an error on failure.
func staticCssHighlightCss() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/css/highlight.css"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/css/highlight.css"
name := "static/css/highlight.css"
bytes, err := bindataRead(path, name)
if err != nil {
@ -186,7 +167,7 @@ func staticCssHighlightCss() (*asset, error) {
// staticCssMenusMinCss reads file data from disk. It returns an error on failure.
func staticCssMenusMinCss() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/css/menus-min.css"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/css/menus-min.css"
name := "static/css/menus-min.css"
bytes, err := bindataRead(path, name)
if err != nil {
@ -204,7 +185,7 @@ func staticCssMenusMinCss() (*asset, error) {
// staticImgCowyoAndroidIcon144x144Png reads file data from disk. It returns an error on failure.
func staticImgCowyoAndroidIcon144x144Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/android-icon-144x144.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/android-icon-144x144.png"
name := "static/img/cowyo/android-icon-144x144.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -222,7 +203,7 @@ func staticImgCowyoAndroidIcon144x144Png() (*asset, error) {
// staticImgCowyoAndroidIcon192x192Png reads file data from disk. It returns an error on failure.
func staticImgCowyoAndroidIcon192x192Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/android-icon-192x192.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/android-icon-192x192.png"
name := "static/img/cowyo/android-icon-192x192.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -240,7 +221,7 @@ func staticImgCowyoAndroidIcon192x192Png() (*asset, error) {
// staticImgCowyoAndroidIcon36x36Png reads file data from disk. It returns an error on failure.
func staticImgCowyoAndroidIcon36x36Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/android-icon-36x36.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/android-icon-36x36.png"
name := "static/img/cowyo/android-icon-36x36.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -258,7 +239,7 @@ func staticImgCowyoAndroidIcon36x36Png() (*asset, error) {
// staticImgCowyoAndroidIcon48x48Png reads file data from disk. It returns an error on failure.
func staticImgCowyoAndroidIcon48x48Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/android-icon-48x48.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/android-icon-48x48.png"
name := "static/img/cowyo/android-icon-48x48.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -276,7 +257,7 @@ func staticImgCowyoAndroidIcon48x48Png() (*asset, error) {
// staticImgCowyoAndroidIcon72x72Png reads file data from disk. It returns an error on failure.
func staticImgCowyoAndroidIcon72x72Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/android-icon-72x72.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/android-icon-72x72.png"
name := "static/img/cowyo/android-icon-72x72.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -294,7 +275,7 @@ func staticImgCowyoAndroidIcon72x72Png() (*asset, error) {
// staticImgCowyoAndroidIcon96x96Png reads file data from disk. It returns an error on failure.
func staticImgCowyoAndroidIcon96x96Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/android-icon-96x96.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/android-icon-96x96.png"
name := "static/img/cowyo/android-icon-96x96.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -312,7 +293,7 @@ func staticImgCowyoAndroidIcon96x96Png() (*asset, error) {
// staticImgCowyoAppleIcon114x114Png reads file data from disk. It returns an error on failure.
func staticImgCowyoAppleIcon114x114Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/apple-icon-114x114.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/apple-icon-114x114.png"
name := "static/img/cowyo/apple-icon-114x114.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -330,7 +311,7 @@ func staticImgCowyoAppleIcon114x114Png() (*asset, error) {
// staticImgCowyoAppleIcon120x120Png reads file data from disk. It returns an error on failure.
func staticImgCowyoAppleIcon120x120Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/apple-icon-120x120.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/apple-icon-120x120.png"
name := "static/img/cowyo/apple-icon-120x120.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -348,7 +329,7 @@ func staticImgCowyoAppleIcon120x120Png() (*asset, error) {
// staticImgCowyoAppleIcon144x144Png reads file data from disk. It returns an error on failure.
func staticImgCowyoAppleIcon144x144Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/apple-icon-144x144.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/apple-icon-144x144.png"
name := "static/img/cowyo/apple-icon-144x144.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -366,7 +347,7 @@ func staticImgCowyoAppleIcon144x144Png() (*asset, error) {
// staticImgCowyoAppleIcon152x152Png reads file data from disk. It returns an error on failure.
func staticImgCowyoAppleIcon152x152Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/apple-icon-152x152.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/apple-icon-152x152.png"
name := "static/img/cowyo/apple-icon-152x152.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -384,7 +365,7 @@ func staticImgCowyoAppleIcon152x152Png() (*asset, error) {
// staticImgCowyoAppleIcon180x180Png reads file data from disk. It returns an error on failure.
func staticImgCowyoAppleIcon180x180Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/apple-icon-180x180.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/apple-icon-180x180.png"
name := "static/img/cowyo/apple-icon-180x180.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -402,7 +383,7 @@ func staticImgCowyoAppleIcon180x180Png() (*asset, error) {
// staticImgCowyoAppleIcon57x57Png reads file data from disk. It returns an error on failure.
func staticImgCowyoAppleIcon57x57Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/apple-icon-57x57.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/apple-icon-57x57.png"
name := "static/img/cowyo/apple-icon-57x57.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -420,7 +401,7 @@ func staticImgCowyoAppleIcon57x57Png() (*asset, error) {
// staticImgCowyoAppleIcon60x60Png reads file data from disk. It returns an error on failure.
func staticImgCowyoAppleIcon60x60Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/apple-icon-60x60.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/apple-icon-60x60.png"
name := "static/img/cowyo/apple-icon-60x60.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -438,7 +419,7 @@ func staticImgCowyoAppleIcon60x60Png() (*asset, error) {
// staticImgCowyoAppleIcon72x72Png reads file data from disk. It returns an error on failure.
func staticImgCowyoAppleIcon72x72Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/apple-icon-72x72.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/apple-icon-72x72.png"
name := "static/img/cowyo/apple-icon-72x72.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -456,7 +437,7 @@ func staticImgCowyoAppleIcon72x72Png() (*asset, error) {
// staticImgCowyoAppleIcon76x76Png reads file data from disk. It returns an error on failure.
func staticImgCowyoAppleIcon76x76Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/apple-icon-76x76.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/apple-icon-76x76.png"
name := "static/img/cowyo/apple-icon-76x76.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -474,7 +455,7 @@ func staticImgCowyoAppleIcon76x76Png() (*asset, error) {
// staticImgCowyoAppleIconPrecomposedPng reads file data from disk. It returns an error on failure.
func staticImgCowyoAppleIconPrecomposedPng() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/apple-icon-precomposed.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/apple-icon-precomposed.png"
name := "static/img/cowyo/apple-icon-precomposed.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -492,7 +473,7 @@ func staticImgCowyoAppleIconPrecomposedPng() (*asset, error) {
// staticImgCowyoAppleIconPng reads file data from disk. It returns an error on failure.
func staticImgCowyoAppleIconPng() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/apple-icon.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/apple-icon.png"
name := "static/img/cowyo/apple-icon.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -510,7 +491,7 @@ func staticImgCowyoAppleIconPng() (*asset, error) {
// staticImgCowyoBrowserconfigXml reads file data from disk. It returns an error on failure.
func staticImgCowyoBrowserconfigXml() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/browserconfig.xml"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/browserconfig.xml"
name := "static/img/cowyo/browserconfig.xml"
bytes, err := bindataRead(path, name)
if err != nil {
@ -528,7 +509,7 @@ func staticImgCowyoBrowserconfigXml() (*asset, error) {
// staticImgCowyoFavicon16x16Png reads file data from disk. It returns an error on failure.
func staticImgCowyoFavicon16x16Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/favicon-16x16.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/favicon-16x16.png"
name := "static/img/cowyo/favicon-16x16.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -546,7 +527,7 @@ func staticImgCowyoFavicon16x16Png() (*asset, error) {
// staticImgCowyoFavicon32x32Png reads file data from disk. It returns an error on failure.
func staticImgCowyoFavicon32x32Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/favicon-32x32.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/favicon-32x32.png"
name := "static/img/cowyo/favicon-32x32.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -564,7 +545,7 @@ func staticImgCowyoFavicon32x32Png() (*asset, error) {
// staticImgCowyoFavicon96x96Png reads file data from disk. It returns an error on failure.
func staticImgCowyoFavicon96x96Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/favicon-96x96.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/favicon-96x96.png"
name := "static/img/cowyo/favicon-96x96.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -582,7 +563,7 @@ func staticImgCowyoFavicon96x96Png() (*asset, error) {
// staticImgCowyoFaviconIco reads file data from disk. It returns an error on failure.
func staticImgCowyoFaviconIco() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/favicon.ico"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/favicon.ico"
name := "static/img/cowyo/favicon.ico"
bytes, err := bindataRead(path, name)
if err != nil {
@ -600,7 +581,7 @@ func staticImgCowyoFaviconIco() (*asset, error) {
// staticImgCowyoManifestJson reads file data from disk. It returns an error on failure.
func staticImgCowyoManifestJson() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/manifest.json"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/manifest.json"
name := "static/img/cowyo/manifest.json"
bytes, err := bindataRead(path, name)
if err != nil {
@ -618,7 +599,7 @@ func staticImgCowyoManifestJson() (*asset, error) {
// staticImgCowyoMsIcon144x144Png reads file data from disk. It returns an error on failure.
func staticImgCowyoMsIcon144x144Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/ms-icon-144x144.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/ms-icon-144x144.png"
name := "static/img/cowyo/ms-icon-144x144.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -636,7 +617,7 @@ func staticImgCowyoMsIcon144x144Png() (*asset, error) {
// staticImgCowyoMsIcon150x150Png reads file data from disk. It returns an error on failure.
func staticImgCowyoMsIcon150x150Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/ms-icon-150x150.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/ms-icon-150x150.png"
name := "static/img/cowyo/ms-icon-150x150.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -654,7 +635,7 @@ func staticImgCowyoMsIcon150x150Png() (*asset, error) {
// staticImgCowyoMsIcon310x310Png reads file data from disk. It returns an error on failure.
func staticImgCowyoMsIcon310x310Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/ms-icon-310x310.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/ms-icon-310x310.png"
name := "static/img/cowyo/ms-icon-310x310.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -672,7 +653,7 @@ func staticImgCowyoMsIcon310x310Png() (*asset, error) {
// staticImgCowyoMsIcon70x70Png reads file data from disk. It returns an error on failure.
func staticImgCowyoMsIcon70x70Png() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/cowyo/ms-icon-70x70.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/cowyo/ms-icon-70x70.png"
name := "static/img/cowyo/ms-icon-70x70.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -690,7 +671,7 @@ func staticImgCowyoMsIcon70x70Png() (*asset, error) {
// staticImgLogoPng reads file data from disk. It returns an error on failure.
func staticImgLogoPng() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/img/logo.png"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/img/logo.png"
name := "static/img/logo.png"
bytes, err := bindataRead(path, name)
if err != nil {
@ -708,7 +689,7 @@ func staticImgLogoPng() (*asset, error) {
// staticJsCowyoJs reads file data from disk. It returns an error on failure.
func staticJsCowyoJs() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/js/cowyo.js"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/js/cowyo.js"
name := "static/js/cowyo.js"
bytes, err := bindataRead(path, name)
if err != nil {
@ -726,7 +707,7 @@ func staticJsCowyoJs() (*asset, error) {
// staticJsDropzoneJs reads file data from disk. It returns an error on failure.
func staticJsDropzoneJs() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/js/dropzone.js"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/js/dropzone.js"
name := "static/js/dropzone.js"
bytes, err := bindataRead(path, name)
if err != nil {
@ -744,7 +725,7 @@ func staticJsDropzoneJs() (*asset, error) {
// staticJsHighlightMinJs reads file data from disk. It returns an error on failure.
func staticJsHighlightMinJs() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/js/highlight.min.js"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/js/highlight.min.js"
name := "static/js/highlight.min.js"
bytes, err := bindataRead(path, name)
if err != nil {
@ -762,7 +743,7 @@ func staticJsHighlightMinJs() (*asset, error) {
// staticJsHighlightPackJs reads file data from disk. It returns an error on failure.
func staticJsHighlightPackJs() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/js/highlight.pack.js"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/js/highlight.pack.js"
name := "static/js/highlight.pack.js"
bytes, err := bindataRead(path, name)
if err != nil {
@ -780,7 +761,7 @@ func staticJsHighlightPackJs() (*asset, error) {
// staticJsJquery183Js reads file data from disk. It returns an error on failure.
func staticJsJquery183Js() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/js/jquery-1.8.3.js"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/js/jquery-1.8.3.js"
name := "static/js/jquery-1.8.3.js"
bytes, err := bindataRead(path, name)
if err != nil {
@ -798,7 +779,7 @@ func staticJsJquery183Js() (*asset, error) {
// staticTextAdjectives reads file data from disk. It returns an error on failure.
func staticTextAdjectives() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/text/adjectives"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/text/adjectives"
name := "static/text/adjectives"
bytes, err := bindataRead(path, name)
if err != nil {
@ -816,7 +797,7 @@ func staticTextAdjectives() (*asset, error) {
// staticTextAdjectivesOld reads file data from disk. It returns an error on failure.
func staticTextAdjectivesOld() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/text/adjectives.old"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/text/adjectives.old"
name := "static/text/adjectives.old"
bytes, err := bindataRead(path, name)
if err != nil {
@ -834,7 +815,7 @@ func staticTextAdjectivesOld() (*asset, error) {
// staticTextAnimals reads file data from disk. It returns an error on failure.
func staticTextAnimals() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/text/animals"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/text/animals"
name := "static/text/animals"
bytes, err := bindataRead(path, name)
if err != nil {
@ -852,7 +833,7 @@ func staticTextAnimals() (*asset, error) {
// staticTextAnimalsAll reads file data from disk. It returns an error on failure.
func staticTextAnimalsAll() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/text/animals.all"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/text/animals.all"
name := "static/text/animals.all"
bytes, err := bindataRead(path, name)
if err != nil {
@ -870,7 +851,7 @@ func staticTextAnimalsAll() (*asset, error) {
// staticTextHowmanyPy reads file data from disk. It returns an error on failure.
func staticTextHowmanyPy() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/text/howmany.py"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/text/howmany.py"
name := "static/text/howmany.py"
bytes, err := bindataRead(path, name)
if err != nil {
@ -888,7 +869,7 @@ func staticTextHowmanyPy() (*asset, error) {
// staticTextRobotsTxt reads file data from disk. It returns an error on failure.
func staticTextRobotsTxt() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/text/robots.txt"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/text/robots.txt"
name := "static/text/robots.txt"
bytes, err := bindataRead(path, name)
if err != nil {
@ -906,7 +887,7 @@ func staticTextRobotsTxt() (*asset, error) {
// staticTextSitemapXml reads file data from disk. It returns an error on failure.
func staticTextSitemapXml() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/static/text/sitemap.xml"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/static/text/sitemap.xml"
name := "static/text/sitemap.xml"
bytes, err := bindataRead(path, name)
if err != nil {
@ -924,7 +905,7 @@ func staticTextSitemapXml() (*asset, error) {
// templatesIndexTmpl reads file data from disk. It returns an error on failure.
func templatesIndexTmpl() (*asset, error) {
path := "/Volumes/Code/go/src/github.com/danielheath/cowyo/templates/index.tmpl"
path := "/Volumes/Code/go/src/github.com/schollz/cowyo/templates/index.tmpl"
name := "templates/index.tmpl"
bytes, err := bindataRead(path, name)
if err != nil {
@ -992,7 +973,6 @@ func AssetNames() []string {
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
"static/.DS_Store": staticDs_store,
"static/css/base-min.css": staticCssBaseMinCss,
"static/css/default.css": staticCssDefaultCss,
"static/css/dropzone.css": staticCssDropzoneCss,
@ -1084,7 +1064,6 @@ type bintree struct {
var _bintree = &bintree{nil, map[string]*bintree{
"static": &bintree{nil, map[string]*bintree{
".DS_Store": &bintree{staticDs_store, map[string]*bintree{}},
"css": &bintree{nil, map[string]*bintree{
"base-min.css": &bintree{staticCssBaseMinCss, map[string]*bintree{}},
"default.css": &bintree{staticCssDefaultCss, map[string]*bintree{}},

View File

@ -5,7 +5,7 @@ import (
"reflect"
"testing"
"dmitri.shuralyov.com/kebabcase"
"dmitri.shuralyov.com/text/kebabcase"
"github.com/shurcooL/graphql/ident"
)

View File

@ -1,5 +1,15 @@
// A GIN middleware providing low-fi security for personal stuff.
// A GIN middleware providing low-fi security for sites with simple needs.
//
// Redirects users to a login page until they provide a secret code.
// No CSRF protection, so any js on the web can log you
// out (or in, if they know the password).
//
// Protects you from brute-force attacks by making all login attempts
// take 1 second (configurable) and serializing them through a mutex.
//
// Scripts can send `Authorization: <secret code>` instead of
// having to keep a cookie jar.
//
package gin_teeny_security
import "github.com/gin-gonic/gin"
@ -11,12 +21,8 @@ import "time"
import "sync"
import "html/template"
// Forces you to a login page until you provide a secret code.
// No CSRF protection, so any script on any page can log you
// out (or in, if they know the password).
// The rest of your site needs XSS protection on forms or any site on the
// net can inject stuff. If you're sending open CORS headers this
// would be particularly bad.
// Convenient entry-point for those using gin-sessions and
// not wanting to override anything.
func RequiresSecretAccessCode(secretAccessCode, path string) gin.HandlerFunc {
cfg := &Config{
Path: path,
@ -26,15 +32,16 @@ func RequiresSecretAccessCode(secretAccessCode, path string) gin.HandlerFunc {
return cfg.Middleware
}
// Main entry point
type Config struct {
Path string // defaults to login
Secret string
RequireAuth func(*gin.Context) bool // defaults to always requiring auth if unset
Template *template.Template
SaveKeyToSession func(*gin.Context, string)
GetKeyFromSession func(*gin.Context) string
Path string // defaults to 'login'
Secret string // the password
RequireAuth func(*gin.Context) bool // defaults to always requiring auth if unset; override to allow some public access.
Template *template.Template // Markup for the login page
SaveKeyToSession func(*gin.Context, string) // Override to use something other than gin-sessions
GetKeyFromSession func(*gin.Context) string // Override to use something other than gin-sessions
LoginAttemptSlowdown time.Duration
LoginAttemptSlowdown time.Duration // Increase to slow-down attempts to brute force your password.
mutex sync.Mutex
}
@ -52,12 +59,14 @@ func (c Config) getKey(ctx *gin.Context) string {
return c.GetKeyFromSession(ctx)
}
// Saves your login status using gin-sessions
func DefaultSetSession(c *gin.Context, secret string) {
session := sessions.Default(c)
session.Set("secretAccessCode", secret)
session.Save()
}
// Gets your login status from gin-sessions
func DefaultGetSession(c *gin.Context) string {
session := sessions.Default(c)
str, ok := session.Get("secretAccessCode").(string)
@ -75,6 +84,12 @@ func (c Config) path() string {
}
func (c Config) requireAuth(ctx *gin.Context) bool {
if ctx.Request.Header.Get("Authorization") != "" {
// Slow down brute-force attempts.
c.mutex.Lock()
defer c.mutex.Unlock()
time.Sleep(c.loginSlowdown())
}
if ctx.Request.Header.Get("Authorization") == c.Secret {
return false
}
@ -114,6 +129,10 @@ var DEFAULT_LOGIN_PAGE = template.Must(template.New("login").Parse(`
<input type="password" name="secretAccessCode" />
<input type="submit" value="Login" />
</form>
<div style="display: none">
CURL users: try setting -H 'Authorization: <your secret>'
</div>
`))
func (cfg *Config) Middleware(c *gin.Context) {

View File

@ -1 +0,0 @@
static/* linguist-vendored

View File

@ -1,27 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
data/*
cowyo

View File

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

View File

@ -1,25 +0,0 @@
# First build step
FROM golang:1.9-alpine as builder
WORKDIR /go/src/cowyo
COPY . .
# Disable crosscompiling
ENV CGO_ENABLED=0
# Install git and make, compile and cleanup
RUN apk add --no-cache git make \
&& go get -u github.com/schollz/cowyo \
&& go get -u github.com/jteeuwen/go-bindata/... \
&& make \
&& apk del --purge git make \
&& rm -rf /var/cache/apk*
# Second build step uses the minimal scratch Docker image
FROM scratch
# Copy the binary from the first step
COPY --from=builder /go/src/cowyo/cowyo /usr/local/bin/cowyo
# Expose data folder
VOLUME /data
EXPOSE 8050
# Start cowyo listening on any host
CMD ["cowyo", "--host", "0.0.0.0"]

View File

@ -1,243 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "dmitri.shuralyov.com/kebabcase"
packages = ["."]
revision = "bf160e40a7918fbe9dc3cc841a023d87242bd2eb"
[[projects]]
name = "github.com/boj/redistore"
packages = ["."]
revision = "fc113767cd6b051980f260d6dbe84b2740c46ab0"
version = "v1.2"
[[projects]]
branch = "master"
name = "github.com/bradfitz/gomemcache"
packages = ["memcache"]
revision = "1952afaa557dc08e8e0d89eafab110fb501c1a2b"
[[projects]]
branch = "master"
name = "github.com/bradleypeabody/gorilla-sessions-memcache"
packages = ["."]
revision = "75ee37df8664a47cd5d9eb84d41e1f2b08086893"
[[projects]]
name = "github.com/garyburd/redigo"
packages = ["internal","redis"]
revision = "34a326de1fea52965fa5ad664d3fc7163dd4b0a1"
version = "v1.2.0"
[[projects]]
branch = "master"
name = "github.com/gin-contrib/multitemplate"
packages = ["."]
revision = "bbc6daf6024bc4c48f334a0490321cd48a24da3d"
[[projects]]
branch = "master"
name = "github.com/gin-contrib/sessions"
packages = ["."]
revision = "cccdeef56346e7037ca92de250c2b55ef5e7ffe3"
[[projects]]
branch = "master"
name = "github.com/gin-contrib/sse"
packages = ["."]
revision = "22d885f9ecc78bf4ee5d72b937e4bbcdc58e8cae"
[[projects]]
name = "github.com/gin-gonic/gin"
packages = [".","binding","render"]
revision = "d459835d2b077e44f7c9b453505ee29881d5d12d"
version = "v1.2"
[[projects]]
branch = "master"
name = "github.com/golang/protobuf"
packages = ["proto"]
revision = "1643683e1b54a9e88ad26d98f81400c8c9d9f4f9"
[[projects]]
name = "github.com/gorilla/context"
packages = ["."]
revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a"
version = "v1.1"
[[projects]]
name = "github.com/gorilla/securecookie"
packages = ["."]
revision = "667fe4e3466a040b780561fe9b51a83a3753eefc"
version = "v1.1"
[[projects]]
name = "github.com/gorilla/sessions"
packages = ["."]
revision = "ca9ada44574153444b00d3fd9c8559e4cc95f896"
version = "v1.1"
[[projects]]
branch = "master"
name = "github.com/jcelliott/lumber"
packages = ["."]
revision = "dd349441af25132d146d7095c6693a15431fc9b1"
[[projects]]
branch = "master"
name = "github.com/kidstuff/mongostore"
packages = ["."]
revision = "256d65ac5b0e35e7c5ebb3f175c0bed1e5c2b253"
[[projects]]
name = "github.com/mattn/go-isatty"
packages = ["."]
revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39"
version = "v0.0.3"
[[projects]]
branch = "master"
name = "github.com/microcosm-cc/bluemonday"
packages = ["."]
revision = "68fecaef60268522d2ac3f0123cec9d3bcab7b6e"
[[projects]]
name = "github.com/russross/blackfriday"
packages = ["."]
revision = "4048872b16cc0fc2c5fd9eacf0ed2c2fedaa0c8c"
version = "v1.5"
[[projects]]
branch = "master"
name = "github.com/schollz/cryptopasta"
packages = ["."]
revision = "dcd61c7d42a11660da3789311050e961c0a5be55"
[[projects]]
branch = "master"
name = "github.com/schollz/versionedtext"
packages = ["."]
revision = "1cef32a305b7272d4df0454533de5e4ba67adfc1"
[[projects]]
branch = "master"
name = "github.com/sergi/go-diff"
packages = ["diffmatchpatch"]
revision = "2fc9cd33b5f86077aa3e0f442fa0476a9fa9a1dc"
[[projects]]
branch = "master"
name = "github.com/shurcooL/github_flavored_markdown"
packages = ["."]
revision = "cccd3ce4f8e394ae9f87de0bd8b37e00625913d9"
[[projects]]
branch = "master"
name = "github.com/shurcooL/go"
packages = ["parserutil","printerutil","reflectfind","reflectsource"]
revision = "c661e953e604ba4a84a3c4e458462a481bd6ce72"
[[projects]]
branch = "master"
name = "github.com/shurcooL/go-goon"
packages = ["."]
revision = "37c2f522c041b74919a9e5e3a6c5c47eb34730a5"
[[projects]]
branch = "master"
name = "github.com/shurcooL/graphql"
packages = ["ident"]
revision = "cf6db17b893acfad0ca1929ba6be45bf854790ed"
[[projects]]
branch = "master"
name = "github.com/shurcooL/highlight_diff"
packages = ["."]
revision = "09bb4053de1b1d872a9f25dc21378fa71dca4e4e"
[[projects]]
branch = "master"
name = "github.com/shurcooL/highlight_go"
packages = ["."]
revision = "78fb10f4a5f89e812a5e26ab723b954a51226086"
[[projects]]
branch = "master"
name = "github.com/shurcooL/octiconssvg"
packages = ["."]
revision = "8c9861b86a08c72d14e0285d0dc313bb6df52295"
[[projects]]
branch = "master"
name = "github.com/shurcooL/sanitized_anchor_name"
packages = ["."]
revision = "86672fcb3f950f35f2e675df2240550f2a50762f"
[[projects]]
branch = "master"
name = "github.com/sourcegraph/annotate"
packages = ["."]
revision = "f4cad6c6324d3f584e1743d8b3e0e017a5f3a636"
[[projects]]
branch = "master"
name = "github.com/sourcegraph/syntaxhighlight"
packages = ["."]
revision = "bd320f5d308e1a3c4314c678d8227a0d72574ae7"
[[projects]]
branch = "master"
name = "github.com/ugorji/go"
packages = ["codec"]
revision = "50189f05eaf5a0c17e5084eb8f7fb91e23699840"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = ["bcrypt","blowfish"]
revision = "bd6f299fb381e4c3393d1c4b1f0b94f5e77650c8"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = ["html","html/atom"]
revision = "01c190206fbdffa42f334f4b2bf2220f50e64920"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "4fe5d7925040acd225bf9c7cee65e82d07f06bff"
[[projects]]
name = "gopkg.in/go-playground/validator.v8"
packages = ["."]
revision = "5f1438d3fca68893a817e4a66806cea46a9e4ebf"
version = "v8.18.2"
[[projects]]
branch = "v2"
name = "gopkg.in/mgo.v2"
packages = [".","bson","internal/json","internal/sasl","internal/scram"]
revision = "3f83fa5005286a7fe593b055f0d7771a7dce4655"
[[projects]]
name = "gopkg.in/urfave/cli.v1"
packages = ["."]
revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1"
version = "v1.20.0"
[[projects]]
branch = "v2"
name = "gopkg.in/yaml.v2"
packages = ["."]
revision = "eb3733d160e74a9c7e442f435eb3bea458e1d19f"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "3b488719d6a087407dfd8d956f305228c41a71472ce5c12d09c3a22cdaacbbcb"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -1,66 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
branch = "master"
name = "github.com/gin-contrib/multitemplate"
[[constraint]]
branch = "master"
name = "github.com/gin-contrib/sessions"
[[constraint]]
name = "github.com/gin-gonic/gin"
version = "1.2.0"
[[constraint]]
branch = "master"
name = "github.com/jcelliott/lumber"
[[constraint]]
branch = "master"
name = "github.com/microcosm-cc/bluemonday"
[[constraint]]
name = "github.com/russross/blackfriday"
version = "1.5"
[[constraint]]
branch = "master"
name = "github.com/schollz/cryptopasta"
[[constraint]]
branch = "master"
name = "github.com/schollz/versionedtext"
[[constraint]]
branch = "master"
name = "github.com/shurcooL/github_flavored_markdown"
[[constraint]]
branch = "master"
name = "golang.org/x/crypto"
[[constraint]]
name = "gopkg.in/urfave/cli.v1"
version = "1.20.0"

View File

@ -1,21 +0,0 @@
MIT License
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
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:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
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,40 +0,0 @@
# Make a release with
# make -j4 release
VERSION=$(shell git describe)
LDFLAGS=-ldflags "-s -w -X main.version=${VERSION}" -a -installsuffix cgo
.PHONY: build
build:
go-bindata static/... templates/...
go build ${LDFLAGS}
.PHONY: quick
quick:
go-bindata static/... templates/...
go build
.PHONY: linuxarm
linuxarm:
env GOOS=linux GOARCH=arm go build ${LDFLAGS} -o dist/cowyo_linux_arm
#cd dist && upx --brute cowyo_linux_arm
.PHONY: linux64
linux64:
env GOOS=linux GOARCH=amd64 go build ${LDFLAGS} -o dist/cowyo_linux_amd64
#cd dist && upx --brute cowyo_linux_amd64
.PHONY: windows
windows:
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:
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 linuxarm

View File

@ -1,124 +0,0 @@
<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.8.0-brightgreen.svg?style=flat-square" alt="Version"></a>
</p>
<p align="center">A feature-rich wiki for minimalists</a></p>
*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.
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).
Getting Started
===============
## Install
If you have go
```
go get -u github.com/schollz/cowyo/...
```
or just [download the latest release](https://github.com/schollz/cowyo/releases/latest).
## Run
To run just double click or from the command line:
```
cowyo
```
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`.
### Running with TLS
Specify a matching pair of SSL Certificate and Key to run cowyo using https. *cowyo* will now run in a secure session.
*N.B. Let's Encrypt is a CA that signs free and signed certificates.*
```
cowyo --cert "/path/to/server.crt" --key "/p/t/server.key"
```
### Running with Docker
You can easily get started with Docker. First pull the latest image and create the volume with:
```
docker run -d --name cowyo -p 8050:8050 schollz/cowyo
```
Then you can stop it with `docker stop cowyo` and start it again with `docker start cowyo`.
### Running as a micro-CMS
There are a couple of command-line flags that you can use to make your own micro-CMS.
```
cowyo -lock 123 -default-page index.html -css mystyle.css
```
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`.
## Usage
*cowyo* is straightforward to use. Here are some of the basic features:
### 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.
## License
MIT

File diff suppressed because one or more lines are too long

View File

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

View File

@ -1,28 +0,0 @@
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
}

View File

@ -1,22 +0,0 @@
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,647 +0,0 @@
package main
import (
"fmt"
"html/template"
"io/ioutil"
"net/http"
"path"
"strconv"
"strings"
"time"
// "github.com/gin-contrib/static"
"github.com/gin-contrib/multitemplate"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"github.com/schollz/cowyo/encrypt"
)
var customCSS []byte
var defaultLock string
var debounceTime int
var diaryMode bool
func serve(host, port, crt_path, key_path string, TLS bool, cssFile string, defaultPage string, defaultPassword string, debounce int, diary bool) {
gin.SetMode(gin.ReleaseMode)
router := gin.Default()
store := sessions.NewCookieStore([]byte("secret"))
router.Use(sessions.Sessions("mysession", store))
router.HTMLRender = loadTemplates("index.tmpl")
// router.Use(static.Serve("/static/", static.LocalFile("./static", true)))
router.GET("/", func(c *gin.Context) {
if defaultPage != "" {
c.Redirect(302, "/"+defaultPage+"/read")
} else {
c.Redirect(302, "/"+randomAlliterateCombo())
}
})
router.GET("/:page", func(c *gin.Context) {
page := c.Param("page")
c.Redirect(302, "/"+page+"/")
})
router.GET("/:page/*command", handlePageRequest)
router.POST("/update", handlePageUpdate)
router.POST("/relinquish", handlePageRelinquish) // relinquish returns the page no matter what (and destroys if nessecary)
router.POST("/exists", handlePageExists)
router.POST("/prime", handlePrime)
router.POST("/lock", handleLock)
router.POST("/publish", handlePublish)
router.POST("/encrypt", handleEncrypt)
router.DELETE("/oldlist", handleClearOldListItems)
router.DELETE("/listitem", deleteListItem)
// start long-processes as threads
go thread_SiteMap()
// collect custom CSS
if len(cssFile) > 0 {
var errRead error
customCSS, errRead = ioutil.ReadFile(cssFile)
if errRead != nil {
fmt.Println(errRead.Error())
return
}
fmt.Printf("Loaded CSS file, %d bytes\n", len(customCSS))
}
// lock all pages automatically
if defaultPassword != "" {
fmt.Println("running with locked pages")
defaultLock = HashPassword(defaultPassword)
}
// set the debounce time
debounceTime = debounce
// set diary mode
diaryMode = diary
if TLS {
http.ListenAndServeTLS(host+":"+port, crt_path, key_path, router)
} else {
router.Run(host + ":" + port)
}
}
func 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).Parse(string(templateString))
if err != nil {
panic(err)
}
r.Add(x, tmplMessage)
}
return r
}
func handlePageRelinquish(c *gin.Context) {
type QueryJSON struct {
Page string `json:"page"`
}
var json QueryJSON
err := c.BindJSON(&json)
if err != nil {
log.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 := Open(json.Page)
name := p.Meta
if name == "" {
name = json.Page
}
text := p.Text.GetCurrent()
isLocked := p.IsEncrypted
isEncrypted := p.IsEncrypted
destroyed := p.IsPrimedForSelfDestruct
if !p.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 thread_SiteMap() {
for {
log.Info("Generating sitemap...")
ioutil.WriteFile(path.Join(pathToData, "sitemap.xml"), []byte(generateSiteMap()), 0644)
log.Info("..finished generating sitemap")
time.Sleep(24 * time.Hour)
}
}
func generateSiteMap() (sitemap string) {
files, _ := ioutil.ReadDir(pathToData)
lastEdited := make([]string, len(files))
names := make([]string, len(files))
i := 0
for _, f := range files {
names[i] = DecodeFileName(f.Name())
p := 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 = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">`
for i := range names {
sitemap += fmt.Sprintf(`
<url>
<loc>https://cowyo.com/%s/read</loc>
<lastmod>%s</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
`, names[i], lastEdited[i])
}
sitemap += "</urlset>"
return
}
func handlePageRequest(c *gin.Context) {
page := c.Param("page")
command := c.Param("command")
if page == "sitemap.xml" {
siteMap, err := ioutil.ReadFile(path.Join(pathToData, "sitemap.xml"))
if err != nil {
c.Data(http.StatusInternalServerError, contentType("sitemap.xml"), []byte(""))
} else {
c.Data(http.StatusOK, contentType("sitemap.xml"), siteMap)
}
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
fmt.Println(filename)
if filename == "static/css/custom.css" {
data = customCSS
} 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
}
p := Open(page)
fmt.Println(command)
if len(command) < 2 {
fmt.Println(p.IsPublished)
if p.IsPublished {
c.Redirect(302, "/"+page+"/read")
} else {
c.Redirect(302, "/"+page+"/edit")
}
return
}
version := c.DefaultQuery("version", "ajksldfjl")
// use the default lock
if defaultLock != "" && p.IsNew() {
p.IsLocked = true
p.PassphraseToUnlock = defaultLock
}
// Disallow anything but viewing locked/encrypted pages
if (p.IsEncrypted || p.IsLocked) &&
(command[0:2] != "/v" && command[0:2] != "/r") {
fmt.Println("IS LOCKED")
c.Redirect(302, "/"+page+"/view")
return
}
// Destroy page if it is opened and primed
if p.IsPrimedForSelfDestruct && !p.IsLocked && !p.IsEncrypted {
p.Update("*This page has self-destructed. You can not return to it.*\n\n" + p.Text.GetCurrent())
p.Erase()
command = "/view"
}
if command == "/erase" {
if !p.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 command[0:3] == "/ra" {
c.Writer.Header().Set("Content-Type", contentType(p.Name))
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
}
log.Debug(command)
log.Debug("%v", command[0:2] != "/e" &&
command[0:2] != "/v" &&
command[0:2] != "/l" &&
command[0:2] != "/h" &&
command[0:2] != "/r")
var FileNames, FileLastEdited []string
var FileSizes, FileNumChanges []int
if page == "ls" {
command = "/view"
FileNames, FileSizes, FileNumChanges, FileLastEdited = DirectoryList()
}
// 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] != "/h",
"DirectoryPage": page == "ls",
"FileNames": FileNames,
"FileSizes": FileSizes,
"FileNumChanges": FileNumChanges,
"FileLastEdited": FileLastEdited,
"Page": page,
"RenderedPage": template.HTML([]byte(rawHTML)),
"RawPage": rawText,
"Versions": versionsInt64,
"VersionsText": versionsText,
"VersionsChangeSums": versionsChangeSums,
"IsLocked": p.IsLocked,
"IsEncrypted": p.IsEncrypted,
"ListItems": renderList(rawText),
"Route": "/" + page + command,
"HasDotInName": strings.Contains(page, "."),
"RecentlyEdited": getRecentlyEdited(page, c),
"IsPublished": p.IsPublished,
"CustomCSS": len(customCSS) > 0,
"Debounce": debounceTime,
"DiaryMode": diaryMode,
"Date": time.Now().Format("2006-01-02"),
})
}
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 handlePageExists(c *gin.Context) {
type QueryJSON struct {
Page string `json:"page"`
}
var json QueryJSON
err := c.BindJSON(&json)
if err != nil {
log.Trace(err.Error())
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Wrong JSON", "exists": false})
return
}
p := 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 handlePageUpdate(c *gin.Context) {
type QueryJSON struct {
Page string `json:"page"`
NewText string `json:"new_text"`
IsEncrypted bool `json:"is_encrypted"`
IsPrimed bool `json:"is_primed"`
Meta string `json:"meta"`
}
var json QueryJSON
err := c.BindJSON(&json)
if err != nil {
log.Trace(err.Error())
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Wrong JSON"})
return
}
if len(json.NewText) > 100000000 {
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
}
log.Trace("Update: %v", json)
p := Open(json.Page)
var message string
success := false
if p.IsLocked {
message = "Locked, must unlock first"
} else if p.IsEncrypted {
message = "Encrypted, must decrypt first"
} 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"
success = true
}
c.JSON(http.StatusOK, gin.H{"success": success, "message": message})
}
func 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
}
log.Trace("Update: %v", json)
p := Open(json.Page)
if p.IsLocked {
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 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 := Open(json.Page)
if defaultLock != "" && p.IsNew() {
p.IsLocked = true
p.PassphraseToUnlock = defaultLock
}
if p.IsEncrypted {
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Encrypted"})
return
}
var message string
if p.IsLocked {
err2 := CheckPasswordHash(json.Passphrase, p.PassphraseToUnlock)
if err2 != nil {
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Can't unlock"})
return
}
p.IsLocked = false
message = "Unlocked"
} else {
p.IsLocked = true
p.PassphraseToUnlock = HashPassword(json.Passphrase)
message = "Locked"
}
fmt.Println(p)
p.Save()
c.JSON(http.StatusOK, gin.H{"success": true, "message": message})
}
func 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 := Open(json.Page)
p.IsPublished = json.Publish
p.Save()
message := "Published"
if !p.IsPublished {
message = "Unpublished"
}
c.JSON(http.StatusOK, gin.H{"success": true, "message": message})
}
func 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 := Open(json.Page)
if p.IsLocked {
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Locked"})
return
}
q := 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 = 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 = 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 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 := 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 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 := Open(json.Page)
if p.IsEncrypted {
c.JSON(http.StatusOK, gin.H{"success": false, "message": "Encrypted"})
return
}
if p.IsLocked {
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"})
}

View File

@ -1,62 +0,0 @@
package main
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
}

View File

@ -1,132 +0,0 @@
package main
import (
"fmt"
"os"
"time"
"gopkg.in/urfave/cli.v1"
)
var version string
var pathToData string
func main() {
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 {
if !c.GlobalBool("debug") {
turnOffDebugger()
}
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()
}
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"))
}
serve(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"))
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.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)",
},
}
app.Commands = []cli.Command{
{
Name: "migrate",
Aliases: []string{"m"},
Usage: "migrate from the old cowyo",
Action: func(c *cli.Context) error {
if !c.GlobalBool("debug") {
turnOffDebugger()
}
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
}
migrate(pathToOldData, pathToData)
return nil
},
},
}
app.Run(os.Args)
}

View File

@ -1,33 +0,0 @@
package main
import (
"fmt"
"io/ioutil"
"path"
)
func migrate(pathToOldData, pathToData string) error {
files, err := ioutil.ReadDir(pathToOldData)
if len(files) == 0 {
return err
}
for _, f := range files {
if f.Mode().IsDir() {
continue
}
fmt.Printf("Migrating %s", f.Name())
p := 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
}

View File

@ -1,116 +0,0 @@
package main
import (
"encoding/json"
"io/ioutil"
"os"
"path"
"regexp"
"strings"
"time"
"github.com/schollz/versionedtext"
)
// Page is the basic struct
type Page struct {
Name string
Text versionedtext.VersionedText
Meta string
RenderedPage string
IsLocked bool
PassphraseToUnlock string
IsEncrypted bool
IsPrimedForSelfDestruct bool
IsPublished bool
}
func Open(name string) (p *Page) {
p = new(Page)
p.Name = name
p.Text = versionedtext.NewVersionedText("")
p.Render()
bJSON, err := ioutil.ReadFile(path.Join(pathToData, encodeToBase32(strings.ToLower(name))+".json"))
if err != nil {
return
}
err = json.Unmarshal(bJSON, &p)
if err != nil {
p = new(Page)
}
return p
}
func DirectoryList() (names []string, lengths []int, numchanges []int, lastEdited []string) {
files, _ := ioutil.ReadDir(pathToData)
names = make([]string, len(files))
lengths = make([]int, len(files))
numchanges = make([]int, len(files))
lastEdited = make([]string, len(files))
for i, f := range files {
names[i] = DecodeFileName(f.Name())
p := Open(names[i])
lengths[i] = len(p.Text.GetCurrent())
numchanges[i] = p.Text.NumEdits()
lastEdited[i] = time.Unix(p.Text.LastEditTime()/1000000000, 0).Format("Mon Jan 2 15:04:05 MST 2006")
}
return
}
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 {
bJSON, err := json.MarshalIndent(p, "", " ")
if err != nil {
return err
}
return ioutil.WriteFile(path.Join(pathToData, encodeToBase32(strings.ToLower(p.Name))+".json"), bJSON, 0644)
}
func (p *Page) IsNew() bool {
return !exists(path.Join(pathToData, encodeToBase32(strings.ToLower(p.Name))+".json"))
}
func (p *Page) Erase() error {
log.Trace("Erasing " + p.Name)
return os.Remove(path.Join(pathToData, encodeToBase32(strings.ToLower(p.Name))+".json"))
}
func (p *Page) Published() bool {
return p.IsPublished
}

View File

@ -1,49 +0,0 @@
package main
import (
// "fmt"
"os"
"strings"
"testing"
)
func TestListFiles(t *testing.T) {
pathToData = "testdata"
os.MkdirAll(pathToData, 0755)
defer os.RemoveAll(pathToData)
p := Open("testpage")
p.Update("Some data")
p = Open("testpage2")
p.Update("A different bunch of data")
p = Open("testpage3")
p.Update("Not much else")
n, l, _, _ := DirectoryList()
if strings.Join(n, " ") != "testpage testpage2 testpage3" {
t.Errorf("Names: %s, Lengths: %d", n, l)
}
}
func TestGeneral(t *testing.T) {
pathToData = "testdata"
os.MkdirAll(pathToData, 0755)
defer os.RemoveAll(pathToData)
p := 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 := Open("testpage")
if strings.TrimSpace(p2.RenderedPage) != "<p><strong>bold</strong> and <em>italic</em></p>" {
t.Errorf("Did not render: '%s'", p2.RenderedPage)
}
}

View File

@ -1,11 +0,0 @@
/*!
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}

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
.hljs{display:block;overflow-x:auto;padding:0.5em;background:#F0F0F0}.hljs,.hljs-subst{color:#444}.hljs-comment{color:#888888}.hljs-keyword,.hljs-attribute,.hljs-selector-tag,.hljs-meta-keyword,.hljs-doctag,.hljs-name{font-weight:bold}.hljs-type,.hljs-string,.hljs-number,.hljs-selector-id,.hljs-selector-class,.hljs-quote,.hljs-template-tag,.hljs-deletion{color:#880000}.hljs-title,.hljs-section{color:#880000;font-weight:bold}.hljs-regexp,.hljs-symbol,.hljs-variable,.hljs-template-variable,.hljs-link,.hljs-selector-attr,.hljs-selector-pseudo{color:#BC6060}.hljs-literal{color:#78A960}.hljs-built_in,.hljs-bullet,.hljs-code,.hljs-addition{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold}

View File

@ -1,7 +0,0 @@
/*!
Pure v0.6.2
Copyright 2013 Yahoo!
Licensed under the BSD License.
https://github.com/yahoo/pure/blob/master/LICENSE.md
*/
.pure-menu{box-sizing:border-box}.pure-menu-fixed{position:fixed;left:0;top:0;z-index:3}.pure-menu-item,.pure-menu-list{position:relative}.pure-menu-list{list-style:none;margin:0;padding:0}.pure-menu-item{padding:0;margin:0;height:100%}.pure-menu-heading,.pure-menu-link{display:block;text-decoration:none;white-space:nowrap}.pure-menu-horizontal{width:100%;white-space:nowrap}.pure-menu-horizontal .pure-menu-list{display:inline-block}.pure-menu-horizontal .pure-menu-heading,.pure-menu-horizontal .pure-menu-item,.pure-menu-horizontal .pure-menu-separator{display:inline-block;zoom:1;vertical-align:middle}.pure-menu-item .pure-menu-item{display:block}.pure-menu-children{display:none;position:absolute;left:100%;top:0;margin:0;padding:0;z-index:3}.pure-menu-horizontal .pure-menu-children{left:0;top:auto;width:inherit}.pure-menu-active>.pure-menu-children,.pure-menu-allow-hover:hover>.pure-menu-children{display:block;position:absolute}.pure-menu-has-children>.pure-menu-link:after{padding-left:.5em;content:"\25B8";font-size:small}.pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after{content:"\25BE"}.pure-menu-scrollable{overflow-y:scroll;overflow-x:hidden}.pure-menu-scrollable .pure-menu-list{display:block}.pure-menu-horizontal.pure-menu-scrollable .pure-menu-list{display:inline-block}.pure-menu-horizontal.pure-menu-scrollable{white-space:nowrap;overflow-y:hidden;overflow-x:auto;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;padding:.5em 0}.pure-menu-horizontal.pure-menu-scrollable::-webkit-scrollbar{display:none}.pure-menu-horizontal .pure-menu-children .pure-menu-separator,.pure-menu-separator{background-color:#ccc;height:1px;margin:.3em 0}.pure-menu-horizontal .pure-menu-separator{width:1px;height:1.3em;margin:0 .3em}.pure-menu-horizontal .pure-menu-children .pure-menu-separator{display:block;width:auto}.pure-menu-heading{text-transform:uppercase;color:#565d64}.pure-menu-link{color:#777}.pure-menu-children{background-color:#fff}.pure-menu-disabled,.pure-menu-heading,.pure-menu-link{padding:.5em 1em}.pure-menu-disabled{opacity:.5}.pure-menu-disabled .pure-menu-link:hover{background-color:transparent}.pure-menu-active>.pure-menu-link,.pure-menu-link:focus,.pure-menu-link:hover{background-color:#eee}.pure-menu-selected .pure-menu-link,.pure-menu-selected .pure-menu-link:visited{color:#000}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
lulling, vile, foul, cheerful, messy, dreadful, uneven, stinky, young, sparkling, sweltering, verdant, hideous, friendly, blistering, rambunctious, carefree, fat, sloppy, gloomy, awful, anemic, minute, stiff, benevolent, ceaseless, large, quick, round, glassy, rusty, scarce, odd, shining, even, dowdy, solemn, scorching, brief, rotten, new, plush, cozy, meandering, apologetic, nimble, busy, strong, great, brilliant, piercing, creepy, miniature, narrow, whimsical, fantastic, cowardly, disgusting, marvelous, snug, stern, stingy, angry, spiky, cheeky, gorgeous, mysterious, flat, clever, charming, dismal, meek, somber, sour, thin, beautiful, stubborn, crazy, challenging, gaunt, salty, indifferent, huge, daring, awkward, picturesque, copious, glowing, truthful, rude, petite, cranky, ornery, brazen, modest, purring, filthy, rotund, short, splendid, hasty, deafening, crawling, furious, tall, mute, ghastly, still, arrogant, crabby, haughty, curly, voiceless, hot, courageous, late, microscopic, vast, stifling, good, disrespectful, bashful, moaning, towering, adventurous, idyllic, careless, shocking, erratic, heavy, square, hard, jagged, gullible, hushed, revolting, content, thrifty, sluggish, eternal, greedy, pleasant, small, demanding, greasy, enormous, hostile, terrible, chilly, speedy, massive, loud, puny, striking, clumsy, soaring, brave, fast, delicious, effortless, bland, thick, little, ancient, silent, wonderful, stuffed, grimy, bitter, muggy, shallow, ridiculous, absent-minded, fuzzy, peculiar, mangy, wide, kind, squeaky, screeching, silly, squealing, spoiled, gigantic, happy, steep, ingenious, modern, juicy, gentle, medium, brawny, curved, lumpy, afraid, amusing, thundering, cooing, oppressive, swollen, grave, sturdy, average, proud, rancid, absurd, entertaining, annoyed, fussy, precise, subtle, gilded, slow, delinquent, nervous, hopeful, rich, adequate, shrill, plump, freezing, nasty, endless, lavish, worried, courteous, bulky, fair, diminutive, groggy, miserable, horrid, crooked, monstrous, superb, contrary, lazy, fidgety, menacing, swift, stale, quarrelsome, quiet, askew, tough, simple, sweet, hardworking, frosty, whispering, famished, crispy, caring, capable, tiny, immense, startled, lovely, high-pitched, tasteless, decrepit, tense, lousy, straight, excited, ugly, stunning, parched, wild, ripe, lonely, optimistic, obnoxious, cavernous, different, harsh, creaky, grand, difficult, temporary, eccentric, muffled, alert, delicate, timid, infamous, enchanting, anxious, humble, edgy, severe, repulsive, desolate, sleepy, slimy, irritable, vigilant, generous, rapid, old-fashioned, hilly, easy, righteous, joyful, surprised, starving, big, early, compassionate, moody, perpetual, dishonest, serious, foolish, soft, old, scared, mighty, trendy, curious, hissing, savage, dense, steaming, broad, slick, creative, icy, adorable, slight, terrified, intense, noisy, cautious, sizzling, blithe, fluttering, faint, delighted, smelly, lively, frightened, gauzy, long, strict, bored, calm, melodic, spicy, relaxed, triangular, dull, wise, dangerous, smooth, cruel, creeping, dawdling, intimidating, exhausted, deep, tasty, obtuse, graceful, tranquil, raspy, selfish, sullen, malicious, ecstatic, wrinkly, opulent, polite, fetid, husky, prudent, skinny, tricky, impatient, loyal, fresh

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +0,0 @@
adjectives = {}
with open('adjectives','r') as f:
for line in f:
word = line.strip().lower()
if word[0] not in adjectives:
adjectives[word[0]] = []
adjectives[word[0]].append(word)
print(len(adjectives.keys()))
animals = {}
for aword in open('animals','r').read().split(','):
word = aword.strip().lower()
if word[0] not in animals:
animals[word[0]] = []
animals[word[0]].append(word)
print(len(animals))
i = 0
for key in adjectives.keys():
if key in animals and key in adjectives:
i = i + len(adjectives[key])*len(animals[key])
print(i)

View File

@ -1,4 +0,0 @@
User-agent: *
Disallow: /
Allow: /Help/view
Allow: /sitemap.xml

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://awwkoala.com/Help/view</loc>
<lastmod>2016-03-18</lastmod>
<changefreq>monthly</changefreq>
<priority>1.0</priority>
</url>
</urlset>

View File

@ -1,621 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="apple-touch-icon" sizes="57x57" href=/static/img/cowyo/apple-icon-57x57.png>
<link rel="apple-touch-icon" sizes="60x60" href=/static/img/cowyo/img/cowyo/apple-icon-60x60.png>
<link rel="apple-touch-icon" sizes="72x72" href=/static/img/cowyo/apple-icon-72x72.png>
<link rel="apple-touch-icon" sizes="76x76" href=/static/img/cowyo/apple-icon-76x76.png>
<link rel="apple-touch-icon" sizes="114x114" href=/static/img/cowyo/apple-icon-114x114.png>
<link rel="apple-touch-icon" sizes="120x120" href=/static/img/cowyo/apple-icon-120x120.png>
<link rel="apple-touch-icon" sizes="144x144" href=/static/img/cowyo/apple-icon-144x144.png>
<link rel="apple-touch-icon" sizes="152x152" href=/static/img/cowyo/apple-icon-152x152.png>
<link rel="apple-touch-icon" sizes="180x180" href=/static/img/cowyo/apple-icon-180x180.png>
<link rel="icon" type="image/png" sizes="192x192" href=/static/img/cowyo/android-icon-192x192.png>
<link rel="icon" type="image/png" sizes="32x32" href=/static/img/cowyo/favicon-32x32.png>
<link rel="icon" type="image/png" sizes="96x96" href=/static/img/cowyo/favicon-96x96.png>
<link rel="icon" type="image/png" sizes="16x16" href=/static/img/cowyo/favicon-16x16.png>
<link rel="manifest" href=/static/img/cowyo/manifest.json>
<meta name="msapplication-TileColor" content="#fff">
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png">
<meta name="theme-color" content="#fff">
{{ if and .CustomCSS .ReadPage }}
<link rel="stylesheet" type="text/css" href="/static/css/custom.css">
{{ else }}
<script type="text/javascript" src="/static/js/jquery-1.8.3.js"></script>
<link rel="stylesheet" type="text/css" href="/static/css/github-markdown.css">
<link rel="stylesheet" type="text/css" href="/static/css/menus-min.css">
<link rel="stylesheet" type="text/css" href="/static/css/base-min.css">
<link rel="stylesheet" href="/static/css/highlight.css">
<script src="/static/js/highlight.min.js"></script>
<script type="text/javascript" src="/static/js/highlight.pack.js"></script>
<style type="text/css">
{{ if .ListPage }}
/* Required for lists */
span { cursor: pointer; }
{{ end }}
body {
background: #fff;
}
.success {
color: #5cb85c;
font-weight: bold;
}
.failure {
color: #d9534f;
font-weight: bold;
}
.pure-menu a {
color: #777;
}
#wrap {
position: absolute;
top: 50px;
left: 0px;
right: 0px;
bottom: 0px;
}
#pad {
height:100%;
}
{{ if .EditPage }}
body {
overflow:hidden;
}
{{ end }}
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;
{{ if .HasDotInName }}
font-family: "Lucida Console", Monaco, monospace;
{{else}}
font-family: 'Open Sans','Segoe UI',Tahoma,Arial,sans-serif;
{{ end }}
}
.markdown-body ul, .markdown-body ol {
padding-left: 0em;
}
@media (min-width: 5em) {
div#menu, div#rendered, 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;
}
}
@media (min-width: 50em) {
div#menu, div#rendered, body#pad textarea {
padding-left: 10%;
padding-right: 10%;
}
.pure-menu-disabled, .pure-menu-heading, .pure-menu-link {
padding: .5em 1em;
}
}
@media (min-width: 70em) {
div#menu, div#rendered, body#pad textarea {
padding-left: 15%;
padding-right: 15%;
}
}
@media (min-width: 100em) {
div#menu, div#rendered, body#pad textarea {
padding-left: 20%;
padding-right: 20%;
}
}
</style>
{{ end }}
<title>{{ .Page }}</title>
<script type='text/javascript'>
oulipo = false;
//<![CDATA[
$(window).load(function() {
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
var timeout;
return function() {
$('#saveEditButton').removeClass()
$('#saveEditButton').text("Editing");
var context = this,
args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
// This will apply the debounce effect on the keyup event
// And it only fires 500ms or half a second after the user stopped typing
$('#userInput').on('keyup', debounce(function() {
console.log('typing occurred');
if (oulipo == true) { $('#userInput').val($('#userInput').val().replace(/e/g,"")); }
$('#saveEditButton').removeClass()
$('#saveEditButton').text("Saving")
upload();
}, {{ .Debounce }}));
function upload() {
$.ajax({
type: 'POST',
url: '/update',
data: JSON.stringify({
new_text: $('#userInput').val(),
page: "{{ .Page }}"
}),
success: function(data) {
$('#saveEditButton').removeClass()
if (data.success == true) {
$('#saveEditButton').addClass("success");
} else {
$('#saveEditButton').addClass("failure");
}
$('#saveEditButton').text(data.message);
},
error: function(xhr, error) {
$('#saveEditButton').removeClass()
$('#saveEditButton').addClass("failure");
$('#saveEditButton').text(error);
},
contentType: "application/json",
dataType: 'json'
});
}
function primeForSelfDestruct() {
$.ajax({
type: 'POST',
url: '/prime',
data: JSON.stringify({
page: "{{ .Page }}"
}),
success: function(data) {
$('#saveEditButton').removeClass()
if (data.success == true) {
$('#saveEditButton').addClass("success");
} else {
$('#saveEditButton').addClass("failure");
}
$('#saveEditButton').text(data.message);
},
error: function(xhr, error) {
$('#saveEditButton').removeClass()
$('#saveEditButton').addClass("failure");
$('#saveEditButton').text(error);
},
contentType: "application/json",
dataType: 'json'
});
}
function lockPage(passphrase) {
$.ajax({
type: 'POST',
url: '/lock',
data: JSON.stringify({
page: "{{ .Page }}",
passphrase: passphrase
}),
success: function(data) {
$('#saveEditButton').removeClass()
if (data.success == true) {
$('#saveEditButton').addClass("success");
} else {
$('#saveEditButton').addClass("failure");
}
$('#saveEditButton').text(data.message);
if (data.success == true && $('#lockPage').text() == "Lock") {
window.location = "/{{ .Page }}/view";
}
if (data.success == true && $('#lockPage').text() == "Unlock") {
window.location = "/{{ .Page }}/edit";
}
},
error: function(xhr, error) {
$('#saveEditButton').removeClass()
$('#saveEditButton').addClass("failure");
$('#saveEditButton').text(error);
},
contentType: "application/json",
dataType: 'json'
});
}
function publishPage() {
$.ajax({
type: 'POST',
url: '/publish',
data: JSON.stringify({
page: "{{ .Page }}",
publish: $('#publishPage').text() == "Publish"
}),
success: function(data) {
$('#saveEditButton').removeClass()
if (data.success == true) {
$('#saveEditButton').addClass("success");
} else {
$('#saveEditButton').addClass("failure");
}
$('#saveEditButton').text(data.message);
if (data.message == "Unpublished") {
$('#publishPage').text("Publish");
} else {
$('#publishPage').text("Unpublish");
}
},
error: function(xhr, error) {
$('#saveEditButton').removeClass()
$('#saveEditButton').addClass("failure");
$('#saveEditButton').text(error);
},
contentType: "application/json",
dataType: 'json'
});
}
function encryptPage(passphrase) {
$.ajax({
type: 'POST',
url: '/encrypt',
data: JSON.stringify({
page: "{{ .Page }}",
passphrase: passphrase
}),
success: function(data) {
$('#saveEditButton').removeClass()
if (data.success == true) {
$('#saveEditButton').addClass("success");
} else {
$('#saveEditButton').addClass("failure");
}
$('#saveEditButton').text(data.message);
if (data.success == true && $('#encryptPage').text() == "Encrypt") {
window.location = "/{{ .Page }}/view";
}
if (data.success == true && $('#encryptPage').text() == "Decrypt") {
window.location = "/{{ .Page }}/edit";
}
},
error: function(xhr, error) {
$('#saveEditButton').removeClass()
$('#saveEditButton').addClass("failure");
$('#saveEditButton').text(error);
},
contentType: "application/json",
dataType: 'json'
});
}
function clearOld() {
$.ajax({
type: 'DELETE',
url: '/oldlist',
data: JSON.stringify({
page: "{{ .Page }}"
}),
success: function(data) {
$('#saveEditButton').removeClass()
if (data.success == true) {
$('#saveEditButton').addClass("success");
} else {
$('#saveEditButton').addClass("failure");
}
$('#saveEditButton').text(data.message);
if (data.success == true) {
window.location = "/{{ .Page }}/list";
}
},
error: function(xhr, error) {
$('#saveEditButton').removeClass();
$('#saveEditButton').addClass("failure");
$('#saveEditButton').text(error);
},
contentType: "application/json",
dataType: 'json'
});
}
$("#encryptPage").click(function(e) {
e.preventDefault();
var passphrase = prompt("Please enter a passphrase. Note: Encrypting will remove all previous history.", "");
if (passphrase != null) {
encryptPage(passphrase);
}
});
$("#erasePage").click(function(e) {
e.preventDefault();
var r = confirm("Are you sure you want to erase?");
if (r == true) {
window.location = "/{{ .Page }}/erase";
} else {
x = "You pressed Cancel!";
}
});
$("#selfDestructPage").click(function(e) {
e.preventDefault();
var r = confirm("This will erase the page the next time it is opened, are you sure you want to do that?");
if (r == true) {
primeForSelfDestruct();
} else {
x = "You pressed Cancel!";
}
});
$("#lockPage").click(function(e) {
e.preventDefault();
var passphrase = prompt("Please enter a passphrase to lock", "");
if (passphrase != null) {
if ($('#lockPage').text() == "Lock") {
$('#saveEditButton').removeClass();
$("#saveEditButton").text("Locking");
} else {
$('#saveEditButton').removeClass();
$("#saveEditButton").text("Unlocking");
}
lockPage(passphrase);
// POST encrypt page
// reload page
}
});
$("#publishPage").click(function(e) {
e.preventDefault();
var message = " This will add your page to the sitemap.xml so it will be indexed by search engines.";
if ($('#publishPage').text() == "Unpublish") {
message = "";
}
var confirmed = confirm("Are you sure?" + message);
if (confirmed == true) {
if ($('#publishPage').text() == "Unpublish") {
$('#saveEditButton').removeClass();
$("#saveEditButton").text("Unpublishing");
} else {
$('#saveEditButton').removeClass();
$("#saveEditButton").text("Publishing");
}
publishPage();
}
});
$("#clearOld").click(function(e) {
e.preventDefault();
var r = confirm("This will erase all cleared list items, are you sure you want to do that? (Versions will stay in history).");
if (r == true) {
clearOld()
} else {
x = "You pressed Cancel!";
}
});
$("textarea").keydown(function(e) {
if(e.keyCode === 9) { // tab was pressed
// get caret position/selection
var start = this.selectionStart;
var end = this.selectionEnd;
var $this = $(this);
var value = $this.val();
// set textarea value to: text before caret + tab + text after caret
$this.val(value.substring(0, start)
+ "\t"
+ value.substring(end));
// put caret at right position again (add one for the tab)
this.selectionStart = this.selectionEnd = start + 1;
// prevent the focus lose
e.preventDefault();
}
});
$('.deletable').click(function(event) {
event.preventDefault();
var lineNum = $(this).attr('id')
$.ajax({
url: "/listitem" + '?' + $.param({
"lineNum": lineNum,
"page": "{{ .Page }}"
}),
type: 'DELETE',
success: function() {
window.location.reload(true);
}
});
});
}); //]]>
</script>
<script>hljs.initHighlightingOnLoad();</script>
</head>
<body id="pad">
<article class="markdown-body">
{{ if .ReadPage }}
<div id="wrap">
<div id="rendered">
{{ .RenderedPage }}
</div>
</div>
{{ else }}
<div class="pure-menu pure-menu-horizontal" id="menu">
<ul class="pure-menu-list">
<li></li>
<!-- Required to keep them level? -->
<li class="pure-menu-item pure-menu-has-children pure-menu-allow-hover">
<a href="#" id="menuLink1" class="pure-menu-link">{{ .Page }}</a>
<ul class="pure-menu-children">
{{ if .DiaryMode }}
<li class="pure-menu-item"><a href="/{{ .Date }}/edit" class="pure-menu-link">New</a></li>
{{ else }}
<li class="pure-menu-item"><a href="/" class="pure-menu-link">New</a></li>
{{ end }}
<li class="pure-menu-item"><a href="https://github.com/schollz/cowyo" class="pure-menu-link">Source</a></li>
{{ if .EditPage }}
<li class="pure-menu-item"><a href="#" class="pure-menu-link" id="publishPage">
{{- if .IsPublished -}}
Unpublish
{{- else -}}
Publish
{{- end -}}
</a></li>
{{ end }}
<hr>
{{ if (or (.IsLocked) (.IsEncrypted) )}}
{{ else }}
<li class="pure-menu-item"><a href="#" class="pure-menu-link" id="encryptPage">{{ if .IsEncrypted }}Decrypt{{ else }}Encrypt{{end}}</a></li>
<li class="pure-menu-item"><a href="#" class="pure-menu-link" id="lockPage">{{ if .IsLocked }}Unlock{{ else }}Lock{{end}}</a></li>
<li class="pure-menu-item"><a href="/{{ .Page }}/history" class="pure-menu-link">History</a></li>
<hr>
<li class="pure-menu-item"><a href="#" class="pure-menu-link" id="selfDestructPage">Self-destruct</a></li>
<li class="pure-menu-item"><a href="#" class="pure-menu-link" id="erasePage">Erase</a></li>
{{ end }}
</ul>
</li>
<!--
<li class="pure-menu-item {{ with .ViewPage }}pure-menu-selected{{ end }}">
<a href="/{{ .Page }}/view" class="pure-menu-link">View</a>
</li>-->
<li class="pure-menu-item pure-menu-has-children pure-menu-allow-hover {{ with .ViewPage }}pure-menu-selected{{ end }}">
<a href="#" id="menuLink1" class="pure-menu-link">View</a>
<ul class="pure-menu-children">
<li class="pure-menu-item">
<a href="/{{ .Page }}/read" class="pure-menu-link">Current</a>
</li>
{{ range .RecentlyEdited}}
<li class="pure-menu-item"><a class="pure-menu-link" href="/{{.}}/read">{{.}}</a></li>
{{ end }}
</ul>
</li>
{{ if (or (.IsLocked) (.IsEncrypted) )}}
{{ if .IsLocked }}
<li class="pure-menu-item"><a href="#" class="pure-menu-link" id="lockPage">{{ if .IsLocked }}Unlock{{ else }}Lock{{end}}</a></li>
<li class="pure-menu-item" class="pure-menu-link"><a href="#"><span id="saveEditButton"></span></a></li>
{{ else }}
<li class="pure-menu-item"><a href="#" class="pure-menu-link" id="encryptPage">{{ if .IsEncrypted }}Decrypt{{ else }}Encrypt{{end}}</a></li>
<li class="pure-menu-item" class="pure-menu-link"><a href="#"><span id="saveEditButton"></span></a></li>
{{ end }}
{{else}}
{{ if .ListPage }}
<li class="pure-menu-item {{ with .ListPage }}pure-menu-selected{{ end }}"><a href="#" class="pure-menu-link" id="clearOld">Clear done</a></li>
{{ else }}
<li class="pure-menu-item {{ with .ListPage }}pure-menu-selected{{ end }}"><a href="/{{ .Page }}/list" class="pure-menu-link">List</a></li>
{{ end }}
<li class="pure-menu-item {{ with .EditPage }}pure-menu-selected{{ end }}"><a href="/{{ .Page }}/edit" class="pure-menu-link"><span id="saveEditButton">Edit</span></a></li>
{{end}}
</ul>
</div>
<div id="wrap">
{{ if .EditPage }} <div id="pad"><textarea autofocus placeholder="Use markdown to write your note! New: you can publish your note when you are done ({{ .Page }} -> Publish)." id="userInput">{{ .RawPage }}</textarea></div>{{ end }}
<div id="rendered">
{{ if .DontKnowPage }} <strong><center>{{ .Route }} not understood!</center></strong>{{ end }}
{{ if .ViewPage }}{{ .RenderedPage }}
{{ end }}
{{ if .HistoryPage }}
<h1>History</h1>
<ul>
{{range $i, $e := .Versions}}
<li style="list-style: none;">
<a href="/{{ $.Page }}/view?version={{$e}}">View</a>&nbsp;&nbsp;<a href="/{{ $.Page }}/list?version={{$e}}">List</a>&nbsp;&nbsp;<a href="/{{ $.Page }}/raw?version={{$e}}">Raw</a>&nbsp;&nbsp;
{{index $.VersionsText $i}}&nbsp;({{if lt (index $.VersionsChangeSums $i) 0}}<span style="color:red">{{else}}<span style="color:green">+{{end}}{{index $.VersionsChangeSums $i}}</span>)</li>
{{end}}
</ul>
{{ end }}
{{ if .ListPage }}
{{ range $index, $element := .ListItems }}
{{ $element }}
{{ end }}
{{ end }}
{{ if .DirectoryPage }}
<table style="width:100%">
<tr>
<th>Document</th>
<th>Current size</th>
<th>Num Edits</th>
<th>Last Edited</th>
</tr>
{{range $i, $e := .FileNames}}
<tr>
<td><a href="/{{ $e }}/view">{{ $e }}</a></td>
<td>{{index $.FileSizes $i}}</td>
<td>{{index $.FileNumChanges $i}}</td>
<td>{{index $.FileLastEdited $i}}</td>
</tr>
{{ end }}
</table>
{{end}}
</div>
</div>
{{ end }}
</article>
</body>
</html>

View File

@ -1,221 +0,0 @@
package main
import (
"encoding/base32"
"encoding/binary"
"encoding/hex"
"math/rand"
"net"
"os"
"strings"
"time"
"github.com/jcelliott/lumber"
"github.com/microcosm-cc/bluemonday"
"github.com/russross/blackfriday"
"github.com/shurcooL/github_flavored_markdown"
"golang.org/x/crypto/bcrypt"
)
var animals []string
var adjectives []string
var aboutPageText string
var log *lumber.ConsoleLogger
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")
log = lumber.NewConsoleLogger(lumber.TRACE)
}
func turnOffDebugger() {
log = lumber.NewConsoleLogger(lumber.WARN)
}
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 timeTrack(start time.Time, name string) {
elapsed := time.Since(start)
log.Debug("%s took %s", name, elapsed)
}
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)
}
// GetLocalIP returns the local ip address
func GetLocalIP() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
return ""
}
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()
}
}
}
return bestIP
}
// 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)
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
return true
}
func MarkdownToHtml(s string) string {
unsafe := blackfriday.MarkdownCommon([]byte(s))
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)
return string(html)
}
func GithubMarkdownToHTML(s string) string {
return string(github_flavored_markdown.Markdown([]byte(s)))
}
func encodeToBase32(s string) string {
return base32.StdEncoding.EncodeToString([]byte(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
}

View File

@ -1,34 +0,0 @@
package main
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")
log.Debug(p)
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")
}
}

View File

@ -14,7 +14,7 @@ import (
"sort"
"strings"
"dmitri.shuralyov.com/kebabcase"
"dmitri.shuralyov.com/text/kebabcase"
"github.com/shurcooL/go-goon"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"

File diff suppressed because it is too large Load Diff