diff --git a/src/index.ts b/src/index.ts index 1d42273..72a36c0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,8 @@ import { connectToDatabaseServer } from './db-utils'; import sendGridMail from '@sendgrid/mail'; import { userProfileRoutes } from './user-profile/routes'; import { revisionRoutes } from './revision/routes'; +import { categoryRoutes } from './user-profile/category-routes'; +import { shelfRoutes } from './user-profile/shelf-routes'; const SENDGRID_API_KEY = process.env.SENDGRID_API_KEY || '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'; @@ -19,6 +21,8 @@ app.set('port', process.env.PORT || 8001); app.use('/', authRoutes); app.use('/', userProfileRoutes); app.use('/', revisionRoutes); +app.use('/', categoryRoutes); +app.use('/', shelfRoutes); app.get('/', (request, response) => { response.send('Server running @ port' + app.get('port')); diff --git a/src/models/category.ts b/src/models/category.ts index d7b3543..fb0d044 100644 --- a/src/models/category.ts +++ b/src/models/category.ts @@ -1,7 +1,6 @@ import { Shelf } from "./shelf"; -export interface Category { - _id: string, +export interface Category { //_id is not needed as it is created by default name: string, icon: string, shelves?: Array, diff --git a/src/models/shelf.ts b/src/models/shelf.ts index e3ad642..c34567a 100644 --- a/src/models/shelf.ts +++ b/src/models/shelf.ts @@ -1,40 +1,33 @@ import { viewPermissionType } from "./variables"; import { Word } from "./word"; -export interface Shelf { - _id: string, - name: string, - description: string, - viewType: viewPermissionType, +interface RevisionHistoryRecord { + day: Date, + wasRecallSuccessful: boolean +} + +interface AddedWord { + word: Word, + notes: Array, + isFavourite: boolean, + nextRevisionDateTime: Date, + spaceBetweenRecall: number, // in Days + revisionHistory : Array, isArchived: boolean, - addedWords: Array<{ - word: Word, - notes: Array, - isFavourite: boolean, - nextRevisionDateTime: Date, - spaceBetweenRecall: number, // in Days - revisionHistory : Array<{ - day: Date, - wasRecallSuccessful: boolean, - }> - }> } -export interface MongoShelf { - _id: string, +export interface Shelf { name: string, - description: string, + description?: string, viewType: viewPermissionType, isArchived: boolean, - addedWords: Array<{ - word: Word, - notes: Array, - isFavourite: boolean, - nextRevisionDateTime: Date, - spaceBetweenRecall: number, // in Days - revisionHistory : Array<{ - day: Date, - wasRecallSuccessful: boolean, - }> - }> + addedWords?: Array +} + +interface MongoAddedWord extends Omit { + word: string, +} + +export interface MongoShelf extends Omit { + addedWords?: Array } diff --git a/src/models/user.ts b/src/models/user.ts index ad1ef8a..72c9b2a 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -12,6 +12,7 @@ export interface User { uncategorised?: Shelf, } -export interface MongoUser extends Omit { - categories?: Array // Category IDs +export interface MongoUser extends Omit, "uncategorised"> { + categories?: Array // Category IDs, + uncategorised: string } \ No newline at end of file diff --git a/src/models/word.ts b/src/models/word.ts index 288a0d6..8aa1cfe 100644 --- a/src/models/word.ts +++ b/src/models/word.ts @@ -7,7 +7,7 @@ interface gramaticalDetail { } export interface Word { - _id: string, + //_id is present by default in the DB name: string, pronounciation: { text: string, @@ -15,11 +15,6 @@ export interface Word { }, similarWords?: Array, grammaticalDetails: Array, - wordStats: { - favouriteCount: number, // Total users who liked this word - addCount: number, // Total users have added this word to their shelf - }, - isArchived: boolean, } export interface MongoWord extends Omit { diff --git a/src/user-profile/category-routes.ts b/src/user-profile/category-routes.ts new file mode 100644 index 0000000..531c68c --- /dev/null +++ b/src/user-profile/category-routes.ts @@ -0,0 +1,119 @@ +import express from 'express'; +import passport from 'passport'; +import { MongoUser } from '../models/user'; +import { DB_NAME } from '../db-utils'; +import { getDatabaseClient } from '../db-utils'; +import { ObjectId } from 'bson'; +import { MongoCategory } from '..//models/category'; + +export const categoryRoutes = express.Router(); + +export const jwtAuthentication = passport.authenticate('jwt', { session: false }); + +// Get Category Details +categoryRoutes.get('/category/', jwtAuthentication, async (request, response) => { + const categoryCollection = getDatabaseClient().db(DB_NAME).collection('categories'); + + const currentCategory = await categoryCollection.findOne({ + _id: new ObjectId(request.body._id), + }); + + if (currentCategory) { + response.status(200); + response.json(currentCategory); + } else { + response.status(400); + response.send("Category ID did not match"); + } + + return; +}); + + +// Add category +categoryRoutes.post('/category/', jwtAuthentication, async (request, response) => { + const user: MongoUser = (request.user as any); + + const categoryCollection = getDatabaseClient().db(DB_NAME).collection('categories'); + const userCollection = getDatabaseClient().db(DB_NAME).collection('users'); + + if (!request.body.name) { + response.status(400); + response.send("Category Name missing"); + return; + } + + try { + const newCategory = await categoryCollection.insertOne({ + name: request.body.name, + icon: request.body.icon ? request.body.icon : "", + isArchived: false, + }); + + if (!user.categories) { + user.categories = []; + } + + user.categories.push(newCategory.insertedId.toHexString()); + + await userCollection.updateOne({ + _id: user._id + }, { + $set: { + categories: user.categories + } + }); + + response.sendStatus(200); + } catch(e) { + response.sendStatus(500); + return; + } + + return; +}); + + +// Update category +categoryRoutes.put('/category/', jwtAuthentication, async (request, response) => { + const categoryCollection = getDatabaseClient().db(DB_NAME).collection('categories'); + + const currentCategory = await categoryCollection.findOne({ + _id: new ObjectId(request.body._id) + }); + + if (!currentCategory) { + response.status(400); + response.send("Category ID did not match"); + return; + } + + if (request.body.isArchived) { + if (typeof request.body.isArchived !== "boolean") { + response.status(400); + response.send("Archived should be a boolean flag"); + return; + } + } + + try { + await categoryCollection.updateOne({ + _id: new ObjectId(request.body._id), + }, { + $set: { + name: request.body.name ? request.body.name : currentCategory.name, + icon: request.body.icon ? request.body.icon : currentCategory.icon, + isArchived: request.body.isArchived !== undefined ? request.body.isArchived : currentCategory.isArchived + } + }); + + response.sendStatus(200); + + } catch (e) { + console.log(e); + response.status(400); + response.send(e.toString()); + } + + return; +}); diff --git a/src/user-profile/routes.ts b/src/user-profile/routes.ts index 157c556..b5a231e 100644 --- a/src/user-profile/routes.ts +++ b/src/user-profile/routes.ts @@ -1,10 +1,6 @@ import express from 'express'; -import passport, { use } from 'passport'; +import passport from 'passport'; import { MongoUser } from '../models/user'; -import { Category, MongoCategory } from '../models/category'; -import { DB_NAME } from '../db-utils'; -import { getDatabaseClient } from '../db-utils'; -import { ObjectId } from 'bson'; export const userProfileRoutes = express.Router(); @@ -22,92 +18,3 @@ userProfileRoutes.get('/profile/', jwtAuthentication, async (request, response) }); return; }); - -userProfileRoutes.post('/category/', jwtAuthentication, async (request, response) => { - const user: MongoUser = (request.user as any); - - const categoryCollection = getDatabaseClient().db(DB_NAME).collection('categories'); - const userCollection = getDatabaseClient().db(DB_NAME).collection('users'); - - if (!request.body.name || !request.body.icon) { - response.status(400); - response.send("Category Name or icon(base64) missing"); - return; - } - - try { - const newCategory = await categoryCollection.insertOne({ - name: request.body.name, - icon: request.body.icon, - isArchived: false, - }); - - if (!user.categories) { - user.categories = []; - } - - user.categories.push(newCategory.insertedId.toHexString()); - - await userCollection.updateOne({ - _id: user._id - }, { - $set: { - categories: user.categories - } - }); - - response.sendStatus(200); - } catch(e) { - response.sendStatus(500); - return; - } - - return; -}); - - -userProfileRoutes.put('/category/', jwtAuthentication, async (request, response) => { - const categoryCollection = getDatabaseClient().db(DB_NAME).collection('categories'); - - let currentCategory; - - try { - currentCategory = await categoryCollection.findOne({ - _id: new ObjectId(request.body._id) - }); - } catch { - if (!currentCategory) { - response.status(400); - response.send("Category ID did not match"); - return; - } - } - - if (request.body.isArchived) { - if (typeof request.body.isArchived !== "boolean") { - response.status(400); - response.send("Archived should be a boolean flag"); - return; - } - } - - try { - await categoryCollection.updateOne({ - _id: new ObjectId(request.body._id), - }, { - $set: { - name: request.body.name ? request.body.name : currentCategory.name, - icon: request.body.icon ? request.body.icon : currentCategory.icon, - isArchived: request.body.isArchived !== undefined ? request.body.isArchived : currentCategory.isArchived - } - }); - - response.sendStatus(200); - - } catch (e) { - response.status(400); - response.json(e); - } - - return; -}); diff --git a/src/user-profile/shelf-routes.ts b/src/user-profile/shelf-routes.ts new file mode 100644 index 0000000..ce61c2a --- /dev/null +++ b/src/user-profile/shelf-routes.ts @@ -0,0 +1,140 @@ +import { ObjectId } from 'bson'; +import express from 'express'; +import passport from 'passport'; +import { MongoCategory } from '../models/category'; +import { DB_NAME } from '../db-utils'; +import { getDatabaseClient } from '../db-utils'; +import { MongoShelf } from '../models/shelf'; + +export const shelfRoutes = express.Router(); + +export const jwtAuthentication = passport.authenticate('jwt', { session: false }); + + +// GET shelf details +shelfRoutes.get('/shelf/', jwtAuthentication, async (request, response) => { + const shelfCollection = getDatabaseClient().db(DB_NAME).collection('shelves'); + + const currentShelf = await shelfCollection.findOne({ + _id: new ObjectId(request.body._id) + }); + + if (!currentShelf) { + response.status(400); + response.send("No shelf with the ID"); + } else { + response.status(200); + response.json(currentShelf); + } + + return; +}); + + +// Add shelf +shelfRoutes.post('/shelf/', jwtAuthentication, async (request, response) => { + const categoryCollection = getDatabaseClient().db(DB_NAME).collection('categories'); + const shelfCollection = getDatabaseClient().db(DB_NAME).collection('shelves'); + + const currentCategory = await categoryCollection.findOne({ + _id: new ObjectId(request.body.categoryId) + }); + + let newShelf; + + if (!currentCategory) { + response.status(400); + response.send("Category ID did not match"); + return; + } + + if (!request.body.name) { + response.sendStatus(400); + response.send("Missing shelf name"); + return; + } + + try { + newShelf = await shelfCollection.insertOne({ + name: request.body.name, + description: request.body.description ? request.body.description : "", + viewType: request.body.viewType ? request.body.viewType : "PRIVATE", + isArchived: false, + }); + } catch (e) { + response.sendStatus(500); + return; + } + + if (!newShelf) { + response.sendStatus(500); + return; + } + + if (!currentCategory.shelves) { + currentCategory.shelves = []; + } + + currentCategory.shelves.push(newShelf.insertedId.toHexString()); + + const updatedCategory = await categoryCollection.updateOne({ + _id: new ObjectId(request.body.categoryId) + }, { + $set : { + shelves: currentCategory.shelves + } + }); + + if (updatedCategory) { + response.sendStatus(200); + } else { + response.sendStatus(500); + } + + return; +}); + + + +// Update shelf +shelfRoutes.put('/shelf/', jwtAuthentication, async (request, response) => { + const shelfCollection = getDatabaseClient().db(DB_NAME).collection('shelves'); + + const currentShelf = await shelfCollection.findOne({ + _id: new ObjectId(request.body._id) + }); + + if (!currentShelf) { + response.status(400); + response.send("No shelf with the ID"); + } + + if (request.body.isArchived) { + if (typeof request.body.isArchived !== "boolean") { + response.status(400); + response.send("Archived should be a boolean flag"); + return; + } + } + + try { + await shelfCollection.updateOne({ + _id: new ObjectId(request.body._id), + }, { + $set: { + name: request.body.name ? request.body.name : currentShelf.name, + description: request.body.description ? request.body.description : currentShelf.description, + isArchived: request.body.isArchived !== undefined ? request.body.isArchived : currentShelf.isArchived + } + }); + + response.sendStatus(200); + + } catch (e) { + console.log(e); + response.status(400); + response.send(e.toString()); + } + + return; +}); \ No newline at end of file