first commit
This commit is contained in:
64
server/authentication.go
Normal file
64
server/authentication.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"opensavecloudserver/authentication"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Credential struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
func Login(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
credential := new(Credential)
|
||||
err = json.Unmarshal(body, credential)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
token, err := authentication.Connect(credential.Username, credential.Password)
|
||||
if err != nil {
|
||||
unauthorized(w, r)
|
||||
return
|
||||
}
|
||||
ok(token, w, r)
|
||||
}
|
||||
|
||||
func Register(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
registration := new(authentication.Registration)
|
||||
err = json.Unmarshal(body, registration)
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
err = authentication.Register(registration)
|
||||
if err != nil {
|
||||
badRequest(err.Error(), w, r)
|
||||
return
|
||||
}
|
||||
payload := successMessage{
|
||||
Message: "You are now registered",
|
||||
Timestamp: time.Now(),
|
||||
Status: 200,
|
||||
}
|
||||
ok(payload, w, r)
|
||||
}
|
||||
1
server/data.go
Normal file
1
server/data.go
Normal file
@@ -0,0 +1 @@
|
||||
package server
|
||||
98
server/response.go
Normal file
98
server/response.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type httpError struct {
|
||||
Status int `json:"status"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Error string `json:"error"`
|
||||
Message string `json:"message"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type successMessage struct {
|
||||
Status int `json:"status"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func internalServerError(w http.ResponseWriter, r *http.Request) {
|
||||
e := httpError{
|
||||
Status: 500,
|
||||
Error: "Internal Server Error",
|
||||
Message: "The server encountered an unexpected condition that prevented it from fulfilling the request.",
|
||||
Path: r.RequestURI,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
payload, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(500)
|
||||
_, err = w.Write(payload)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func unauthorized(w http.ResponseWriter, r *http.Request) {
|
||||
e := httpError{
|
||||
Status: 401,
|
||||
Error: "Unauthorized",
|
||||
Message: "The request has not been completed because it lacks valid authentication credentials for the requested resource.",
|
||||
Path: r.RequestURI,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
payload, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.Header().Add("WWW-Authenticate", "Custom realm=\"Login via /api/login\"")
|
||||
w.WriteHeader(401)
|
||||
_, err = w.Write(payload)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func ok(obj interface{}, w http.ResponseWriter, _ *http.Request) {
|
||||
payload, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
_, err = w.Write(payload)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func badRequest(message string, w http.ResponseWriter, r *http.Request) {
|
||||
e := httpError{
|
||||
Status: 400,
|
||||
Error: "Bad Request",
|
||||
Message: message,
|
||||
Path: r.RequestURI,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
payload, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(400)
|
||||
_, err = w.Write(payload)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
78
server/server.go
Normal file
78
server/server.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"log"
|
||||
"net/http"
|
||||
"opensavecloudserver/authentication"
|
||||
"opensavecloudserver/config"
|
||||
)
|
||||
|
||||
type ContextKey string
|
||||
|
||||
const UserIdKey ContextKey = "userId"
|
||||
|
||||
// Serve start the http server
|
||||
func Serve() {
|
||||
router := chi.NewRouter()
|
||||
router.Use(middleware.Logger)
|
||||
router.Use(recovery)
|
||||
router.Route("/api", func(r chi.Router) {
|
||||
r.Post("/login", Login)
|
||||
if config.Features().AllowRegister {
|
||||
r.Post("/register", Register)
|
||||
}
|
||||
r.Route("/system", func(systemRouter chi.Router) {
|
||||
systemRouter.Get("/information", Information)
|
||||
})
|
||||
r.Group(func(secureRouter chi.Router) {
|
||||
secureRouter.Use(authMiddleware)
|
||||
})
|
||||
})
|
||||
log.Println("Server is listening...")
|
||||
err := http.ListenAndServe(":8080", router)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// authMiddleware filter the request
|
||||
func authMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
header := r.Header.Get("Authorization")
|
||||
if len(header) > 7 {
|
||||
userId, err := authentication.ParseToken(header[7:])
|
||||
if err != nil {
|
||||
unauthorized(w, r)
|
||||
return
|
||||
}
|
||||
ctx := context.WithValue(r.Context(), UserIdKey, userId)
|
||||
r = r.WithContext(ctx)
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
unauthorized(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func userIdFromContext(ctx context.Context) (int, error) {
|
||||
if userId, ok := ctx.Value(UserIdKey).(int); ok {
|
||||
return userId, nil
|
||||
}
|
||||
return 0, errors.New("userId not found in context")
|
||||
}
|
||||
|
||||
func recovery(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err != nil {
|
||||
internalServerError(w, r)
|
||||
}
|
||||
}()
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
29
server/system.go
Normal file
29
server/system.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"opensavecloudserver/config"
|
||||
"opensavecloudserver/constant"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type information struct {
|
||||
AllowRegister bool `json:"allow_register"`
|
||||
Version string `json:"version"`
|
||||
ApiVersion int `json:"api_version"`
|
||||
GoVersion string `json:"go_version"`
|
||||
OsName string `json:"os_name"`
|
||||
OsArchitecture string `json:"os_architecture"`
|
||||
}
|
||||
|
||||
func Information(w http.ResponseWriter, r *http.Request) {
|
||||
info := information{
|
||||
AllowRegister: config.Features().AllowRegister,
|
||||
Version: constant.Version,
|
||||
ApiVersion: constant.ApiVersion,
|
||||
GoVersion: runtime.Version(),
|
||||
OsName: runtime.GOOS,
|
||||
OsArchitecture: runtime.GOARCH,
|
||||
}
|
||||
ok(info, w, r)
|
||||
}
|
||||
Reference in New Issue
Block a user