show unread mail count in topbar
[browser-rts.git] / src / api.ts
index 50d4f06d014eeb92b75c7bc8df2a63b665d4795b..5b1edc657a5969056141dc3190231d429106e519 100644 (file)
@@ -1,7 +1,7 @@
 import { HttpServer } from './lib/server';
 import * as config from './config';
 import { AccountRepository } from './repository/accounts';
-import { CityRepository } from './repository/city';
+import { City, CityRepository } from './repository/city';
 import { MailRepository } from './repository/mail';
 import {BadInputError, ERROR_CODE, NotFoundError} from './errors';
 import { renderKingomOverview } from './render/kingdom-overview';
@@ -19,6 +19,7 @@ import { renderCost } from './render/costs';
 import {renderMailroom, renderMessage} from './render/mail';
 import {topbar} from './render/topbar';
 import {renderPublicChatMessage} from './render/chat-message';
+import validator from 'validator';
 
 
 const server = new HttpServer(config.API_PORT);
@@ -99,6 +100,8 @@ server.get<{params: { cityId: string }}, string>('/city/:cityId', async req => {
 server.get<{}, string>('/poll/overview', async req => {
        const account = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
        const city = await cityRepo.getUsersCity(account.id);
+  const unreadMail = await mailRepo.countUnread(account.id);
+
 
   const usage = {
     foodUsagePerTick: await cityRepo.foodUsagePerTick(city),
@@ -110,13 +113,14 @@ server.get<{}, string>('/poll/overview', async req => {
        return renderKingomOverview({
     ...city,
     ...usage
-  }, account) + topbar({...city, ...usage});
+  }, account) + topbar({...city, ...usage}, unreadMail);
 });
 
 server.get<{}, string>('/poll/construction', async req => {
        const account = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
        const city = await cityRepo.getUsersCity(account.id);
        const buildings = await cityRepo.buildingRepository.list();
+  const unreadMail = await mailRepo.countUnread(account.id);
 
        const buildQueues = await cityRepo.getBuildQueues(account.id);
   const usage = {
@@ -125,12 +129,13 @@ server.get<{}, string>('/poll/construction', async req => {
     energyUsagePerTick: await cityRepo.energyUsagePerTick(city),
     energyProductionPerTick: await cityRepo.energyProductionPerTick(city)
   }
-       return renderLandDevelopment(city, buildings, buildQueues) + topbar({...city, ...usage});
+       return renderLandDevelopment(city, buildings, buildQueues) + topbar({...city, ...usage}, unreadMail);
 });
 
 server.get<{}, string>('/poll/unit-training', async req => {
        const account = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
        const city = await cityRepo.getUsersCity(account.id);
+  const unreadMail = await mailRepo.countUnread(account.id);
 
        const unitTrainingQueues = await cityRepo.getUnitTrainingQueues(account.id);
        const units = await cityRepo.unitRepository.list();
@@ -144,12 +149,13 @@ server.get<{}, string>('/poll/unit-training', async req => {
        return renderUnitTraining(city, units, unitTrainingQueues) + topbar({
     ...city,
     ...usage
-  });
+  }, unreadMail);
 });
 
 server.post<{body: {sector: string}}, string>('/poll/map', async req => {
        const account = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
        const city = await cityRepo.getUsersCity(account.id);
+  const unreadMail = await mailRepo.countUnread(account.id);
 
   let sector = city.sector_id;
   if(req.body.sector) {
@@ -171,12 +177,13 @@ server.post<{body: {sector: string}}, string>('/poll/map', async req => {
        return renderOverworldMap(await cityRepo.findAllInSector(sector), city, sector) + topbar({
     ...city,
     ...usage
-  });
+  }, unreadMail);
 });
 
 server.get<{}, string>('/poll/mailroom', async req => {
        const account = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
        const city = await cityRepo.getUsersCity(account.id);
+  const unreadMail = await mailRepo.countUnread(account.id);
 
   const usage = {
     foodUsagePerTick: await cityRepo.foodUsagePerTick(city),
@@ -188,7 +195,7 @@ server.get<{}, string>('/poll/mailroom', async req => {
        return renderMailroom(await mailRepo.listReceivedMessages(account.id)) + topbar({
     ...city,
     ...usage
-  });
+  }, unreadMail);
 });
 
 
@@ -335,24 +342,29 @@ server.post<{
                });
        }, 'reload-outgoing-attacks');
 
-server.get<void, string>('/messages', async req => {
-       const acct = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
-       const msgs = await mailRepo.listReceivedMessages(acct.id);
-
-       return JSON.stringify(msgs);
-});
-
 server.get<{params: {id: string}}, string>('/messages/:id', async req => {
        const acct = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
+  const city = await cityRepo.getUsersCity(acct.id);
        const msg = await mailRepo.getMessage(req.params.id, acct.id);
 
        if(!msg) {
                throw new NotFoundError('No such message', ERROR_CODE.DUPLICATE_CACHE_KEY);
        }
 
+  const usage = {
+    foodUsagePerTick: await cityRepo.foodUsagePerTick(city),
+    foodProductionPerTick: await cityRepo.foodProductionPerTick(city),
+    energyUsagePerTick: await cityRepo.energyUsagePerTick(city),
+    energyProductionPerTick: await cityRepo.energyProductionPerTick(city)
+  }
+
        await mailRepo.markAsRead(msg.id, msg.to_account);
+  const unreadMail = await mailRepo.countUnread(acct.id);
 
-       return renderMessage(msg);
+       return renderMailroom(await mailRepo.listReceivedMessages(acct.id), msg) + topbar({
+    ...city,
+    ...usage
+  }, unreadMail);
 });
 
 server.get<void, string>('/attacks/outgoing', async req => {
@@ -379,6 +391,92 @@ server.post<{body: {message: string}}, void>('/chat', async req => {
   return;
 });
 
+server.post<{params: {queueId: string}}, void>('/construction/:queueId/cancel', async req => {
+       const acct = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
+       const city = await cityRepo.getUsersCity(acct.id);
+
+  if(!validator.isUUID(req.params.queueId)) {
+    throw new BadInputError('Invalid queue ID', ERROR_CODE.INVALID_BUILD_QUEUE);
+  }
+
+  // validate that this is an actual queue
+  const queue = await cityRepo.buildQueue.FindOne({owner: city.owner, id: req.params.queueId});
+
+  if(!queue) {
+    throw new NotFoundError('That queue does not exist', ERROR_CODE.INVALID_BUILD_QUEUE);
+  }
+
+  const [, building] = await Promise.all([
+    cityRepo.buildQueue.Delete({
+      owner: city.owner,
+      id: req.params.queueId
+    }),
+    cityRepo.buildingRepository.findBySlug(queue.building_type)
+  ]);
+
+  // now that it's deleted we can give the player back some percentage 
+  // of resources based on how close they are to completion.
+  const diff = (queue.due - Date.now()) / (queue.due - queue.created);
+  // force a 20% loss minimum
+  const finalDiff = diff < 0.2 ? 0.2 : diff;
+
+  const costReturn: Partial<City> = {
+    id: city.id,
+    credits: city.credits + Math.floor(building.credits * queue.amount * diff),
+    alloys: city.alloys + Math.floor(building.alloys * queue.amount * diff),
+    energy: city.energy + Math.floor(building.energy * queue.amount * diff),
+    usedSpace: city.usedSpace - (building.land * queue.amount)
+  };
+
+  console.log('update', costReturn)
+
+  await cityRepo.save(costReturn);
+
+}, 'reload-construction-queue');
+
+server.post<{params: {queueId: string}}, void>('/training/:queueId/cancel', async req => {
+       const acct = await accountRepo.validate(req.authInfo.accountId, req.authInfo.token);
+       const city = await cityRepo.getUsersCity(acct.id);
+
+  if(!validator.isUUID(req.params.queueId)) {
+    throw new BadInputError('Invalid queue ID', ERROR_CODE.INVALID_BUILD_QUEUE);
+  }
+  
+  const queue = await cityRepo.unitTrainigQueue.FindOne({owner: city.owner, id: req.params.queueId});
+  if(!queue) {
+    throw new NotFoundError('That queue does not exist', ERROR_CODE.INVALID_BUILD_QUEUE);
+  }
+
+  const [, unit] = await Promise.all([
+    cityRepo.unitTrainigQueue.Delete({
+      owner: city.owner,
+      id: req.params.queueId
+    }),
+    cityRepo.unitRepository.FindOne({slug: queue.unit_type})
+  ]);
+
+  // now that it's deleted we can give the player back some percentage 
+  // of resources based on how close they are to completion.
+  const diff = (queue.due - Date.now()) / (queue.due - queue.created);
+  // force a 20% loss minimum
+  const finalDiff = diff < 0.2 ? 0.2 : diff;
+
+  const costReturn: Partial<City> = {
+    id: city.id,
+    credits: city.credits + Math.floor(unit.credits * queue.amount * diff),
+    food: city.food + Math.floor(unit.food * queue.amount * diff),
+    population: city.population + Math.floor(unit.population * queue.amount),
+    soldiers: city.soldiers + Math.floor(unit.soldiers * queue.amount),
+    attackers: city.attackers + Math.floor(unit.attackers * queue.amount),
+    defenders: city.defenders + Math.floor(unit.attackers * queue.amount)
+  };
+
+  console.log('update', costReturn)
+
+  await cityRepo.save(costReturn);
+
+}, 'reload-unit-training');
+
 server.get<void, string>('/server-stats', async req => {
   const date = new Date();
   return `