summaryrefslogtreecommitdiff
path: root/http-server
diff options
context:
space:
mode:
Diffstat (limited to 'http-server')
-rw-r--r--http-server/database.go67
-rw-r--r--http-server/go.mod5
-rw-r--r--http-server/main.go221
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)
+ }
+}