Skip to content

Commit c9ab80e

Browse files
committed
complete with password based authentication
1 parent b95d716 commit c9ab80e

File tree

5 files changed

+92
-11
lines changed

5 files changed

+92
-11
lines changed

backend/README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
1+
# Website Backend
2+
3+
Contains a few functionalities needed to run the website.
4+
5+
- Submit feedbacks, contact and comments
6+
- A directory of examples
7+
18

2-
# Typescript module

backend/src/app.tsx

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,63 @@
11
import * as express from 'express';
22
import * as dotenv from 'dotenv';
3-
import feedbackRoutes from './routes/feedback.routes';
4-
import healthRoutes from './routes/health.routes';
53
import * as swaggerUi from 'swagger-ui-express';
64
import * as YAML from 'yamljs';
75
import * as path from 'path';
6+
import * as match from 'path-to-regexp';
7+
import feedbackRoutes from './routes/feedback.routes';
8+
import healthRoutes from './routes/health.routes';
89

910

1011
dotenv.config();
1112
const swaggerDocument = YAML.load(path.join(__dirname, './docs/openapi.yaml'));
1213

14+
// Parse swaggerDocument to find paths with security defined
15+
const securedPaths: string[] = [];
16+
if (swaggerDocument && swaggerDocument.paths) {
17+
for (const [route, methods] of Object.entries(swaggerDocument.paths)) {
18+
// @ts-ignore
19+
for (const [method, details] of Object.entries<any>(methods)) {
20+
if (details && details.security && details.security.length > 0) {
21+
if (!securedPaths.includes(route)) {
22+
securedPaths.push(route);
23+
}
24+
}
25+
}
26+
}
27+
}
28+
console.debug('Secured paths:', securedPaths);
29+
30+
1331
const app = express();
32+
1433
// Middleware
34+
// JSON body parser
1535
app.use(express.json());
36+
// Authentication middleware
37+
// @ts-ignore
38+
app.use((req, res, next) => {
39+
const isSecured = securedPaths.some(pathPattern => {
40+
// Convert OpenAPI path patterns (e.g., /feedbacks/{id}) to path-to-regexp style (e.g., /feedbacks/:id)
41+
const openApiPattern = pathPattern.replace(/{([^}]+)}/g, ':$1');
42+
const matcher = match.match(openApiPattern, { decode: decodeURIComponent, end: true });
43+
return matcher(req.path) !== false;
44+
});
45+
console.debug('Request path:', req.path, 'isSecured:', isSecured);
46+
if (isSecured) {
47+
const token = req.headers['x-api-key'];
48+
if (!token || token !== process.env.AUTH_TOKEN) {
49+
console.warn('Unauthorized access attempt:', req.path);
50+
return res.status(401).json({ message: 'Unauthorized' });
51+
}
52+
}
53+
next();
54+
});
1655

1756
// Routes
1857
// @ts-ignore
1958
app.get('/', (req, res) => res.redirect('/api-docs'));
2059
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
2160
app.use('/feedbacks', feedbackRoutes);
22-
app.use('/health', healthRoutes)
61+
app.use('/health', healthRoutes);
2362

2463
export default app;

backend/src/db.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import * as dotenv from 'dotenv';
44
dotenv.config();
55

66
const POSTGRES_URI = process.env.POSTGRES_URI || 'postgres://user:password@localhost:5432/myapp';
7-
console.log(`Connecting to PostgreSQL at ${POSTGRES_URI}`);
8-
// PostgreSQL Connection
97
export const sequelize = new Sequelize(POSTGRES_URI, {dialect: "postgres"});
108

119
sequelize.authenticate()

backend/src/docs/openapi.yaml

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,32 @@ components:
2424
text:
2525
type: string
2626

27+
FeedbackRow:
28+
type: object
29+
description: Feedback data structure for database storage
30+
properties:
31+
id:
32+
type: string
33+
format: uuid
34+
example: "123e4567-e89b-12d3-a456-426614174000"
35+
name:
36+
type: string
37+
type:
38+
type: string
39+
example: "bug" # or "feature"
40+
email:
41+
type: string
42+
text:
43+
type: string
44+
created_at:
45+
type: string
46+
format: date-time
47+
example: "2023-10-01T12:00:00Z"
48+
updated_at:
49+
type: string
50+
format: date-time
51+
example: "2023-10-01T12:00:00Z"
52+
2753
ResponseMessage:
2854
type: object
2955
description: Plain response with a message field
@@ -42,13 +68,21 @@ components:
4268
message:
4369
type: string
4470
example: "Service is healthy"
71+
72+
securitySchemes:
73+
ApiKeyAuth:
74+
type: apiKey
75+
in: header
76+
name: X-API-KEY # name of the header, query parameter or cookie
4577

4678

4779
paths:
4880
/feedbacks:
4981
post:
5082
summary: Submit feedback
5183
operationId: submitFeedback
84+
security:
85+
- ApiKeyAuth: []
5286
requestBody:
5387
required: true
5488
content:
@@ -84,6 +118,8 @@ paths:
84118
get:
85119
summary: Get all feedbacks
86120
operationId: getFeedbacks
121+
security:
122+
- ApiKeyAuth: []
87123
responses:
88124
'200':
89125
description: List of feedbacks
@@ -92,7 +128,7 @@ paths:
92128
schema:
93129
type: array
94130
items:
95-
$ref: '#/components/schemas/Feedback'
131+
$ref: '#/components/schemas/FeedbackRow'
96132
'500':
97133
description: Internal server error
98134
content:
@@ -104,6 +140,8 @@ paths:
104140
get:
105141
summary: Get feedback by ID
106142
operationId: getFeedbackById
143+
security:
144+
- ApiKeyAuth: []
107145
parameters:
108146
- name: id
109147
in: path
@@ -116,15 +154,14 @@ paths:
116154
content:
117155
application/json:
118156
schema:
119-
$ref: '#/components/schemas/Feedback'
157+
$ref: '#/components/schemas/FeedbackRow'
120158
'404':
121159
description: Feedback not found
122160
content:
123161
application/json:
124162
schema:
125163
$ref: '#/components/schemas/ResponseMessage'
126164

127-
128165
/health:
129166
get:
130167
summary: Health check
@@ -142,6 +179,7 @@ paths:
142179
application/json:
143180
schema:
144181
$ref: '#/components/schemas/HealthStatus'
182+
145183

146184

147185

backend/src/models/feedback.model.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ Feedback.init(
3232
{
3333
sequelize,
3434
modelName: 'Feedback',
35-
tableName: 'Feedbacks',
35+
tableName: 'feedbacks',
3636
timestamps: true,
3737
createdAt: 'created_at',
3838
updatedAt: 'updated_at',
39-
underscored: true, // Use snake_case for column names
39+
underscored: true,
4040
indexes: [
4141
{
4242
unique: true,

0 commit comments

Comments
 (0)