add refresh state from file
This commit is contained in:
parent
db417e50d9
commit
0eea1b1ff4
@ -160,6 +160,31 @@ async fn delete_game(
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[post("/refresh")]
|
||||||
|
async fn refresh_state(
|
||||||
|
_token: auth::AdminToken,
|
||||||
|
game_list: &rocket::State<Mutex<Vec<Game>>>,
|
||||||
|
user_list: &rocket::State<Mutex<Vec<User>>>,
|
||||||
|
) -> items::RefreshResponse {
|
||||||
|
if let Some((new_games, new_users)) = store::load_state() {
|
||||||
|
let mut games = game_list.lock().await;
|
||||||
|
let mut users = user_list.lock().await;
|
||||||
|
|
||||||
|
*games = new_games;
|
||||||
|
*users = new_users;
|
||||||
|
|
||||||
|
items::RefreshResponse {
|
||||||
|
success: true,
|
||||||
|
message: "State refreshed from file".to_string(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
items::RefreshResponse {
|
||||||
|
success: false,
|
||||||
|
message: "Failed to load state from file".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[post("/opinion", data = "<req>")]
|
#[post("/opinion", data = "<req>")]
|
||||||
async fn add_opinion(
|
async fn add_opinion(
|
||||||
token: auth::Token,
|
token: auth::Token,
|
||||||
@ -405,6 +430,7 @@ async fn main() -> Result<(), std::io::Error> {
|
|||||||
add_game,
|
add_game,
|
||||||
update_game,
|
update_game,
|
||||||
delete_game,
|
delete_game,
|
||||||
|
refresh_state,
|
||||||
get_game_thumbnail,
|
get_game_thumbnail,
|
||||||
get_games_batch
|
get_games_batch
|
||||||
],
|
],
|
||||||
|
|||||||
@ -90,6 +90,11 @@ export interface LogoutResponse {
|
|||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RefreshResponse {
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface AuthStatusRequest {
|
export interface AuthStatusRequest {
|
||||||
token: string;
|
token: string;
|
||||||
}
|
}
|
||||||
@ -837,6 +842,82 @@ export const LogoutResponse: MessageFns<LogoutResponse> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function createBaseRefreshResponse(): RefreshResponse {
|
||||||
|
return { success: false, message: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RefreshResponse: MessageFns<RefreshResponse> = {
|
||||||
|
encode(message: RefreshResponse, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
|
||||||
|
if (message.success !== false) {
|
||||||
|
writer.uint32(8).bool(message.success);
|
||||||
|
}
|
||||||
|
if (message.message !== "") {
|
||||||
|
writer.uint32(18).string(message.message);
|
||||||
|
}
|
||||||
|
return writer;
|
||||||
|
},
|
||||||
|
|
||||||
|
decode(input: BinaryReader | Uint8Array, length?: number): RefreshResponse {
|
||||||
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
||||||
|
const end = length === undefined ? reader.len : reader.pos + length;
|
||||||
|
const message = createBaseRefreshResponse();
|
||||||
|
while (reader.pos < end) {
|
||||||
|
const tag = reader.uint32();
|
||||||
|
switch (tag >>> 3) {
|
||||||
|
case 1: {
|
||||||
|
if (tag !== 8) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
message.success = reader.bool();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
if (tag !== 18) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
message.message = reader.string();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((tag & 7) === 4 || tag === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
reader.skip(tag & 7);
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
|
||||||
|
fromJSON(object: any): RefreshResponse {
|
||||||
|
return {
|
||||||
|
success: isSet(object.success) ? globalThis.Boolean(object.success) : false,
|
||||||
|
message: isSet(object.message) ? globalThis.String(object.message) : "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
toJSON(message: RefreshResponse): unknown {
|
||||||
|
const obj: any = {};
|
||||||
|
if (message.success !== false) {
|
||||||
|
obj.success = message.success;
|
||||||
|
}
|
||||||
|
if (message.message !== "") {
|
||||||
|
obj.message = message.message;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
|
||||||
|
create<I extends Exact<DeepPartial<RefreshResponse>, I>>(base?: I): RefreshResponse {
|
||||||
|
return RefreshResponse.fromPartial(base ?? ({} as any));
|
||||||
|
},
|
||||||
|
fromPartial<I extends Exact<DeepPartial<RefreshResponse>, I>>(object: I): RefreshResponse {
|
||||||
|
const message = createBaseRefreshResponse();
|
||||||
|
message.success = object.success ?? false;
|
||||||
|
message.message = object.message ?? "";
|
||||||
|
return message;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
function createBaseAuthStatusRequest(): AuthStatusRequest {
|
function createBaseAuthStatusRequest(): AuthStatusRequest {
|
||||||
return { token: "" };
|
return { token: "" };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -172,7 +172,7 @@ function App() {
|
|||||||
</div>
|
</div>
|
||||||
<ShaderBackground theme={theme} />
|
<ShaderBackground theme={theme} />
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<PersonList people={people} />} />
|
<Route path="/" element={<PersonList people={people} onShowToast={addToast} />} />
|
||||||
<Route path="/games" element={<GameList onShowToast={addToast} />} />
|
<Route path="/games" element={<GameList onShowToast={addToast} />} />
|
||||||
<Route path="/filter" element={<GameFilter />} />
|
<Route path="/filter" element={<GameFilter />} />
|
||||||
<Route path="/person/:name" element={<PersonDetails />} />
|
<Route path="/person/:name" element={<PersonDetails />} />
|
||||||
|
|||||||
@ -1,20 +1,41 @@
|
|||||||
import { Person } from "../items";
|
import { Person } from "../items";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { get_auth_status } from "./api";
|
import { get_auth_status, refresh_state, get_is_admin } from "./api";
|
||||||
|
import type { ToastType } from "./Toast";
|
||||||
import "./PersonList.css"
|
import "./PersonList.css"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
people: Person[];
|
people: Person[];
|
||||||
|
onShowToast?: (message: string, type?: ToastType) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PersonList = ({ people }: Props) => {
|
export const PersonList = ({ people, onShowToast }: Props) => {
|
||||||
const [current_user, set_current_user] = useState<string>("");
|
const [current_user, set_current_user] = useState<string>("");
|
||||||
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||||
|
|
||||||
get_auth_status().then((res) => {
|
get_auth_status().then((res) => {
|
||||||
if (res) {
|
if (res) {
|
||||||
set_current_user(res.username);
|
set_current_user(res.username);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleRefresh = async () => {
|
||||||
|
setIsRefreshing(true);
|
||||||
|
try {
|
||||||
|
await refresh_state();
|
||||||
|
onShowToast?.("State refreshed from file successfully", "success");
|
||||||
|
window.location.reload();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
onShowToast?.("Failed to refresh state from file", "error");
|
||||||
|
} finally {
|
||||||
|
setIsRefreshing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const isAdmin = get_is_admin();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
@ -26,6 +47,43 @@ export const PersonList = ({ people }: Props) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<h2>People List</h2>
|
<h2>People List</h2>
|
||||||
|
{isAdmin && (
|
||||||
|
<button
|
||||||
|
onClick={handleRefresh}
|
||||||
|
disabled={isRefreshing}
|
||||||
|
className="btn-secondary"
|
||||||
|
style={{
|
||||||
|
padding: "0.5rem 1rem",
|
||||||
|
fontSize: "0.9rem",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "0.5rem",
|
||||||
|
opacity: isRefreshing ? 0.7 : 1,
|
||||||
|
cursor: isRefreshing ? "not-allowed" : "pointer",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isRefreshing ? (
|
||||||
|
<>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
width: "14px",
|
||||||
|
height: "14px",
|
||||||
|
border: "2px solid rgba(255,255,255,0.3)",
|
||||||
|
borderTopColor: "currentColor",
|
||||||
|
borderRadius: "50%",
|
||||||
|
animation: "spin 0.8s linear infinite",
|
||||||
|
}}
|
||||||
|
></span>
|
||||||
|
Refreshing...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span>🔄</span>
|
||||||
|
Refresh from File
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="grid-container">
|
<div className="grid-container">
|
||||||
{people.map((person, index) => {
|
{people.map((person, index) => {
|
||||||
|
|||||||
@ -55,3 +55,9 @@ export const get_auth_status = async (): Promise<AuthStatusResponse | null> => {
|
|||||||
export const get_is_admin = (): boolean => {
|
export const get_is_admin = (): boolean => {
|
||||||
return localStorage.getItem("isAdmin") === "true";
|
return localStorage.getItem("isAdmin") === "true";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const refresh_state = async (): Promise<void> => {
|
||||||
|
await apiFetch("/api/refresh", {
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@ -50,6 +50,11 @@ message LogoutResponse {
|
|||||||
string message = 2;
|
string message = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message RefreshResponse {
|
||||||
|
bool success = 1;
|
||||||
|
string message = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message AuthStatusRequest { string token = 1; }
|
message AuthStatusRequest { string token = 1; }
|
||||||
|
|
||||||
message AuthStatusResponse {
|
message AuthStatusResponse {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user