Merge branch 'master' into dbms

This commit is contained in:
2021-12-11 00:46:38 +08:00
13 changed files with 173 additions and 75 deletions

View File

@@ -411,10 +411,10 @@ Anonymous API can be called by anonymous.
Currently only few APIs in font-end.
- `/#/share/39`
- `/#/files/39/share`
Share a specific file.
- `/#/search-folders/2614`
- `/#/folders/2614`
Show files in a specific folder.

View File

@@ -188,6 +188,15 @@ func (database *Database) FindFolder(folder string) (int64, error) {
return id, nil
}
func (database *Database) FindFile(folderId int64, filename string) (int64, error) {
var id int64
err := database.stmt.findFile.QueryRow(folderId, filename).Scan(&id)
if err != nil {
return 0, err
}
return id, nil
}
func (database *Database) InsertFolder(folder string) (int64, error) {
result, err := database.stmt.insertFolder.Exec(folder, filepath.Base(folder))
if err != nil {
@@ -217,6 +226,13 @@ func (database *Database) Insert(path string, filesize int64) error {
return err
}
}
// if file exists, skip it
_, err = database.FindFile(folderId, filename)
if err == nil {
return nil
}
err = database.InsertFile(folderId, filename, filesize)
if err != nil {
return err

View File

@@ -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
feedback TEXT NOT NULL,
header TEXT NOT NULL
);`
var initUsersTableQuery = `CREATE TABLE IF NOT EXISTS users (
@@ -104,6 +105,8 @@ VALUES (?, ?);`
var findFolderQuery = `SELECT id FROM folders WHERE folder = ? LIMIT 1;`
var findFileQuery = `SELECT id FROM files WHERE folder_id = ? AND filename = ? LIMIT 1;`
var insertFileQuery = `INSERT INTO files (folder_id, filename, filesize)
VALUES (?, ?, ?);`
@@ -147,8 +150,8 @@ JOIN folders ON files.folder_id = folders.id
ORDER BY RANDOM()
LIMIT ?;`
var insertFeedbackQuery = `INSERT INTO feedbacks (time, feedback)
VALUES (?, ?);`
var insertFeedbackQuery = `INSERT INTO feedbacks (time, feedback, header)
VALUES (?, ?, ?);`
type Stmt struct {
initFilesTable *sql.Stmt
@@ -166,6 +169,7 @@ type Stmt struct {
insertFolder *sql.Stmt
insertFile *sql.Stmt
findFolder *sql.Stmt
findFile *sql.Stmt
searchFiles *sql.Stmt
getFolder *sql.Stmt
dropFiles *sql.Stmt
@@ -316,6 +320,12 @@ func NewPreparedStatement(sqlConn *sql.DB) (*Stmt, error) {
return nil, err
}
// init findFile statement
stmt.findFile, err = sqlConn.Prepare(findFileQuery)
if err != nil {
return nil, err
}
// init insertFile stmt
stmt.insertFile, err = sqlConn.Prepare(insertFileQuery)
if err != nil {

View File

@@ -9,6 +9,7 @@ import "./App.css";
import GetRandomFiles from "./component/GetRandomFiles";
import SearchFiles from "./component/SearchFiles";
import SearchFolders from "./component/SearchFolders";
import FilesInFolder from "./component/FilesInFolder";
import Manage from "./component/Manage";
import Share from "./component/Share";
import AudioPlayer from "./component/AudioPlayer";
@@ -28,10 +29,10 @@ function App() {
<NavLink to="/" className="nav-link">
Feeling luckly
</NavLink>
<NavLink to="/search-files" className="nav-link">
<NavLink to="/files" className="nav-link">
Files
</NavLink>
<NavLink to="/search-folders" className="nav-link">
<NavLink to="/folders" className="nav-link">
Folders
</NavLink>
<NavLink to="/manage" className="nav-link">
@@ -47,20 +48,20 @@ function App() {
element={<GetRandomFiles setPlayingFile={setPlayingFile} />}
/>
<Route
path="/search-files"
path="/files"
element={<SearchFiles setPlayingFile={setPlayingFile} />}
/>
<Route
path="/search-folders"
path="/folders"
element={<SearchFolders setPlayingFile={setPlayingFile} />}
/>
<Route
path="/search-folders/:id"
element={<SearchFolders setPlayingFile={setPlayingFile} />}
path="/folders/:id"
element={<FilesInFolder setPlayingFile={setPlayingFile} />}
/>
<Route path="/manage" element={<Manage />} />
<Route
path="/share/:id"
path="/files/:id/share"
element={<Share setPlayingFile={setPlayingFile} />}
/>
</Routes>

View File

@@ -80,7 +80,7 @@ function AudioPlayer(props) {
<button
onClick={() =>
navigate(`search-folders/${props.playingFile.folder_id}`)
navigate(`/folders/${props.playingFile.folder_id}`)
}
>
{props.playingFile.foldername}

View File

@@ -33,7 +33,7 @@ function FileDialog(props) {
</button>
<button
onClick={() => {
navigate(`/share/${props.file.id}`);
navigate(`/files/${props.file.id}/share`);
props.setShowStatus(false);
}}
>

View File

@@ -25,7 +25,7 @@ function FileEntry(props) {
</td>
<td
className="clickable"
onClick={() => navigate(`/search-folders/${props.file.folder_id}`)}
onClick={() => navigate(`/folders/${props.file.folder_id}`)}
>
{props.file.foldername}
</td>

View File

@@ -0,0 +1,60 @@
import { useParams } from "react-router";
import { useState, useEffect } from "react";
import FilesTable from "./FilesTable";
function FilesInFolder(props) {
let params = useParams();
const [files, setFiles] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [offset, setOffset] = useState(0);
const limit = 10;
useEffect(() => {
setIsLoading(true);
fetch("/api/v1/get_files_in_folder", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
folder_id: parseInt(params.id),
offset: offset,
limit: limit,
}),
})
.then((response) => response.json())
.then((data) => {
setFiles(data.files ? data.files : []);
})
.catch((error) => alert(error))
.finally(() => {
setIsLoading(false);
});
}, [params.id, offset]);
function nextPage() {
setOffset(offset + limit);
}
function lastPage() {
const offsetValue = offset - limit;
if (offsetValue < 0) {
return;
}
setOffset(offsetValue);
}
return (
<div className="page">
<h3>Files in Folder</h3>
<div className="search_toolbar">
<button onClick={lastPage}>Last page</button>
<button disabled>
{isLoading ? "Loading..." : `${offset} - ${offset + files.length}`}
</button>
<button onClick={nextPage}>Next page</button>
</div>
<FilesTable setPlayingFile={props.setPlayingFile} files={files} />
</div>
);
}
export default FilesInFolder;

View File

@@ -1,6 +1,9 @@
import FileEntry from "./FileEntry";
function FilesTable(props) {
if (props.files.length === 0) {
return null;
}
return (
<table>
<thead>

View File

@@ -2,6 +2,9 @@ import { useNavigate } from "react-router";
function FoldersTable(props) {
let navigate = useNavigate();
if (props.folders.length === 0) {
return null;
}
return (
<table>
<thead>
@@ -14,12 +17,12 @@ function FoldersTable(props) {
{props.folders.map((folder) => (
<tr key={folder.id}>
<td
onClick={() => navigate(`/search-folders/${folder.id}`)}
onClick={() => navigate(`/folders/${folder.id}`)}
className="clickable"
>
{folder.foldername}
</td>
<td onClick={() => navigate(`/search-folders/${folder.id}`)}>
<td onClick={() => navigate(`/folders/${folder.id}`)}>
<button>View</button>
</td>
</tr>

View File

@@ -1,9 +1,51 @@
import { useState } from "react";
function Manage() {
const [token, setToken] = useState("");
const [walkPath, setWalkPath] = useState("");
function updateDatabase() {
fetch("/api/v1/walk", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
token: token,
root: walkPath,
pattern: [".wav", ".mp3"],
}),
})
.then((res) => res.json())
.then((data) => {
console.log(data);
});
}
return (
<div>
<h2>Manage</h2>
<input
type="text"
value={token}
placeholder="token"
onChange={(e) => setToken(e.target.value)}
/>
<input
type="text"
value={walkPath}
placeholder="walk path"
onChange={(e) => setWalkPath(e.target.value)}
/>
<button
onClick={() => {
updateDatabase();
}}
>
Update Database
</button>
</div>
)
);
}
export default Manage;

View File

@@ -9,25 +9,14 @@ function SearchFiles(props) {
const limit = 10;
function searchFiles() {
if (
filename === "" &&
(props.folder === undefined || props.folder.id === undefined)
) {
return;
}
const folder = props.folder ? props.folder : {};
const url = folder.id
? "/api/v1/get_files_in_folder"
: "/api/v1/search_files";
setIsLoading(true);
fetch(url, {
fetch("/api/v1/search_files", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
filename: filename,
limit: limit,
offset: offset,
folder_id: folder.id,
}),
})
.then((response) => response.json())
@@ -36,7 +25,7 @@ function SearchFiles(props) {
setFiles(files);
})
.catch((error) => {
alert("get_files_in_folder error: " + error);
alert("search_files error: " + error);
})
.finally(() => {
setIsLoading(false);
@@ -55,13 +44,12 @@ function SearchFiles(props) {
setOffset(offsetValue);
}
useEffect(() => searchFiles(), [offset, props.folder]); // eslint-disable-line react-hooks/exhaustive-deps
useEffect(() => searchFiles(), [offset]); // eslint-disable-line react-hooks/exhaustive-deps
return (
<div className="page">
<h3>Search Files</h3>
<div className="search_toolbar">
{!props.folder && (
<input
onChange={(event) => setFilename(event.target.value)}
onKeyDown={(event) => {
@@ -72,18 +60,13 @@ function SearchFiles(props) {
type="text"
placeholder="Enter filename"
/>
)}
<button
disabled={!!props.folder}
onClick={() => {
searchFiles();
}}
>
{isLoading ? "Loading..." : "Search"}
</button>
{props.folder && props.folder.foldername && (
<button onClick={searchFiles}>{props.folder.foldername}</button>
)}
<button onClick={lastPage}>Last page</button>
<button disabled>
{offset} - {offset + files.length}

View File

@@ -1,12 +1,9 @@
import { useEffect, useState } from "react";
import { useParams } from "react-router";
import FoldersTable from "./FoldersTable";
import SearchFiles from "./SearchFiles";
function SearchFolders(props) {
function SearchFolders() {
const [foldername, setFoldername] = useState("");
const [folders, setFolders] = useState([]);
const [folder, setFolder] = useState({});
const [offset, setOffset] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const limit = 10;
@@ -27,13 +24,7 @@ function SearchFolders(props) {
})
.then((response) => response.json())
.then((data) => {
let folders;
if (data.folders) {
folders = data.folders;
} else {
folders = [];
}
setFolders(folders);
setFolders(data.folders ? data.folders : []);
})
.catch((error) => {
alert("search_folders error: " + error);
@@ -55,17 +46,7 @@ function SearchFolders(props) {
setOffset(offsetValue);
}
function viewFolder(folder) {
setFolder(folder);
}
let params = useParams();
useEffect(() => searchFolder(), [offset]); // eslint-disable-line react-hooks/exhaustive-deps
useEffect(() => {
if (params.id !== undefined) {
setFolder({ id: parseInt(params.id) });
}
}, [params.id]);
return (
<div className="page">
@@ -90,8 +71,7 @@ function SearchFolders(props) {
</button>
<button onClick={nextPage}>Next page</button>
</div>
<FoldersTable viewFolder={viewFolder} folders={folders} />
<SearchFiles setPlayingFile={props.setPlayingFile} folder={folder} />
<FoldersTable folders={folders} />
</div>
);
}