/ src / api.ts
api.ts
  1  
  2  // So need to return an array of users with their trust levels of other users
  3  // included, and then the frontend can parse that. 
  4  
  5  /**
  6   * The whole world kind of needs to be loaded and cached here, and then you can
  7   * request it from the perspective of a user and only the relevant things to 
  8   * that user are returned instead of the entire world. So that the client has the 
  9   * minimal stuff to render, while the server caches the world in memory. 
 10   * 
 11   * Need some way of the server world auto-updating and caching when new messages 
 12   * come in from the network too. How would that work? Need some kind of automatic
 13   * state and flow update. 
 14   */
 15  
 16  import express from 'express'
 17  import levelup from 'levelup';
 18  import leveldown from 'leveldown';
 19  import { createClient } from 'redis';
 20  import { User } from './models';
 21  
 22  
 23  const app = express();
 24  
 25  const MAX_USER_ID = 200000;
 26  const MAX_DEPTH = 3;
 27  const db = levelup(leveldown('./likesdb'))
 28  
 29  let client;
 30  let totalGets = 0;
 31  
 32  type UserData = Record<string, string>
 33  
 34  const users: User[] = [];
 35  
 36  async function getSingleUserData(userId, depth = 1): Promise<UserData> {
 37    let userData = {};
 38    try {
 39      totalGets++;
 40      userData = await client.get(userId);
 41    } catch (e) {
 42      return {};
 43    }
 44    if (!userData) {
 45      return {};
 46    }
 47    const userDataObject = JSON.parse(userData.toString());
 48    return userDataObject
 49  }
 50  
 51  async function getMultiUserData(userData: UserData, depth = 1) {
 52    if (!userData) {
 53      return {};
 54    }
 55    if (depth >= MAX_DEPTH) {
 56      return userData;
 57    }
 58    const subUserKeys = Object.keys(userData);
 59    totalGets++;
 60    const subUserData = await client.mGet(subUserKeys);
 61    const userDataFilled = await Promise.all(
 62      subUserData.map(async (userData) => {
 63        const expandedData = await getMultiUserData(JSON.parse(userData), ++depth);
 64        return expandedData;
 65      })
 66    );
 67    return userDataFilled;
 68  }
 69  
 70  async function loadUsers(maxUserId) {
 71    console.time("loadUsers");
 72    console.log("Loading Users");
 73    for (let i = 0; i < maxUserId; i++) {
 74      users[i] = new User(i);
 75    }
 76  
 77    console.log("Setting trust levels");
 78    for (let i = 0; i < maxUserId; i++) {
 79      let userData;
 80      try {
 81        userData = await db.get(i.toString());
 82      } catch (e) {
 83        continue;
 84      }
 85      if (!userData) continue;
 86      const userDataObject = JSON.parse(userData.toString());
 87      Object.entries(userDataObject).forEach(([id, trust]) => {
 88        try {
 89          users[i].trustUser(users[id], trust);
 90        } catch (e) {
 91          // Ignore
 92        }
 93      });
 94    }
 95  
 96    console.timeEnd("loadUsers");
 97  }
 98  
 99  async function getUserTrustLevels(userId): Promise<Record<string, number>> {
100    const user = users[Number(userId)];
101    console.log("User: ", user);
102    user.calculateTrust(MAX_DEPTH);
103    return user.getTrustLevels();
104  }
105  
106  async function init() {
107    await db.open();
108    client = await createClient()
109      .on('error', err => console.log('Redis Client Error', err))
110      .connect();
111    await loadUsers(MAX_USER_ID);
112  }
113  
114  app.get('/user/:id', async (req, res) => {
115    totalGets = 0;
116    const id = req.params.id;
117    console.log("Getting user ", id);
118    console.time('getUserData');
119    const userTrustLevels = await getUserTrustLevels(req.params.id);
120    console.timeEnd('getUserData');
121    console.log("Total gets: ", totalGets);
122    res.json(userTrustLevels);
123  });
124  
125  init();
126  console.log("Api running");
127  app.listen(4000);