Skip to content

Commit

Permalink
Merge pull request #155 from WKHAllen/dev
Browse files Browse the repository at this point in the history
1.2.1 update
  • Loading branch information
WKHAllen authored Aug 2, 2020
2 parents 631afd0 + c9496cb commit 0cea0db
Show file tree
Hide file tree
Showing 20 changed files with 339 additions and 110 deletions.
10 changes: 6 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
node_modules
.vscode
node_modules/
__pycache__/
.vscode/
src/build/
backups/
*.DS_Store
src/processenv.js
.env
src/build
todo.txt
*.DS_Store
126 changes: 126 additions & 0 deletions scripts/dbBackup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import psycopg2
import os
import pathlib
import json
import decimal
from datetime import datetime
from parseEnv import parseEnv

# Get the project root path
def getRootPath():
thisPath = str(pathlib.Path(__file__).parent.absolute())
rootPath = os.path.split(thisPath)[0]
return rootPath

# Get the path of the .env file
def getEnvPath():
rootPath = getRootPath()
envPath = os.path.join(rootPath, ".env")
return envPath

# Get the path to the backups directory
def getBackupPath():
rootPath = getRootPath()
backupPath = os.path.join(rootPath, "backups")
return backupPath

# Get a database backup path
def getDBBackupPath(backupDir):
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
dbFile = f"norsebooks.backup.{timestamp}.json"
dbPath = os.path.join(backupDir, dbFile)
return dbPath

# Get the list of tables in the database
def getTables(db):
sql = "SELECT table_name AS table FROM information_schema.tables WHERE table_schema = 'public';"
db.execute(sql)
tables = [row[0] for row in db.fetchall()]
return tables

# Get a table's column names
def getTableColumns(db, table):
sql = f"SELECT column_name AS column FROM information_schema.columns WHERE table_name = '{table}';"
db.execute(sql)
columns = [row[0] for row in db.fetchall()]
return columns

# Get all data in a table
def getTableData(db, table, columns):
if "id" not in columns:
sql = f"SELECT * FROM {table};"
else:
sql = f"SELECT * FROM {table} ORDER BY id ASC;"
db.execute(sql)
tableData = db.fetchall()
return tableData

# Convert Decimal objects to strings
def fixDecimalValues(tableData):
fixedData = []

for row in tableData:
fixedRow = []

for value in row:
if type(value) != decimal.Decimal:
fixedRow.append(value)
else:
fixedRow.append(str(value))

fixedData.append(tuple(fixedRow))

return fixedData

# Save the database backup
def saveBackup(dbPath, dbData):
with open(dbPath, "w") as f:
json.dump(dbData, f, indent=2)

# Perform the backup
def backup(db, backupDir):
dbPath = getDBBackupPath(backupDir)
dbData = {}

tables = getTables(db)
for table in tables:
tableColumns = getTableColumns(db, table)
tableData = getTableData(db, table, tableColumns)

dbData[table] = {}
dbData[table]["columns"] = tableColumns
dbData[table]["data"] = fixDecimalValues(tableData)

saveBackup(dbPath, dbData)

return dbPath

# Back up the database
def backupDB(dbUrl, backupDir):
conn = psycopg2.connect(dbUrl, sslmode="require")
cur = conn.cursor()

os.makedirs(backupDir, exist_ok=True)

dbPath = backup(cur, backupDir)

cur.close()
conn.close()

return dbPath

# Execute the backup script
def main():
print("Backing up database...")

envFile = getEnvPath()
env = parseEnv(envFile)
dbUrl = env["DATABASE_URL"]
backupPath = getBackupPath()

dbPath = backupDB(dbUrl, backupPath)

print(f"Database has been backed up to {dbPath}")

if __name__ == "__main__":
main()
8 changes: 8 additions & 0 deletions scripts/parseEnv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
def parseEnv(envFile):
env = {}
with open(envFile, "r") as f:
for line in f:
splitLine = line.strip().split("=")
if len(splitLine) == 2:
env[splitLine[0]] = splitLine[1]
return env
4 changes: 2 additions & 2 deletions src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ function logError(stmt: string, params: any[], res: QueryResult<any>, err: Error
export class DB {
pool: Pool;

constructor(dbURL: string, ssl: boolean, max: number) {
constructor(dbURL: string, ssl: boolean = true, max: number = 20) {
this.pool = new Pool({
connectionString: dbURL,
ssl: ssl || true,
ssl: ssl,
max: max
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ app.use('/password-reset', routes.passwordResetRoute);
app.use('/profile', routes.profileRoute);
app.use('/register', routes.registerRoute);
app.use('/register-success', routes.registerSuccessRoute);
app.use('/reportBook', routes.reportBookRoute);
app.use('/reports', routes.reportBookRoute);
app.use('/terms-and-conditions', routes.termsAndConditionsRoute);
app.use('/verify', routes.verifyRoute);

Expand Down
3 changes: 2 additions & 1 deletion src/routes/book.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ router.get('/:bookId', (req: Request, res: Response) => {
contactInfo: userBookInfo.contactinfo,
bookOwner: userId === userBookInfo.id,
bookId: req.params.bookId,
canReport: !alreadyReported && !reportedRecently
reported: alreadyReported,
canReport: !reportedRecently
});
});
});
Expand Down
50 changes: 27 additions & 23 deletions src/routes/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,34 @@ router.get('/', auth, (req: Request, res: Response) => {
services.UserService.getContactInfo(userId, (userContactInfo) => {
services.PlatformService.getPlatforms((platforms) => {
services.UserService.getUserBooks(userId, (booksListed) => {
var joinTimestamp = new Date(userInfo.jointimestamp * 1000).toDateString();
var contactPlatform = userContactInfo.contactplatformid;
if (contactPlatform === null) contactPlatform = '';
var contactInfo = userContactInfo.contactinfo;
if (contactInfo === null) contactInfo = '';
renderPage(req, res, 'profile', {
title: 'Your profile',
error: req.session.errorMsg || undefined,
firstname: userInfo.firstname,
lastname: userInfo.lastname,
email: userInfo.email + '@luther.edu',
imageUrl: userInfo.imageurl,
joined: joinTimestamp,
itemsSold: userInfo.itemssold,
moneyMade: formatPrice(userInfo.moneymade),
books: userInfo.itemslisted,
platforms: platforms,
contactInfoExists: contactPlatform !== '' && contactInfo !== '',
contactPlatform: contactPlatform,
contactInfo: contactInfo,
booksListed: booksListed,
hasListings: booksListed.length > 0
services.UserService.getUserBookReports(userId, (booksReported) => {
var joinTimestamp = new Date(userInfo.jointimestamp * 1000).toDateString();
var contactPlatform = userContactInfo.contactplatformid;
if (contactPlatform === null) contactPlatform = '';
var contactInfo = userContactInfo.contactinfo;
if (contactInfo === null) contactInfo = '';
renderPage(req, res, 'profile', {
title: 'Your profile',
error: req.session.errorMsg || undefined,
firstname: userInfo.firstname,
lastname: userInfo.lastname,
email: userInfo.email + '@luther.edu',
imageUrl: userInfo.imageurl,
joined: joinTimestamp,
itemsSold: userInfo.itemssold,
moneyMade: formatPrice(userInfo.moneymade),
books: userInfo.itemslisted,
platforms: platforms,
contactInfoExists: contactPlatform !== '' && contactInfo !== '',
contactPlatform: contactPlatform,
contactInfo: contactInfo,
booksListed: booksListed,
hasListings: booksListed.length > 0,
booksReported: booksReported,
hasReports: booksReported.length > 0
});
req.session.errorMsg = undefined;
});
req.session.errorMsg = undefined;
});
});
});
Expand Down
19 changes: 18 additions & 1 deletion src/routes/reportBook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as services from '../services';
export var router = Router();

// Report book event
router.post('/:bookId', auth, (req: Request, res: Response) => {
router.post('/reportBook/:bookId', auth, (req: Request, res: Response) => {
services.AuthService.getAuthUser(req.session.sessionId, (userId) => {
services.BookService.getBookInfo(req.params.bookId, (bookInfo) => {
services.ReportService.userReportedBook(userId, bookInfo.id, (alreadyReported) => {
Expand All @@ -23,3 +23,20 @@ router.post('/:bookId', auth, (req: Request, res: Response) => {
});
});
});

// Unreport book event
router.post('/unreportBook/:bookId', auth, (req: Request, res: Response) => {
services.AuthService.getAuthUser(req.session.sessionId, (userId) => {
services.BookService.getBookInfo(req.params.bookId, (bookInfo) => {
services.ReportService.userReportedBook(userId, bookInfo.id, (hasReported) => {
if (hasReported) {
services.ReportService.unreportBook(userId, bookInfo.id, () => {
res.redirect(`/book/${req.params.bookId}`);
});
} else {
res.redirect(`/book/${req.params.bookId}`);
}
});
});
});
});
1 change: 1 addition & 0 deletions src/routes/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ export function adminAuth (req: Request, res: Response, next: NextFunction) {
// Render a page
export function renderPage(req: Request, res: Response, page: string, options: any) {
options = options || {};
options.url = req.originalUrl;
services.MetaService.getMeta('Version', (version) => {
options.version = version;
if (!req.session || !req.session.sessionId) {
Expand Down
11 changes: 10 additions & 1 deletion src/services/report.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { mainDB, reportTimeout, boolCallback, numberCallback, getTime } from './util';
import { mainDB, reportTimeout, boolCallback, numberCallback, getTime, voidCallback } from './util';
import { BookService } from './book';
import { MetaService } from './meta';

Expand Down Expand Up @@ -26,6 +26,15 @@ export module ReportService {
});
}

// Unreport a book
export function unreportBook(userId: number, bookId: string, callback?: voidCallback) {
var sql = `DELETE FROM Report WHERE userId = ? AND bookId = ?`;
var params = [userId, bookId];
mainDB.execute(sql, params, (rows) => {
if (callback) callback();
});
}

// Check if a user has already reported a book
export function userReportedBook(userId: number, bookId: string, callback?: boolCallback) {
var sql = `SELECT id FROM Report WHERE bookId = ? AND userId = ?;`;
Expand Down
13 changes: 13 additions & 0 deletions src/services/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,19 @@ export module UserService {
});
}

// Get a list of the books a user has reported
export function getUserBookReports(userId: number, callback?: rowsCallback) {
var sql = `
SELECT Book.bookId as bookId, title, author, reportTimestamp
FROM Book JOIN Report ON Book.id = Report.bookId
WHERE Report.userId = ?
ORDER BY Report.reportTimestamp;`;
var params = [userId];
mainDB.execute(sql, params, (rows) => {
if (callback) callback(rows);
});
}

// Check if a user is an admin
export function isAdmin(userId: number, callback?: boolCallback) {
var sql = `SELECT id FROM NBUser WHERE id = ? AND admin = 1;`;
Expand Down
3 changes: 1 addition & 2 deletions src/services/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { MiscService } from './misc';
import { PasswordResetService } from './passwordReset';
import { SessionService } from './session';

export const debug = Boolean(Number(process.env.DEBUG));
export const dbURL = process.env.DATABASE_URL;
export const maxDBClients = 20;
export const saltRounds = 12;
Expand All @@ -23,7 +22,7 @@ export const feedbackTimeout = 7 * 24 * 60 * 60 * 1000; // one week
export const staticTablePath = 'tables';

// The database object
export var mainDB = new db.DB(dbURL, !debug, maxDBClients);
export var mainDB = new db.DB(dbURL, true, maxDBClients);

// Callback types
export type voidCallback = () => void;
Expand Down
7 changes: 6 additions & 1 deletion src/static/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ footer .footer-list-link {
padding: 0px 20px;
}

#index .card-link {
text-decoration: none;
}

#index .thumbnail {
width: 250px;
height: 250px;
Expand All @@ -166,6 +170,7 @@ footer .footer-list-link {
#index .card-price {
font-size: 1rem;
color: black;
padding-left: 8px;
}

#index .card-body {
Expand Down Expand Up @@ -292,7 +297,7 @@ footer .footer-list-link {
margin:0 auto;
}

#your-listings tbody tr:hover {
#your-listings tbody tr:hover, #reported-books tbody tr:hover {
background-color: rgb(243, 243, 244);
}

Expand Down
1 change: 0 additions & 1 deletion src/static/js/admin-reports.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,4 @@ function joinTitles() {
// When the page is ready
$(() => {
joinTitles();
improveTimestamps();
});
1 change: 0 additions & 1 deletion src/static/js/admin-users.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,5 @@ function showOrderBy() {

// When the page is ready
$(() => {
improveTimestamps();
showOrderBy();
});
Loading

0 comments on commit 0cea0db

Please sign in to comment.