Compare commits
4 Commits
732fbacc61
...
3a968df15b
Author | SHA1 | Date | |
---|---|---|---|
3a968df15b
|
|||
e8d8e8d70b
|
|||
25ee1d3299
|
|||
9d7ad260f2
|
@@ -47,6 +47,15 @@ func Start() {
|
||||
r.Post("/new", NewMessage)
|
||||
})
|
||||
|
||||
r.Route("/users", func(r chi.Router) {
|
||||
r.Get("/", ListUsers)
|
||||
r.Route("/{userID}", func(r chi.Router) {
|
||||
r.Use(UserCtx) // Load user
|
||||
r.Get("/", GetUser)
|
||||
})
|
||||
r.Post("/new", NewUser)
|
||||
})
|
||||
|
||||
if *routes {
|
||||
fmt.Println(docgen.MarkdownRoutesDoc(r, docgen.MarkdownOpts{
|
||||
ProjectPath: "git.dubyatp.xyz/chat-api-server",
|
||||
|
41
api/db.go
41
api/db.go
@@ -8,7 +8,7 @@ import (
|
||||
"git.dubyatp.xyz/chat-api-server/db"
|
||||
)
|
||||
|
||||
func dbGetUser(id int64) (*User, error) {
|
||||
func dbGetUser(id string) (*User, error) {
|
||||
data := db.ExecDB("users")
|
||||
if data == nil {
|
||||
return nil, errors.New("failed to load users database")
|
||||
@@ -17,9 +17,9 @@ func dbGetUser(id int64) (*User, error) {
|
||||
users := data["users"].([]interface{})
|
||||
for _, u := range users {
|
||||
user := u.(map[string]interface{})
|
||||
if int64(user["ID"].(float64)) == id {
|
||||
if user["ID"].(string) == id {
|
||||
return &User{
|
||||
ID: int64(user["ID"].(float64)),
|
||||
ID: user["ID"].(string),
|
||||
Name: user["Name"].(string),
|
||||
}, nil
|
||||
}
|
||||
@@ -27,6 +27,27 @@ func dbGetUser(id int64) (*User, error) {
|
||||
return nil, errors.New("User not found")
|
||||
}
|
||||
|
||||
func dbGetAllUsers() ([]*User, error) {
|
||||
data := db.ExecDB("users")
|
||||
if data == nil {
|
||||
return nil, errors.New("failed to load users database")
|
||||
}
|
||||
|
||||
users := data["users"].([]interface{})
|
||||
var result []*User
|
||||
for _, u := range users {
|
||||
user := u.(map[string]interface{})
|
||||
result = append(result, &User{
|
||||
ID: user["ID"].(string),
|
||||
Name: user["Name"].(string),
|
||||
})
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil, errors.New("no users found")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func dbGetMessage(id string) (*Message, error) {
|
||||
data := db.ExecDB("messages")
|
||||
if data == nil {
|
||||
@@ -52,7 +73,7 @@ func dbGetMessage(id string) (*Message, error) {
|
||||
}
|
||||
return &Message{
|
||||
ID: message["ID"].(string),
|
||||
UserID: int64(message["UserID"].(float64)),
|
||||
UserID: message["UserID"].(string),
|
||||
Body: message["Body"].(string),
|
||||
Timestamp: timestamp,
|
||||
Edited: edited,
|
||||
@@ -88,7 +109,7 @@ func dbGetAllMessages() ([]*Message, error) {
|
||||
}
|
||||
result = append(result, &Message{
|
||||
ID: message["ID"].(string),
|
||||
UserID: int64(message["UserID"].(float64)),
|
||||
UserID: message["UserID"].(string),
|
||||
Body: message["Body"].(string),
|
||||
Timestamp: timestamp,
|
||||
Edited: edited,
|
||||
@@ -100,7 +121,7 @@ func dbGetAllMessages() ([]*Message, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func dbAddUser(id int64, name string) error {
|
||||
func dbAddUser(user *User) error {
|
||||
currentData := db.ExecDB("users")
|
||||
if currentData == nil {
|
||||
return fmt.Errorf("error reading users database")
|
||||
@@ -111,12 +132,12 @@ func dbAddUser(id int64, name string) error {
|
||||
return fmt.Errorf("users data is in an unexpected format")
|
||||
}
|
||||
|
||||
user := map[string]interface{}{
|
||||
"ID": float64(id), // JSON numbers are float64 by default
|
||||
"Name": name,
|
||||
dbUser := map[string]interface{}{
|
||||
"ID": user.ID,
|
||||
"Name": user.Name,
|
||||
}
|
||||
|
||||
users = append(users, user)
|
||||
users = append(users, dbUser)
|
||||
return db.WriteDB("users", users)
|
||||
}
|
||||
|
||||
|
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
@@ -110,15 +109,14 @@ func newMessageID() string {
|
||||
}
|
||||
|
||||
func NewMessage(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseMultipartForm(32 << 20)
|
||||
err := r.ParseMultipartForm(64 << 10)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to parse form", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
userIDStr := r.FormValue("user_id")
|
||||
userID, err := strconv.ParseInt(userIDStr, 10, 64)
|
||||
if err != nil {
|
||||
userID := r.FormValue("user_id")
|
||||
if userID == "" {
|
||||
http.Error(w, "Invalid user ID", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@@ -150,7 +148,7 @@ type messageKey struct{}
|
||||
|
||||
type Message struct {
|
||||
ID string `json:"id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Body string `json:"body"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Edited time.Time `json:"edited"`
|
||||
@@ -175,7 +173,7 @@ type MessageResponse struct {
|
||||
func (m MessageResponse) MarshalJSON() ([]byte, error) {
|
||||
type OrderedMessageResponse struct {
|
||||
ID string `json:"id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Body string `json:"body"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Edited *string `json:"edited,omitempty"` // Use a pointer to allow null values
|
||||
|
@@ -30,6 +30,14 @@ func NewMessageListResponse(messages []*Message) []render.Renderer {
|
||||
return list
|
||||
}
|
||||
|
||||
func NewUserListResponse(users []*User) []render.Renderer {
|
||||
list := []render.Renderer{}
|
||||
for _, user := range users {
|
||||
list = append(list, NewUserPayloadResponse(user))
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func NewUserPayloadResponse(user *User) *UserPayload {
|
||||
return &UserPayload{User: user}
|
||||
}
|
||||
|
90
api/user.go
90
api/user.go
@@ -1,7 +1,95 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/render"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func UserCtx(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var user *User
|
||||
var err error
|
||||
|
||||
if userID := chi.URLParam(r, "userID"); userID != "" {
|
||||
user, err = dbGetUser(userID)
|
||||
} else {
|
||||
render.Render(w, r, ErrNotFound)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
render.Render(w, r, ErrNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.WithValue(r.Context(), userKey{}, user)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
func GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
user, ok := r.Context().Value(userKey{}).(*User)
|
||||
if !ok || user == nil {
|
||||
render.Render(w, r, ErrNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if err := render.Render(w, r, NewUserPayloadResponse(user)); err != nil {
|
||||
render.Render(w, r, ErrRender(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func ListUsers(w http.ResponseWriter, r *http.Request) {
|
||||
dbUsers, err := dbGetAllUsers()
|
||||
if err != nil {
|
||||
render.Render(w, r, ErrRender(err))
|
||||
return
|
||||
}
|
||||
if err := render.RenderList(w, r, NewUserListResponse(dbUsers)); err != nil {
|
||||
render.Render(w, r, ErrRender(err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func newUserID() string {
|
||||
return "user_" + uuid.New().String()
|
||||
}
|
||||
|
||||
func NewUser(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseMultipartForm(64 << 10)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to parse form", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
newUserName := r.FormValue("name")
|
||||
|
||||
newUser := User{
|
||||
ID: newUserID(),
|
||||
Name: newUserName,
|
||||
}
|
||||
|
||||
err = dbAddUser(&newUser)
|
||||
if err != nil {
|
||||
render.Render(w, r, ErrRender(err))
|
||||
return
|
||||
}
|
||||
|
||||
render.Render(w, r, NewUserPayloadResponse(&newUser))
|
||||
}
|
||||
|
||||
func (u *UserPayload) Render(w http.ResponseWriter, r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type userKey struct{}
|
||||
|
||||
type User struct {
|
||||
ID int64 `json:"id"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
|
@@ -53,10 +53,10 @@
|
||||
- **/{messageID}**
|
||||
- [MessageCtx]()
|
||||
- **/**
|
||||
- _DELETE_
|
||||
- [DeleteMessage]()
|
||||
- _GET_
|
||||
- [GetMessage]()
|
||||
- _DELETE_
|
||||
- [DeleteMessage]()
|
||||
|
||||
</details>
|
||||
<details>
|
||||
@@ -101,5 +101,50 @@
|
||||
- [Start.func2]()
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>`/users`</summary>
|
||||
|
||||
- [RequestID]()
|
||||
- [Logger]()
|
||||
- [Recoverer]()
|
||||
- [URLFormat]()
|
||||
- [SetContentType.func1]()
|
||||
- **/users**
|
||||
- **/**
|
||||
- _GET_
|
||||
- [ListUsers]()
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>`/users/new`</summary>
|
||||
|
||||
- [RequestID]()
|
||||
- [Logger]()
|
||||
- [Recoverer]()
|
||||
- [URLFormat]()
|
||||
- [SetContentType.func1]()
|
||||
- **/users**
|
||||
- **/new**
|
||||
- _POST_
|
||||
- [NewUser]()
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>`/users/{userID}`</summary>
|
||||
|
||||
- [RequestID]()
|
||||
- [Logger]()
|
||||
- [Recoverer]()
|
||||
- [URLFormat]()
|
||||
- [SetContentType.func1]()
|
||||
- **/users**
|
||||
- **/{userID}**
|
||||
- [UserCtx]()
|
||||
- **/**
|
||||
- _GET_
|
||||
- [GetUser]()
|
||||
|
||||
</details>
|
||||
|
||||
Total # of routes: 10
|
||||
|
||||
Total # of routes: 7
|
@@ -4,97 +4,97 @@
|
||||
"Edited": null,
|
||||
"ID": "1",
|
||||
"Timestamp": "2024-12-25T05:00:40Z",
|
||||
"UserID": 1
|
||||
"UserID": "user_8d7cd2ed-0aa2-4810-a172-42dd58563a54"
|
||||
},
|
||||
{
|
||||
"Body": "world",
|
||||
"Edited": null,
|
||||
"ID": "2",
|
||||
"Timestamp": "2024-12-25T05:00:43Z",
|
||||
"UserID": 2
|
||||
"UserID": "user_63dac6ad-f255-4af8-a057-4b064a982a84"
|
||||
},
|
||||
{
|
||||
"Body": "abababa",
|
||||
"Edited": null,
|
||||
"ID": "3",
|
||||
"Timestamp": "2024-12-25T05:01:20Z",
|
||||
"UserID": 1
|
||||
"UserID": "user_8d7cd2ed-0aa2-4810-a172-42dd58563a54"
|
||||
},
|
||||
{
|
||||
"Body": "bitch",
|
||||
"Edited": null,
|
||||
"ID": "4",
|
||||
"Timestamp": "2024-12-25T05:05:55Z",
|
||||
"UserID": 2
|
||||
"UserID": "user_63dac6ad-f255-4af8-a057-4b064a982a84"
|
||||
},
|
||||
{
|
||||
"Body": "NIBBA",
|
||||
"Edited": null,
|
||||
"ID": "5",
|
||||
"Timestamp": "2025-03-24T14:48:28.249221047-04:00",
|
||||
"UserID": 1
|
||||
"UserID": "user_8d7cd2ed-0aa2-4810-a172-42dd58563a54"
|
||||
},
|
||||
{
|
||||
"Body": "nibby",
|
||||
"Edited": null,
|
||||
"ID": "6",
|
||||
"Timestamp": "2025-03-24T14:49:03.246929039-04:00",
|
||||
"UserID": 1
|
||||
"UserID": "user_8d7cd2ed-0aa2-4810-a172-42dd58563a54"
|
||||
},
|
||||
{
|
||||
"Body": "aaaaababananana",
|
||||
"Edited": null,
|
||||
"ID": "msg_60f70a47-3be2-4315-869a-d6f151ec262a",
|
||||
"Timestamp": "2025-03-24T15:01:07.14371835-04:00",
|
||||
"UserID": 1
|
||||
"UserID": "user_8d7cd2ed-0aa2-4810-a172-42dd58563a54"
|
||||
},
|
||||
{
|
||||
"Body": "ababa abbott",
|
||||
"Edited": null,
|
||||
"ID": "msg_94cbc26d-9098-4fa9-bd21-794516c2263d",
|
||||
"Timestamp": "2025-03-24T20:34:57.198849367-04:00",
|
||||
"UserID": 1
|
||||
"UserID": "user_8d7cd2ed-0aa2-4810-a172-42dd58563a54"
|
||||
},
|
||||
{
|
||||
"Body": "AAAAAA",
|
||||
"Edited": null,
|
||||
"ID": "msg_ca8483db-e823-45c4-882c-fe0930610ba9",
|
||||
"Timestamp": "2025-03-24T21:17:04.350827576-04:00",
|
||||
"UserID": 1
|
||||
"UserID": "user_8d7cd2ed-0aa2-4810-a172-42dd58563a54"
|
||||
},
|
||||
{
|
||||
"Body": "i am a femboiiiii",
|
||||
"Edited": null,
|
||||
"ID": "msg_fcdbb48a-4ea5-4fb3-b925-3a15eb7c291c",
|
||||
"Timestamp": "2025-03-24T21:27:48.565290147-04:00",
|
||||
"UserID": 2
|
||||
"UserID": "user_63dac6ad-f255-4af8-a057-4b064a982a84"
|
||||
},
|
||||
{
|
||||
"Body": "i love soap",
|
||||
"Edited": "2025-03-27T14:49:14-04:00",
|
||||
"ID": "msg_59851eb1-2e63-46c1-b496-55566c414e33",
|
||||
"Timestamp": "2025-03-27T14:40:26-04:00",
|
||||
"UserID": 1
|
||||
"UserID": "user_8d7cd2ed-0aa2-4810-a172-42dd58563a54"
|
||||
},
|
||||
{
|
||||
"Body": "I'd like to interject for a moment. What you're referring to as Linux, is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux. Linux is not an operating system unto itself, but rather another free component of a fully functioning GNU system made useful by the GNU corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX. Many computer users run a modified version of the GNU system every day, without realizing it. Through a peculiar turn of events, the version of GNU which is widely used today is often called “Linux,” and many of its users are not aware that it is basically the GNU system, developed by the GNU Project. There really is a Linux, and these people are using it, but it is just a part of the system they use.\n\nLinux is the kernel: the program in the system that allocates the machine's resources to the other programs that you run. The kernel is an essential part of an operating system, but useless by itself; it can only function in the context of a complete operating system. Linux is normally used in combination with the GNU operating system: the whole system is basically GNU with Linux added, or GNU/Linux. All the so-called “Linux” distributions are really distributions of GNU/Linux.",
|
||||
"Edited": "2025-03-27T18:59:12-04:00",
|
||||
"Body": "I'd just like to interject for a moment. What you're referring to as Linux, is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux. Linux is not an operating system unto itself, but rather another free component of a fully functioning GNU system made useful by the GNU corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX. Many computer users run a modified version of the GNU system every day, without realizing it. Through a peculiar turn of events, the version of GNU which is widely used today is often called “Linux,” and many of its users are not aware that it is basically the GNU system, developed by the GNU Project. There really is a Linux, and these people are using it, but it is just a part of the system they use.\n\nLinux is the kernel: the program in the system that allocates the machine's resources to the other programs that you run. The kernel is an essential part of an operating system, but useless by itself; it can only function in the context of a complete operating system. Linux is normally used in combination with the GNU operating system: the whole system is basically GNU with Linux added, or GNU/Linux. All the so-called “Linux” distributions are really distributions of GNU/Linux.",
|
||||
"Edited": "2025-03-27T20:35:33-04:00",
|
||||
"ID": "msg_d77f8e0f-5c23-4c10-984f-b07559e7c5ed",
|
||||
"Timestamp": "2025-03-27T18:56:27-04:00",
|
||||
"UserID": 1
|
||||
"UserID": "user_8d7cd2ed-0aa2-4810-a172-42dd58563a54"
|
||||
},
|
||||
{
|
||||
"Body": "oh \n\n\nok",
|
||||
"Edited": null,
|
||||
"ID": "msg_8d0d8e24-2c1d-4337-afdb-06d1a121e486",
|
||||
"Timestamp": "2025-03-27T18:57:52-04:00",
|
||||
"UserID": 2
|
||||
"UserID": "user_63dac6ad-f255-4af8-a057-4b064a982a84"
|
||||
},
|
||||
{
|
||||
"Body": "we shall ATTACK at the edge of propaganda",
|
||||
"Edited": null,
|
||||
"ID": "msg_dc55edfd-e0f7-4923-b686-df90ad4bb108",
|
||||
"Timestamp": "2025-03-27T19:00:17-04:00",
|
||||
"UserID": 2
|
||||
"UserID": "user_63dac6ad-f255-4af8-a057-4b064a982a84"
|
||||
}
|
||||
]
|
@@ -1,10 +1,10 @@
|
||||
[
|
||||
{
|
||||
"ID": 1,
|
||||
"ID": "user_8d7cd2ed-0aa2-4810-a172-42dd58563a54",
|
||||
"Name": "duby"
|
||||
},
|
||||
{
|
||||
"ID": 2,
|
||||
"ID": "user_63dac6ad-f255-4af8-a057-4b064a982a84",
|
||||
"Name": "astolfo"
|
||||
}
|
||||
]
|
Reference in New Issue
Block a user