Add: support feedback
This commit is contained in:
@@ -76,6 +76,7 @@ func NewAPI(config Config) (*API, error) {
|
||||
apiMux.HandleFunc("/get_file_stream", api.HandleGetFileStream)
|
||||
apiMux.HandleFunc("/get_ffmpeg_config_list", api.HandleGetFfmpegConfigs)
|
||||
apiMux.HandleFunc("/feedback", api.HandleFeedback)
|
||||
apiMux.HandleFunc("/get_feedbacks", api.HandleGetFeedbacks)
|
||||
apiMux.HandleFunc("/get_file_info", api.HandleGetFileInfo)
|
||||
apiMux.HandleFunc("/get_file_stream_direct", api.HandleGetFileStreamDirect)
|
||||
apiMux.HandleFunc("/prepare_file_stream_direct", api.HandlePrepareFileStreamDirect)
|
||||
|
||||
@@ -4,12 +4,13 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"msw-open-music/pkg/database"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FeedbackRequest struct {
|
||||
Feedback string `json:"feedback"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
func (api *API) HandleFeedback(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -21,12 +22,12 @@ func (api *API) HandleFeedback(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// check empty feedback
|
||||
if feedbackRequest.Feedback == "" {
|
||||
if feedbackRequest.Content == "" {
|
||||
api.HandleErrorString(w, r, `"feedback" can't be empty`)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("[api] Feedback", feedbackRequest.Feedback)
|
||||
log.Println("[api] Feedback", feedbackRequest.Content)
|
||||
|
||||
headerBuff := &bytes.Buffer{}
|
||||
err = r.Header.Write(headerBuff)
|
||||
@@ -36,10 +37,45 @@ func (api *API) HandleFeedback(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
header := headerBuff.String()
|
||||
|
||||
err = api.Db.InsertFeedback(time.Now().Unix(), feedbackRequest.Feedback, header)
|
||||
userID, err := api.GetUserID(w, r)
|
||||
if err != nil {
|
||||
api.HandleError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = api.Db.InsertFeedback(time.Now().Unix(), feedbackRequest.Content, userID, header)
|
||||
if err != nil {
|
||||
api.HandleError(w, r, err)
|
||||
return
|
||||
}
|
||||
api.HandleOK(w, r)
|
||||
}
|
||||
|
||||
type GetFeedbacksResponse struct {
|
||||
Feedbacks []*database.Feedback `json:"feedbacks"`
|
||||
}
|
||||
|
||||
func (api *API) HandleGetFeedbacks(w http.ResponseWriter, r *http.Request) {
|
||||
// check if admin
|
||||
err := api.CheckAdmin(w, r)
|
||||
if err != nil {
|
||||
api.HandleError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
feedbacks, err := api.Db.GetFeedbacks()
|
||||
if err != nil {
|
||||
api.HandleError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp := &GetFeedbacksResponse{
|
||||
Feedbacks: feedbacks,
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(resp)
|
||||
if err != nil {
|
||||
api.HandleError(w, r, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,6 @@ import (
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func (database *Database) InsertFeedback(time int64, feedback string, header string) error {
|
||||
_, err := database.stmt.insertFeedback.Exec(time, feedback, header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (database *Database) GetRandomFiles(limit int64) ([]File, error) {
|
||||
rows, err := database.stmt.getRandomFiles.Query(limit)
|
||||
if err != nil {
|
||||
|
||||
32
pkg/database/method_feedback.go
Normal file
32
pkg/database/method_feedback.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package database
|
||||
|
||||
func (database *Database) InsertFeedback(time int64, content string, userID int64, header string) error {
|
||||
_, err := database.stmt.insertFeedback.Exec(time, content, userID, header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (database *Database) GetFeedbacks() ([]*Feedback, error) {
|
||||
rows, err := database.stmt.getFeedbacks.Query()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
feedbacks := make([]*Feedback, 0)
|
||||
for rows.Next() {
|
||||
feedback := &Feedback{
|
||||
User: &User{},
|
||||
}
|
||||
err := rows.Scan(
|
||||
&feedback.ID, &feedback.Time, &feedback.Content, &feedback.Header,
|
||||
&feedback.User.ID, &feedback.User.Username, &feedback.User.Role, &feedback.User.Active, &feedback.User.AvatarId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
feedbacks = append(feedbacks, feedback)
|
||||
}
|
||||
return feedbacks, nil
|
||||
}
|
||||
@@ -21,7 +21,8 @@ var initFoldersTableQuery = `CREATE TABLE IF NOT EXISTS folders (
|
||||
var initFeedbacksTableQuery = `CREATE TABLE IF NOT EXISTS feedbacks (
|
||||
id INTEGER PRIMARY KEY,
|
||||
time INTEGER NOT NULL,
|
||||
feedback TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
header TEXT NOT NULL
|
||||
);`
|
||||
|
||||
@@ -168,8 +169,16 @@ WHERE file_has_tag.tag_id = ?
|
||||
ORDER BY RANDOM()
|
||||
LIMIT ?;`
|
||||
|
||||
var insertFeedbackQuery = `INSERT INTO feedbacks (time, feedback, header)
|
||||
VALUES (?, ?, ?);`
|
||||
var insertFeedbackQuery = `INSERT INTO feedbacks (time, content, user_id, header)
|
||||
VALUES (?, ?, ?, ?);`
|
||||
|
||||
var getFeedbacksQuery = `SELECT
|
||||
feedbacks.id, feedbacks.time, feedbacks.content, feedbacks.header,
|
||||
users.id, users.username, users.role, users.active, users.avatar_id
|
||||
FROM feedbacks
|
||||
JOIN users ON feedbacks.user_id = users.id
|
||||
ORDER BY feedbacks.time
|
||||
;`
|
||||
|
||||
var insertUserQuery = `INSERT INTO users (username, password, role, active, avatar_id)
|
||||
VALUES (?, ?, ?, ?, ?);`
|
||||
@@ -280,6 +289,7 @@ type Stmt struct {
|
||||
getRandomFiles *sql.Stmt
|
||||
getRandomFilesWithTag *sql.Stmt
|
||||
insertFeedback *sql.Stmt
|
||||
getFeedbacks *sql.Stmt
|
||||
insertUser *sql.Stmt
|
||||
countUser *sql.Stmt
|
||||
countAdmin *sql.Stmt
|
||||
@@ -517,6 +527,12 @@ func NewPreparedStatement(sqlConn *sql.DB) (*Stmt, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// init getFeedbacks
|
||||
stmt.getFeedbacks, err = sqlConn.Prepare(getFeedbacksQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// init insertUser
|
||||
stmt.insertUser, err = sqlConn.Prepare(insertUserQuery)
|
||||
if err != nil {
|
||||
|
||||
@@ -48,6 +48,15 @@ type Review struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type Feedback struct {
|
||||
ID int64 `json:"id"`
|
||||
UserId int64 `json:"user_id"`
|
||||
User *User `json:"user"`
|
||||
Content string `json:"content"`
|
||||
Header string `json:"header"`
|
||||
Time int64 `json:"time"`
|
||||
}
|
||||
|
||||
var (
|
||||
RoleAnonymous = int64(0)
|
||||
RoleAdmin = int64(1)
|
||||
|
||||
@@ -18,6 +18,7 @@ import AudioPlayer from "./component/AudioPlayer";
|
||||
import UserStatus from "./component/UserStatus";
|
||||
import ReviewPage from "./component/ReviewPage";
|
||||
import UserProfile from "./component/UserProfile";
|
||||
import FeedbackPage from "./component/FeedbackPage";
|
||||
import { useState } from "react";
|
||||
|
||||
function App() {
|
||||
@@ -70,6 +71,10 @@ function App() {
|
||||
path="/manage"
|
||||
element={<Manage user={user} setUser={setUser} />}
|
||||
/>
|
||||
<Route
|
||||
path="/manage/feedbacks"
|
||||
element={<FeedbackPage user={user} />}
|
||||
/>
|
||||
<Route
|
||||
path="/manage/login"
|
||||
element={<Login user={user} setUser={setUser} />}
|
||||
|
||||
83
web/src/component/FeedbackPage.js
Normal file
83
web/src/component/FeedbackPage.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { convertIntToDateTime } from "./Common";
|
||||
|
||||
function FeedbackPage() {
|
||||
const [content, setContext] = useState("");
|
||||
const [feedbacks, setFeedbacks] = useState([]);
|
||||
|
||||
function getFeedbacks() {
|
||||
fetch("/api/v1/get_feedbacks")
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
if (data.error) {
|
||||
console.log(data.error);
|
||||
} else {
|
||||
setFeedbacks(data.feedbacks);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function submitFeedback() {
|
||||
fetch("/api/v1/feedback", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content: content,
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
if (data.error) {
|
||||
alert(data.error);
|
||||
} else {
|
||||
setContext("");
|
||||
getFeedbacks();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getFeedbacks();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="page">
|
||||
<h3>Feedback</h3>
|
||||
<textarea value={content} onChange={(e) => setContext(e.target.value)} />
|
||||
<button onClick={() => submitFeedback()}>Submit</button>
|
||||
<div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User</th>
|
||||
<th>Feedback</th>
|
||||
<th>Date</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{feedbacks.map((feedback) => (
|
||||
<tr key={feedback._id}>
|
||||
<td>
|
||||
<Link to={`/manage/users/${feedback.user.id}`}>
|
||||
@{feedback.user.username}
|
||||
</Link>
|
||||
</td>
|
||||
<td>{feedback.content}</td>
|
||||
<td>{convertIntToDateTime(feedback.time)}</td>
|
||||
<td>
|
||||
<button onClick={() => {}}>Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FeedbackPage;
|
||||
@@ -46,6 +46,7 @@ function Manage(props) {
|
||||
<hr />
|
||||
<button onClick={() => navigate("/manage/tags")}>Tags</button>
|
||||
<button onClick={() => navigate("/manage/users")}>Users</button>
|
||||
<button onClick={() => navigate("/manage/feedbacks")}>Feedbacks</button>
|
||||
<Database />
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user