Загрузить файлы в «/»
This commit is contained in:
parent
3a36e69921
commit
ba744770f1
2 changed files with 405 additions and 0 deletions
384
main.go
Normal file
384
main.go
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
var (
|
||||
db *sql.DB
|
||||
basePath string
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID int `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Active bool `json:"active"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
ID int `json:"id"`
|
||||
FromID int `json:"from_id"`
|
||||
ToID int `json:"to_id"`
|
||||
Text string `json:"text"`
|
||||
Username string `json:"username"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Получаем базовый путь из переменной окружения
|
||||
basePath = os.Getenv("BASE_PATH")
|
||||
if basePath == "" {
|
||||
basePath = "/"
|
||||
}
|
||||
// Убираем слеш в конце если есть
|
||||
basePath = strings.TrimSuffix(basePath, "/")
|
||||
if basePath == "" {
|
||||
basePath = "/"
|
||||
}
|
||||
|
||||
var err error
|
||||
db, err = sql.Open("sqlite", "./chat.db")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
initDB()
|
||||
|
||||
// Роуты с учетом базового пути
|
||||
http.HandleFunc(basePath+"/", serveIndex)
|
||||
http.HandleFunc(basePath+"/api/register", handleRegister)
|
||||
http.HandleFunc(basePath+"/api/login", handleLogin)
|
||||
http.HandleFunc(basePath+"/api/users", handleGetUsers)
|
||||
http.HandleFunc(basePath+"/api/messages", handleMessages)
|
||||
http.HandleFunc(basePath+"/api/admin/users", handleAdminGetUsers)
|
||||
http.HandleFunc(basePath+"/api/admin/toggle", handleAdminToggle)
|
||||
|
||||
// Статические файлы
|
||||
fs := http.FileServer(http.Dir("static"))
|
||||
http.Handle(basePath+"/static/", http.StripPrefix(basePath+"/static/", fs))
|
||||
|
||||
fmt.Printf("Чат запущен на :3100 (базовый путь: %s)\n", basePath)
|
||||
log.Fatal(http.ListenAndServe(":3100", nil))
|
||||
}
|
||||
|
||||
func initDB() {
|
||||
queries := []string{
|
||||
`CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
password TEXT NOT NULL,
|
||||
active INTEGER DEFAULT 0,
|
||||
is_admin INTEGER DEFAULT 0
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS messages (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
from_id INTEGER NOT NULL,
|
||||
to_id INTEGER NOT NULL,
|
||||
text TEXT NOT NULL,
|
||||
timestamp INTEGER NOT NULL
|
||||
)`,
|
||||
`INSERT OR IGNORE INTO users (username, password, active, is_admin)
|
||||
-- // ПОМЕНЯТЬ НА СВОИ
|
||||
VALUES ('admin', 'admin123', 1, 1)`,
|
||||
}
|
||||
|
||||
for _, q := range queries {
|
||||
if _, err := db.Exec(q); err != nil {
|
||||
log.Printf("Error executing query: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func serveIndex(w http.ResponseWriter, r *http.Request) {
|
||||
// Перенаправляем /parley на /parley/
|
||||
if r.URL.Path == basePath && basePath != "/" {
|
||||
http.Redirect(w, r, basePath+"/", http.StatusMovedPermanently)
|
||||
return
|
||||
}
|
||||
|
||||
tmpl, err := template.ParseFiles("static/index.html")
|
||||
if err != nil {
|
||||
http.Error(w, "Template error", 500)
|
||||
return
|
||||
}
|
||||
|
||||
data := struct {
|
||||
BasePath string
|
||||
}{
|
||||
BasePath: basePath,
|
||||
}
|
||||
|
||||
tmpl.Execute(w, data)
|
||||
}
|
||||
|
||||
func handleRegister(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "Method not allowed", 405)
|
||||
return
|
||||
}
|
||||
|
||||
var user User
|
||||
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
|
||||
jsonError(w, "Invalid request")
|
||||
return
|
||||
}
|
||||
|
||||
if user.Username == "" || user.Password == "" {
|
||||
jsonError(w, "Username and password required")
|
||||
return
|
||||
}
|
||||
|
||||
_, err := db.Exec("INSERT INTO users (username, password) VALUES (?, ?)",
|
||||
user.Username, user.Password)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "UNIQUE") {
|
||||
jsonError(w, "Username already exists")
|
||||
} else {
|
||||
jsonError(w, "Registration failed")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
jsonResponse(w, map[string]interface{}{"message": "Registration successful. Wait for admin activation."})
|
||||
}
|
||||
|
||||
func handleLogin(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "Method not allowed", 405)
|
||||
return
|
||||
}
|
||||
|
||||
var user User
|
||||
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
|
||||
jsonError(w, "Invalid request")
|
||||
return
|
||||
}
|
||||
|
||||
var dbUser User
|
||||
var activeInt, adminInt int
|
||||
err := db.QueryRow("SELECT id, username, active, is_admin FROM users WHERE username = ? AND password = ?",
|
||||
user.Username, user.Password).Scan(&dbUser.ID, &dbUser.Username, &activeInt, &adminInt)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
jsonError(w, "Invalid credentials")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
jsonError(w, "Database error")
|
||||
return
|
||||
}
|
||||
|
||||
dbUser.Active = activeInt == 1
|
||||
dbUser.IsAdmin = adminInt == 1
|
||||
|
||||
if !dbUser.Active {
|
||||
jsonError(w, "Account not activated. Contact admin.")
|
||||
return
|
||||
}
|
||||
|
||||
jsonResponse(w, map[string]interface{}{
|
||||
"user_id": dbUser.ID,
|
||||
"username": dbUser.Username,
|
||||
"is_admin": dbUser.IsAdmin,
|
||||
})
|
||||
}
|
||||
|
||||
func handleGetUsers(w http.ResponseWriter, r *http.Request) {
|
||||
userID, err := strconv.Atoi(r.URL.Query().Get("user_id"))
|
||||
if err != nil {
|
||||
jsonError(w, "Invalid user_id")
|
||||
return
|
||||
}
|
||||
|
||||
// Проверяем, активен ли пользователь
|
||||
var activeInt int
|
||||
err = db.QueryRow("SELECT active FROM users WHERE id = ?", userID).Scan(&activeInt)
|
||||
if err != nil || activeInt != 1 {
|
||||
jsonError(w, "User not active")
|
||||
return
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT id, username FROM users WHERE active = 1 AND id != ?", userID)
|
||||
if err != nil {
|
||||
jsonError(w, "Database error")
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var users []map[string]interface{}
|
||||
for rows.Next() {
|
||||
var id int
|
||||
var username string
|
||||
rows.Scan(&id, &username)
|
||||
users = append(users, map[string]interface{}{
|
||||
"id": id,
|
||||
"username": username,
|
||||
})
|
||||
}
|
||||
|
||||
jsonResponse(w, users)
|
||||
}
|
||||
|
||||
func handleMessages(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case "GET":
|
||||
getMessages(w, r)
|
||||
case "POST":
|
||||
sendMessage(w, r)
|
||||
default:
|
||||
http.Error(w, "Method not allowed", 405)
|
||||
}
|
||||
}
|
||||
|
||||
func getMessages(w http.ResponseWriter, r *http.Request) {
|
||||
userID, _ := strconv.Atoi(r.URL.Query().Get("user_id"))
|
||||
partnerID, _ := strconv.Atoi(r.URL.Query().Get("partner_id"))
|
||||
|
||||
rows, err := db.Query(`
|
||||
SELECT m.id, m.from_id, m.to_id, m.text, m.timestamp, u.username
|
||||
FROM messages m
|
||||
JOIN users u ON m.from_id = u.id
|
||||
WHERE (m.from_id = ? AND m.to_id = ?) OR (m.from_id = ? AND m.to_id = ?)
|
||||
ORDER BY m.timestamp ASC
|
||||
LIMIT 100`, userID, partnerID, partnerID, userID)
|
||||
|
||||
if err != nil {
|
||||
jsonError(w, "Database error")
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var messages []Message
|
||||
for rows.Next() {
|
||||
var msg Message
|
||||
rows.Scan(&msg.ID, &msg.FromID, &msg.ToID, &msg.Text, &msg.Timestamp, &msg.Username)
|
||||
messages = append(messages, msg)
|
||||
}
|
||||
|
||||
if messages == nil {
|
||||
messages = []Message{}
|
||||
}
|
||||
|
||||
jsonResponse(w, messages)
|
||||
}
|
||||
|
||||
func sendMessage(w http.ResponseWriter, r *http.Request) {
|
||||
var msg Message
|
||||
if err := json.NewDecoder(r.Body).Decode(&msg); err != nil {
|
||||
jsonError(w, "Invalid request")
|
||||
return
|
||||
}
|
||||
|
||||
msg.Timestamp = time.Now().Unix()
|
||||
|
||||
result, err := db.Exec("INSERT INTO messages (from_id, to_id, text, timestamp) VALUES (?, ?, ?, ?)",
|
||||
msg.FromID, msg.ToID, msg.Text, msg.Timestamp)
|
||||
if err != nil {
|
||||
jsonError(w, "Failed to send message")
|
||||
return
|
||||
}
|
||||
|
||||
id, _ := result.LastInsertId()
|
||||
msg.ID = int(id)
|
||||
|
||||
// Получаем username отправителя
|
||||
db.QueryRow("SELECT username FROM users WHERE id = ?", msg.FromID).Scan(&msg.Username)
|
||||
|
||||
jsonResponse(w, msg)
|
||||
}
|
||||
|
||||
func handleAdminGetUsers(w http.ResponseWriter, r *http.Request) {
|
||||
adminID, err := strconv.Atoi(r.URL.Query().Get("admin_id"))
|
||||
if err != nil {
|
||||
jsonError(w, "Invalid admin_id")
|
||||
return
|
||||
}
|
||||
|
||||
// Проверяем, что запрос от админа
|
||||
var isAdmin int
|
||||
db.QueryRow("SELECT is_admin FROM users WHERE id = ?", adminID).Scan(&isAdmin)
|
||||
if isAdmin != 1 {
|
||||
jsonError(w, "Not admin")
|
||||
return
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT id, username, active FROM users")
|
||||
if err != nil {
|
||||
jsonError(w, "Database error")
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var users []map[string]interface{}
|
||||
for rows.Next() {
|
||||
var id, active int
|
||||
var username string
|
||||
rows.Scan(&id, &username, &active)
|
||||
users = append(users, map[string]interface{}{
|
||||
"id": id,
|
||||
"username": username,
|
||||
"active": active == 1,
|
||||
})
|
||||
}
|
||||
|
||||
jsonResponse(w, users)
|
||||
}
|
||||
|
||||
func handleAdminToggle(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "Method not allowed", 405)
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
AdminID int `json:"admin_id"`
|
||||
UserID int `json:"user_id"`
|
||||
Active bool `json:"active"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
jsonError(w, "Invalid request")
|
||||
return
|
||||
}
|
||||
|
||||
// Проверяем, что запрос от админа
|
||||
var isAdmin int
|
||||
db.QueryRow("SELECT is_admin FROM users WHERE id = ?", req.AdminID).Scan(&isAdmin)
|
||||
if isAdmin != 1 {
|
||||
jsonError(w, "Not admin")
|
||||
return
|
||||
}
|
||||
|
||||
activeInt := 0
|
||||
if req.Active {
|
||||
activeInt = 1
|
||||
}
|
||||
|
||||
db.Exec("UPDATE users SET active = ? WHERE id = ?", activeInt, req.UserID)
|
||||
jsonResponse(w, map[string]string{"message": "User status updated"})
|
||||
}
|
||||
|
||||
func jsonResponse(w http.ResponseWriter, data interface{}) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(data)
|
||||
}
|
||||
|
||||
func jsonError(w http.ResponseWriter, message string) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": message})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue