@ -6,6 +6,7 @@ const { Resolver } = require('did-resolver');
const express = require ( 'express' ) ;
const { getResolver } = require ( 'ethr-did-resolver' ) ;
const fs = require ( 'fs' ) ;
const { DateTime } = require ( 'luxon' ) ;
const multer = require ( 'multer' ) ;
const path = require ( 'path' ) ;
const sqlite3 = require ( 'sqlite3' ) . verbose ( ) ;
@ -15,7 +16,7 @@ require('dotenv').config()
const app = express ( ) ;
app . use ( cors ( ) ) ;
const port = process . env . PORT || 3000 ;
const port = process . env . PORT || 3001 ;
// file name also referenced in flyway.conf and potentially in .env files or in environment variables
const dbFile = process . env . SQLITE_FILE || './image-db.sqlite' ;
@ -34,6 +35,8 @@ const db = new sqlite3.Database(dbFile, (err) => {
}
} ) ;
const endorserApiUrl = process . env . ENDORSER_API_URL || 'http://localhost:3000' ;
// Configure AWS
const s3Client = new S3Client ( {
region : process . env . AWS_REGION ,
@ -56,12 +59,81 @@ app.post('/image', uploadMulter.single('image'), async (req, res) => {
return res . status ( 401 ) . send ( JSON . stringify ( { success : false , message : 'Missing "Bearer JWT" in Authorization header.' } ) ) ;
}
const jwt = auth . substring ( 'Bearer ' . length ) ;
const verified = await didJwt . verifyJWT ( jwt , { resolver } ) ;
if ( ! verified ) {
return res . status ( 401 ) . send ( JSON . stringify ( { success : false , message : 'Got invalid JWT in Authorization header.' } ) ) ;
const verified = await didJwt . verifyJWT ( jwt , { resolver } ) ;
if ( ! verified . verified ) {
const errorTime = new Date ( ) . toISOString ( ) ;
console . log ( errorTime , 'Got invalid JWT in Authorization header:' , verified ) ;
return res . status ( 401 ) . send ( JSON . stringify ( { success : false , message : 'Got invalid JWT in Authorization header. See server logs at ' + errorTime } ) ) ;
}
const issuerDid = verified . issuer ;
// Check the user's limits, first from the DB and then from the server
let limitPerWeek = await new Promise ( ( resolve , reject ) => {
db . get (
'SELECT per_week FROM user WHERE did = ?' ,
[ issuerDid ] ,
( dbErr , row ) => {
if ( dbErr ) {
console . error ( 'Error getting user record from database:' , dbErr )
// may not matter, so continue
}
resolve ( row ? . per_week ) ;
}
) ;
} ) ;
if ( limitPerWeek == null ) {
const headers = {
'Authorization' : ` Bearer ${ jwt } ` ,
'Content-Type' : 'application/json'
}
const response = await fetch ( endorserApiUrl + '/api/report/rateLimits' , { headers } ) ;
if ( response . status !== 200 ) {
console . error ( "Got bad response of" , response . status , "when checking rate limits for" , issuerDid ) ;
return res . status ( 400 ) . send ( JSON . stringify ( { success : false , message : 'Got bad status of ' + response . status + ' when checking limits with endorser server. Verify that the account exists and that the JWT works for that server.' } ) ) ;
} else {
const body = await response . json ( ) ;
limitPerWeek = body . maxClaimsPerWeek
await new Promise ( ( resolve , reject ) => {
db . run (
'INSERT INTO user (did, per_week) VALUES (?, ?)' ,
[ issuerDid , limitPerWeek ] ,
( dbErr ) => {
if ( dbErr ) {
console . error ( "Error inserting user record for" , issuerDid , "into database:" , dbErr ) ;
// we can continue... it just means we'll check the endorser server again next time
}
resolve ( ) ;
}
) ;
} ) ;
}
}
if ( limitPerWeek == null ) {
return res . status ( 400 ) . send ( JSON . stringify ( { success : false , message : 'Unable to determine rate limits for this user. Verify that the account exists and that the JWT works for that server.' } ) ) ;
}
// check the user's claims so far this week
const startOfWeekDate = DateTime . utc ( ) . startOf ( 'week' ) // luxon weeks start on Mondays
const startOfWeekString = startOfWeekDate . toISO ( )
let imagesCount = await new Promise ( ( resolve , reject ) => {
db . get (
'SELECT COUNT(*) AS week_count FROM image WHERE did = ? AND time >= ?' ,
[ issuerDid , startOfWeekString ] ,
( dbErr , row ) => {
if ( dbErr ) {
console . error ( currentDate , "Error counting records for" , issuerDid , "into database:" , dbErr ) ;
// we can continue... it just means we'll check the endorser server again next time
}
resolve ( row ? . week_count ) ;
}
) ;
} ) ;
if ( imagesCount >= limitPerWeek ) {
return res . status ( 400 ) . send ( JSON . stringify ( { success : false , message : 'You have reached your weekly limit of ' + limitPerWeek + ' images.' } ) ) ;
}
// Read the file from the temporary location
fs . readFile ( req . file . path , async ( err , data ) => {
if ( err ) throw err ; // Handle error
@ -85,19 +157,26 @@ app.post('/image', uploadMulter.single('image'), async (req, res) => {
const currentDate = new Date ( ) . toISOString ( ) ;
const localFile = req . file . path . startsWith ( uploadDir + '/' ) ? req . file . path . substring ( uploadDir . length + 1 ) : req . file . path ;
const finalUrl = ` https:// ${ bucketName } .s3.amazonaws.com/ ${ fileName } ` ;
await db . run ( 'INSERT INTO image (time, did, local_file, size, final_file, url) VALUES (?, ?, ?, ?, ?, ?)' , [
await new Promise ( ( resolve , reject ) => {
db . run (
'INSERT INTO image (time, did, local_file, size, final_file, url) VALUES (?, ?, ?, ?, ?, ?)' ,
[
currentDate ,
issuerDid ,
localFile ,
req . file . size ,
fileName ,
finalUrl
] , ( dbErr ) => {
] ,
( dbErr ) => {
if ( dbErr ) {
console . error ( currentDate , "Error inserting record from" , issuerDid , "into database:" , dbErr ) ;
// don't continue because then we'll have storage we cannot track (and potentially limit)
throw dbErr ;
reject ( dbErr ) ;
}
resolve ( ) ;
}
) ;
} ) ;
// send to AWS