diff options
| author | Andrew <saintruler@gmail.com> | 2021-04-28 13:54:25 +0400 |
|---|---|---|
| committer | Andrew <saintruler@gmail.com> | 2021-04-28 13:54:25 +0400 |
| commit | 3f672bf327b949a8335fb53b4649bdeef90fdd98 (patch) | |
| tree | e61d43c8d6d84674aaea7dbbdef2356253088d4a /http-server | |
| parent | f3088f22e319b7183f9ad29d582574d3a06666ed (diff) | |
Added http server.
Diffstat (limited to 'http-server')
| -rw-r--r-- | http-server/database.go | 67 | ||||
| -rw-r--r-- | http-server/go.mod | 5 | ||||
| -rw-r--r-- | http-server/main.go | 221 |
3 files changed, 293 insertions, 0 deletions
diff --git a/http-server/database.go b/http-server/database.go new file mode 100644 index 0000000..d13e8c1 --- /dev/null +++ b/http-server/database.go @@ -0,0 +1,67 @@ +package main + +import ( + "database/sql" + _ "github.com/mattn/go-sqlite3" +) + +type SQLConnection struct { + db *sql.DB +} + +func (conn *SQLConnection) init(dbpath string) error { + var err error + conn.db, err = sql.Open("sqlite3", dbpath) + return err +} + +func (conn *SQLConnection) registerUser(username string, key string) error { + _, err := conn.db.Exec("INSERT INTO users (username, key) VALUES (?, ?)", username, key) + return err +} + +func (conn *SQLConnection) checkUserRegistered(username string) (bool, error) { + result, err := conn.db.Query("SELECT username FROM users WHERE username = ?", username) + if err != nil { + return false, err + } + for result.Next() { + _ = result.Close() + return true, nil + } + return false, nil +} + +func (conn *SQLConnection) saveMessage(message Message) error { + var err error + + query := `INSERT INTO messages (userId, data, timestamp) values ( + (SELECT id FROM users WHERE username = ?), ?, ? + )` + _, err = conn.db.Exec(query, message.User, message.Data, message.Timestamp) + + return err +} + +func (conn *SQLConnection) getMessagesSince(timestamp int64) ([]Message, error) { + var err error + var msg []Message + + query := `SELECT username, data, timestamp FROM users JOIN messages + WHERE users.id = messages.userId AND timestamp > ? + ORDER BY timestamp;` + + result, err := conn.db.Query(query, timestamp) + if err != nil { + return msg, err + } + + for result.Next() { + var message Message + _ = result.Scan(&message.User, &message.Data, &message.Timestamp) + msg = append(msg, message) + } + _ = result.Close() + + return msg, err +} diff --git a/http-server/go.mod b/http-server/go.mod new file mode 100644 index 0000000..3ada4ae --- /dev/null +++ b/http-server/go.mod @@ -0,0 +1,5 @@ +module vasthecat.ru/coursework/http-server + +go 1.16 + +require github.com/mattn/go-sqlite3 v1.14.7 diff --git a/http-server/main.go b/http-server/main.go new file mode 100644 index 0000000..af7d4be --- /dev/null +++ b/http-server/main.go @@ -0,0 +1,221 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "strconv" + "time" +) + +type Response struct { + Message string +} + +type Request struct { + User string + Data string +} + +type Message struct { + User string + Data string + Timestamp int64 +} + +func parseRequest(w http.ResponseWriter, r *http.Request) (Request, error) { + var resp Request + + body, _ := io.ReadAll(r.Body) + err := json.Unmarshal(body, &resp) + if err != nil { + _ = badRequest(w) + return resp, err + } + return resp, nil +} + +func jsonResponse(w http.ResponseWriter, resp Response) error { + data, err := json.Marshal(resp) + if err != nil { + return err + } + _, err = w.Write(data) + return err +} + +func methodNotAllowedResponse(w http.ResponseWriter) error { + w.WriteHeader(http.StatusMethodNotAllowed) + return jsonResponse(w, Response{ + Message: "К серверу разрешены обращения только по методу POST", + }) +} + +func badRequest(w http.ResponseWriter) error { + w.WriteHeader(http.StatusBadRequest) + return jsonResponse(w, Response{ + Message: "Не удалось обработать запрос", + }) +} + +func serverError(w http.ResponseWriter) error { + w.WriteHeader(http.StatusInternalServerError) + return jsonResponse(w, Response{ + Message: "Произошла внутренняя ошибка", + }) +} + +func index(w http.ResponseWriter, r *http.Request) { + _, _ = fmt.Fprintf(w, "Hello there") +} + +func register(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + _ = methodNotAllowedResponse(w) + return + } + + req, err := parseRequest(w, r) + if err != nil { + return + } + + userRegistered, err := db.checkUserRegistered(req.User) + if err != nil { + _ = serverError(w) + return + } + + // TODO(andrew): Добавить проверку действительности ключа + if false { + w.WriteHeader(http.StatusBadRequest) + _ = jsonResponse(w, Response{ + Message: "Указанный ключ не является действительным", + }) + return + } + + if userRegistered { + w.WriteHeader(http.StatusBadRequest) + _ = jsonResponse(w, Response{ + Message: "Пользователь с таким именем уже зарегистрирован", + }) + return + } + + err = db.registerUser(req.User, req.Data) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + _ = jsonResponse(w, Response{ + Message: fmt.Sprintf("%s", err), + }) + return + } + + _ = jsonResponse(w, Response{ + Message: "Пользователь успешно зарегистрирован", + }) +} + +func sendMessage(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + _ = methodNotAllowedResponse(w) + return + } + + req, err := parseRequest(w, r) + if err != nil { + return + } + + // TODO(andrew): Добавить аутентификацию + if false { + w.WriteHeader(http.StatusForbidden) + _ = jsonResponse(w, Response{ + Message: "Запрос не прошёл аутентификацию", + }) + return + } + + msg := Message{ + User: req.User, + Data: req.Data, + Timestamp: time.Now().Unix(), + } + + err = db.saveMessage(msg) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + _ = jsonResponse(w, Response{ + Message: fmt.Sprintf("%s", err), + }) + return + } + + _ = jsonResponse(w, Response{ + Message: "Сообщение успешно сохранено", + }) +} + +func pollMessages(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + _ = methodNotAllowedResponse(w) + return + } + + req, err := parseRequest(w, r) + if err != nil { + return + } + + // TODO(andrew): Добавить аутентификацию + if false { + w.WriteHeader(http.StatusForbidden) + _ = jsonResponse(w, Response{ + Message: "Запрос не прошёл аутентификацию", + }) + return + } + + timestamp, err := strconv.ParseInt(req.Data, 10, 64) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + _ = jsonResponse(w, Response{ + Message: "Указана неверная временная отметка", + }) + return + } + + messages, err := db.getMessagesSince(timestamp) + if err != nil { + _ = serverError(w) + return + } + + data, _ := json.Marshal(messages) + _ = jsonResponse(w, Response{ + Message: string(data), + }) +} + +var db SQLConnection + +func main() { + err := db.init("chat.sqlite3") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + http.HandleFunc("/", index) + http.HandleFunc("/api/register", register) + http.HandleFunc("/api/sendMessage", sendMessage) + http.HandleFunc("/api/pollMessages", pollMessages) + err = http.ListenAndServe("localhost:8080", nil) + if err != nil { + fmt.Println(err) + os.Exit(1) + } +} |