Skip to content

Commit cef4406

Browse files
authored
Merge pull request #127 from Turing-Sandbox/SUMMA-34
SUMMA-34: Image Storage in Development and AWS S3
2 parents 5687a46 + da0220e commit cef4406

File tree

11 files changed

+431
-284
lines changed

11 files changed

+431
-284
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,7 @@ backend/node_modules
183183
**/node_modules
184184

185185

186-
package-lock.json
186+
package-lock.json
187+
188+
189+
local_uploads

backend/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"@types/compression": "^1.7.5",
2020
"@types/cookie-parser": "^1.4.3",
2121
"@types/cors": "^2.8.18",
22-
"@types/express": "^4.17.1",
22+
"@types/express": "^4.17.22",
2323
"@types/formidable": "^3.4.5",
2424
"@types/jest": "^29.5.14",
2525
"@types/ms": "^2.1.0",
@@ -34,6 +34,8 @@
3434
},
3535
"dependencies": {
3636
"@algolia/client-search": "^5.20.4",
37+
"@aws-sdk/client-s3": "^3.816.0",
38+
"@aws-sdk/s3-request-presigner": "^3.816.0",
3739
"@google/generative-ai": "^0.24.1",
3840
"@langchain/core": "^0.3.57",
3941
"@types/jsonwebtoken": "^9.0.7",

backend/src/app.config.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,15 @@ export const appConfig = {
1616
// Lower default body-parser limits to mitigate DoS risk; adjust per-route as needed
1717
json: json({ limit: "2mb" }),
1818
urlencoded: urlencoded({ extended: true, limit: "2mb" }),
19-
helmet: helmet(),
19+
helmet: helmet({
20+
contentSecurityPolicy: {
21+
directives: {
22+
...helmet.contentSecurityPolicy.getDefaultDirectives(),
23+
"img-src": ["'self'", "http://localhost:3000", "data:"],
24+
},
25+
},
26+
crossOriginResourcePolicy: { policy: "cross-origin" },
27+
}),
2028
compression: compression(),
2129
cookieParser: cookieParser(),
2230
},

backend/src/app.ts

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import express from 'express';
2-
import rateLimit from 'express-rate-limit';
3-
import { appConfig } from './app.config';
4-
import { errorHandler } from './shared/middleware/error.middleware';
5-
import { stripeWebhookMiddleware } from './shared/middleware/stripe.middleware';
6-
import { logger } from './shared/utils/logger';
7-
import { createErrorResponse } from './shared/utils/response';
1+
import express from "express";
2+
import rateLimit from "express-rate-limit";
3+
import { appConfig } from "./app.config";
4+
import { errorHandler } from "./shared/middleware/error.middleware";
5+
import { stripeWebhookMiddleware } from "./shared/middleware/stripe.middleware";
6+
import { logger } from "./shared/utils/logger";
7+
import { createErrorResponse } from "./shared/utils/response";
8+
import path from "path";
89

910
// Import routes
1011
import userRoutes from './modules/user/routes/user.routes';
@@ -17,7 +18,6 @@ import webhookRoutes from './modules/subscription/routes/webhook.routes';
1718
import searchRoutes from './modules/search/routes/search.routes';
1819
import summarizationRoutes from './modules/ai/routes/summarization.routes';
1920

20-
2121
const app = express();
2222

2323
// Apply basic middleware
@@ -39,37 +39,41 @@ app.use((req, res, next) => {
3939
const limiter = rateLimit({
4040
...appConfig.rateLimiting,
4141
handler: (req, res) => {
42-
res.status(429).json(
43-
createErrorResponse('Too many requests, please try again later')
44-
);
42+
res
43+
.status(429)
44+
.json(createErrorResponse("Too many requests, please try again later"));
4545
},
4646
});
4747
app.use(limiter);
4848

4949
// Health check endpoint
50-
app.get('/health', (req, res) => {
51-
res.json({ status: 'ok', timestamp: new Date().toISOString() });
50+
app.get("/health", (req, res) => {
51+
res.json({ status: "ok", timestamp: new Date().toISOString() });
5252
});
5353

5454
// API routes
55-
app.use('/user', userRoutes);
56-
app.use('/comment', commentRoutes);
57-
app.use('/content', contentRoutes);
58-
app.use('/subscription', subscriptionRoutes);
59-
app.use('/notification', notificationRoutes);
60-
app.use('/oauth', oauthRoutes);
61-
app.use('/webhook', webhookRoutes);
62-
app.use('/search', searchRoutes);
55+
app.use("/user", userRoutes);
56+
app.use("/comment", commentRoutes);
57+
app.use("/content", contentRoutes);
58+
app.use("/subscription", subscriptionRoutes);
59+
app.use("/notification", notificationRoutes);
60+
app.use("/oauth", oauthRoutes);
61+
app.use("/webhook", webhookRoutes);
62+
app.use("/search", searchRoutes);
6363
app.use('/ai', summarizationRoutes);
64+
app.use(
65+
"/local_uploads",
66+
express.static(path.join(process.cwd(), "local_uploads"))
67+
);
6468

65-
app.get('/', (_, res) => {
66-
res.send('Server is Listening!');
69+
app.get("/", (_, res) => {
70+
res.send("Server is Listening!");
6771
});
6872

6973
// 404 handler
7074
app.use((req, res) => {
7175
res.status(404).json({
72-
error: `Cannot ${req.method} ${req.path}`
76+
error: `Cannot ${req.method} ${req.path}`,
7377
});
7478
});
7579

backend/src/modules/content/controllers/content.controller.ts

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,40 +20,51 @@ export class ContentController {
2020
console.log(error);
2121
res
2222
.status(500)
23-
.json({ error: error as string || "Failed to create content" });
23+
.json({ error: (error as string) || "Failed to create content" });
2424
}
2525
}
2626

2727
static async uploadThumbnail(req: Request, res: Response) {
2828
console.log("Uploading Thumbnail...");
29-
console.log(req.body);
30-
console.log(req.params);
3129
const form = new IncomingForm();
3230
form.parse(req, async (err, fields, files: any) => {
3331
if (err) {
3432
console.error("Error parsing form: ", err);
3533
return res.status(500).json({ error: "Failed to upload thumbnail." });
3634
}
3735

36+
// Check if files.thumbnail exists and is an array
37+
if (
38+
!files.thumbnail ||
39+
!Array.isArray(files.thumbnail) ||
40+
!files.thumbnail[0]
41+
) {
42+
return res.status(400).json({ error: "No file uploaded." });
43+
}
44+
3845
const file = files.thumbnail[0];
3946
const fileName = file.newFilename;
4047
const fileType = file.mimetype;
48+
const filePath = file.filepath; // Use 'filepath' for formidable v2+
4149

42-
// Upload thumbnail to storage
4350
try {
44-
// Upload thumbnail
51+
if (!filePath) {
52+
return res.status(400).json({ error: "File path is missing." });
53+
}
54+
55+
// Pass the file object with the correct path to StorageService
4556
const response = await StorageService.uploadFile(
46-
file,
57+
{ ...file, path: filePath }, // Ensure 'path' is set if your StorageService expects it
4758
"thumbnails",
4859
fileName,
49-
fileType
60+
fileType,
5061
);
5162
res.status(201).json(response);
5263
} catch (error: any) {
5364
console.log(error);
5465
res
5566
.status(500)
56-
.json({ error: error as string || "Failed to upload thumbnail" });
67+
.json({ error: error.message || "Failed to upload thumbnail" });
5768
}
5869
});
5970
}
@@ -82,7 +93,7 @@ export class ContentController {
8293
// const confirmation = await axios.get(`${apiURL}/content/${contentId}`)
8394
const confirmation = await ContentService.getContent(contentId);
8495
const owner_id = confirmation?.creatorUID;
85-
96+
8697
if (userId == owner_id) {
8798
//check whether they are allowed to edit the content
8899
const response = await ContentService.editContent(contentId, data);
@@ -137,11 +148,15 @@ export class ContentController {
137148
const fileType = file.mimetype;
138149

139150
try {
151+
if (!file) {
152+
return res.status(400).json({ error: "No file uploaded." });
153+
}
154+
140155
const response = await StorageService.uploadFile(
141156
file,
142157
"thumbnails",
143158
fileName,
144-
fileType
159+
fileType,
145160
);
146161

147162
const updateData = JSON.parse(fields.data);
@@ -433,7 +448,7 @@ export class ContentController {
433448
static async getRelatedContent(req: Request, res: Response) {
434449
console.log("Fetching Related Content...");
435450
const { contentId } = req.params;
436-
const userId = req.query.userId as string || undefined;
451+
const userId = (req.query.userId as string) || undefined;
437452
const limit = req.query.limit ? parseInt(req.query.limit as string) : 5;
438453

439454
try {

0 commit comments

Comments
 (0)