Exemple #1
0
// 	handle PUT(/tab/:token/:bookid/:tabid)
func changeTab(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	lib.Trace(2, "handler:changeTab start")
	var result d.Result
	bookid := ps.ByName("bookid")
	login := &d.Login{Token: ps.ByName("token"), Bookid: bookid, Type: "edit"}
	resultChan := d.Data("auth", login)
	if result = <-resultChan; result.Status != d.DataOk {
		http.Error(w, result.Val.(string), http.StatusBadRequest)
		return
	}
	request := new(tabRequest)
	if decodeErr := json.NewDecoder(r.Body).Decode(request); decodeErr != nil {
		http.Error(w, "Program Error: tabRequest json decode", http.StatusInternalServerError)
		return
	}
	// change tab
	tab := &d.Tab{
		Id:        ps.ByName("tabid"),
		Bookid:    bookid,
		TabNumber: request.TabNumber,
		TabName:   request.TabName,
		Hidden:    request.Hidden,
	}
	resultChan = d.Data("changeTab", tab)
	result = <-resultChan
	w.Write([]byte("Update Successful"))

	lib.Trace(2, "handler:changeTab end")
}
Exemple #2
0
// 	handle PUT(/note/:token/:bookid/:tabid/:noteid)
func changeNote(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	lib.Trace(2, "handler:changeNote start")
	var result d.Result
	bookid := ps.ByName("bookid")
	login := &d.Login{Token: ps.ByName("token"), Bookid: bookid, Type: "edit"}
	resultChan := d.Data("auth", login)
	if result = <-resultChan; result.Status != d.DataOk {
		http.Error(w, result.Val.(string), http.StatusInternalServerError)
		return
	}
	request := new(noteRequest)
	if decodeErr := json.NewDecoder(r.Body).Decode(request); decodeErr != nil {
		http.Error(w, "Program Error: noteRequest json decode", http.StatusInternalServerError)
		return
	}
	note := &d.Note{
		Id:       ps.ByName("noteid"),
		Content:  request.Content,
		When:     time.Now(),
		Html:     request.Html,
		Markdown: request.Markdown,
		Mono:     request.Mono,
	}
	noteParms := &d.NoteParms{
		Bookid: bookid,
		Tabid:  ps.ByName("tabid"),
		Note:   note,
	}
	resultChan = d.Data("changeNote", noteParms)
	result = <-resultChan
	w.Write([]byte("Update Successful"))

	lib.Trace(2, "handler:changeNote end")
}
Exemple #3
0
func viewBook(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	lib.Trace(2, "handler:viewBook start")
	// get book
	book := &d.Book{BookName: ps.ByName("bookname")}
	resultChan := d.Data("getBook", book)
	result := <-resultChan
	if result.Status != d.DataOk { // resultVal contains errMsg
		http.Error(w, result.Val.(string), http.StatusBadRequest)
		return
	}
	// get book tabs
	bookTabs := &d.GetBookTabs{Bookid: book.Id} // see data/getbooktabs.go
	resultChan = d.Data("getBookTabs", bookTabs)
	result = <-resultChan

	// build response
	response := &loginResponse{
		Bookid:    book.Id,
		BookName:  book.BookName,
		Tabs:      bookTabs.TabInfoMap,
		Broadcast: getBroadcast(),
	}
	// add Login
	login := &d.Login{Bookid: book.Id, Type: "view"}
	resultChan = d.Data("addLogin", login)
	result = <-resultChan
	response.Token = login.Token

	jsonResponseWriter(w, r, response)

	lib.Trace(2, "handler:viewBook end")
}
Exemple #4
0
// jsonResponseWriter - convert response data to json, gzip if allowed, write to http.ResponseWriter
func jsonResponseWriter(w http.ResponseWriter, r *http.Request, data interface{}) error {
	var err error
	var jsonData []byte
	if jsonData, err = json.Marshal(data); err != nil {
		return err
	}
	w.Header().Set("Content-Type", "application/json")

	gzipOk := strings.Contains(fmt.Sprint(r.Header["Accept-Encoding"]), "gzip")
	if gzipOk {
		w.Header().Set("Content-Encoding", "gzip")
		compressor := common.GzipWriterPool.Get().(*gzip.Writer)
		compressor.Reset(w)
		_, err = compressor.Write(jsonData)
		compressor.Close()
		common.GzipWriterPool.Put(compressor)
		lib.Trace(3, "json response gzip")
	} else {
		_, err = w.Write(jsonData)
	}
	if err != nil {
		lib.Trace(0, "jsonResponseWriter error: ", err.Error())
	}
	return err
}
Exemple #5
0
// handle GET(/tabnotes/:token/:bookid/:tabid)
func getTabNotes(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	lib.Trace(2, "handler:getTabNotes start")
	var result d.Result
	bookid := ps.ByName("bookid")
	login := &d.Login{Token: ps.ByName("token"), Bookid: bookid, Type: "view"}
	resultChan := d.Data("auth", login)
	if result = <-resultChan; result.Status != d.DataOk {
		http.Error(w, result.Val.(string), http.StatusInternalServerError)
		return
	}
	// get tab notes
	tabNotes := &d.GetTabNotes{
		Bookid: bookid,
		Tabid:  ps.ByName("tabid"),
		ETag:   r.Header.Get("If-None-Match"),
	} // see data/gettabnotes.go
	lib.Trace(1, "Request ETag =", tabNotes.ETag)
	resultChan = d.Data("getTabNotes", tabNotes)
	result = <-resultChan
	if result.Status == d.DataNotChanged {
		w.WriteHeader(http.StatusNotModified)
	} else {
		w.Header().Set("Etag", tabNotes.ETag)
		jsonResponseWriter(w, r, tabNotes.Notes) // Notes = map[string]*d.NoteResponseRec
	}
	lib.Trace(2, "handler:getTabNotes end", len(tabNotes.Notes))
}
Exemple #6
0
func dataDispatch() {
	var req request
	for {
		req = <-requestChan
		req.data.process(req.action, req.resultChan)
		lib.Trace(2, "data request done")
		if req.action == "shutdown" {
			lib.Trace(0, "data dispatch stopped")
			break
		}
	}
}
Exemple #7
0
func dbWriteDispatch() {
	var req request
	for {
		req = <-dbWriteChan
		lib.Trace(2, "dbWrite Request", req.action)
		req.data.process(req.action, req.resultChan)
		lib.Trace(2, "dbWrite Request done")
		if req.action == "shutdowndb" {
			lib.Trace(0, "dbWrite stopped")
			break
		}
	}
}
Exemple #8
0
// handles requests with URL = /shutdown/:keyword
func shutdown(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if ps.ByName("keyword") != "downboy" {
		http.Error(w, "Sorry Charlie", http.StatusInternalServerError)
		lib.Trace(0, "shutdown - invalid keyword")
		return
	}
	w.Write([]byte("shutdown request received"))
	lib.Trace(0, "shutdown request received")
	resultChan := d.Data("shutdown", &d.SimpleRequest{nil})
	<-resultChan
	time.Sleep(3 * time.Second) // wait for Trace buffer to clear
	os.Exit(0)
}
Exemple #9
0
func createBook(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	lib.Trace(2, "handler:createBook start")
	request := new(loginRequest)
	if decodeErr := json.NewDecoder(r.Body).Decode(request); decodeErr != nil {
		http.Error(w, "Program Error: loginRequest json decode", http.StatusInternalServerError)
		return
	}
	// add book
	book := &d.Book{
		BookName:   ps.ByName("bookname"),
		AccessCode: request.AccessCode,
	}
	resultChan := d.Data("addBook", book)
	result := <-resultChan
	if result.Status != d.DataOk { // resultVal contains errMsg
		http.Error(w, result.Val.(string), http.StatusBadRequest)
		return
	}
	// add tab
	tab := &d.Tab{
		Bookid:    book.Id,
		TabNumber: 1,
		TabName:   "About",
		Hidden:    false,
	}
	resultChan = d.Data("addTab", tab)
	result = <-resultChan

	// get book tabs (will only be the 1 just added)
	bookTabs := &d.GetBookTabs{Bookid: book.Id} // see data/getbooktabs.go
	resultChan = d.Data("getBookTabs", bookTabs)
	result = <-resultChan

	// build response
	response := &loginResponse{
		Bookid:    book.Id,
		BookName:  book.BookName,
		Tabs:      bookTabs.TabInfoMap,
		Broadcast: getBroadcast(),
	}
	// add Login
	login := &d.Login{Bookid: book.Id, Type: "edit"}
	resultChan = d.Data("addLogin", login)
	result = <-resultChan
	response.Token = login.Token

	jsonResponseWriter(w, r, response)

	lib.Trace(2, "handler:createBook end")
}
Exemple #10
0
func Scheduler() {
	schedule := make([]*item, 0, 10)

	schedule = append(schedule, &item{
		name:     "backup database",
		whenType: atTime,
		when:     3, // run @ 3 am
		run:      backupDatabase,
	})
	/*
		schedule = append(schedule, &item{
			name:     "audit",
			whenType: atTime,
			when:     4, // run @ 4 am
			run:      audit,
		})
		schedule = append(schedule, &item{
			name:     "remove old logins",
			whenType: onInterval,
			when:     30, // run every 30 minutes
			run:      removeOldLogins,
		})
	*/
	for {
		time.Sleep(10 * time.Minute)
		now := time.Now()
		lib.Trace(0, "--- Scheduled Tasks ---")
		for _, task := range schedule {
			switch task.whenType {
			case atTime:
				if now.Hour() >= task.when && now.Day() != task.lastRun.Day() {
					lib.Trace(0, "running ", task.name)
					task.lastRun = time.Now()
					task.run()
				}
			case onInterval:
				minsSinceLastRun := now.Sub(task.lastRun) / time.Minute
				if minsSinceLastRun >= time.Duration(task.when) {
					lib.Trace(0, "running ", task.name)
					task.lastRun = time.Now()
					task.run()
				}
			default:
				log.Fatal("invalid schedule whenType - ", task.whenType)
			}
		}
	}
}
Exemple #11
0
func DataStart(dbName string) {
	lib.Trace(0, "Data Start Begin")

	dbStart(dbName) // see db.go

	bookMap = make(map[string]*Book)
	bookTabs = make(map[string]tabMap)

	startupDataLoad()

	requestChan = make(chan request, 100)

	go dataDispatch()

	lib.Trace(0, "Data Start Complete")
}
Exemple #12
0
func etagMatch(r *http.Request, serverETag string) bool {
	requestETag := r.Header.Get("If-None-Match")
	lib.Trace(2, "requestEtag:", requestETag, ",  serverETag:", serverETag)
	if requestETag == serverETag {
		return true
	}
	return false
}
Exemple #13
0
func DBBkup(path string) {
	db.View(func(tx *bolt.Tx) error {
		err := tx.CopyFile(path, 0600)
		if err != nil {
			lib.Trace(0, "*** ERROR *** DB Bkup Error: ", err.Error())
		}
		return err
	})
}
Exemple #14
0
func dbStart(dbFile string) {
	lib.Trace(0, "dbStart Begin")
	var err error
	db, err = bolt.Open(dbFile, 0600, nil)
	if err != nil {
		log.Fatal("db open fail", err)
	}
	dbWriteChan = make(chan request, 100)

	go dbWriteDispatch()

	// if 1st run for db, following will create bkts for startup
	db.Update(func(tx *bolt.Tx) error {
		_, err1 := tx.CreateBucketIfNotExists(bs("books"))
		_, err2 := tx.CreateBucketIfNotExists(bs("control"))
		return lib.CheckErrs(errFatal, "db startup error", err1, err2)
	})
	lib.Trace(0, "dbStart Complete")
}
Exemple #15
0
func startupDataLoad() {
	lib.Trace(0, "startupDataLoad Begin")
	db.Update(func(tx *bolt.Tx) error {
		// load all book docs into bookMap
		bktBooks := openBooks(tx)
		cursor := bktBooks.Cursor()
		for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
			book := new(Book)
			if err := json.Unmarshal(v, book); err != nil {
				log.Fatal("startupDataLoad json Unmarshal failed", err)
			}
			lib.Trace(0, book.BookName, book.AccessCode)
			bookMap[book.Id] = book
		}
		bktControl := openControl(tx)
		sequentialid, _ = bktControl.NextSequence()
		return nil
	})
	lib.Trace(0, "startupDataLoad End, sequentialid =", sequentialid)
}
Exemple #16
0
func (this *SimpleRequest) process(action string, resultChan chan Result) {
	switch action {
	case "shutdown":
		dbWrite("shutdowndb", this, resultChan) // clear db write buffer

	case "shutdowndb":
		db.Close()
		lib.Trace(0, "db closed")
		resultChan <- Result{DataOk, nil}
		log.Println("shutdown done")
	}
}
Exemple #17
0
// WebStart - setup handlers and start ListenAndServ
func WebStart(httpPort string) {
	lib.Trace(0, "WebStart")
	loadIndexHtml()
	gzipCodeJS()

	router := httprouter.New()

	router.GET("/book/:bookname", returnIndexHtml) // user specified URL
	router.GET("/code.js", returnCodeJS)           // all js code, gzipped

	// handlers in bookhandlers.go
	router.POST("/open/:bookname", openBook)
	router.GET("/view/:bookname", viewBook)
	router.POST("/create/:bookname", createBook)

	// handlers in tabhandlers.go
	router.POST("/tab/:token/:bookid", addTab)
	router.PUT("/tab/:token/:bookid/:tabid", changeTab)
	router.GET("/tabnotes/:token/:bookid/:tabid", getTabNotes)

	// handlers in notehandlers.go
	router.POST("/note/:token/:bookid/:tabid", addNote)
	router.PUT("/note/:token/:bookid/:tabid/:noteid", changeNote)
	router.PUT("/positionnote/:token/:bookid/:tabid/:noteid", positionNote)
	router.DELETE("/note/:token/:bookid/:tabid/:noteid", deleteNote)

	// handlers in specialhandlers.go
	router.GET("/accesscode", getAccessCode)
	router.GET("/shutdown/:keyword", shutdown)

	router.NotFound = http.FileServer(http.Dir("static"))

	lib.Trace(0, "handlers assigned - listening on ", httpPort)

	if httpPort == ":https" {
		log.Fatal(http.ListenAndServeTLS(httpPort, "tls/cert.pem", "tls/key.pem", router))
	} else {
		log.Fatal(http.ListenAndServe(httpPort, router))
	}
}
Exemple #18
0
// 	handle DELETE(/note/:token/:bookid/:tabid/:noteid)
func deleteNote(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	lib.Trace(2, "handler:deleteNote start")
	var result d.Result
	bookid := ps.ByName("bookid")
	login := &d.Login{Token: ps.ByName("token"), Bookid: bookid, Type: "edit"}
	resultChan := d.Data("auth", login)
	if result = <-resultChan; result.Status != d.DataOk {
		http.Error(w, result.Val.(string), http.StatusInternalServerError)
		return
	}
	note := &d.Note{Id: ps.ByName("noteid")}
	noteParms := &d.NoteParms{
		Bookid: bookid,
		Tabid:  ps.ByName("tabid"),
		Note:   note,
	}
	resultChan = d.Data("deleteNote", noteParms)
	result = <-resultChan
	w.Write([]byte("Update Successful"))

	lib.Trace(2, "handler:deleteNote end")
}
Exemple #19
0
func (this *GetTabNotes) process(action string, resultChan chan Result) {
	tab := bookTabs[this.Bookid][this.Tabid]
	lib.Trace(1, "Tab ETag =", tab.ETag)
	if tab.ETag == this.ETag {
		resultChan <- Result{DataNotChanged, nil}
		return
	}
	this.ETag = tab.ETag // loaded into response header
	this.Notes = make(map[string]*NoteResponseRec)
	previds := tabPrevids[this.Tabid] // tabPrevids map kept updated, so db is not accessed
	db.View(func(tx *bolt.Tx) error {
		bktBook := openBook(tx, this.Bookid)
		bktTab := openTab(bktBook, this.Tabid)
		bktNotes := openNotes(bktTab)
		if bktNotes == nil {
			log.Fatal("GetTabNotes bkt problem (bookid, tabid)", this.Bookid, this.Tabid)
		}
		cursor := bktNotes.Cursor()
		for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
			note := new(Note)
			if err := json.Unmarshal(v, note); err != nil {
				log.Fatal("GetTabNotes json Unmarshal failed", err)
			}
			dte := date.TimeToSimpleDate(note.When)
			this.Notes[note.Id] = &NoteResponseRec{
				Content:  note.Content,
				When:     date.SimpleDateToArray(dte),
				Mono:     note.Mono,
				Html:     note.Html,
				Markdown: note.Markdown,
				Previd:   previds[note.Id],
			}
		}
		return nil
	})
	resultChan <- Result{DataOk, nil}
}
Exemple #20
0
// Data - called by web handlers to access data
//	places data request on requestChan which is processed by dataDispatch goroutine (see above)
//  requester receives unique result channel
//	data.Processor method is sent this channel which places result on it
func Data(action string, data processor) chan Result {
	lib.Trace(2, "data request received", action)
	resultChan := make(chan Result)
	requestChan <- request{action, data, resultChan}
	return resultChan
}
Exemple #21
0
func (this *Book) process(action string, resultChan chan Result) {
	switch action {

	// getBook options:
	//		if Id is loaded, match on Id
	//		if AccessCode is loaded, match on AccessCode & BookName
	//		if only BookName is loaded, match on BookName

	case "getBook": // all book docs loaded into bookMap at startup
		var book *Book
		if this.Id != "" { // match on book id
			book = bookMap[this.Id]
		} else if this.AccessCode != "" { // match on AccessCode and BookName
			for _, v := range bookMap {
				if v.AccessCode == this.AccessCode {
					if v.PlainBookName == lib.PlainString(this.BookName) {
						book = v
						break
					}
				}
			}
		} else { // match on BookName
			matchBookName := lib.PlainString(this.BookName)
			for _, v := range bookMap {
				if v.PlainBookName == matchBookName {
					book = v
					break
				}
			}
		}
		if book != nil {
			this.Id = book.Id
			this.BookName = book.BookName
			this.Email = book.Email
			this.AccessCode = book.AccessCode
			this.PlainBookName = book.PlainBookName
			resultChan <- Result{DataOk, nil}
		} else {
			resultChan <- Result{DataNotFound, "Book Not Found"}
		}

	case "addBook":
		// check for dupe book name
		newName := lib.PlainString(this.BookName)
		for _, v := range bookMap {
			if newName == v.PlainBookName {
				resultChan <- Result{DataDuplicate, "Duplicate - Notebook Name Already Exists"}
				lib.Trace(0, "dupe book name", newName)
				return
			}
		}
		this.Id = getNextid()
		this.PlainBookName = newName
		bookMap[this.Id] = this
		bookTabs[this.Id] = make(tabMap)
		dbWrite("saveNewBook", this, resultChan) // save new Book to db

	case "saveNewBook":
		var err, err1, err2, err3 error
		var k, v []byte

		tx, _ := db.Begin(true)
		bumpSequentialid(tx) // required for all db adds

		// --- save book to "books" bkt -----------------------
		bktBooks := openBooks(tx)
		k = bs(this.Id)
		v, err1 = json.Marshal(this)
		err2 = bktBooks.Put(k, v)

		// --- create bkt to hold this book's data & Commit ---
		tx.CreateBucket(bs("book_" + this.Id))
		err3 = tx.Commit()
		if err = lib.CheckErrs(errNotFatal, action, err1, err2, err3); err != nil {
			tx.Rollback()
			log.Fatal("Fatal DB Error-creating book bkts, Ending Program")
		}

		// --- create "tabs" bkt inside this book's bkt & Commit ---
		tx, _ = db.Begin(true)
		bktBook := openBook(tx, this.Id)
		_, err1 = bktBook.CreateBucket(bs("tabs"))
		err2 = tx.Commit()
		if err = lib.CheckErrs(errNotFatal, action, err1, err2); err != nil {
			tx.Rollback()
			log.Fatal("Fatal DB Error-creating tabs bkt, Ending Program")
		}
		resultChan <- Result{DataOk, this.Id}

	case "saveBookChange": // not currently used
		db.Update(func(tx *bolt.Tx) error {
			bktBooks := openBooks(tx)
			k := bs(this.Id)
			v, _ := json.Marshal(this)
			err := bktBooks.Put(k, v)
			if err != nil {
				log.Fatal("book not changed", err)
			}
			return nil
		})

	default:
		log.Fatal("Book.process action invalid", action)
	}
}