seed.rs

  1use collab::{Error, Result};
  2use db::{Db, PostgresDb, UserId};
  3use rand::prelude::*;
  4use serde::{de::DeserializeOwned, Deserialize};
  5use std::fmt::Write;
  6use time::{Duration, OffsetDateTime};
  7
  8#[allow(unused)]
  9#[path = "../db.rs"]
 10mod db;
 11
 12#[derive(Debug, Deserialize)]
 13struct GitHubUser {
 14    id: usize,
 15    login: String,
 16    email: Option<String>,
 17}
 18
 19#[tokio::main]
 20async fn main() {
 21    let mut rng = StdRng::from_entropy();
 22    let database_url = std::env::var("DATABASE_URL").expect("missing DATABASE_URL env var");
 23    let db = PostgresDb::new(&database_url, 5)
 24        .await
 25        .expect("failed to connect to postgres database");
 26    let github_token = std::env::var("GITHUB_TOKEN").expect("missing GITHUB_TOKEN env var");
 27    let client = reqwest::Client::new();
 28
 29    let current_user =
 30        fetch_github::<GitHubUser>(&client, &github_token, "https://api.github.com/user").await;
 31    let staff_users = fetch_github::<Vec<GitHubUser>>(
 32        &client,
 33        &github_token,
 34        "https://api.github.com/orgs/zed-industries/teams/staff/members",
 35    )
 36    .await;
 37
 38    let mut zed_users = Vec::new();
 39    zed_users.push((current_user, true));
 40    zed_users.extend(staff_users.into_iter().map(|user| (user, true)));
 41
 42    let user_count = db
 43        .get_all_users(0, 200)
 44        .await
 45        .expect("failed to load users from db")
 46        .len();
 47    if user_count < 100 {
 48        let mut last_user_id = None;
 49        for _ in 0..10 {
 50            let mut uri = "https://api.github.com/users?per_page=100".to_string();
 51            if let Some(last_user_id) = last_user_id {
 52                write!(&mut uri, "&since={}", last_user_id).unwrap();
 53            }
 54            let users = fetch_github::<Vec<GitHubUser>>(&client, &github_token, &uri).await;
 55            if let Some(last_user) = users.last() {
 56                last_user_id = Some(last_user.id);
 57                zed_users.extend(users.into_iter().map(|user| (user, false)));
 58            } else {
 59                break;
 60            }
 61        }
 62    }
 63
 64    let mut zed_user_ids = Vec::<UserId>::new();
 65    for (github_user, admin) in zed_users {
 66        if let Some(user) = db
 67            .get_user_by_github_login(&github_user.login)
 68            .await
 69            .expect("failed to fetch user")
 70        {
 71            zed_user_ids.push(user.id);
 72        } else {
 73            zed_user_ids.push(
 74                db.create_user(&github_user.login, github_user.email.as_deref(), admin)
 75                    .await
 76                    .expect("failed to insert user"),
 77            );
 78        }
 79    }
 80
 81    let zed_org_id = if let Some(org) = db
 82        .find_org_by_slug("zed")
 83        .await
 84        .expect("failed to fetch org")
 85    {
 86        org.id
 87    } else {
 88        db.create_org("Zed", "zed")
 89            .await
 90            .expect("failed to insert org")
 91    };
 92
 93    let general_channel_id = if let Some(channel) = db
 94        .get_org_channels(zed_org_id)
 95        .await
 96        .expect("failed to fetch channels")
 97        .iter()
 98        .find(|c| c.name == "General")
 99    {
100        channel.id
101    } else {
102        let channel_id = db
103            .create_org_channel(zed_org_id, "General")
104            .await
105            .expect("failed to insert channel");
106
107        let now = OffsetDateTime::now_utc();
108        let max_seconds = Duration::days(100).as_seconds_f64();
109        let mut timestamps = (0..1000)
110            .map(|_| now - Duration::seconds_f64(rng.gen_range(0_f64..=max_seconds)))
111            .collect::<Vec<_>>();
112        timestamps.sort();
113        for timestamp in timestamps {
114            let sender_id = *zed_user_ids.choose(&mut rng).unwrap();
115            let body = lipsum::lipsum_words(rng.gen_range(1..=50));
116            db.create_channel_message(channel_id, sender_id, &body, timestamp, rng.gen())
117                .await
118                .expect("failed to insert message");
119        }
120        channel_id
121    };
122
123    for user_id in zed_user_ids {
124        db.add_org_member(zed_org_id, user_id, true)
125            .await
126            .expect("failed to insert org membership");
127        db.add_channel_member(general_channel_id, user_id, true)
128            .await
129            .expect("failed to insert channel membership");
130    }
131}
132
133async fn fetch_github<T: DeserializeOwned>(
134    client: &reqwest::Client,
135    access_token: &str,
136    url: &str,
137) -> T {
138    let response = client
139        .get(url)
140        .bearer_auth(&access_token)
141        .header("user-agent", "zed")
142        .send()
143        .await
144        .expect(&format!("failed to fetch '{}'", url));
145    response
146        .json()
147        .await
148        .expect(&format!("failed to deserialize github user from '{}'", url))
149}