|
- package main
-
- import (
- "biukop/sfm/loan"
- "database/sql"
- "encoding/json"
- "fmt"
- "github.com/brianvoe/gofakeit/v6"
- log "github.com/sirupsen/logrus"
- "net/http"
- "net/http/httputil"
- "strconv"
- "strings"
- "time"
- )
-
- const apiV1Prefix = "/api/v1/"
-
- type apiV1HandlerMap struct {
- Method string
- Path string //regex
- Handler func(http.ResponseWriter, *http.Request, *loan.Session)
- }
-
- var apiV1Handler = []apiV1HandlerMap{
- {"POST", "login", apiV1Login},
- {"GET", "login", apiV1DumpRequest},
- }
-
- //apiV1Main version 1 main entry for all REST API
- //
- func apiV1Main(w http.ResponseWriter, r *http.Request) {
- logRequestDebug(httputil.DumpRequest(r, true))
- w.Header().Set("Content-Type", "application/json;charset=UTF-8")
-
- //track browser, and take session from cookie
- session := loan.Session{}
- apiV1InitSessionByBrowserId(w, r, &session)
-
- //try session login first, if not an empty session will be created
- e := apiV1InitSessionByHttpHeader(r, &session)
- if e != nil {
- log.Warnf("Fail to InitSession %+v", session)
- apiV1Client403Error(w, r)
- return
- }
- session.RenewIfExpireSoon()
- session.SetRemote(r) //make sure they are using latest remote
-
- //we have a session now, either guest or valid user
- //search through handler
- path := r.URL.Path[len(apiV1Prefix):] //strip API prefix
- for _, node := range apiV1Handler {
- if r.Method == node.Method && path == node.Path {
- node.Handler(w, r, &session)
- e = session.Write() //finish this session to DB
- if e != nil {
- log.Warnf("Failed to Save Session %+v \n reason \n%s\n", session, e.Error())
- }
- return
- }
- }
-
- //Catch for all
- e = session.Write() //finish this session to DB
- apiV1DumpRequest(w, r, &session)
- }
-
- func apiV1InitSessionByBrowserId(w http.ResponseWriter, r *http.Request, session *loan.Session) {
- var mid string
- inCookie, e := r.Cookie("mid")
- if e == nil {
- mid = inCookie.Value
- } else {
- mid = strconv.Itoa(int(time.Now().Unix())) + "-" + gofakeit.UUID()
- }
-
- var sid string
- inCookie, e = r.Cookie("session")
- if e == nil {
- sid = inCookie.Value
- if sid != "" {
- e = session.Read(sid)
- if e == nil {
- if mid != session.Get("mid") {
- session.MarkEmpty()
- }
- }
- } else { //create a new session
- session.InitGuest(time.Now().Add(loan.DefaultSessionDuration))
- session.Add("mid", mid)
- }
- }
-
- //add tracking cookie
- expiration := time.Now().Add(365 * 24 * time.Hour)
- cookie := http.Cookie{Name: "session", Value: session.Id, Expires: expiration}
- http.SetCookie(w, &cookie)
-
- cookie = http.Cookie{Name: "mid", Value: mid, Expires: expiration}
- http.SetCookie(w, &cookie)
-
- }
-
- func apiV1InitSessionByHttpHeader(r *http.Request, ss *loan.Session) (e error) {
- sid := r.Header.Get("Biukop-Session")
-
- //make sure session id is given
- if sid != "" {
- //Try to retrieve a copy of DB session
- trial := loan.Session{}
- e = trial.Retrieve(r)
- if e == nil { //we got existing session from DB
- e = trial.ValidateRequest(r)
- if e != nil { // db session does not match request
- log.Warnf("failed session login %+v, %s", ss, time.Now().Format(time.RFC1123))
- ss.InitGuest(time.Now().Add(loan.DefaultSessionDuration))
- e = nil
- } else { //else, we have logged this user
- *ss = trial //overwrite the incoming session
- }
- } else if e == sql.ErrNoRows { //db does not have required session.
- log.Warn("DB has no corresponding session ", sid)
- } else { // retrieve has error
- log.Warnf("Retrieve Session %s encountered error %s", sid, e.Error())
- }
- }
-
- //if there is not session incoming
- if ss.IsEmpty() { //cookie may have initialized a session
- ss.InitGuest(time.Now().Add(loan.DefaultSessionDuration))
- e = nil //we try to init an empty one
- }
-
- return
- }
-
- func apiV1ErrorCheck(e error) {
- if nil != e {
- panic(e.Error()) //TODO: detailed error check, truck all caller
- }
- }
-
- func apiV1Server500Error(w http.ResponseWriter, r *http.Request) {
-
- w.WriteHeader(500)
- fmt.Fprintf(w, "Server Internal Error "+time.Now().Format(time.RFC1123))
-
- //write log
- dump := logRequestDebug(httputil.DumpRequest(r, true))
- dump = strings.TrimSpace(dump)
- log.Warnf("Unhandled Protocol = %s path= %s", r.Method, r.URL.Path)
- }
-
- func apiV1Client403Error(w http.ResponseWriter, r *http.Request) {
- w.WriteHeader(403)
- type struct403 struct {
- Error int
- ErrorMsg string
- }
- e403 := struct403{Error: 403, ErrorMsg: "Not Authorized " + time.Now().Format(time.RFC1123)}
- msg403, _ := json.Marshal(e403)
- fmt.Fprintln(w, string(msg403))
-
- //write log
- dump := logRequestDebug(httputil.DumpRequest(r, true))
- dump = strings.TrimSpace(dump)
- log.Warnf("Not authorized http(%s) path= %s, %s", r.Method, r.URL.Path, dump)
- }
|