mirror of
https://github.com/Xevion/banner.git
synced 2026-01-31 10:23:39 -06:00
fix: implement i64 serialization for JavaScript compatibility, fixing avatar URL display
This commit is contained in:
+42
-1
@@ -1,10 +1,46 @@
|
|||||||
//! `sqlx` models for the database schema.
|
//! `sqlx` models for the database schema.
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
|
||||||
|
/// Serialize an `i64` as a string to avoid JavaScript precision loss for values exceeding 2^53.
|
||||||
|
fn serialize_i64_as_string<S: Serializer>(value: &i64, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
serializer.serialize_str(&value.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialize an `i64` from either a number or a string.
|
||||||
|
fn deserialize_i64_from_string<'de, D: Deserializer<'de>>(
|
||||||
|
deserializer: D,
|
||||||
|
) -> Result<i64, D::Error> {
|
||||||
|
use serde::de;
|
||||||
|
|
||||||
|
struct I64OrStringVisitor;
|
||||||
|
|
||||||
|
impl<'de> de::Visitor<'de> for I64OrStringVisitor {
|
||||||
|
type Value = i64;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
formatter.write_str("an integer or a string containing an integer")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_i64<E: de::Error>(self, value: i64) -> Result<i64, E> {
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u64<E: de::Error>(self, value: u64) -> Result<i64, E> {
|
||||||
|
i64::try_from(value).map_err(|_| E::custom(format!("u64 {value} out of i64 range")))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E: de::Error>(self, value: &str) -> Result<i64, E> {
|
||||||
|
value.parse().map_err(de::Error::custom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_any(I64OrStringVisitor)
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents a meeting time stored as JSONB in the courses table.
|
/// Represents a meeting time stored as JSONB in the courses table.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
@@ -162,6 +198,11 @@ pub struct ScrapeJob {
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
|
#[serde(
|
||||||
|
serialize_with = "serialize_i64_as_string",
|
||||||
|
deserialize_with = "deserialize_i64_from_string"
|
||||||
|
)]
|
||||||
|
#[ts(type = "string")]
|
||||||
pub discord_id: i64,
|
pub discord_id: i64,
|
||||||
pub discord_username: String,
|
pub discord_username: String,
|
||||||
pub discord_avatar_hash: Option<String>,
|
pub discord_avatar_hash: Option<String>,
|
||||||
|
|||||||
+1
-1
@@ -167,7 +167,7 @@ export class BannerApiClient {
|
|||||||
return this.request<User[]>("/admin/users");
|
return this.request<User[]>("/admin/users");
|
||||||
}
|
}
|
||||||
|
|
||||||
async setUserAdmin(discordId: bigint, isAdmin: boolean): Promise<User> {
|
async setUserAdmin(discordId: string, isAdmin: boolean): Promise<User> {
|
||||||
const response = await this.fetchFn(`${this.baseUrl}/admin/users/${discordId}/admin`, {
|
const response = await this.fetchFn(`${this.baseUrl}/admin/users/${discordId}/admin`, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Shield, ShieldOff } from "@lucide/svelte";
|
|||||||
|
|
||||||
let users = $state<User[]>([]);
|
let users = $state<User[]>([]);
|
||||||
let error = $state<string | null>(null);
|
let error = $state<string | null>(null);
|
||||||
let updating = $state<bigint | null>(null);
|
let updating = $state<string | null>(null);
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user