commit 3e3b9a8c61ec0f39d17bc5b8fd56fbe90217174d Author: Aurélie DELHAIE Date: Mon Apr 8 17:17:57 2024 +0000 first commit diff --git a/cmd/server/api/api.go b/cmd/server/api/api.go new file mode 100644 index 0000000..3c23c94 --- /dev/null +++ b/cmd/server/api/api.go @@ -0,0 +1,35 @@ +package api + +import ( + "fmt" + "net/http" + + "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" +) + +type ( + Server struct { + handler http.Handler + services *Services + } + + Services struct { + } +) + +func New(services *Services) *Server { + r := chi.NewRouter() + r.Use(middleware.RequestID) + r.Use(middleware.Logger) + r.Use(middleware.Recoverer) + + return &Server{ + handler: r, + services: services, + } +} + +func (s *Server) ListenAndServe(port uint16) error { + return http.ListenAndServe(fmt.Sprintf(":%d", port), s.handler) +} diff --git a/cmd/server/main.go b/cmd/server/main.go new file mode 100644 index 0000000..78e541e --- /dev/null +++ b/cmd/server/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "cloudsync/cmd/server/api" + "cloudsync/pkg/data" + "fmt" + "log" + "runtime" +) + +const version = "0.1.0-alpha" + +func main() { + fmt.Printf(" -- CloudSync server | Version %s (%s/%s) --\n\n", version, runtime.GOOS, runtime.GOARCH) + + data.New("./") + server := api.New(nil) + log.Println("[INFO] starting server on port 8080") + err := server.ListenAndServe(8080) + if err != nil { + log.Fatalf("[PANIC] %s", err) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..60a3f72 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module cloudsync + +go 1.22 + +require ( + github.com/go-chi/chi/v5 v5.0.12 + github.com/mattn/go-sqlite3 v1.14.22 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..dac5808 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= +github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= diff --git a/pkg/data/data.go b/pkg/data/data.go new file mode 100644 index 0000000..c8e5848 --- /dev/null +++ b/pkg/data/data.go @@ -0,0 +1,58 @@ +package data + +import ( + "database/sql" + "errors" + "log" + "path/filepath" + + _ "github.com/mattn/go-sqlite3" +) + +type ( + Repository struct { + path string + conn *sql.DB + } +) + +func New(path string) *Repository { + dbPath := filepath.Join(path, "system.db") + conn, err := sql.Open("sqlite3", dbPath) + if err != nil { + log.Fatal(err) + } + + r := &Repository{ + path: path, + conn: conn, + } + + if err := r.check(); err != nil { + log.Fatalf("[PANIC] %s", err) + } + return r +} + +func (r *Repository) Close() error { + return r.conn.Close() +} + +func (r *Repository) check() error { + rows, err := r.conn.Query("SELECT value FROM system WHERE key = 'db.version'") + if err != nil { + m := new(migratorVersion1) + m.conn = r.conn + return m.create() + } + defer rows.Close() + var version string + if !rows.Next() { + return errors.New("no version found in the system database") + } + if err := rows.Scan(&version); err != nil { + return err + } + log.Printf("[INFO] database loaded with version %s", version) + return nil +} diff --git a/pkg/data/db_struct_v1.sql b/pkg/data/db_struct_v1.sql new file mode 100644 index 0000000..3676943 --- /dev/null +++ b/pkg/data/db_struct_v1.sql @@ -0,0 +1,40 @@ +CREATE TABLE system ( + key string primary key, + value string +); + +CREATE TABLE users ( + uuid string primary key, + username string, + password string +); + +CREATE TABLE roles ( + uuid string primary key, + descriptor string +); + +CREATE TABLE role_permissions ( + uuid string primary key, + role string, + system_permission string +); + +CREATE TABLE user_roles ( + uuid string primary key, + role string, + user string +); + +CREATE TABLE saves ( + uuid string primary key, + name string, + user string, + path string +); + +INSERT INTO system (key, value) VALUES ('db.version', '1'); +INSERT INTO users (uuid, username) VALUES ('4b5b9489-973c-44e7-bae0-5ab23b56abe7', 'root'); +INSERT INTO roles (uuid, descriptor) VALUES ('4b5b9489-973c-44e7-bae0-5ab23b56abe7', 'root'); +INSERT INTO role_permissions (uuid, role, system_permission) VALUES ('4b5b9489-973c-44e7-bae0-5ab23b56abe7', '4b5b9489-973c-44e7-bae0-5ab23b56abe7', '*.*'); +INSERT INTO user_roles (uuid, role, user) VALUES ('4b5b9489-973c-44e7-bae0-5ab23b56abe7', '4b5b9489-973c-44e7-bae0-5ab23b56abe7', '4b5b9489-973c-44e7-bae0-5ab23b56abe7'); diff --git a/pkg/data/migration.go b/pkg/data/migration.go new file mode 100644 index 0000000..03587ff --- /dev/null +++ b/pkg/data/migration.go @@ -0,0 +1,27 @@ +package data + +import ( + "database/sql" + _ "embed" + "log" +) + +//go:embed db_struct_v1.sql +var dbStructV1SQL string + +type ( + migratorVersion1 struct { + conn *sql.DB + } +) + +func (m *migratorVersion1) create() error { + log.Printf("[INFO] no database, creating system database structure...") + _, err := m.conn.Exec(dbStructV1SQL) + if err != nil { + return err + } + log.Printf("[INFO] root user and role inserted by default") + log.Println("[INFO] database created with success!") + return nil +} diff --git a/pkg/http/http.go b/pkg/http/http.go new file mode 100644 index 0000000..f745dfc --- /dev/null +++ b/pkg/http/http.go @@ -0,0 +1,101 @@ +package http + +import ( + "encoding/json" + "io" + "time" +) + +type ( + BaseResponse interface { + IsSuccess() bool + SentAt() time.Time + Copy(w io.Writer) error + } + Response struct { + status uint16 + timestamp time.Time + } + + Message struct { + Response + message string + } + + Object struct { + Response + data any + } +) + +func MakeResponse(status uint16) Response { + return Response{ + status: status, + timestamp: time.Now(), + } +} + +func MakeMessage(status uint16, message string) Message { + r := MakeResponse(status) + return Message{ + Response: r, + message: message, + } +} + +func MakeObject(status uint16, data any) Object { + r := MakeResponse(status) + return Object{ + Response: r, + data: data, + } +} + +func (r Response) IsSuccess() bool { + return r.status >= 200 && r.status < 300 +} + +func (r Response) SentAt() time.Time { + return r.timestamp +} + +func (r Response) Copy(w io.Writer) error { + encoder := json.NewEncoder(w) + err := encoder.Encode(r) + return err +} + +func (r Response) MarshalJSON() ([]byte, error) { + return json.Marshal(r.toMap()) +} + +func (r Response) toMap() map[string]any { + return map[string]any{ + "status": r.status, + "timestamp": r.timestamp, + } +} + +func (m Message) Copy(w io.Writer) error { + encoder := json.NewEncoder(w) + err := encoder.Encode(m) + return err +} + +func (m Message) MarshalJSON() ([]byte, error) { + mp := m.toMap() + mp["message"] = m.message + return json.Marshal(mp) +} + +func (o Object) Copy(w io.Writer) error { + encoder := json.NewEncoder(w) + err := encoder.Encode(o) + return err +} + +func (o Object) MarshalJSON() ([]byte, error) { + mp := o.toMap() + mp["data"] = o.data + return json.Marshal(mp) +} diff --git a/system.db b/system.db new file mode 100644 index 0000000..8d17ff5 Binary files /dev/null and b/system.db differ