fix: no more id collisions

This commit is contained in:
trisua 2025-05-06 16:13:48 -04:00
parent 71d017693c
commit 0b3573a513
11 changed files with 151 additions and 120 deletions

109
Cargo.lock generated
View file

@ -1211,6 +1211,19 @@ dependencies = [
"slab",
]
[[package]]
name = "generator"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
dependencies = [
"cc",
"libc",
"log",
"rustversion",
"windows",
]
[[package]]
name = "generic-array"
version = "0.14.7"
@ -2035,6 +2048,19 @@ version = "0.4.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
[[package]]
name = "loom"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
dependencies = [
"cfg-if",
"generator",
"scoped-tls",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "loop9"
version = "0.1.5"
@ -3210,6 +3236,12 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "scopeguard"
version = "1.2.0"
@ -3461,6 +3493,15 @@ dependencies = [
"serde",
]
[[package]]
name = "snowflaked"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "398d462c4c454399be452039b24b0aa0ecb4c7a57f6ae615f5d25de2b032f850"
dependencies = [
"loom",
]
[[package]]
name = "socket2"
version = "0.5.8"
@ -3773,10 +3814,10 @@ dependencies = [
"chrono",
"comrak",
"hex_fmt",
"num-bigint",
"rand 0.9.1",
"serde",
"sha2",
"snowflaked",
"uuid 1.16.0",
]
@ -4633,6 +4674,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-core"
version = "0.52.0"
@ -4695,6 +4745,21 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
@ -4727,6 +4792,12 @@ dependencies = [
"windows_x86_64_msvc 0.53.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
@ -4739,6 +4810,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
@ -4751,6 +4828,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@ -4775,6 +4858,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
@ -4787,6 +4876,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
@ -4799,6 +4894,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
@ -4811,6 +4912,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"

View file

@ -234,7 +234,7 @@ and show_community and community.id != config.town_square or question %}
{% else %}
<details>
<summary
class="card flex gap-2 items-center secondary red w-full"
class="card flex gap-2 items-center tertiary red w-full"
>
{{ icon "triangle-alert" }}
<b>{{ post.context.content_warning }}</b>

View file

@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use totp_rs::TOTP;
use tetratto_shared::{
hash::{hash_salted, salt},
snow::AlmostSnowflake,
snow::Snowflake,
unix_epoch_timestamp,
};
@ -227,10 +227,7 @@ impl User {
let password = hash_salted(password, salt.clone());
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
created: unix_epoch_timestamp() as usize,
username,
password,
@ -408,10 +405,7 @@ impl Notification {
/// Returns a new [`Notification`].
pub fn new(title: String, content: String, owner: usize) -> Self {
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
created: unix_epoch_timestamp() as usize,
title,
content,
@ -434,10 +428,7 @@ impl UserFollow {
/// Create a new [`UserFollow`].
pub fn new(initiator: usize, receiver: usize) -> Self {
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
created: unix_epoch_timestamp() as usize,
initiator,
receiver,
@ -465,10 +456,7 @@ impl UserBlock {
/// Create a new [`UserBlock`].
pub fn new(initiator: usize, receiver: usize) -> Self {
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
created: unix_epoch_timestamp() as usize,
initiator,
receiver,
@ -488,10 +476,7 @@ impl IpBlock {
/// Create a new [`IpBlock`].
pub fn new(initiator: usize, receiver: String) -> Self {
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
created: unix_epoch_timestamp() as usize,
initiator,
receiver,
@ -532,10 +517,7 @@ impl UserWarning {
/// Create a new [`UserWarning`].
pub fn new(user: usize, moderator: usize, content: String) -> Self {
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
created: unix_epoch_timestamp() as usize,
receiver: user,
moderator,

View file

@ -1,5 +1,5 @@
use serde::{Serialize, Deserialize};
use tetratto_shared::{snow::AlmostSnowflake, unix_epoch_timestamp};
use tetratto_shared::{snow::Snowflake, unix_epoch_timestamp};
use super::communities_permissions::CommunityPermission;
@ -30,10 +30,7 @@ impl Channel {
/// Create a new [`Channel`].
pub fn new(community: usize, owner: usize, position: usize, title: String) -> Self {
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
community,
owner,
created: unix_epoch_timestamp() as usize,
@ -84,10 +81,7 @@ impl Message {
let now = unix_epoch_timestamp() as usize;
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
channel,
owner,
created: now,

View file

@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use tetratto_shared::{snow::AlmostSnowflake, unix_epoch_timestamp};
use tetratto_shared::{snow::Snowflake, unix_epoch_timestamp};
use super::communities_permissions::CommunityPermission;
#[derive(Clone, Serialize, Deserialize)]
@ -30,10 +30,7 @@ impl Community {
/// Create a new [`Community`].
pub fn new(title: String, owner: usize) -> Self {
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
created: unix_epoch_timestamp() as usize,
title: title.clone(),
context: CommunityContext {
@ -145,10 +142,7 @@ impl CommunityMembership {
/// Create a new [`CommunityMembership`].
pub fn new(owner: usize, community: usize, role: CommunityPermission) -> Self {
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
created: unix_epoch_timestamp() as usize,
owner,
community,
@ -250,10 +244,7 @@ impl Post {
owner: usize,
) -> Self {
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
created: unix_epoch_timestamp() as usize,
content,
owner,
@ -328,10 +319,7 @@ impl Question {
ip: String,
) -> Self {
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
created: unix_epoch_timestamp() as usize,
owner,
receiver,

View file

@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use tetratto_shared::{snow::AlmostSnowflake, unix_epoch_timestamp};
use tetratto_shared::{snow::Snowflake, unix_epoch_timestamp};
use super::reactions::AssetType;
@ -15,10 +15,7 @@ impl AuditLogEntry {
/// Create a new [`AuditLogEntry`].
pub fn new(moderator: usize, content: String) -> Self {
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
created: unix_epoch_timestamp() as usize,
moderator,
content,
@ -40,10 +37,7 @@ impl Report {
/// Create a new [`Report`].
pub fn new(owner: usize, content: String, asset: usize, asset_type: AssetType) -> Self {
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
created: unix_epoch_timestamp() as usize,
owner,
content,

View file

@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use tetratto_shared::{snow::AlmostSnowflake, unix_epoch_timestamp};
use tetratto_shared::{snow::Snowflake, unix_epoch_timestamp};
/// All of the items which support reactions.
#[derive(Serialize, Deserialize)]
@ -28,10 +28,7 @@ impl Reaction {
/// Create a new [`Reaction`].
pub fn new(owner: usize, asset: usize, asset_type: AssetType, is_like: bool) -> Self {
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
created: unix_epoch_timestamp() as usize,
owner,
asset,

View file

@ -1,5 +1,5 @@
use serde::{Serialize, Deserialize};
use tetratto_shared::{snow::AlmostSnowflake, unix_epoch_timestamp};
use tetratto_shared::{snow::Snowflake, unix_epoch_timestamp};
#[derive(Serialize, Deserialize, PartialEq, Eq)]
pub enum ActionType {
@ -32,10 +32,7 @@ impl ActionRequest {
/// Create a new [`ActionRequest`].
pub fn new(owner: usize, action_type: ActionType, linked_asset: usize) -> Self {
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
created: unix_epoch_timestamp() as usize,
owner,
action_type,

View file

@ -1,5 +1,5 @@
use serde::{Serialize, Deserialize};
use tetratto_shared::{snow::AlmostSnowflake, unix_epoch_timestamp};
use tetratto_shared::{snow::Snowflake, unix_epoch_timestamp};
#[derive(Serialize, Deserialize)]
pub enum MediaType {
@ -27,10 +27,7 @@ impl MediaUpload {
/// Create a new [`MediaUpload`].
pub fn new(what: MediaType, owner: usize) -> Self {
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
created: unix_epoch_timestamp() as usize,
owner,
what,
@ -61,10 +58,7 @@ impl CustomEmoji {
is_animated: bool,
) -> Self {
Self {
id: AlmostSnowflake::new(1234567890)
.to_string()
.parse::<usize>()
.unwrap(),
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
created: unix_epoch_timestamp() as usize,
owner,
community,

View file

@ -11,8 +11,8 @@ ammonia = "4.1.0"
chrono = "0.4.41"
comrak = "0.38.0"
hex_fmt = "0.3.0"
num-bigint = "0.4.6"
rand = "0.9.1"
serde = "1.0.219"
sha2 = "0.10.8"
snowflaked = "1.0.3"
uuid = { version = "1.16.0", features = ["v4"] }

View file

@ -1,51 +1,29 @@
//! Almost Snowflake
//!
//! Random IDs which include timestamp information (like Twitter Snowflakes)
//!
//! IDs are generated with 41 bits of an epoch timestamp, 10 bits of a machine/server ID, and 12 bits of randomly generated numbers.
//!
//! ```
//! tttttttttttttttttttttttttttttttttttttttttiiiiiiiiiirrrrrrrrrrrr...
//! Timestamp ID Seed
//! ```
use crate::epoch_timestamp;
use std::time::{UNIX_EPOCH, Duration};
use serde::{Deserialize, Serialize};
use snowflaked::{Builder, Generator};
use num_bigint::BigInt;
use rand::Rng;
static SEED_LEN: usize = 12;
// static ID_LEN: usize = 10;
pub const EPOCH_2024: u64 = 1704067200000;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct AlmostSnowflake(String);
pub struct Snowflake(String);
pub fn bigint(input: usize) -> BigInt {
BigInt::from(input)
}
impl Snowflake {
pub fn builder() -> Builder {
Builder::new().epoch(UNIX_EPOCH + Duration::from_millis(EPOCH_2024))
}
impl AlmostSnowflake {
/// Create a new [`AlmostSnowflake`]
pub fn new(server_id: usize) -> Self {
// generate random bytes
let mut bytes = String::new();
let mut rng = rand::rng();
for _ in 1..=SEED_LEN {
bytes.push_str(&rng.random_range(0..10).to_string())
}
// build id
let mut id = bigint(epoch_timestamp(2024) as usize) << 22_u128;
id |= bigint((server_id % 1024) << 12);
id |= bigint((bytes.parse::<usize>().unwrap() + 1) % 4096);
// return
Self(id.to_string())
pub fn new() -> Self {
Self(
Self::builder()
.build::<Generator>()
.generate::<u64>()
.to_string(),
)
}
}
impl std::fmt::Display for AlmostSnowflake {
impl std::fmt::Display for Snowflake {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}