286 lines
7.5 KiB
Go
286 lines
7.5 KiB
Go
package srv
|
|
|
|
import (
|
|
tu "chat/tu"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
)
|
|
|
|
func (s *Server) handlePing(w http.ResponseWriter, r *http.Request) {
|
|
clientIP := getClientIP(r)
|
|
s.updateActivity(clientIP)
|
|
s.cleanupActivity()
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
|
|
func (s *Server) handleUsername(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPut {
|
|
http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
clientIP := getClientIP(r)
|
|
|
|
var req struct {
|
|
Username string `json:"username"`
|
|
}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
s.mu.Lock()
|
|
|
|
if len(req.Username) > s.Config.Options.NameMaxLength {
|
|
http.Error(w, fmt.Sprintf(`{"error": "Username too long (%v out of %v characters maximum)"}`, len(req.Username), s.Config.Options.NameMaxLength), http.StatusRequestEntityTooLarge)
|
|
s.mu.Unlock()
|
|
return
|
|
}
|
|
|
|
if !validUsername(req.Username) {
|
|
http.Error(w, fmt.Sprintf(`{"error": "Username must only contain alphanumeric characters and/or underscores"}`), http.StatusBadRequest)
|
|
s.mu.Unlock()
|
|
return
|
|
}
|
|
|
|
if s.Database.UserNameExists(req.Username) {
|
|
http.Error(w, fmt.Sprintf(`{"error": "Username already exists"}`), http.StatusConflict)
|
|
s.mu.Unlock()
|
|
return
|
|
}
|
|
|
|
if s.Database.UserExists(clientIP) {
|
|
s.Database.UserNameChange(clientIP, req.Username)
|
|
} else {
|
|
s.Database.UserAdd(clientIP, req.Username)
|
|
}
|
|
|
|
s.mu.Unlock()
|
|
|
|
json.NewEncoder(w).Encode(map[string]string{"status": "Username registered"})
|
|
}
|
|
|
|
func (s *Server) handleMessages(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodPatch:
|
|
w.Header().Set("Content-Type", "application/json")
|
|
var req struct {
|
|
MessageId string `json:"messageId"`
|
|
MessageContent string `json:"messageContent"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
clientIP := getClientIP(r)
|
|
if affected, err := s.Database.MessageEditIfOwner(req.MessageId, req.MessageContent, clientIP); err != nil {
|
|
http.Error(w, `{"error": "Unauthorized"}`, http.StatusNotFound)
|
|
return
|
|
} else if affected == 0 {
|
|
http.Error(w, `{"error": "Message not found"}`, http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(map[string]string{
|
|
"status": "Message edited successfully",
|
|
})
|
|
|
|
case http.MethodGet:
|
|
w.Header().Set("Content-Type", "text/html")
|
|
|
|
var body string
|
|
messages := s.Database.MessagesGet()
|
|
for _, msg := range messages {
|
|
clientIP := getClientIP(r)
|
|
timeZone := s.Database.UserGetTimezone(clientIP)
|
|
timeLocal := tu.TimeStringToTimeInLocation(msg.Timestamp, timeZone)
|
|
edited := ""
|
|
if msg.Edited {
|
|
edited = "(edited)"
|
|
}
|
|
body += fmt.Sprintf(`<p>%s<br><span class="username">%s</span><br><span class="timestamp">%s %s</span><br><span class="message">%s</span><br></p>`,
|
|
msg.Id, msg.SenderUsername, timeLocal, edited, msg.Content)
|
|
}
|
|
|
|
w.Write([]byte(getMessageTemplate(body)))
|
|
case http.MethodPut:
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
// Get client's IP
|
|
clientIP := getClientIP(r)
|
|
|
|
s.mu.Lock()
|
|
exists := s.Database.UserExists(clientIP)
|
|
username := s.Database.UserNameGet(clientIP)
|
|
s.mu.Unlock()
|
|
|
|
if !exists {
|
|
errorFmt := fmt.Sprintf(`{"error": "IP %s not registered with username"}`, clientIP)
|
|
http.Error(w, errorFmt, http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
var msg struct {
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&msg); err != nil {
|
|
http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
s.Database.MessageAdd(clientIP, msg.Message)
|
|
|
|
json.NewEncoder(w).Encode(map[string]string{
|
|
"status": "Message received",
|
|
"from": username,
|
|
})
|
|
case http.MethodDelete:
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
var req struct {
|
|
MessageId string `json:"messageId"`
|
|
}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
clientIP := getClientIP(r)
|
|
if affected, err := s.Database.MessageDeleteIfOwner(req.MessageId, clientIP); err != nil {
|
|
http.Error(w, `{"error": "Unauthorized"}`, http.StatusNotFound)
|
|
return
|
|
} else if affected == 0 {
|
|
http.Error(w, `{"error": "Message not found"}`, http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
json.NewEncoder(w).Encode(map[string]string{
|
|
"status": "Message deleted",
|
|
})
|
|
default:
|
|
http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
}
|
|
|
|
func (s *Server) handleUsernameStatus(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
clientIP := getClientIP(r)
|
|
|
|
s.mu.Lock()
|
|
exists := s.Database.UserExists(clientIP)
|
|
username := s.Database.UserNameGet(clientIP)
|
|
s.mu.Unlock()
|
|
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
"hasUsername": exists,
|
|
"username": username,
|
|
})
|
|
}
|
|
|
|
func (s *Server) handleUsers(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
clientIP := getClientIP(r)
|
|
s.updateActivity(clientIP)
|
|
s.cleanupActivity()
|
|
s.mu.Lock()
|
|
var users []string
|
|
for ip := range s.Connected {
|
|
// for all connected, get their usernames
|
|
users = append(users, s.Database.UserNameGet(ip))
|
|
}
|
|
s.mu.Unlock()
|
|
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
"users": users,
|
|
})
|
|
}
|
|
|
|
func (s *Server) handleTimezone(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
clientIP := getClientIP(r)
|
|
|
|
var req struct {
|
|
Timezone string `json:"timezone"`
|
|
}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
s.mu.Lock()
|
|
|
|
if !s.Database.UserExists(clientIP) {
|
|
http.Error(w, `{"error": "User not registered"}`, http.StatusUnauthorized)
|
|
s.mu.Unlock()
|
|
return
|
|
}
|
|
|
|
s.Database.UserTimezoneSet(clientIP, req.Timezone)
|
|
s.mu.Unlock()
|
|
json.NewEncoder(w).Encode(map[string]string{
|
|
"status": "success",
|
|
})
|
|
}
|
|
|
|
func (s *Server) handleRoot(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/" {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "text/html")
|
|
file := readFile(s.Config.Paths.IndexHtmlPath)
|
|
w.Write(file)
|
|
}
|
|
|
|
func (s *Server) handleJs(w http.ResponseWriter, r *http.Request) {
|
|
_ = r
|
|
w.Header().Set("Content-Type", "application/javascript")
|
|
file := readFile(s.Config.Paths.IndexJsPath)
|
|
w.Write(file)
|
|
}
|
|
|
|
func (s *Server) handleCss(w http.ResponseWriter, r *http.Request) {
|
|
_ = r
|
|
w.Header().Set("Content-Type", "text/css")
|
|
file := readFile(s.Config.Paths.IndexCssPath)
|
|
w.Write(file)
|
|
}
|
|
|
|
func (s *Server) handleMessagesLength(w http.ResponseWriter, r *http.Request) {
|
|
// should return the number of messages in the database
|
|
if r.Method != http.MethodGet {
|
|
http.Error(w, `{"error": "Method not allowed"}`, http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
messages := s.Database.MessagesGet()
|
|
json.NewEncoder(w).Encode(map[string]int{
|
|
"length": len(messages),
|
|
})
|
|
}
|