Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

re-organised controller code

  • Loading branch information...
commit 7457e7c9ddf6a4b5b8d63c74ff2e82cca301d506 1 parent 7e71a97
@jaredwilkening jaredwilkening authored
View
106 README.md
@@ -19,7 +19,7 @@ Shock is actively being developed at [github.com/MG-RAST/Shock](http://github.co
<br>
Building:
---------
-Shock (requires go=>1.0.0 [golang.org/doc/install/source](http://golang.org/doc/install/source), git, mercurial, bazaar):
+Shock (requires go=>1.1.0 [golang.org](http://golang.org/), git, mercurial, bazaar):
go get github.com/MG-RAST/Shock/...
@@ -114,8 +114,6 @@ Routes Overview
- [/node/{id}](#get_node) view node, download file (full or partial)
- [/node/{id}/acl]() view node acls
- [/node/{id}/acl/{type}]() view node acls of type {type}
-- [/user](#get_users) list users (admin users only)
-- [/user/{id}](#get_user) view user
#####PUT
@@ -126,7 +124,6 @@ Routes Overview
#####POST
- [/node](#post_node) create node
-- [/user](#post_user) create user
#####DELETE
@@ -144,22 +141,11 @@ Routes Overview
Authentication:
---------------
-Shock currently supports two forms of Authentication. Http Basic Auth with local user support and Globus Online Nexus oauth implementation. See configuration for more details.
-
-### Basic Auth
-In this configuration Shock locally stores user information. Users must create accounts via the [user api](#post_user). Once this is done they can pass basic auth headers to authenticate.
-
-Example
-
- curl --user username:password ...
-
-<br>
+Shock supports multiple forms of Authentication via plugin modules. Credentials are cached for 1 hour to speed up high transaction loads. Server restarts will clear the credential cache.
### Globus Online
In this configuration Shock locally stores only uuids for users that it has already seen. The registration of new users is done exclusively with the external auth provider. The user api is disabled in this mode.
-__Note__: Using the basic auth method shown below is significantly slower than the bearer token. Its highly discouraged for large numbers of request.
-
Examples:
# globus online username & password
@@ -205,27 +191,6 @@ Data Types
}
<br>
-### User:
-
-- uuid: unique identifier
-- name: username
-- passwd: all responds are masked "**********"
-- admin: boolean
-
-##### user example:
-
- {
- "data": {
- "uuid": "67394386a4acac62fdb851d78691ee48"
- "name": "joeuser",
- "passwd": "**********",
- "admin": false,
- },
- "error": null,
- "status": 200
- }
-
-<br>
### Index:
Currently there is support for two types of indices: virtual and file.
@@ -514,73 +479,6 @@ Modify node, create index
"status": <http status of request>
}
-<a name="post_user"/>
-<br>
-### POST /user
-
-Create user (basic auth only)
-
-Requires Basic Auth encoded as 'username:password'. To create an admin user 'username:password:secret-key:true' where secret-key was specified at server start.
-
-##### example
-
- # regular user (when config Anonymous:create-user=true)
- curl -X POST --user joeuser:1234 http://<host>[:<port>]/user
-
- # regular user (when config Anonymous:create-user=false)
- curl -X POST --user joeuser:1234:supersupersecret:false http://<host>[:<port>]/user
-
- # admin user
- curl -X POST --user joeuser:1234:supersupersecret:true http://<host>[:<port>]/user
-
-##### returns
-
- {
- "data": {<user>},
- "error": <error message or null>,
- "status": <http status of request>
- }
-
-<a name="get_user"/>
-<br>
-### GET /user/{id}
-
-View user (basic auth only)
-
-Requires Basic Auth encoded username:password. Regular user are able to see their own information while Admin user are able to access all.
-
-##### example
-
- curl -X GET --user joeuser:1234 http://<host>[:<port>]/user/{id}
-
-##### returns
-
- {
- "data": {<user>},
- "error": <error message or null>,
- "status": <http status of request>
- }
-
-<a name="get_users"/>
-<br>
-### GET /user
-
-List users (basic auth only)
-
-Requires Basic Auth encoded username:password. Restricted to Admin users.
-
-##### example
-
- curl -X GET --user joeadmin:12345 http://<host>[:<port>]/user
-
-##### returns
-
- {
- "data": {[<user>,...]},
- "error": <error message or null>,
- "status": <http status of request>
- }
-
<br>
License
---
View
22 shock-server/controller/controller.go
@@ -0,0 +1,22 @@
+package controller
+
+import (
+ "github.com/MG-RAST/Shock/shock-server/controller/node"
+ "github.com/MG-RAST/Shock/shock-server/controller/node/acl"
+ "github.com/MG-RAST/Shock/shock-server/controller/preauth"
+ "github.com/jaredwilkening/goweb"
+)
+
+type Controller struct {
+ Node *node.Controller
+ Acl map[string]goweb.ControllerFunc
+ Preauth func(*goweb.Context)
+}
+
+func New() *Controller {
+ return &Controller{
+ Node: new(node.Controller),
+ Acl: map[string]goweb.ControllerFunc{"base": acl.Controller, "typed": acl.ControllerTyped},
+ Preauth: preauth.PreAuthRequest,
+ }
+}
View
32 shock-server/nodeAclController.go → shock-server/controller/node/acl/acl.go
@@ -1,9 +1,11 @@
-package main
+package acl
import (
"errors"
e "github.com/MG-RAST/Shock/shock-server/errors"
+ "github.com/MG-RAST/Shock/shock-server/logger"
"github.com/MG-RAST/Shock/shock-server/node"
+ "github.com/MG-RAST/Shock/shock-server/request"
"github.com/MG-RAST/Shock/shock-server/user"
"github.com/jaredwilkening/goweb"
"net/http"
@@ -15,11 +17,11 @@ var (
)
// GET, POST, PUT, DELETE: /node/{nid}/acl/
-var AclController goweb.ControllerFunc = func(cx *goweb.Context) {
- LogRequest(cx.Request)
- u, err := AuthenticateRequest(cx.Request)
+var Controller goweb.ControllerFunc = func(cx *goweb.Context) {
+ request.Log(cx.Request)
+ u, err := request.Authenticate(cx.Request)
if err != nil && err.Error() != e.NoAuth {
- handleAuthError(err, cx)
+ request.AuthError(err, cx)
return
}
@@ -42,7 +44,7 @@ var AclController goweb.ControllerFunc = func(cx *goweb.Context) {
} else {
// In theory the db connection could be lost between
// checking user and load but seems unlikely.
- log.Error("Err@node_Read:LoadNode: " + err.Error())
+ logger.Error("Err@node_Read:LoadNode: " + err.Error())
cx.RespondWithError(http.StatusInternalServerError)
return
}
@@ -85,11 +87,11 @@ var AclController goweb.ControllerFunc = func(cx *goweb.Context) {
}
// GET, POST, PUT, DELETE: /node/{nid}/acl/{type}
-var AclControllerTyped goweb.ControllerFunc = func(cx *goweb.Context) {
- LogRequest(cx.Request)
- u, err := AuthenticateRequest(cx.Request)
+var ControllerTyped goweb.ControllerFunc = func(cx *goweb.Context) {
+ request.Log(cx.Request)
+ u, err := request.Authenticate(cx.Request)
if err != nil && err.Error() != e.NoAuth {
- handleAuthError(err, cx)
+ request.AuthError(err, cx)
return
}
@@ -118,7 +120,7 @@ var AclControllerTyped goweb.ControllerFunc = func(cx *goweb.Context) {
} else {
// In theory the db connection could be lost between
// checking user and load but seems unlikely.
- log.Error("Err@node_Read:LoadNode: " + err.Error())
+ logger.Error("Err@node_Read:LoadNode: " + err.Error())
cx.RespondWithError(http.StatusInternalServerError)
return
}
@@ -182,8 +184,8 @@ var AclControllerTyped goweb.ControllerFunc = func(cx *goweb.Context) {
func parseAclRequest(cx *goweb.Context) (ids map[string][]string, err error) {
ids = map[string][]string{}
users := map[string][]string{}
- query := &Query{list: cx.Request.URL.Query()}
- params, _, err := ParseMultipartForm(cx.Request)
+ query := request.Q(cx.Request.URL.Query())
+ params, _, err := request.ParseMultipartForm(cx.Request)
if err != nil && err.Error() == "request Content-Type isn't multipart/form-data" && (query.Has("all") || query.Has("read") || query.Has("write") || query.Has("delete")) {
if query.Has("all") {
users["all"] = strings.Split(query.Value("all"), ",")
@@ -233,8 +235,8 @@ func parseAclRequest(cx *goweb.Context) (ids map[string][]string, err error) {
func parseAclRequestTyped(cx *goweb.Context) (ids []string, err error) {
var users []string
- query := &Query{list: cx.Request.URL.Query()}
- params, _, err := ParseMultipartForm(cx.Request)
+ query := request.Q(cx.Request.URL.Query())
+ params, _, err := request.ParseMultipartForm(cx.Request)
if err != nil && err.Error() == "request Content-Type isn't multipart/form-data" && query.Has("users") {
users = strings.Split(query.Value("users"), ",")
} else if params["users"] != "" {
View
143 shock-server/nodeController.go → shock-server/controller/node/node.go
@@ -1,15 +1,18 @@
-package main
+package node
import (
"fmt"
"github.com/MG-RAST/Shock/shock-server/conf"
e "github.com/MG-RAST/Shock/shock-server/errors"
"github.com/MG-RAST/Shock/shock-server/indexer"
+ "github.com/MG-RAST/Shock/shock-server/logger"
"github.com/MG-RAST/Shock/shock-server/node"
"github.com/MG-RAST/Shock/shock-server/node/file"
"github.com/MG-RAST/Shock/shock-server/node/filter"
"github.com/MG-RAST/Shock/shock-server/preauth"
+ "github.com/MG-RAST/Shock/shock-server/request"
"github.com/MG-RAST/Shock/shock-server/user"
+ "github.com/MG-RAST/Shock/shock-server/util"
"github.com/jaredwilkening/goweb"
"io"
"labix.org/v2/mgo/bson"
@@ -20,22 +23,22 @@ import (
"time"
)
-type NodeController struct{}
+type Controller struct{}
// Options: /node
-func (cr *NodeController) Options(cx *goweb.Context) {
- LogRequest(cx.Request)
+func (cr *Controller) Options(cx *goweb.Context) {
+ request.Log(cx.Request)
cx.RespondWithOK()
return
}
// POST: /node
-func (cr *NodeController) Create(cx *goweb.Context) {
+func (cr *Controller) Create(cx *goweb.Context) {
// Log Request and check for Auth
- LogRequest(cx.Request)
- u, err := AuthenticateRequest(cx.Request)
+ request.Log(cx.Request)
+ u, err := request.Authenticate(cx.Request)
if err != nil && err.Error() != e.NoAuth {
- handleAuthError(err, cx)
+ request.AuthError(err, cx)
return
}
@@ -50,7 +53,7 @@ func (cr *NodeController) Create(cx *goweb.Context) {
}
// Parse uploaded form
- params, files, err := ParseMultipartForm(cx.Request)
+ params, files, err := request.ParseMultipartForm(cx.Request)
if err != nil {
// If not multipart/form-data it will create an empty node.
// TODO: create another request parser for non-multipart request
@@ -58,7 +61,7 @@ func (cr *NodeController) Create(cx *goweb.Context) {
if err.Error() == "request Content-Type isn't multipart/form-data" {
n, err := node.CreateNodeUpload(u, params, files)
if err != nil {
- log.Error("Error at create empty: " + err.Error())
+ logger.Error("Error at create empty: " + err.Error())
cx.RespondWithError(http.StatusInternalServerError)
return
}
@@ -75,7 +78,7 @@ func (cr *NodeController) Create(cx *goweb.Context) {
// Some error other than request encoding. Theoretically
// could be a lost db connection between user lookup and parsing.
// Blame the user, Its probaby their fault anyway.
- log.Error("Error parsing form: " + err.Error())
+ logger.Error("Error parsing form: " + err.Error())
cx.RespondWithError(http.StatusBadRequest)
return
}
@@ -83,7 +86,7 @@ func (cr *NodeController) Create(cx *goweb.Context) {
// Create node
n, err := node.CreateNodeUpload(u, params, files)
if err != nil {
- log.Error("err@node_CreateNodeUpload: " + err.Error())
+ logger.Error("err@node_CreateNodeUpload: " + err.Error())
cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
return
}
@@ -92,11 +95,11 @@ func (cr *NodeController) Create(cx *goweb.Context) {
}
// DELETE: /node/{id}
-func (cr *NodeController) Delete(id string, cx *goweb.Context) {
- LogRequest(cx.Request)
- u, err := AuthenticateRequest(cx.Request)
+func (cr *Controller) Delete(id string, cx *goweb.Context) {
+ request.Log(cx.Request)
+ u, err := request.Authenticate(cx.Request)
if err != nil && err.Error() != e.NoAuth {
- handleAuthError(err, cx)
+ request.AuthError(err, cx)
return
}
if u == nil {
@@ -116,7 +119,7 @@ func (cr *NodeController) Delete(id string, cx *goweb.Context) {
} else {
// In theory the db connection could be lost between
// checking user and load but seems unlikely.
- log.Error("Err@node_Read:Delete: " + err.Error())
+ logger.Error("Err@node_Read:Delete: " + err.Error())
cx.RespondWithError(http.StatusInternalServerError)
return
}
@@ -126,7 +129,7 @@ func (cr *NodeController) Delete(id string, cx *goweb.Context) {
cx.RespondWithOK()
return
} else {
- log.Error("Err@node_Delet:Delete: " + err.Error())
+ logger.Error("Err@node_Delet:Delete: " + err.Error())
cx.RespondWithError(http.StatusInternalServerError)
}
return
@@ -134,12 +137,12 @@ func (cr *NodeController) Delete(id string, cx *goweb.Context) {
// GET: /node/{id}
// ToDo: clean up this function. About to get unmanageable
-func (cr *NodeController) Read(id string, cx *goweb.Context) {
+func (cr *Controller) Read(id string, cx *goweb.Context) {
// Log Request and check for Auth
- LogRequest(cx.Request)
- u, err := AuthenticateRequest(cx.Request)
+ request.Log(cx.Request)
+ u, err := request.Authenticate(cx.Request)
if err != nil && err.Error() != e.NoAuth {
- handleAuthError(err, cx)
+ request.AuthError(err, cx)
return
}
@@ -154,7 +157,7 @@ func (cr *NodeController) Read(id string, cx *goweb.Context) {
}
// Gather query params
- query := &Query{list: cx.Request.URL.Query()}
+ query := request.Q(cx.Request.URL.Query())
var fFunc filter.FilterFunc = nil
if query.Has("filter") {
@@ -175,11 +178,11 @@ func (cr *NodeController) Read(id string, cx *goweb.Context) {
} else {
// In theory the db connection could be lost between
// checking user and load but seems unlikely.
- log.Error("Err@node_Read:LoadNode:" + id + ":" + err.Error())
+ logger.Error("Err@node_Read:LoadNode:" + id + ":" + err.Error())
n, err = node.LoadFromDisk(id)
if err != nil {
- log.Error("Err@node_Read:LoadNodeFromDisk:" + id + ":" + err.Error())
+ logger.Error("Err@node_Read:LoadNodeFromDisk:" + id + ":" + err.Error())
cx.RespondWithError(http.StatusInternalServerError)
return
}
@@ -203,7 +206,7 @@ func (cr *NodeController) Read(id string, cx *goweb.Context) {
if query.Has("index") {
//handling bam file
if query.Value("index") == "bai" {
- s := &streamer{rs: []file.SectionReader{}, ws: cx.ResponseWriter, contentType: "application/octet-stream", filename: filename, size: n.File.Size, filter: fFunc}
+ s := &request.Streamer{R: []file.SectionReader{}, W: cx.ResponseWriter, ContentType: "application/octet-stream", Filename: filename, Size: n.File.Size, Filter: fFunc}
var region string
@@ -212,13 +215,13 @@ func (cr *NodeController) Read(id string, cx *goweb.Context) {
region = query.Value("region")
}
- argv, err := ParseSamtoolsArgs(query)
+ argv, err := request.ParseSamtoolsArgs(query)
if err != nil {
cx.RespondWithErrorMessage("Invaid args in query url", http.StatusBadRequest)
return
}
- err = s.stream_samtools(n.FilePath(), region, argv...)
+ err = s.StreamSamtools(n.FilePath(), region, argv...)
if err != nil {
cx.RespondWithErrorMessage("error while involking samtools", http.StatusBadRequest)
return
@@ -235,7 +238,7 @@ func (cr *NodeController) Read(id string, cx *goweb.Context) {
// open file
r, err := n.FileReader()
if err != nil {
- log.Error("Err@node_Read:Open: " + err.Error())
+ logger.Error("Err@node_Read:Open: " + err.Error())
cx.RespondWithError(http.StatusInternalServerError)
return
}
@@ -258,7 +261,7 @@ func (cr *NodeController) Read(id string, cx *goweb.Context) {
idx.Set(map[string]interface{}{"ChunkSize": csize})
}
var size int64 = 0
- s := &streamer{rs: []file.SectionReader{}, ws: cx.ResponseWriter, contentType: "application/octet-stream", filename: filename, filter: fFunc}
+ s := &request.Streamer{R: []file.SectionReader{}, W: cx.ResponseWriter, ContentType: "application/octet-stream", Filename: filename, Filter: fFunc}
for _, p := range query.List("part") {
pos, length, err := idx.Part(p)
if err != nil {
@@ -266,30 +269,30 @@ func (cr *NodeController) Read(id string, cx *goweb.Context) {
return
}
size += length
- s.rs = append(s.rs, io.NewSectionReader(r, pos, length))
+ s.R = append(s.R, io.NewSectionReader(r, pos, length))
}
- s.size = size
- err = s.stream()
+ s.Size = size
+ err = s.Stream()
if err != nil {
// causes "multiple response.WriteHeader calls" error but better than no response
cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
- log.Error("err:@node_Read s.stream: " + err.Error())
+ logger.Error("err:@node_Read s.stream: " + err.Error())
}
} else {
nf, err := n.FileReader()
if err != nil {
// File not found or some sort of file read error.
// Probably deserves more checking
- log.Error("err:@node_Read node.FileReader: " + err.Error())
+ logger.Error("err:@node_Read node.FileReader: " + err.Error())
cx.RespondWithError(http.StatusBadRequest)
return
}
- s := &streamer{rs: []file.SectionReader{nf}, ws: cx.ResponseWriter, contentType: "application/octet-stream", filename: filename, size: n.File.Size, filter: fFunc}
- err = s.stream()
+ s := &request.Streamer{R: []file.SectionReader{nf}, W: cx.ResponseWriter, ContentType: "application/octet-stream", Filename: filename, Size: n.File.Size, Filter: fFunc}
+ err = s.Stream()
if err != nil {
// causes "multiple response.WriteHeader calls" error but better than no response
cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
- log.Error("err:@node_Read: s.stream: " + err.Error())
+ logger.Error("err:@node_Read: s.stream: " + err.Error())
}
}
return
@@ -305,11 +308,11 @@ func (cr *NodeController) Read(id string, cx *goweb.Context) {
if query.Has("filename") {
options["filename"] = query.Value("filename")
}
- if p, err := preauth.New(RandString(20), "download", n.Id, options); err != nil {
+ if p, err := preauth.New(util.RandString(20), "download", n.Id, options); err != nil {
cx.RespondWithError(http.StatusInternalServerError)
- log.Error("err:@node_Read download_url: " + err.Error())
+ logger.Error("err:@node_Read download_url: " + err.Error())
} else {
- cx.RespondWithData(urlResponse{Url: apiUrl(cx) + "/preauth/" + p.Id, ValidTill: p.ValidTill.Format(time.ANSIC)})
+ cx.RespondWithData(util.UrlResponse{Url: util.ApiUrl(cx) + "/preauth/" + p.Id, ValidTill: p.ValidTill.Format(time.ANSIC)})
}
}
} else {
@@ -321,17 +324,17 @@ func (cr *NodeController) Read(id string, cx *goweb.Context) {
// GET: /node
// To do:
// - Iterate node queries
-func (cr *NodeController) ReadMany(cx *goweb.Context) {
+func (cr *Controller) ReadMany(cx *goweb.Context) {
// Log Request and check for Auth
- LogRequest(cx.Request)
- u, err := AuthenticateRequest(cx.Request)
+ request.Log(cx.Request)
+ u, err := request.Authenticate(cx.Request)
if err != nil && err.Error() != e.NoAuth {
- handleAuthError(err, cx)
+ request.AuthError(err, cx)
return
}
// Gather query params
- query := &Query{list: cx.Request.URL.Query()}
+ query := request.Q(cx.Request.URL.Query())
// Setup query and nodes objects
q := bson.M{}
@@ -380,16 +383,16 @@ func (cr *NodeController) ReadMany(cx *goweb.Context) {
limit := 25
offset := 0
if query.Has("limit") {
- limit = ToInt(query.Value("limit"))
+ limit = util.ToInt(query.Value("limit"))
}
if query.Has("offset") {
- offset = ToInt(query.Value("offset"))
+ offset = util.ToInt(query.Value("offset"))
}
// Get nodes from db
count, err := nodes.GetPaginated(q, limit, offset)
if err != nil {
- log.Error("err " + err.Error())
+ logger.Error("err " + err.Error())
cx.RespondWithError(http.StatusBadRequest)
return
}
@@ -398,17 +401,17 @@ func (cr *NodeController) ReadMany(cx *goweb.Context) {
}
// PUT: /node/{id} -> multipart-form
-func (cr *NodeController) Update(id string, cx *goweb.Context) {
+func (cr *Controller) Update(id string, cx *goweb.Context) {
// Log Request and check for Auth
- LogRequest(cx.Request)
- u, err := AuthenticateRequest(cx.Request)
+ request.Log(cx.Request)
+ u, err := request.Authenticate(cx.Request)
if err != nil && err.Error() != e.NoAuth {
- handleAuthError(err, cx)
+ request.AuthError(err, cx)
return
}
// Gather query params
- query := &Query{list: cx.Request.URL.Query()}
+ query := request.Q(cx.Request.URL.Query())
// Fake public user
if u == nil {
@@ -426,7 +429,7 @@ func (cr *NodeController) Update(id string, cx *goweb.Context) {
} else {
// In theory the db connection could be lost between
// checking user and load but seems unlikely.
- log.Error("Err@node_Update:LoadNode: " + err.Error())
+ logger.Error("Err@node_Update:LoadNode: " + err.Error())
cx.RespondWithError(http.StatusInternalServerError)
return
}
@@ -434,7 +437,7 @@ func (cr *NodeController) Update(id string, cx *goweb.Context) {
if query.Has("index") {
if conf.Bool(conf.Conf["perf-log"]) {
- log.Perf("START indexing: " + id)
+ logger.Perf("START indexing: " + id)
}
if !n.HasFile() {
@@ -445,7 +448,7 @@ func (cr *NodeController) Update(id string, cx *goweb.Context) {
if query.Value("index") == "bai" {
//bam index is created by the command-line tool samtools
if ext := n.FileExt(); ext == ".bam" {
- if err := CreateBamIndex(n.FilePath()); err != nil {
+ if err := request.CreateBamIndex(n.FilePath()); err != nil {
cx.RespondWithErrorMessage("Error while creating bam index", http.StatusBadRequest)
return
}
@@ -468,13 +471,13 @@ func (cr *NodeController) Update(id string, cx *goweb.Context) {
idxer := newIndexer(f)
count, err := idxer.Create()
if err != nil {
- log.Error("err " + err.Error())
+ logger.Error("err " + err.Error())
cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
return
}
if err := idxer.Dump(n.IndexPath() + "/" + query.Value("index") + ".idx"); err != nil {
- log.Error("err " + err.Error())
+ logger.Error("err " + err.Error())
cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
return
}
@@ -490,11 +493,11 @@ func (cr *NodeController) Update(id string, cx *goweb.Context) {
}
if err := n.SetIndexInfo(query.Value("index"), idxInfo); err != nil {
- log.Error("err@node.SetIndexInfo: " + err.Error())
+ logger.Error("err@node.SetIndexInfo: " + err.Error())
}
if conf.Bool(conf.Conf["perf-log"]) {
- log.Perf("END indexing: " + id)
+ logger.Perf("END indexing: " + id)
}
cx.RespondWithOK()
@@ -502,11 +505,11 @@ func (cr *NodeController) Update(id string, cx *goweb.Context) {
} else {
if conf.Bool(conf.Conf["perf-log"]) {
- log.Perf("START PUT data: " + id)
+ logger.Perf("START PUT data: " + id)
}
- params, files, err := ParseMultipartForm(cx.Request)
+ params, files, err := request.ParseMultipartForm(cx.Request)
if err != nil {
- log.Error("err@node_ParseMultipartForm: " + err.Error())
+ logger.Error("err@node_ParseMultipartForm: " + err.Error())
cx.RespondWithError(http.StatusBadRequest)
return
}
@@ -520,13 +523,13 @@ func (cr *NodeController) Update(id string, cx *goweb.Context) {
return
}
}
- log.Error("err@node_Update: " + id + ":" + err.Error())
+ logger.Error("err@node_Update: " + id + ":" + err.Error())
cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
return
}
cx.RespondWithData(n)
if conf.Bool(conf.Conf["perf-log"]) {
- log.Perf("END PUT data: " + id)
+ logger.Perf("END PUT data: " + id)
}
}
return
@@ -534,13 +537,13 @@ func (cr *NodeController) Update(id string, cx *goweb.Context) {
// Will not implement
// PUT: /node
-func (cr *NodeController) UpdateMany(cx *goweb.Context) {
- LogRequest(cx.Request)
+func (cr *Controller) UpdateMany(cx *goweb.Context) {
+ request.Log(cx.Request)
cx.RespondWithError(http.StatusNotImplemented)
}
// DELETE: /node
-func (cr *NodeController) DeleteMany(cx *goweb.Context) {
- LogRequest(cx.Request)
+func (cr *Controller) DeleteMany(cx *goweb.Context) {
+ request.Log(cx.Request)
cx.RespondWithError(http.StatusNotImplemented)
}
View
20 shock-server/preAuthController.go → shock-server/controller/preauth/preauth.go
@@ -1,23 +1,25 @@
-package main
+package preauth
import (
e "github.com/MG-RAST/Shock/shock-server/errors"
+ "github.com/MG-RAST/Shock/shock-server/logger"
"github.com/MG-RAST/Shock/shock-server/node"
"github.com/MG-RAST/Shock/shock-server/node/file"
"github.com/MG-RAST/Shock/shock-server/preauth"
+ "github.com/MG-RAST/Shock/shock-server/request"
"github.com/jaredwilkening/goweb"
"net/http"
)
func PreAuthRequest(cx *goweb.Context) {
- LogRequest(cx.Request)
+ request.Log(cx.Request)
id := cx.PathParams["id"]
if p, err := preauth.Load(id); err != nil {
if err.Error() == e.MongoDocNotFound {
cx.RespondWithNotFound()
} else {
cx.RespondWithError(http.StatusInternalServerError)
- log.Error("err:@preAuth load: " + err.Error())
+ logger.Error("err:@preAuth load: " + err.Error())
}
return
} else {
@@ -36,30 +38,30 @@ func PreAuthRequest(cx *goweb.Context) {
}
} else {
cx.RespondWithError(http.StatusInternalServerError)
- log.Error("err:@preAuth loadnode: " + err.Error())
+ logger.Error("err:@preAuth loadnode: " + err.Error())
}
}
return
}
func streamDownload(cx *goweb.Context, n *node.Node, filename string) {
- query := &Query{list: cx.Request.URL.Query()}
+ query := request.Q(cx.Request.URL.Query())
nf, err := n.FileReader()
if err != nil {
// File not found or some sort of file read error.
// Probably deserves more checking
- log.Error("err:@preAuth node.FileReader: " + err.Error())
+ logger.Error("err:@preAuth node.FileReader: " + err.Error())
cx.RespondWithError(http.StatusBadRequest)
return
}
if query.Has("filename") {
filename = query.Value("filename")
}
- s := &streamer{rs: []file.SectionReader{nf}, ws: cx.ResponseWriter, contentType: "application/octet-stream", filename: filename, size: n.File.Size, filter: nil}
- err = s.stream()
+ s := &request.Streamer{R: []file.SectionReader{nf}, W: cx.ResponseWriter, ContentType: "application/octet-stream", Filename: filename, Size: n.File.Size, Filter: nil}
+ err = s.Stream()
if err != nil {
// causes "multiple response.WriteHeader calls" error but better than no response
cx.RespondWithErrorMessage(err.Error(), http.StatusBadRequest)
- log.Error("err:@preAuth: s.stream: " + err.Error())
+ logger.Error("err:@preAuth: s.stream: " + err.Error())
}
}
View
24 shock-server/logger/logger.go
@@ -7,8 +7,11 @@ import (
"os"
)
-//type level int
+var (
+ Log *Logger
+)
+//type level int
type m struct {
log string
lvl l4g.Level
@@ -20,6 +23,10 @@ type Logger struct {
logs map[string]l4g.Logger
}
+func Initialize() {
+ Log = New()
+}
+
func New() *Logger {
l := &Logger{queue: make(chan m, 1024), logs: map[string]l4g.Logger{}}
l.logs["access"] = make(l4g.Logger)
@@ -70,11 +77,21 @@ func (l *Logger) Warning(log string, message string) {
return
}
+func Info(log string, message string) {
+ Log.Log(log, l4g.INFO, message)
+ return
+}
+
func (l *Logger) Info(log string, message string) {
l.Log(log, l4g.INFO, message)
return
}
+func Error(message string) {
+ Log.Log("error", l4g.ERROR, message)
+ return
+}
+
func (l *Logger) Error(message string) {
l.Log("error", l4g.ERROR, message)
return
@@ -85,6 +102,11 @@ func (l *Logger) Critical(log string, message string) {
return
}
+func Perf(message string) {
+ Log.Log("perf", l4g.INFO, message)
+ return
+}
+
func (l *Logger) Perf(message string) {
l.Log("perf", l4g.INFO, message)
return
View
28 shock-server/main.go
@@ -3,6 +3,7 @@ package main
import (
"fmt"
"github.com/MG-RAST/Shock/shock-server/conf"
+ "github.com/MG-RAST/Shock/shock-server/controller"
"github.com/MG-RAST/Shock/shock-server/db"
"github.com/MG-RAST/Shock/shock-server/logger"
"github.com/MG-RAST/Shock/shock-server/node"
@@ -25,39 +26,39 @@ func launchSite(control chan int) {
err := goweb.ListenAndServeRoutesTLS(":"+conf.Conf["site-port"], conf.Conf["ssl-cert"], conf.Conf["ssl-key"], r)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: site: %v\n", err)
- log.Error("ERROR: site: " + err.Error())
+ logger.Error("ERROR: site: " + err.Error())
}
} else {
err := goweb.ListenAndServeRoutes(":"+conf.Conf["site-port"], r)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: site: %v\n", err)
- log.Error("ERROR: site: " + err.Error())
+ logger.Error("ERROR: site: " + err.Error())
}
}
control <- 1 //we are ending
}
func launchAPI(control chan int) {
+ c := controller.New()
goweb.ConfigureDefaultFormatters()
r := &goweb.RouteManager{}
- r.MapFunc("/preauth/{id}", PreAuthRequest, goweb.GetMethod)
- r.Map("/node/{nid}/acl/{type}", AclControllerTyped)
- r.Map("/node/{nid}/acl", AclController)
- r.MapRest("/node", new(NodeController))
- r.MapRest("/user", new(UserController))
+ r.MapFunc("/preauth/{id}", c.Preauth, goweb.GetMethod)
+ r.Map("/node/{nid}/acl/{type}", c.Acl["typed"])
+ r.Map("/node/{nid}/acl", c.Acl["base"])
+ r.MapRest("/node", c.Node)
r.MapFunc("*", ResourceDescription, goweb.GetMethod)
r.MapFunc("*", RespondOk, goweb.OptionsMethod)
if conf.Bool(conf.Conf["ssl"]) {
err := goweb.ListenAndServeRoutesTLS(":"+conf.Conf["api-port"], conf.Conf["ssl-cert"], conf.Conf["ssl-key"], r)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: api: %v\n", err)
- log.Error("ERROR: api: " + err.Error())
+ logger.Error("ERROR: api: " + err.Error())
}
} else {
err := goweb.ListenAndServeRoutes(":"+conf.Conf["api-port"], r)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: api: %v\n", err)
- log.Error("ERROR: api: " + err.Error())
+ logger.Error("ERROR: api: " + err.Error())
}
}
control <- 1 //we are ending
@@ -65,18 +66,19 @@ func launchAPI(control chan int) {
func main() {
conf.Initialize()
+ logger.Initialize()
+ log = logger.Log
db.Initialize()
user.Initialize()
node.Initialize()
- log = logger.New()
printLogo()
conf.Print()
if _, err := os.Stat(conf.Conf["data-path"] + "/temp"); err != nil && os.IsNotExist(err) {
if err := os.Mkdir(conf.Conf["data-path"]+"/temp", 0777); err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
- log.Error("ERROR: " + err.Error())
+ logger.Error("ERROR: " + err.Error())
}
}
@@ -86,14 +88,14 @@ func main() {
err := reload(conf.RELOAD)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %v\n", err)
- log.Error("ERROR: " + err.Error())
+ logger.Error("ERROR: " + err.Error())
}
fmt.Println("Done")
}
//launch server
control := make(chan int)
- go log.Handle()
+ go logger.Log.Handle()
go launchSite(control)
go launchAPI(control)
<-control //block till something dies
View
183 shock-server/request/request.go
@@ -0,0 +1,183 @@
+package request
+
+import (
+ "crypto/md5"
+ "crypto/sha1"
+ "errors"
+ "fmt"
+ "github.com/MG-RAST/Shock/shock-server/auth"
+ "github.com/MG-RAST/Shock/shock-server/conf"
+ e "github.com/MG-RAST/Shock/shock-server/errors"
+ "github.com/MG-RAST/Shock/shock-server/logger"
+ "github.com/MG-RAST/Shock/shock-server/node"
+ "github.com/MG-RAST/Shock/shock-server/user"
+ "github.com/jaredwilkening/goweb"
+ "hash"
+ "math/rand"
+ "net"
+ "net/http"
+ "os"
+)
+
+type checkSumCom struct {
+ buf []byte
+ n int
+ checksum string
+}
+
+type Query struct {
+ list map[string][]string
+}
+
+func Q(l map[string][]string) *Query {
+ return &Query{list: l}
+}
+
+func (q *Query) Has(key string) bool {
+ if _, has := q.list[key]; has {
+ return true
+ }
+ return false
+}
+
+func (q *Query) Value(key string) string {
+ return q.list[key][0]
+}
+
+func (q *Query) List(key string) []string {
+ return q.list[key]
+}
+
+func (q *Query) All() map[string][]string {
+ return q.list
+}
+
+func Log(req *http.Request) {
+ host, _, _ := net.SplitHostPort(req.RemoteAddr)
+ // failed attempt to get the host in ipv4
+ //addrs, _ := net.LookupIP(host)
+ //fmt.Println(addrs)
+ suffix := ""
+ if _, auth := req.Header["Authorization"]; auth {
+ suffix += " AUTH"
+ }
+
+ if l, has := req.Header["Content-Length"]; has {
+ suffix += " Content-Length: " + l[0]
+ }
+ url := ""
+ if req.URL.RawQuery != "" {
+ url = fmt.Sprintf("%s %s?%s", req.Method, req.URL.Path, req.URL.RawQuery)
+ } else {
+ url = fmt.Sprintf("%s %s", req.Method, req.URL.Path)
+ }
+ logger.Info("access", host+" \""+url+suffix+"\"")
+}
+
+func Authenticate(req *http.Request) (u *user.User, err error) {
+ if _, ok := req.Header["Authorization"]; !ok {
+ err = errors.New(e.NoAuth)
+ return
+ }
+ header := req.Header.Get("Authorization")
+ u, err = auth.Authenticate(header)
+ return
+}
+
+func AuthError(err error, cx *goweb.Context) {
+ switch err.Error() {
+ case e.MongoDocNotFound:
+ cx.RespondWithErrorMessage("Invalid username or password", http.StatusBadRequest)
+ return
+ case e.InvalidAuth:
+ cx.RespondWithErrorMessage("Invalid Authorization header", http.StatusBadRequest)
+ return
+ }
+ logger.Error("Error at Auth: " + err.Error())
+ cx.RespondWithError(http.StatusInternalServerError)
+ return
+}
+
+// helper function for create & update
+func ParseMultipartForm(r *http.Request) (params map[string]string, files node.FormFiles, err error) {
+ params = make(map[string]string)
+ files = make(node.FormFiles)
+ reader, err := r.MultipartReader()
+ if err != nil {
+ return
+ }
+ for {
+ if part, err := reader.NextPart(); err == nil {
+ if part.FileName() == "" {
+ buffer := make([]byte, 32*1024)
+ n, err := part.Read(buffer)
+ if n == 0 || err != nil {
+ break
+ }
+ params[part.FormName()] = fmt.Sprintf("%s", buffer[0:n])
+ } else {
+ tmpPath := fmt.Sprintf("%s/temp/%d%d", conf.Conf["data-path"], rand.Int(), rand.Int())
+ /*
+ if fname[len(fname)-3:] == ".gz" && params["decompress"] == "true" {
+ fname = fname[:len(fname)-3]
+ reader, err = gzip.NewReader(&part)
+ if err != nil {
+ break
+ }
+ } else {
+ reader = &part
+ }
+ */
+ files[part.FormName()] = node.FormFile{Name: part.FileName(), Path: tmpPath, Checksum: make(map[string]string)}
+ if tmpFile, err := os.Create(tmpPath); err == nil {
+ buffer := make([]byte, 32*1024)
+ md5c := make(chan checkSumCom)
+ sha1c := make(chan checkSumCom)
+ writeChecksum(md5.New, md5c)
+ writeChecksum(sha1.New, sha1c)
+ for {
+ n, err := part.Read(buffer)
+ if n == 0 || err != nil {
+ md5c <- checkSumCom{n: 0}
+ sha1c <- checkSumCom{n: 0}
+ break
+ }
+ md5c <- checkSumCom{buf: buffer[0:n], n: n}
+ sha1c <- checkSumCom{buf: buffer[0:n], n: n}
+ tmpFile.Write(buffer[0:n])
+ }
+ md5r := <-md5c
+ sha1r := <-sha1c
+ files[part.FormName()].Checksum["md5"] = md5r.checksum
+ files[part.FormName()].Checksum["sha1"] = sha1r.checksum
+ tmpFile.Close()
+ } else {
+ return nil, nil, err
+ }
+ }
+ } else if err.Error() != "EOF" {
+ fmt.Println("err here")
+ return nil, nil, err
+ } else {
+ break
+ }
+ }
+ return
+}
+
+func writeChecksum(f func() hash.Hash, c chan checkSumCom) {
+ go func() {
+ h := f()
+ for {
+ select {
+ case b := <-c:
+ if b.n == 0 {
+ c <- checkSumCom{checksum: fmt.Sprintf("%x", h.Sum(nil))}
+ return
+ } else {
+ h.Write(b.buf[0:b.n])
+ }
+ }
+ }
+ }()
+}
View
38 shock-server/streamer.go → shock-server/request/streamer.go
@@ -1,4 +1,4 @@
-package main
+package request
import (
"errors"
@@ -11,29 +11,29 @@ import (
"path/filepath"
)
-type streamer struct {
- rs []file.SectionReader
- ws http.ResponseWriter
- contentType string
- filename string
- size int64
- filter filter.FilterFunc
+type Streamer struct {
+ R []file.SectionReader
+ W http.ResponseWriter
+ ContentType string
+ Filename string
+ Size int64
+ Filter filter.FilterFunc
}
-func (s *streamer) stream() (err error) {
- s.ws.Header().Set("Content-Type", s.contentType)
- s.ws.Header().Set("Content-Disposition", fmt.Sprintf(" attachment; filename=%s", s.filename))
- if s.size > 0 && s.filter == nil {
- s.ws.Header().Set("Content-Length", fmt.Sprint(s.size))
+func (s *Streamer) Stream() (err error) {
+ s.W.Header().Set("Content-Type", s.ContentType)
+ s.W.Header().Set("Content-Disposition", fmt.Sprintf(" attachment; filename=%s", s.Filename))
+ if s.Size > 0 && s.Filter == nil {
+ s.W.Header().Set("Content-Length", fmt.Sprint(s.Size))
}
- for _, sr := range s.rs {
+ for _, sr := range s.R {
var rs io.Reader
- if s.filter != nil {
- rs = s.filter(sr)
+ if s.Filter != nil {
+ rs = s.Filter(sr)
} else {
rs = sr
}
- _, err := io.Copy(s.ws, rs)
+ _, err := io.Copy(s.W, rs)
if err != nil {
return err
}
@@ -41,7 +41,7 @@ func (s *streamer) stream() (err error) {
return
}
-func (s *streamer) stream_samtools(filePath string, region string, args ...string) (err error) {
+func (s *Streamer) StreamSamtools(filePath string, region string, args ...string) (err error) {
//involking samtools in command line:
//samtools view [-c] [-H] [-f INT] ... filname.bam [region]
@@ -66,7 +66,7 @@ func (s *streamer) stream_samtools(filePath string, region string, args ...strin
return err
}
- go io.Copy(s.ws, stdout)
+ go io.Copy(s.W, stdout)
err = cmd.Wait()
if err != nil {
View
197 shock-server/util.go
@@ -1,24 +1,12 @@
package main
import (
- "crypto/md5"
- "crypto/sha1"
- "errors"
"fmt"
- "github.com/MG-RAST/Shock/shock-server/auth"
"github.com/MG-RAST/Shock/shock-server/conf"
- e "github.com/MG-RAST/Shock/shock-server/errors"
- "github.com/MG-RAST/Shock/shock-server/node"
- "github.com/MG-RAST/Shock/shock-server/user"
"github.com/jaredwilkening/goweb"
- "hash"
- "math/rand"
"net"
"net/http"
- "os"
- "strconv"
"strings"
- "time"
)
const logo = `
@@ -34,143 +22,14 @@ const logo = `
| | | | | | | | | | | | | |
+-------------+ +----+ +----+ +--------------+ +--------------+ +----+ +----+`
-const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"
-
-type checkSumCom struct {
- buf []byte
- n int
- checksum string
-}
-
-type urlResponse struct {
- Url string `json:"url"`
- ValidTill string `json:"validtill"`
-}
-
-func ToInt(s string) (i int) {
- i, _ = strconv.Atoi(s)
- return
-}
-
-func RandString(l int) (s string) {
- rand.Seed(time.Now().UTC().UnixNano())
- c := make([]byte, l)
- for i := 0; i < l; i++ {
- c[i] = chars[rand.Intn(len(chars))]
- }
- return string(c)
-}
-
func printLogo() {
fmt.Println(logo)
return
}
-type Query struct {
- list map[string][]string
-}
-
-func (q *Query) Has(key string) bool {
- if _, has := q.list[key]; has {
- return true
- }
- return false
-}
-
-func (q *Query) Value(key string) string {
- return q.list[key][0]
-}
-
-func (q *Query) List(key string) []string {
- return q.list[key]
-}
-
-func (q *Query) All() map[string][]string {
- return q.list
-}
-
-// helper function for create & update
-func ParseMultipartForm(r *http.Request) (params map[string]string, files node.FormFiles, err error) {
- params = make(map[string]string)
- files = make(node.FormFiles)
- reader, err := r.MultipartReader()
- if err != nil {
- return
- }
- for {
- if part, err := reader.NextPart(); err == nil {
- if part.FileName() == "" {
- buffer := make([]byte, 32*1024)
- n, err := part.Read(buffer)
- if n == 0 || err != nil {
- break
- }
- params[part.FormName()] = fmt.Sprintf("%s", buffer[0:n])
- } else {
- tmpPath := fmt.Sprintf("%s/temp/%d%d", conf.Conf["data-path"], rand.Int(), rand.Int())
- /*
- if fname[len(fname)-3:] == ".gz" && params["decompress"] == "true" {
- fname = fname[:len(fname)-3]
- reader, err = gzip.NewReader(&part)
- if err != nil {
- break
- }
- } else {
- reader = &part
- }
- */
- files[part.FormName()] = node.FormFile{Name: part.FileName(), Path: tmpPath, Checksum: make(map[string]string)}
- if tmpFile, err := os.Create(tmpPath); err == nil {
- buffer := make([]byte, 32*1024)
- md5c := make(chan checkSumCom)
- sha1c := make(chan checkSumCom)
- writeChecksum(md5.New, md5c)
- writeChecksum(sha1.New, sha1c)
- for {
- n, err := part.Read(buffer)
- if n == 0 || err != nil {
- md5c <- checkSumCom{n: 0}
- sha1c <- checkSumCom{n: 0}
- break
- }
- md5c <- checkSumCom{buf: buffer[0:n], n: n}
- sha1c <- checkSumCom{buf: buffer[0:n], n: n}
- tmpFile.Write(buffer[0:n])
- }
- md5r := <-md5c
- sha1r := <-sha1c
- files[part.FormName()].Checksum["md5"] = md5r.checksum
- files[part.FormName()].Checksum["sha1"] = sha1r.checksum
- tmpFile.Close()
- } else {
- return nil, nil, err
- }
- }
- } else if err.Error() != "EOF" {
- fmt.Println("err here")
- return nil, nil, err
- } else {
- break
- }
- }
- return
-}
-
-func writeChecksum(f func() hash.Hash, c chan checkSumCom) {
- go func() {
- h := f()
- for {
- select {
- case b := <-c:
- if b.n == 0 {
- c <- checkSumCom{checksum: fmt.Sprintf("%x", h.Sum(nil))}
- return
- } else {
- h.Write(b.buf[0:b.n])
- }
- }
- }
- }()
+type urlResponse struct {
+ Url string `json:"url"`
+ ValidTill string `json:"validtill"`
}
type resource struct {
@@ -188,6 +47,19 @@ func RespondOk(cx *goweb.Context) {
return
}
+func ResourceDescription(cx *goweb.Context) {
+ LogRequest(cx.Request)
+ r := resource{
+ R: []string{"node", "user"},
+ U: apiUrl(cx) + "/",
+ D: siteUrl(cx) + "/",
+ C: conf.Conf["admin-email"],
+ I: "Shock",
+ T: "Shock",
+ }
+ cx.WriteResponse(r, 200)
+}
+
func apiUrl(cx *goweb.Context) string {
if conf.Conf["api-url"] != "" {
return conf.Conf["api-url"]
@@ -204,19 +76,6 @@ func siteUrl(cx *goweb.Context) string {
return "http://" + cx.Request.Host
}
-func ResourceDescription(cx *goweb.Context) {
- LogRequest(cx.Request)
- r := resource{
- R: []string{"node", "user"},
- U: apiUrl(cx) + "/",
- D: siteUrl(cx) + "/",
- C: conf.Conf["admin-email"],
- I: "Shock",
- T: "Shock",
- }
- cx.WriteResponse(r, 200)
-}
-
func Site(cx *goweb.Context) {
LogRequest(cx.Request)
http.ServeFile(cx.ResponseWriter, cx.Request, conf.Conf["site-path"]+"/pages/main.html")
@@ -253,27 +112,3 @@ func LogRequest(req *http.Request) {
}
log.Info("access", host+" \""+url+suffix+"\"")
}
-
-func AuthenticateRequest(req *http.Request) (u *user.User, err error) {
- if _, ok := req.Header["Authorization"]; !ok {
- err = errors.New(e.NoAuth)
- return
- }
- header := req.Header.Get("Authorization")
- u, err = auth.Authenticate(header)
- return
-}
-
-func handleAuthError(err error, cx *goweb.Context) {
- switch err.Error() {
- case e.MongoDocNotFound:
- cx.RespondWithErrorMessage("Invalid username or password", http.StatusBadRequest)
- return
- case e.InvalidAuth:
- cx.RespondWithErrorMessage("Invalid Authorization header", http.StatusBadRequest)
- return
- }
- log.Error("Error at Auth: " + err.Error())
- cx.RespondWithError(http.StatusInternalServerError)
- return
-}
View
37 shock-server/util/util.go
@@ -0,0 +1,37 @@
+package util
+
+import (
+ "github.com/MG-RAST/Shock/shock-server/conf"
+ "github.com/jaredwilkening/goweb"
+ "math/rand"
+ "strconv"
+ "time"
+)
+
+const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890"
+
+type UrlResponse struct {
+ Url string `json:"url"`
+ ValidTill string `json:"validtill"`
+}
+
+func RandString(l int) (s string) {
+ rand.Seed(time.Now().UTC().UnixNano())
+ c := make([]byte, l)
+ for i := 0; i < l; i++ {
+ c[i] = chars[rand.Intn(len(chars))]
+ }
+ return string(c)
+}
+
+func ToInt(s string) (i int) {
+ i, _ = strconv.Atoi(s)
+ return
+}
+
+func ApiUrl(cx *goweb.Context) string {
+ if conf.Conf["api-url"] != "" {
+ return conf.Conf["api-url"]
+ }
+ return "http://" + cx.Request.Host
+}
Please sign in to comment.
Something went wrong with that request. Please try again.