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

Merge pull request #40 from schollz/upload

CLI support for uploading/downloading via curl
This commit is contained in:
Zack 2016-03-28 20:21:46 -04:00
commit 7e723e65b5
6 changed files with 157 additions and 130 deletions

View File

@ -2,7 +2,7 @@
# [cowyo.com](http://cowyo.com/) # [cowyo.com](http://cowyo.com/)
[![Version 0.94](https://img.shields.io/badge/version-0.94-brightgreen.svg)]() [![Go Report Card](https://goreportcard.com/badge/github.com/schollz/cowyo)](https://goreportcard.com/report/github.com/schollz/cowyo) [![Join the chat at https://gitter.im/schollz/cowyo](https://badges.gitter.im/schollz/cowyo.svg)](https://gitter.im/schollz/cowyo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Version 0.95](https://img.shields.io/badge/version-0.95-brightgreen.svg)]() [![Go Report Card](https://goreportcard.com/badge/github.com/schollz/cowyo)](https://goreportcard.com/report/github.com/schollz/cowyo) [![Join the chat at https://gitter.im/schollz/cowyo](https://badges.gitter.im/schollz/cowyo.svg)](https://gitter.im/schollz/cowyo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
This is a self-contained notepad webserver that makes sharing easy and _fast_. The most important feature here is *simplicity*. There are many other features as well including versioning, page locking, self-destructing messages, encryption, math support, and listifying. Read on to learn more about the features. This is a self-contained notepad webserver that makes sharing easy and _fast_. The most important feature here is *simplicity*. There are many other features as well including versioning, page locking, self-destructing messages, encryption, math support, and listifying. Read on to learn more about the features.
@ -44,6 +44,23 @@ This is a self-contained notepad webserver that makes sharing easy and _fast_. T
<br> <br>
**CLI support**. Want to upload/download from a server? Its super easy. Upload/download files like this:
```bash
$ echo "Hello, world!" > hi.txt
$ curl --upload-file hi.txt cowyo.com
File uploaded to http://cowyo.com/hi.txt
$ curl cowyo.com/test.txt
Hello, world!
```
or just skip the file-creation step,
```bash
$ echo "Wow, so easy" | curl --upload-file "-" cowyo.com
File uploaded to http://cowyo.com/CautiousCommonLoon
$ curl cowyo.com/CautiousCommonLoon
Wow, so easy
```
<br>
**Keyboard Shortcuts**. Quickly transition between Edit/View/List by using `Ctl+Shift+E` to Edit, `Ctl+Shift+Z` to View, and `Ctl+Shift+L` to Listify. **Keyboard Shortcuts**. Quickly transition between Edit/View/List by using `Ctl+Shift+E` to Edit, `Ctl+Shift+Z` to View, and `Ctl+Shift+L` to Listify.

37
db.go
View File

@ -43,43 +43,6 @@ type WikiData struct {
Locked string Locked string
} }
func hasPassword(title string) (bool, error) {
title = strings.ToLower(title)
if !open {
Open(RuntimeArgs.DatabaseLocation)
defer Close()
}
hasPassword := false
err := db.View(func(tx *bolt.Tx) error {
var err error
b := tx.Bucket([]byte("datas"))
if b == nil {
return fmt.Errorf("db must be opened before loading")
}
k := []byte(title)
val := b.Get(k)
if val == nil {
return nil
}
var p WikiData
err = p.decode(val)
if err != nil {
return err
}
for _, line := range strings.Split(p.CurrentText, "\n") {
if strings.Contains(line, "<") == true && strings.Contains(line, ">") == true && strings.Contains(line, "user") == true && strings.Contains(line, "password") == true && strings.Contains(line, "public") == true {
hasPassword = true
}
}
return nil
})
if err != nil {
fmt.Printf("Could not get WikiData: %s", err)
return false, err
}
return hasPassword, nil
}
func getCurrentText(title string, version int) (string, []versionsInfo, bool, time.Duration, bool, string) { func getCurrentText(title string, version int) (string, []versionsInfo, bool, time.Duration, bool, string) {
Open(RuntimeArgs.DatabaseLocation) Open(RuntimeArgs.DatabaseLocation)
defer Close() defer Close()

64
main.go
View File

@ -1,12 +1,9 @@
package main package main
import ( import (
"bytes"
"flag" "flag"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"log"
"os" "os"
"path" "path"
@ -38,10 +35,8 @@ var RuntimeArgs struct {
} }
var VersionNum string var VersionNum string
const _24K = (1 << 20) * 24
func main() { func main() {
VersionNum = "0.94" VersionNum = "0.95"
// _, executableFile, _, _ := runtime.Caller(0) // get full path of this file // _, executableFile, _, _ := runtime.Caller(0) // get full path of this file
cwd, _ := os.Getwd() cwd, _ := os.Getwd()
databaseFile := path.Join(cwd, "data.db") databaseFile := path.Join(cwd, "data.db")
@ -107,61 +102,8 @@ Options:`)
r.GET("/", newNote) r.GET("/", newNote)
r.HEAD("/", func(c *gin.Context) { c.Status(200) }) r.HEAD("/", func(c *gin.Context) { c.Status(200) })
r.GET("/:title", editNote) r.GET("/:title", editNote)
r.PUT("/:title", func(c *gin.Context) { r.PUT("/:title", putFile)
filename := c.Param("title") r.PUT("/", putFile)
fmt.Println(filename)
fmt.Println(c.Request.Body)
fmt.Println(c.Request.ContentLength)
fmt.Println(c.Request.Header)
contentLength := c.Request.ContentLength
var reader io.Reader
reader = c.Request.Body
if contentLength == -1 {
// queue file to disk, because s3 needs content length
var err error
var f io.Reader
f = reader
var b bytes.Buffer
n, err := io.CopyN(&b, f, _24K+1)
if err != nil && err != io.EOF {
log.Printf("%s", err.Error())
}
if n > _24K {
file, err := ioutil.TempFile("./", "transfer-")
if err != nil {
log.Printf("%s", err.Error())
}
defer file.Close()
n, err = io.Copy(file, io.MultiReader(&b, f))
if err != nil {
os.Remove(file.Name())
log.Printf("%s", err.Error())
}
reader, err = os.Open(file.Name())
} else {
reader = bytes.NewReader(b.Bytes())
}
contentLength = n
}
buf := new(bytes.Buffer)
buf.ReadFrom(reader)
fmt.Println("---------------")
fmt.Println(buf.String())
fmt.Println("---------------")
fmt.Println(c.ContentType())
fmt.Println(c.Request.Header)
fmt.Println("---------------")
p := WikiData{filename, "", []string{}, []string{}, false, ""}
p.save(buf.String())
})
r.GET("/:title/*option", everythingElse) r.GET("/:title/*option", everythingElse)
r.POST("/:title/*option", encryptionRoute) r.POST("/:title/*option", encryptionRoute)
r.DELETE("/listitem", deleteListItem) r.DELETE("/listitem", deleteListItem)

25
ratelimiter.go Executable file
View File

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

122
routes.go
View File

@ -1,9 +1,12 @@
package main package main
import ( import (
"bytes"
"fmt" "fmt"
"html/template" "html/template"
"io"
"io/ioutil" "io/ioutil"
"log"
"net/http" "net/http"
"os" "os"
"path" "path"
@ -18,6 +21,65 @@ import (
"github.com/russross/blackfriday" "github.com/russross/blackfriday"
) )
const _24K = (1 << 20) * 24
func putFile(c *gin.Context) {
if isIPBanned(c.ClientIP()) {
c.Data(200, "text/plain", []byte("You are rate limited to 20 requests/hour."))
return
}
filename := c.Param("title")
if len(filename) == 0 {
filename = randomAlliterateCombo()
}
contentLength := c.Request.ContentLength
var reader io.Reader
reader = c.Request.Body
if contentLength == -1 {
// queue file to disk, because s3 needs content length
var err error
var f io.Reader
f = reader
var b bytes.Buffer
n, err := io.CopyN(&b, f, _24K+1)
if err != nil && err != io.EOF {
log.Printf("%s", err.Error())
}
if n > _24K {
file, err := ioutil.TempFile("./", "transfer-")
if err != nil {
log.Printf("%s", err.Error())
}
defer file.Close()
n, err = io.Copy(file, io.MultiReader(&b, f))
if err != nil {
os.Remove(file.Name())
log.Printf("%s", err.Error())
}
reader, err = os.Open(file.Name())
} else {
reader = bytes.NewReader(b.Bytes())
}
contentLength = n
}
buf := new(bytes.Buffer)
buf.ReadFrom(reader)
// p := WikiData{filename, "", []string{}, []string{}, false, ""}
// p.save(buf.String())
var p WikiData
p.load(strings.ToLower(filename))
p.save(buf.String())
c.Data(200, "text/plain", []byte("File uploaded to http://"+RuntimeArgs.ExternalIP+"/"+filename))
}
type EncryptionPost struct { type EncryptionPost struct {
Text string `form:"text" json:"text" binding:"required"` Text string `form:"text" json:"text" binding:"required"`
Password string `form:"password" json:"password" binding:"required"` Password string `form:"password" json:"password" binding:"required"`
@ -158,37 +220,37 @@ func editNote(c *gin.Context) {
} else if strings.ToLower(title) == "help" { //}&& strings.Contains(AllowedIPs, c.ClientIP()) != true { } else if strings.ToLower(title) == "help" { //}&& strings.Contains(AllowedIPs, c.ClientIP()) != true {
c.Redirect(302, "/Help/view") c.Redirect(302, "/Help/view")
} else { } else {
locked, _ := hasPassword(title) version := c.DefaultQuery("version", "-1")
if locked { versionNum, _ := strconv.Atoi(version)
c.Redirect(302, "/"+title+"/view") currentText, versions, currentVersion, totalTime, encrypted, locked := getCurrentText(title, versionNum)
} else { if strings.Contains(c.Request.Header.Get("User-Agent"), "curl/") {
version := c.DefaultQuery("version", "-1") c.Data(200, "text/plain", []byte(currentText))
versionNum, _ := strconv.Atoi(version) return
currentText, versions, currentVersion, totalTime, encrypted, locked := getCurrentText(title, versionNum)
if encrypted || len(locked) > 0 {
c.Redirect(302, "/"+title+"/view")
}
if strings.Contains(currentText, "self-destruct\n") || strings.Contains(currentText, "\nself-destruct") {
c.Redirect(302, "/"+title+"/view")
}
numRows := len(strings.Split(currentText, "\n")) + 10
totalTimeString := totalTime.String()
if totalTime.Seconds() < 1 {
totalTimeString = "< 1 s"
}
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"Title": title,
"WikiName": RuntimeArgs.WikiName,
"ExternalIP": RuntimeArgs.ExternalIP,
"CurrentText": currentText,
"NumRows": numRows,
"Versions": versions,
"TotalTime": totalTimeString,
"SocketType": RuntimeArgs.Socket,
"NoEdit": !currentVersion,
})
} }
if encrypted || len(locked) > 0 {
c.Redirect(302, "/"+title+"/view")
return
}
if strings.Contains(currentText, "self-destruct\n") || strings.Contains(currentText, "\nself-destruct") {
c.Redirect(302, "/"+title+"/view")
return
}
numRows := len(strings.Split(currentText, "\n")) + 10
totalTimeString := totalTime.String()
if totalTime.Seconds() < 1 {
totalTimeString = "< 1 s"
}
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"Title": title,
"WikiName": RuntimeArgs.WikiName,
"ExternalIP": RuntimeArgs.ExternalIP,
"CurrentText": currentText,
"NumRows": numRows,
"Versions": versions,
"TotalTime": totalTimeString,
"SocketType": RuntimeArgs.Socket,
"NoEdit": !currentVersion,
})
} }
} }

View File

@ -2,7 +2,7 @@
# CowYo # CowYo
## Collections of Organized Words You Open ## Collections of Organized Words You Open
![Version 0.94](https://img.shields.io/badge/version-0.94-brightgreen.svg) ![Version 0.95](https://img.shields.io/badge/version-0.95-brightgreen.svg)
This is a self-contained wiki webserver that makes sharing easy and _fast_. The most important feature here is *simplicity*. There are many other features as well including versioning, page locking, self-destructing messages, encryption, math support, and listifying. Read on to learn more about the features. **CowYo** is also [Open Source](https://github.com/schollz/cowyo). This is a self-contained wiki webserver that makes sharing easy and _fast_. The most important feature here is *simplicity*. There are many other features as well including versioning, page locking, self-destructing messages, encryption, math support, and listifying. Read on to learn more about the features. **CowYo** is also [Open Source](https://github.com/schollz/cowyo).
@ -45,6 +45,24 @@ This is a self-contained wiki webserver that makes sharing easy and _fast_. The
<br> <br>
**CLI support**. Want to upload/download from a server? Its super easy. Upload/download files like this:
```bash
$ echo "Hello, world!" > hi.txt
$ curl --upload-file hi.txt cowyo.com
File uploaded to http://cowyo.com/hi.txt
$ curl cowyo.com/test.txt
Hello, world!
```
or just skip the file-creation step,
```bash
$ echo "Wow, so easy" | curl --upload-file "-" cowyo.com
File uploaded to http://cowyo.com/CautiousCommonLoon
$ curl cowyo.com/CautiousCommonLoon
Wow, so easy
```
<br>
**Keyboard Shortcuts**. Quickly transition between Edit/View/List by using `Ctl+Shift+E` to Edit, `Ctl+Shift+Z` to View, and `Ctl+Shift+L` to Listify. **Keyboard Shortcuts**. Quickly transition between Edit/View/List by using `Ctl+Shift+E` to Edit, `Ctl+Shift+Z` to View, and `Ctl+Shift+L` to Listify.
**Admin controls**. The Admin can view/delete all the documents by setting the `-a YourAdminKey` when starting the program. Then the admin has access to the `/ls/YourAdminKey` to view and delete any of the pages. **Admin controls**. The Admin can view/delete all the documents by setting the `-a YourAdminKey` when starting the program. Then the admin has access to the `/ls/YourAdminKey` to view and delete any of the pages.