From 3e3b9a8c61ec0f39d17bc5b8fd56fbe90217174d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lie=20DELHAIE?= Date: Mon, 8 Apr 2024 17:17:57 +0000 Subject: [PATCH] first commit --- cmd/server/api/api.go | 35 +++++++++++++ cmd/server/main.go | 23 +++++++++ go.mod | 8 +++ go.sum | 4 ++ pkg/data/data.go | 58 ++++++++++++++++++++++ pkg/data/db_struct_v1.sql | 40 +++++++++++++++ pkg/data/migration.go | 27 ++++++++++ pkg/http/http.go | 101 ++++++++++++++++++++++++++++++++++++++ system.db | Bin 0 -> 53248 bytes 9 files changed, 296 insertions(+) create mode 100644 cmd/server/api/api.go create mode 100644 cmd/server/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 pkg/data/data.go create mode 100644 pkg/data/db_struct_v1.sql create mode 100644 pkg/data/migration.go create mode 100644 pkg/http/http.go create mode 100644 system.db 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 0000000000000000000000000000000000000000..8d17ff5a3beeba01fc0bf749167239d2a9b04378 GIT binary patch literal 53248 zcmeI(Pfyce9Ki9XY;0p}l*EvRlif-AGa>>a2ZLyc7ZRhecrvqvs*8-xJ{^kjvWtlq z-h;1z_&W3i7v6@RJQ<(18)Lx8FaoiBn{Dl$c71-o&+}|a`^%P}ER{URd|7eJmS;|h zL7{2lzG(^}`qeY5p3NnpHsZ}UYONjG?zGu2CN@83^#4= zzcXL6`AjPFsndsPM*sl?5I_I{1Q0*~f%6gg*q<~EL#sdYEPLIN)}|wam(0Vbiwi4@ z=E}l@rA0GnGH<3$wbW{*HB)+SY2%eybxUQ--7?=eTjRmb4Xf-lc7_c#>9}nTRm*$b zXh@HYWD>?bLldQqHRrv2yRI%tidN051n-MMuf@q=mHp6{G;%quJ|6_?pSI{$RK%^V zbX&-+_HN?#Pt_LYp}5l{Q|W~9DAy^>R!_yr)^>k=IGHr^d9CgQ5&Jz9tBzYPNm;6F z$fNI5ZP6dy`&T*0E$KPsL!BPD&!-Z`a=ueSM>{W09&PD+(Hl;i>j$|73CYB&J!^2vAD2Oi{mGHHvEyA#^;ibM(jo~`}E!(u#Yt8AJ9vSON z81uuO2D32z^+5I_I{1Q0*~0R#|00D;H|WZSO)!}|ZO(03z~Ac-J= z00IagfB*srAb@HKV#Xqr|g+K zmhH^Cm5LYE|M!Kye+6hr5dj1cKmY**5I_I{1Q0*~frts*h!y&}xd#x||M!Hx7cl_R zK>z^+5I_I{1Q0*~0R#|0Ald?BTC6bC&9y)E|7cGHvO@p?1Q0*~0R#|0009ILh`zwt z>i;`J--*5n86toH0tg_000IagfB*srAn>0EJXU}1FI?-UxZlk&qZ6b2|NlQ3dBQ{h z0R#|0009ILKmY**5J2DrfwR^Bw}rla!bxaC009ILKmY**5I_I{1Q0*~fpZY3`sMvX zH>LZ3bqw|Ya~MWWi2wo!Abz^+5I_I{1Q0*~ p0R#|00D(&p@az9v|6d9{og#n$0tg_000IagfB*srAaG#zV)n literal 0 HcmV?d00001