Add: insert review
This commit is contained in:
@@ -92,6 +92,8 @@ func NewAPI(config Config) (*API, error) {
|
||||
apiMux.HandleFunc("/delete_tag_on_file", api.HandleDeleteTagOnFile)
|
||||
// folder
|
||||
apiMux.HandleFunc("/update_foldername", api.HandleUpdateFoldername)
|
||||
// review
|
||||
apiMux.HandleFunc("/insert_review", api.HandleInsertReview)
|
||||
// below needs token
|
||||
apiMux.HandleFunc("/walk", api.HandleWalk)
|
||||
apiMux.HandleFunc("/reset", api.HandleReset)
|
||||
|
||||
@@ -11,6 +11,7 @@ var (
|
||||
ErrNotLoggedIn = errors.New("not logged in")
|
||||
ErrNotAdmin = errors.New("not admin")
|
||||
ErrEmpty = errors.New("Empty field detected, please fill in all fields")
|
||||
ErrAnonymous = errors.New("Anonymous user detected, please login")
|
||||
)
|
||||
|
||||
type Error struct {
|
||||
|
||||
42
pkg/api/handle_review.go
Normal file
42
pkg/api/handle_review.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"msw-open-music/pkg/database"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// review.FileId, review.Content
|
||||
func (api *API) HandleInsertReview(w http.ResponseWriter, r *http.Request) {
|
||||
review := &database.Review{}
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(review)
|
||||
if err != nil {
|
||||
api.HandleError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
// check not anonymous
|
||||
err = api.CheckNotAnonymous(w, r)
|
||||
if err != nil {
|
||||
api.HandleError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
review.UserId, err = api.GetUserID(w, r)
|
||||
if err != nil {
|
||||
api.HandleError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
review.CreatedAt = time.Now().Unix()
|
||||
|
||||
err = api.Db.InsertReview(review)
|
||||
if err != nil {
|
||||
api.HandleError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
api.HandleOK(w, r)
|
||||
}
|
||||
@@ -165,6 +165,25 @@ func (api *API) CheckAdmin(w http.ResponseWriter, r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (api *API) CheckNotAnonymous(w http.ResponseWriter, r *http.Request) error {
|
||||
session, _ := api.store.Get(r, api.defaultSessionName)
|
||||
userId, ok := session.Values["userId"]
|
||||
if !ok {
|
||||
return ErrNotLoggedIn
|
||||
}
|
||||
|
||||
user, err := api.Db.GetUserById(userId.(int64))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.Role == database.RoleAnonymous {
|
||||
return ErrAnonymous
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (api *API) GetUserID(w http.ResponseWriter, r *http.Request) (int64, error) {
|
||||
session, _ := api.store.Get(r, api.defaultSessionName)
|
||||
userId, ok := session.Values["userId"]
|
||||
|
||||
10
pkg/database/method_review.go
Normal file
10
pkg/database/method_review.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package database
|
||||
|
||||
func (database *Database) InsertReview(review *Review) error {
|
||||
_, err := database.stmt.insertReview.Exec(
|
||||
review.UserId,
|
||||
review.FileId,
|
||||
review.CreatedAt,
|
||||
review.Content)
|
||||
return err
|
||||
}
|
||||
@@ -71,10 +71,12 @@ var initLikesTableQuery = `CREATE TABLE IF NOT EXISTS likes (
|
||||
var initReviewsTableQuery = `CREATE TABLE IF NOT EXISTS reviews (
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER NOT NULL,
|
||||
time INTEGER NOT NULL,
|
||||
modified_time INTEGER DEFAULT 0,
|
||||
review TEXT NOT NULL,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
file_id INTEGER NOT NULL,
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL DEFAULT 0,
|
||||
content TEXT NOT NULL,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id),
|
||||
FOREIGN KEY (file_id) REFERENCES files(id)
|
||||
);`
|
||||
|
||||
var initPlaybacksTableQuery = `CREATE TABLE IF NOT EXISTS playbacks (
|
||||
@@ -202,6 +204,9 @@ var deleteTagOnFileQuery = `DELETE FROM file_has_tag WHERE tag_id = ? AND file_i
|
||||
|
||||
var updateFoldernameQuery = `UPDATE folders SET foldername = ? WHERE id = ?;`
|
||||
|
||||
var insertReviewQuery = `INSERT INTO reviews (user_id, file_id, created_at, content)
|
||||
VALUES (?, ?, ?, ?);`
|
||||
|
||||
type Stmt struct {
|
||||
initFilesTable *sql.Stmt
|
||||
initFoldersTable *sql.Stmt
|
||||
@@ -242,6 +247,7 @@ type Stmt struct {
|
||||
getTagsOnFile *sql.Stmt
|
||||
deleteTagOnFile *sql.Stmt
|
||||
updateFoldername *sql.Stmt
|
||||
insertReview *sql.Stmt
|
||||
}
|
||||
|
||||
func NewPreparedStatement(sqlConn *sql.DB) (*Stmt, error) {
|
||||
@@ -546,5 +552,11 @@ func NewPreparedStatement(sqlConn *sql.DB) (*Stmt, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// init insertReview
|
||||
stmt.insertReview, err = sqlConn.Prepare(insertReviewQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return stmt, err
|
||||
}
|
||||
|
||||
@@ -36,6 +36,17 @@ type Tag struct {
|
||||
CreatedByUser *User `json:"created_by_user"`
|
||||
}
|
||||
|
||||
type Review struct {
|
||||
ID int64 `json:"id"`
|
||||
FileId int64 `json:"file_id"`
|
||||
File *File `json:"file"`
|
||||
UserId int64 `json:"user_id"`
|
||||
User *User `json:"user"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
var (
|
||||
RoleAnonymous = int64(0)
|
||||
RoleAdmin = int64(1)
|
||||
|
||||
@@ -14,6 +14,7 @@ import Tags from "./component/Tags";
|
||||
import EditTag from "./component/EditTag";
|
||||
import AudioPlayer from "./component/AudioPlayer";
|
||||
import UserStatus from "./component/UserStatus";
|
||||
import ReviewPage from "./component/ReviewPage";
|
||||
import { useState } from "react";
|
||||
|
||||
function App() {
|
||||
@@ -90,6 +91,10 @@ function App() {
|
||||
path="/files/:id/share"
|
||||
element={<Share setPlayingFile={setPlayingFile} />}
|
||||
/>
|
||||
<Route
|
||||
path="/files/:id/review"
|
||||
element={<ReviewPage setPlayingFile={setPlayingFile} />}
|
||||
/>
|
||||
</Routes>
|
||||
</main>
|
||||
<footer>
|
||||
|
||||
@@ -106,6 +106,13 @@ function FileInfo(props) {
|
||||
>
|
||||
Play
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate(`/files/${params.id}/review`);
|
||||
}}
|
||||
>
|
||||
Review
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
navigate(`/files/${params.id}/share`);
|
||||
|
||||
43
web/src/component/ReviewPage.js
Normal file
43
web/src/component/ReviewPage.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import { useState } from "react";
|
||||
import { useParams } from "react-router";
|
||||
|
||||
function ReviewPage() {
|
||||
let params = useParams();
|
||||
const [newReview, setNewReview] = useState("");
|
||||
|
||||
function submitReview() {
|
||||
fetch("/api/v1/insert_review", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content: newReview,
|
||||
file_id: parseInt(params.id),
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
if (data.error) {
|
||||
alert(data.error);
|
||||
} else {
|
||||
setNewReview("");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="page">
|
||||
<h3>Review Page</h3>
|
||||
<div>
|
||||
<textarea
|
||||
value={newReview}
|
||||
onChange={(e) => setNewReview(e.target.value)}
|
||||
/>
|
||||
<button onClick={() => submitReview()}>Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ReviewPage;
|
||||
Reference in New Issue
Block a user