WIP for laptop

This commit is contained in:
Julia Lange 2025-08-29 16:54:32 -07:00
parent 55b583b6e6
commit 3b98eb4a95
Signed by: Julia
SSH key fingerprint: SHA256:5DJcfxa5/fKCYn57dcabJa2vN2e6eT0pBerYi5SUbto
5 changed files with 93 additions and 14 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
.env
.pds-data/
target/
tmp/

37
Cargo.lock generated
View file

@ -79,6 +79,18 @@ dependencies = [
"tracing-subscriber", "tracing-subscriber",
] ]
[[package]]
name = "argon2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
dependencies = [
"base64ct",
"blake2",
"cpufeatures",
"password-hash",
]
[[package]] [[package]]
name = "async-lock" name = "async-lock"
version = "3.4.0" version = "3.4.0"
@ -283,6 +295,15 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "blake2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest",
]
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
version = "0.10.4" version = "0.10.4"
@ -667,11 +688,16 @@ dependencies = [
name = "entryway" name = "entryway"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"argon2",
"async-trait",
"atproto", "atproto",
"http 1.3.1", "http 1.3.1",
"router", "router",
"serde", "serde",
"serde_json", "serde_json",
"sqlx",
"thiserror 2.0.12",
"time",
"tokio", "tokio",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
@ -1589,6 +1615,17 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "password-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
"rand_core",
"subtle",
]
[[package]] [[package]]
name = "pem-rfc7468" name = "pem-rfc7468"
version = "0.7.0" version = "0.7.0"

View file

@ -0,0 +1,23 @@
-- PDS Entryway Account Management Schema
-- Minimal schema for account creation and authentication
-- Actor table - stores public identity information
CREATE TABLE actor (
did VARCHAR PRIMARY KEY,
handle VARCHAR,
created_at VARCHAR NOT NULL
);
-- Case-insensitive unique index on handle
CREATE UNIQUE INDEX actor_handle_lower_idx ON actor (LOWER(handle));
-- Account table - stores private authentication data
CREATE TABLE account (
did VARCHAR PRIMARY KEY,
email VARCHAR NOT NULL,
password_scrypt VARCHAR NOT NULL,
email_confirmed_at VARCHAR
);
-- Case-insensitive unique index on email
CREATE UNIQUE INDEX account_email_lower_idx ON account (LOWER(email));

View file

@ -18,13 +18,21 @@ impl Default for Router {
Self::new() Self::new()
} }
} }
impl Router { impl<S> Router<S>
where
S: Clone + Send + Sync + 'static,
{
pub fn new() -> Self { pub fn new() -> Self {
let router = AxumRouter::new(); let router = AxumRouter::new();
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127,0,0,1)), 6702); let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127,0,0,1)), 6702);
Router { router, addr } Router { router, addr }
} }
pub fn with_state<S2>(mut self, state: S) -> Router<S2> {
self.router = self.with_state(S);
self
}
pub fn add_endpoint<E: Endpoint>(mut self, endpoint: E) -> Self { pub fn add_endpoint<E: Endpoint>(mut self, endpoint: E) -> Self {
self.router = endpoint.add_to_router(self.router); self.router = endpoint.add_to_router(self.router);
self self

View file

@ -52,8 +52,11 @@ pub fn response(code: StatusCode, message: &str) -> Response {
error(code, "", message) error(code, "", message)
} }
pub struct QueryInput { pub struct QueryInput<S = ()>
where S: Clone + Send + Sync + 'static,
{
pub parameters: HashMap<String, String>, pub parameters: HashMap<String, String>,
pub state: S,
} }
impl<S> FromRequestParts<S> for QueryInput impl<S> FromRequestParts<S> for QueryInput
where where
@ -61,23 +64,26 @@ where
{ {
type Rejection = Response; type Rejection = Response;
async fn from_request_parts(parts: &mut Parts, _state: &S) async fn from_request_parts(parts: &mut Parts, state: &S)
-> Result<Self, Self::Rejection> { -> Result<Self, Self::Rejection> {
let query_params: Result<Query<HashMap<String, String>>, QueryRejection> = Query::try_from_uri(&parts.uri); let query_params: Result<Query<HashMap<String, String>>, QueryRejection> = Query::try_from_uri(&parts.uri);
match query_params { match query_params {
Ok(p) => Ok(QueryInput { parameters: p.0 }), Ok(p) => Ok(QueryInput { parameters: p.0, state }),
Err(e) => Err(error(StatusCode::BAD_REQUEST, "Bad Parameters", &e.body_text())), Err(e) => Err(error(StatusCode::BAD_REQUEST, "Bad Parameters", &e.body_text())),
} }
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ProcedureInput<J> { pub struct ProcedureInput<J, S = ()>
where S: Clone + Send + Sync + 'static,
{
pub parameters: HashMap<String, String>, pub parameters: HashMap<String, String>,
pub input: J, pub input: J,
pub state: S,
} }
impl<J, S> FromRequest<S> for ProcedureInput<J> impl<J, S> FromRequest<S> for ProcedureInput<J, S>
where where
J: for<'de> serde::Deserialize<'de> + Send + 'static, J: for<'de> serde::Deserialize<'de> + Send + 'static,
Bytes: FromRequest<S>, Bytes: FromRequest<S>,
@ -95,7 +101,7 @@ where
.map(|Json(v)| v) .map(|Json(v)| v)
.map_err(|e| error(StatusCode::BAD_REQUEST, "Bad Parameters", &e.body_text()))?; .map_err(|e| error(StatusCode::BAD_REQUEST, "Bad Parameters", &e.body_text()))?;
Ok(ProcedureInput { parameters, input }) Ok(ProcedureInput { parameters, input, state })
} }
} }
@ -125,14 +131,14 @@ where
} }
impl XrpcEndpoint { impl XrpcEndpoint {
pub fn new_query<Q>(nsid: Nsid, query: Q) -> Self pub fn new_query<Q, S>(nsid: Nsid, query: Q) -> Self
where where
Q: XrpcHandler<QueryInput> + Clone Q: XrpcHandler<QueryInput> + Clone
{ {
XrpcEndpoint { XrpcEndpoint {
path: Path::Nsid(nsid), path: Path::Nsid(nsid),
resolver: get(async move | mut parts: Parts | -> Response { resolver: get(async move | mut parts: Parts, state: &S | -> Response {
match QueryInput::from_request_parts(&mut parts, &()).await { match QueryInput<S>::from_request_parts(&mut parts, state).await {
Ok(qi) => query.call(qi).await, Ok(qi) => query.call(qi).await,
Err(e) => e Err(e) => e
} }
@ -140,15 +146,15 @@ impl XrpcEndpoint {
} }
} }
pub fn new_procedure<P, J>(nsid: Nsid, procedure: P) -> Self pub fn new_procedure<P, J, S>(nsid: Nsid, procedure: P) -> Self
where where
P: XrpcHandler<ProcedureInput<J>> + Clone, P: XrpcHandler<ProcedureInput<J, S>> + Clone,
J: for<'de> serde::Deserialize<'de> + Send + 'static, J: for<'de> serde::Deserialize<'de> + Send + 'static,
{ {
XrpcEndpoint { XrpcEndpoint {
path: Path::Nsid(nsid), path: Path::Nsid(nsid),
resolver: post(async move | req: Request | -> Response { resolver: post(async move | req: Request, state: &S | -> Response {
match ProcedureInput::<J>::from_request(req, &()).await { match ProcedureInput::<J, S>::from_request(req, &state).await {
Ok(pi) => procedure.call(pi).await, Ok(pi) => procedure.call(pi).await,
Err(e) => e Err(e) => e
} }