Init simple Go React blog with Goxygen
This commit is contained in:
parent
634a24fe19
commit
1ada5abed1
31 changed files with 873 additions and 0 deletions
36
simple-blog/server/db/db.go
Normal file
36
simple-blog/server/db/db.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"simple-blog/model"
|
||||
)
|
||||
|
||||
type DB interface {
|
||||
GetTechnologies() ([]*model.Technology, error)
|
||||
}
|
||||
|
||||
type PostgresDB struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewDB(db *sql.DB) DB {
|
||||
return PostgresDB{db: db}
|
||||
}
|
||||
|
||||
func (d PostgresDB) GetTechnologies() ([]*model.Technology, error) {
|
||||
rows, err := d.db.Query("select name, details from technologies")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var tech []*model.Technology
|
||||
for rows.Next() {
|
||||
t := new(model.Technology)
|
||||
err = rows.Scan(&t.Name, &t.Details)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tech = append(tech, t)
|
||||
}
|
||||
return tech, nil
|
||||
}
|
5
simple-blog/server/go.mod
Normal file
5
simple-blog/server/go.mod
Normal file
|
@ -0,0 +1,5 @@
|
|||
module simple-blog
|
||||
|
||||
go 1.22
|
||||
|
||||
require github.com/lib/pq v1.10.9
|
2
simple-blog/server/go.sum
Normal file
2
simple-blog/server/go.sum
Normal file
|
@ -0,0 +1,2 @@
|
|||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
6
simple-blog/server/model/technology.go
Normal file
6
simple-blog/server/model/technology.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package model
|
||||
|
||||
type Technology struct {
|
||||
Name string `json:"name"`
|
||||
Details string `json:"details"`
|
||||
}
|
35
simple-blog/server/server.go
Normal file
35
simple-blog/server/server.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"os"
|
||||
"simple-blog/db"
|
||||
"simple-blog/web"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
func main() {
|
||||
d, err := sql.Open("postgres", dataSource())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer d.Close()
|
||||
// CORS is enabled only in prod profile
|
||||
cors := os.Getenv("profile") == "prod"
|
||||
app := web.NewApp(db.NewDB(d), cors)
|
||||
err = app.Serve()
|
||||
log.Println("Error", err)
|
||||
}
|
||||
|
||||
func dataSource() string {
|
||||
host := "localhost"
|
||||
pass := "pass"
|
||||
if os.Getenv("profile") == "prod" {
|
||||
host = "db"
|
||||
pass = os.Getenv("db_pass")
|
||||
}
|
||||
return "postgresql://" + host + ":5432/goxygen" +
|
||||
"?user=goxygen&sslmode=disable&password=" + pass
|
||||
}
|
63
simple-blog/server/web/app.go
Normal file
63
simple-blog/server/web/app.go
Normal file
|
@ -0,0 +1,63 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"simple-blog/db"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
d db.DB
|
||||
handlers map[string]http.HandlerFunc
|
||||
}
|
||||
|
||||
func NewApp(d db.DB, cors bool) App {
|
||||
app := App{
|
||||
d: d,
|
||||
handlers: make(map[string]http.HandlerFunc),
|
||||
}
|
||||
techHandler := app.GetTechnologies
|
||||
if !cors {
|
||||
techHandler = disableCors(techHandler)
|
||||
}
|
||||
app.handlers["/api/technologies"] = techHandler
|
||||
app.handlers["/"] = http.FileServer(http.Dir("/webapp")).ServeHTTP
|
||||
return app
|
||||
}
|
||||
|
||||
func (a *App) Serve() error {
|
||||
for path, handler := range a.handlers {
|
||||
http.Handle(path, handler)
|
||||
}
|
||||
log.Println("Web server is available on port 8080")
|
||||
return http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
|
||||
func (a *App) GetTechnologies(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
technologies, err := a.d.GetTechnologies()
|
||||
if err != nil {
|
||||
sendErr(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
err = json.NewEncoder(w).Encode(technologies)
|
||||
if err != nil {
|
||||
sendErr(w, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func sendErr(w http.ResponseWriter, code int, message string) {
|
||||
resp, _ := json.Marshal(map[string]string{"error": message})
|
||||
http.Error(w, string(resp), code)
|
||||
}
|
||||
|
||||
// Needed in order to disable CORS for local development
|
||||
func disableCors(h http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "*")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "*")
|
||||
h(w, r)
|
||||
}
|
||||
}
|
57
simple-blog/server/web/app_test.go
Normal file
57
simple-blog/server/web/app_test.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"simple-blog/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type MockDb struct {
|
||||
tech []*model.Technology
|
||||
err error
|
||||
}
|
||||
|
||||
func (m *MockDb) GetTechnologies() ([]*model.Technology, error) {
|
||||
return m.tech, m.err
|
||||
}
|
||||
|
||||
func TestApp_GetTechnologies(t *testing.T) {
|
||||
app := App{d: &MockDb{
|
||||
tech: []*model.Technology{
|
||||
{"Tech1", "Details1"},
|
||||
{"Tech2", "Details2"},
|
||||
},
|
||||
}}
|
||||
|
||||
r, _ := http.NewRequest("GET", "/api/technologies", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
app.GetTechnologies(w, r)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got %v want %v", w.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
want := `[{"name":"Tech1","details":"Details1"},{"name":"Tech2","details":"Details2"}]` + "\n"
|
||||
if got := w.Body.String(); got != want {
|
||||
t.Errorf("handler returned unexpected body: got %v want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApp_GetTechnologies_WithDBError(t *testing.T) {
|
||||
app := App{d: &MockDb{
|
||||
tech: nil,
|
||||
err: errors.New("unknown error"),
|
||||
}}
|
||||
|
||||
r, _ := http.NewRequest("GET", "/api/technologies", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
app.GetTechnologies(w, r)
|
||||
|
||||
if w.Code != http.StatusInternalServerError {
|
||||
t.Errorf("handler returned wrong status code: got %v want %v", w.Code, http.StatusOK)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue