@@ -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')); | |||
@@ -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<Shelf>, | |||
@@ -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<string>, | |||
isFavourite: boolean, | |||
nextRevisionDateTime: Date, | |||
spaceBetweenRecall: number, // in Days | |||
revisionHistory : Array<RevisionHistoryRecord>, | |||
isArchived: boolean, | |||
addedWords: Array<{ | |||
word: Word, | |||
notes: Array<string>, | |||
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<string>, | |||
isFavourite: boolean, | |||
nextRevisionDateTime: Date, | |||
spaceBetweenRecall: number, // in Days | |||
revisionHistory : Array<{ | |||
day: Date, | |||
wasRecallSuccessful: boolean, | |||
}> | |||
}> | |||
addedWords?: Array<AddedWord> | |||
} | |||
interface MongoAddedWord extends Omit<AddedWord, "word"> { | |||
word: string, | |||
} | |||
export interface MongoShelf extends Omit<Shelf, "addedWords"> { | |||
addedWords?: Array<MongoAddedWord> | |||
} |
@@ -12,6 +12,7 @@ export interface User { | |||
uncategorised?: Shelf, | |||
} | |||
export interface MongoUser extends Omit<User, "categories"> { | |||
categories?: Array<string> // Category IDs | |||
export interface MongoUser extends Omit<Omit<User, "categories">, "uncategorised"> { | |||
categories?: Array<string> // Category IDs, | |||
uncategorised: string | |||
} |
@@ -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<Word>, | |||
grammaticalDetails: Array<gramaticalDetail>, | |||
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<Word, "similarWords"> { | |||
@@ -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<MongoCategory>('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<MongoCategory>('categories'); | |||
const userCollection = getDatabaseClient().db(DB_NAME).collection<MongoUser>('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; | |||
}); |
@@ -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; | |||
}); |
@@ -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<MongoShelf>('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<MongoCategory>('categories'); | |||
const shelfCollection = getDatabaseClient().db(DB_NAME).collection<MongoShelf>('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<MongoShelf>('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; | |||
}); |