Skip to content

Commit 9544eb5

Browse files
authored
feat: add frontend-backend file synchronization (#14)
1 parent ab3101a commit 9544eb5

File tree

113 files changed

+3626
-1454
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+3626
-1454
lines changed

Cargo.lock

Lines changed: 0 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ actix = "0.13"
1010
actix-cors = "0.7.1"
1111
actix_error_proc = { version = "1.1.4", features = ["thiserror"] }
1212
actix-web = "4"
13+
actix-ws = "0.3.0"
1314
chrono = "0.4"
14-
cola = "0.5.0"
1515
dotenv = "0.15.0"
1616
env_logger = "0.11.7"
1717
futures = "0.3"
@@ -26,9 +26,9 @@ tokio.workspace = true
2626
uuid = { version = "1.3", features = ["serde", "v4"] }
2727

2828
# Para los tests de integracion
29+
[dev-dependencies]
2930
awc = "3" # Cliente HTTP y WebSocket para Actix
3031
actix-rt = { version = "2.10.0", features = ["macros"] }
3132
futures-util = "0.3"
3233
lazy_static = "1.4"
3334
once_cell = "1.17"
34-
actix-ws = "0.3.0"

backend/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ cargo run --bin backend
1717
```
1818
para ejecutar los tests
1919
```
20-
cargo test --test integration_test
20+
cargo test --no-fail-fast --test integration_test
21+
cargo test --no-fail-fast --bin backend --verbose
2122
```
2223

2324
## Things to know

backend/src/auth/github.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use crate::utils::expect_var;
77
#[derive(Deserialize, Debug)]
88
pub struct GitHubUser {
99
pub login: String,
10-
pub name: Option<String>,
1110
pub avatar_url: String,
1211
}
1312

backend/src/auth/jwt.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use std::sync::LazyLock;
22

33
use actix_web::HttpRequest;
4-
use chrono::{Duration, Utc};
4+
use chrono::{Duration, TimeDelta, Utc};
55
use serde::{Deserialize, Serialize};
66

77
use crate::expect_var;
88
use crate::http_errors::HttpErrors;
99

1010
pub static JWT_SECRET: LazyLock<String> = LazyLock::new(|| expect_var!("JWT_SECRET"));
11+
const JWT_EXP: TimeDelta = Duration::hours(12);
1112

1213
#[derive(Debug, Serialize, Deserialize)]
1314
pub struct RgUserData {
@@ -19,7 +20,7 @@ pub struct RgUserData {
1920

2021
impl RgUserData {
2122
pub fn new(id: String, name: String, is_guest: bool) -> Self {
22-
let exp = Utc::now() + Duration::hours(12);
23+
let exp = Utc::now() + JWT_EXP;
2324

2425
Self {
2526
id,

backend/src/auth/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
pub mod github;
2-
pub mod handlers;
32
pub mod jwt;
3+
pub mod routes;
Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use actix_error_proc::{proof_route, HttpResult};
2-
use actix_web::{get, web, HttpResponse, Responder};
2+
use actix_web::{get, web, HttpRequest, HttpResponse, Responder};
33
use oauth2::{AuthorizationCode, CsrfToken, Scope, TokenResponse};
44
use serde::Deserialize;
55
use uuid::Uuid;
@@ -18,7 +18,7 @@ pub struct AuthRequest {
1818
}
1919

2020
#[get("/auth")]
21-
pub async fn oauth(oauth: web::Data<OAuthData>) -> impl Responder {
21+
pub async fn auth(oauth: web::Data<OAuthData>) -> impl Responder {
2222
let (auth_url, _csrf_token) = oauth
2323
.client
2424
.authorize_url(CsrfToken::new_random)
@@ -30,8 +30,15 @@ pub async fn oauth(oauth: web::Data<OAuthData>) -> impl Responder {
3030
.finish()
3131
}
3232

33+
#[get("/auth/me")]
34+
pub async fn me(req: HttpRequest) -> HttpResult<HttpErrors> {
35+
let user_info = jwt::get_user_info(&req)?;
36+
37+
Ok(HttpResponse::Ok().json(user_info))
38+
}
39+
3340
#[get("/auth/callback")]
34-
async fn auth_callback(
41+
async fn callback(
3542
query: web::Query<AuthRequest>,
3643
oauth_data: web::Data<OAuthData>,
3744
) -> HttpResult<HttpErrors> {
@@ -69,8 +76,8 @@ struct GuestLoginRequest {
6976
guest_name: String,
7077
}
7178

72-
#[proof_route(post("/login-guest"))]
73-
async fn guest_jwt(body: web::Json<GuestLoginRequest>) -> HttpResult<HttpErrors> {
79+
#[proof_route(post("/auth/guest"))]
80+
async fn login_guest(body: web::Json<GuestLoginRequest>) -> HttpResult<HttpErrors> {
7481
let guest_name = &body.guest_name;
7582
let guest_uuid = Uuid::new_v4().to_string();
7683
let jwt = RgUserData::new(guest_uuid.clone(), guest_name.clone(), true).encode()?;
@@ -87,7 +94,7 @@ struct UpdateNameRequest {
8794
new_name: String,
8895
}
8996

90-
#[proof_route(post("/update-name"))]
97+
#[proof_route(post("/auth/update"))]
9198
async fn update_name(
9299
body: web::Json<UpdateNameRequest>,
93100
req: actix_web::HttpRequest,

backend/src/collab/action.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use serde::{Deserialize, Serialize};
2+
3+
#[derive(Serialize, Deserialize, Clone, Debug)]
4+
#[serde(tag = "kind", rename_all = "snake_case")]
5+
pub enum Action {
6+
Insertion {
7+
from: usize,
8+
text: String,
9+
owner: String,
10+
},
11+
Deletion {
12+
from: usize,
13+
to: usize,
14+
owner: String,
15+
},
16+
}

backend/src/collab/document.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use std::collections::HashMap;
2+
3+
use serde::Serialize;
4+
use tokio::sync::Notify;
5+
6+
use super::ot::{apply_actions, transform_actions};
7+
use super::Action;
8+
9+
#[derive(Debug)]
10+
pub struct Document {
11+
pub buffer: String,
12+
/// Users can subscribe to document events
13+
pub notify: Notify,
14+
pub history: Vec<Action>,
15+
pub cursors: HashMap<String, Vec<(usize, usize)>>,
16+
}
17+
18+
#[derive(Clone, Debug, Serialize)]
19+
pub struct DocumentInfo {
20+
pub text: String,
21+
pub revision: usize,
22+
}
23+
24+
impl From<&Document> for DocumentInfo {
25+
fn from(value: &Document) -> Self {
26+
Self {
27+
text: value.buffer.clone(),
28+
revision: value.history.len(),
29+
}
30+
}
31+
}
32+
33+
impl Document {
34+
pub fn new() -> Self {
35+
Document {
36+
buffer: String::new(),
37+
notify: Notify::new(),
38+
history: Vec::new(),
39+
cursors: HashMap::new(),
40+
}
41+
}
42+
43+
pub fn new_with(buffer: String) -> Self {
44+
Document {
45+
buffer,
46+
notify: Notify::new(),
47+
history: Vec::new(),
48+
cursors: HashMap::new(),
49+
}
50+
}
51+
52+
pub fn fork(&self) -> Self {
53+
Document {
54+
buffer: self.buffer.clone(),
55+
history: self.history.clone(),
56+
notify: Notify::new(),
57+
cursors: HashMap::new(),
58+
}
59+
}
60+
61+
pub fn revision(&self) -> usize {
62+
self.history.len()
63+
}
64+
65+
/// Add actions to document history.
66+
/// - Transform desynchorized actions
67+
/// - Notify to document listeners
68+
pub fn compose(&mut self, revision: usize, mut actions: Vec<Action>) -> Vec<Action> {
69+
if revision == self.revision() {
70+
self.buffer = apply_actions(&self.buffer, &actions);
71+
self.history.extend(actions.iter().cloned());
72+
self.notify.notify_waiters();
73+
return actions;
74+
} else if revision > self.history.len() {
75+
log::warn!("Someone comes from the future");
76+
return Vec::new();
77+
}
78+
79+
let desynchronized_history = &self.history[revision..];
80+
81+
transform_actions(actions.as_mut_slice(), desynchronized_history);
82+
83+
self.buffer = apply_actions(&self.buffer, &actions);
84+
self.history.extend_from_slice(&actions);
85+
self.notify.notify_waiters();
86+
87+
actions
88+
}
89+
}

backend/src/collab/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
mod action;
2+
mod document;
3+
pub mod ot;
4+
5+
pub use action::Action;
6+
pub use document::{Document, DocumentInfo};

0 commit comments

Comments
 (0)