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_file_stream", api.HandleGetFileStream)
|
||||||
apiMux.HandleFunc("/get_ffmpeg_config_list", api.HandleGetFfmpegConfigs)
|
apiMux.HandleFunc("/get_ffmpeg_config_list", api.HandleGetFfmpegConfigs)
|
||||||
apiMux.HandleFunc("/feedback", api.HandleFeedback)
|
apiMux.HandleFunc("/feedback", api.HandleFeedback)
|
||||||
|
apiMux.HandleFunc("/get_feedbacks", api.HandleGetFeedbacks)
|
||||||
apiMux.HandleFunc("/get_file_info", api.HandleGetFileInfo)
|
apiMux.HandleFunc("/get_file_info", api.HandleGetFileInfo)
|
||||||
apiMux.HandleFunc("/get_file_stream_direct", api.HandleGetFileStreamDirect)
|
apiMux.HandleFunc("/get_file_stream_direct", api.HandleGetFileStreamDirect)
|
||||||
apiMux.HandleFunc("/prepare_file_stream_direct", api.HandlePrepareFileStreamDirect)
|
apiMux.HandleFunc("/prepare_file_stream_direct", api.HandlePrepareFileStreamDirect)
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
|
"msw-open-music/pkg/database"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FeedbackRequest struct {
|
type FeedbackRequest struct {
|
||||||
Feedback string `json:"feedback"`
|
Content string `json:"content"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) HandleFeedback(w http.ResponseWriter, r *http.Request) {
|
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
|
// check empty feedback
|
||||||
if feedbackRequest.Feedback == "" {
|
if feedbackRequest.Content == "" {
|
||||||
api.HandleErrorString(w, r, `"feedback" can't be empty`)
|
api.HandleErrorString(w, r, `"feedback" can't be empty`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("[api] Feedback", feedbackRequest.Feedback)
|
log.Println("[api] Feedback", feedbackRequest.Content)
|
||||||
|
|
||||||
headerBuff := &bytes.Buffer{}
|
headerBuff := &bytes.Buffer{}
|
||||||
err = r.Header.Write(headerBuff)
|
err = r.Header.Write(headerBuff)
|
||||||
@@ -36,10 +37,45 @@ func (api *API) HandleFeedback(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
header := headerBuff.String()
|
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 {
|
if err != nil {
|
||||||
api.HandleError(w, r, err)
|
api.HandleError(w, r, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
api.HandleOK(w, r)
|
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"
|
"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) {
|
func (database *Database) GetRandomFiles(limit int64) ([]File, error) {
|
||||||
rows, err := database.stmt.getRandomFiles.Query(limit)
|
rows, err := database.stmt.getRandomFiles.Query(limit)
|
||||||
if err != nil {
|
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 (
|
var initFeedbacksTableQuery = `CREATE TABLE IF NOT EXISTS feedbacks (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
time INTEGER NOT NULL,
|
time INTEGER NOT NULL,
|
||||||
feedback TEXT NOT NULL,
|
content TEXT NOT NULL,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
header TEXT NOT NULL
|
header TEXT NOT NULL
|
||||||
);`
|
);`
|
||||||
|
|
||||||
@@ -168,8 +169,16 @@ WHERE file_has_tag.tag_id = ?
|
|||||||
ORDER BY RANDOM()
|
ORDER BY RANDOM()
|
||||||
LIMIT ?;`
|
LIMIT ?;`
|
||||||
|
|
||||||
var insertFeedbackQuery = `INSERT INTO feedbacks (time, feedback, header)
|
var insertFeedbackQuery = `INSERT INTO feedbacks (time, content, user_id, header)
|
||||||
VALUES (?, ?, ?);`
|
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)
|
var insertUserQuery = `INSERT INTO users (username, password, role, active, avatar_id)
|
||||||
VALUES (?, ?, ?, ?, ?);`
|
VALUES (?, ?, ?, ?, ?);`
|
||||||
@@ -280,6 +289,7 @@ type Stmt struct {
|
|||||||
getRandomFiles *sql.Stmt
|
getRandomFiles *sql.Stmt
|
||||||
getRandomFilesWithTag *sql.Stmt
|
getRandomFilesWithTag *sql.Stmt
|
||||||
insertFeedback *sql.Stmt
|
insertFeedback *sql.Stmt
|
||||||
|
getFeedbacks *sql.Stmt
|
||||||
insertUser *sql.Stmt
|
insertUser *sql.Stmt
|
||||||
countUser *sql.Stmt
|
countUser *sql.Stmt
|
||||||
countAdmin *sql.Stmt
|
countAdmin *sql.Stmt
|
||||||
@@ -517,6 +527,12 @@ func NewPreparedStatement(sqlConn *sql.DB) (*Stmt, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// init getFeedbacks
|
||||||
|
stmt.getFeedbacks, err = sqlConn.Prepare(getFeedbacksQuery)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// init insertUser
|
// init insertUser
|
||||||
stmt.insertUser, err = sqlConn.Prepare(insertUserQuery)
|
stmt.insertUser, err = sqlConn.Prepare(insertUserQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -48,6 +48,15 @@ type Review struct {
|
|||||||
Content string `json:"content"`
|
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 (
|
var (
|
||||||
RoleAnonymous = int64(0)
|
RoleAnonymous = int64(0)
|
||||||
RoleAdmin = int64(1)
|
RoleAdmin = int64(1)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import AudioPlayer from "./component/AudioPlayer";
|
|||||||
import UserStatus from "./component/UserStatus";
|
import UserStatus from "./component/UserStatus";
|
||||||
import ReviewPage from "./component/ReviewPage";
|
import ReviewPage from "./component/ReviewPage";
|
||||||
import UserProfile from "./component/UserProfile";
|
import UserProfile from "./component/UserProfile";
|
||||||
|
import FeedbackPage from "./component/FeedbackPage";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
@@ -70,6 +71,10 @@ function App() {
|
|||||||
path="/manage"
|
path="/manage"
|
||||||
element={<Manage user={user} setUser={setUser} />}
|
element={<Manage user={user} setUser={setUser} />}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path="/manage/feedbacks"
|
||||||
|
element={<FeedbackPage user={user} />}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/manage/login"
|
path="/manage/login"
|
||||||
element={<Login user={user} setUser={setUser} />}
|
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 />
|
<hr />
|
||||||
<button onClick={() => navigate("/manage/tags")}>Tags</button>
|
<button onClick={() => navigate("/manage/tags")}>Tags</button>
|
||||||
<button onClick={() => navigate("/manage/users")}>Users</button>
|
<button onClick={() => navigate("/manage/users")}>Users</button>
|
||||||
|
<button onClick={() => navigate("/manage/feedbacks")}>Feedbacks</button>
|
||||||
<Database />
|
<Database />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user