Skip to content

Commit

Permalink
Merge branch 'main' into production
Browse files Browse the repository at this point in the history
  • Loading branch information
riya09 committed Jan 18, 2025
2 parents bdcce9c + 26a43cc commit 635e6c6
Show file tree
Hide file tree
Showing 47 changed files with 7,182 additions and 387 deletions.
128 changes: 128 additions & 0 deletions apps/jonogon-core/src/api/http/handlers/jobabs/attachment.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import {TContextCreator} from '../../../trpc/context.mjs';
import sharp from 'sharp';
import {nanoid} from 'nanoid';
import {logger} from '../../../../logger.mjs';
import {Request, Response} from 'express';
import {z} from 'zod';
import { requireAuth, requireModeratorOrAdmin } from '../../../utility/auth-utils.js';

export function createJobabAttachmentHandler(createContext: TContextCreator) {
return async (req: Request, res: Response) => {
try {
const ctx = await createContext({req, res});

const queryParams = z
.object({
filename: z.string(),
type: z.enum(['image', 'file']),
})
.safeParse(req.query);

if (!queryParams.success) {
return res.status(400).json({
message: 'query params were not sent correctly',
error: queryParams.error,
});
}

if (!req.body) {
return res.status(400).json({
message: 'file where?',
});
}

// check if the user is logged in
requireAuth(ctx, res, 'must be logged in to upload attachments');

// Check if user is moderator or admin
requireModeratorOrAdmin(ctx, res, 'only moderators can add attachments to jobabs');

const jobab = await ctx.services.postgresQueryBuilder
.selectFrom('jobabs')
.selectAll()
.where('id', '=', req.params.id)
.where('deleted_at', 'is', null)
.executeTakeFirst();

if (!jobab) {
return res.status(404).json({
message: 'jobab not found',
});
}

const fileName = nanoid();

if (queryParams.data.type === 'image') {
const imageKey = `jobab_attachment_${fileName}.jpg`;
const thumbnailKey = `jobab_attachment_thumbnail_${fileName}.jpg`;

const imageStream = await sharp(req.body)
.rotate()
.resize(1960)
.jpeg()
.toBuffer();

const thumbnailStream = await sharp(req.body)
.rotate()
.resize(512)
.jpeg()
.toBuffer();

await Promise.all([
ctx.services.fileStorage.storeFile(imageKey, imageStream),
ctx.services.fileStorage.storeFile(
thumbnailKey,
thumbnailStream,
),
]);

const result = await ctx.services.postgresQueryBuilder
.insertInto('jobab_attachments')
.values({
jobab_id: req.params.id,
filename: queryParams.data.filename,
attachment: imageKey,
})
.returning(['id'])
.executeTakeFirst();

return res.json({
message: 'image-added',
data: {
attachment_id: result?.id,
url: await ctx.services.fileStorage.getFileURL(imageKey),
},
});
} else {
const ext = queryParams.data.filename.split('.').pop();
const fileKey = `jobab_attachment_${fileName}.${ext}`;

await ctx.services.fileStorage.storeFile(fileKey, req.body);

const result = await ctx.services.postgresQueryBuilder
.insertInto('jobab_attachments')
.values({
jobab_id: req.params.id,
filename: queryParams.data.filename,
attachment: fileKey,
})
.returning(['id'])
.executeTakeFirst();

return res.json({
message: 'attachment-added',
data: {
attachment_id: result?.id,
url: await ctx.services.fileStorage.getFileURL(fileKey),
},
});
}
} catch (error) {
logger.error('error uploading jobab attachment', {error});

res.status(500).json({
message: 'INTERNAL SERVER ERROR',
});
}
};
}
13 changes: 6 additions & 7 deletions apps/jonogon-core/src/api/http/handlers/petitions/attachment.mts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {nanoid} from 'nanoid';
import {logger} from '../../../../logger.mjs';
import {Request, Response} from 'express';
import {z} from 'zod';
import {requireAuth} from '../../../utility/auth-utils.js';

export function createPetitionAttachmentHandler(
createContext: TContextCreator,
Expand Down Expand Up @@ -32,11 +33,8 @@ export function createPetitionAttachmentHandler(
});
}

if (!ctx.auth?.user_id) {
return res.status(401).json({
message: 'must be logged in to set a profile picture',
});
}
// check if the user is logged in
requireAuth(ctx, res, 'must be logged in to set a profile picture');

const petition = await ctx.services.postgresQueryBuilder
.selectFrom('petitions')
Expand All @@ -51,13 +49,14 @@ export function createPetitionAttachmentHandler(
}

if (
`${petition.created_by}` !== `${ctx.auth.user_id}` &&
!ctx.auth.is_user_admin
`${petition.created_by}` !== `${ctx.auth?.user_id}` &&
!ctx.auth!.is_user_admin
) {
return res.status(403).json({
message: 'you are not the creator of this petition',
});
}


const fileName = nanoid();

Expand Down
89 changes: 89 additions & 0 deletions apps/jonogon-core/src/api/http/handlers/respondents/image.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import {TContextCreator} from '../../../trpc/context.mjs';
import sharp from 'sharp';
import {nanoid} from 'nanoid';
import {logger} from '../../../../logger.mjs';
import {Request, Response} from 'express';
import {z} from 'zod';
import {requireModeratorOrAdmin} from '../../../utility/auth-utils.js';

const querySchema = z.object({
type: z.enum(['organization', 'expert']),
});

export function createRespondentImageHandler(createContext: TContextCreator) {
return async (req: Request, res: Response) => {
try {
const ctx = await createContext({req, res});

const queryParams = querySchema.safeParse(req.query);

if (!queryParams.success) {
return res.status(400).json({
message: 'query params were not sent correctly',
error: queryParams.error,
});
}

if (!req.body) {
return res.status(400).json({
message: 'file where?',
});
}

// Check if user is moderator or admin
requireModeratorOrAdmin(
ctx,
res,
'only moderators can add images to respondents',
);

const fileName = nanoid();
const imageKey = `respondent_${queryParams.data.type}_${fileName}.jpg`;
const thumbnailKey = `respondent_${queryParams.data.type}_thumbnail_${fileName}.jpg`;

// Process image in two sizes like petition attachments
const imageStream = await sharp(req.body)
.rotate()
.resize(1024, 1024, {
fit: 'cover',
position: 'attention',
})
.jpeg()
.toBuffer();

const thumbnailStream = await sharp(req.body)
.rotate()
.resize(512, 512, {
fit: 'cover',
position: 'attention',
})
.jpeg()
.toBuffer();

// Store both sizes
await Promise.all([
ctx.services.fileStorage.storeFile(imageKey, imageStream),
ctx.services.fileStorage.storeFile(
thumbnailKey,
thumbnailStream,
),
]);

return res.json({
message: 'image-uploaded',
data: {
imageKey,
thumbnail_url:
await ctx.services.fileStorage.getFileURL(thumbnailKey),
image_url:
await ctx.services.fileStorage.getFileURL(imageKey),
},
});
} catch (error) {
logger.error('error uploading respondent image', error);
res.status(500).json({
message: 'INTERNAL SERVER ERROR',
});
}
};
}
10 changes: 4 additions & 6 deletions apps/jonogon-core/src/api/http/handlers/users/picture.mts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@ import {nanoid} from 'nanoid';
import {logger} from '../../../../logger.mjs';
import {Request, Response} from 'express';
import {env} from '../../../../env.mjs';
import {requireAuth} from '../../../utility/auth-utils.js';

export function createProfilePictureHandler(createContext: TContextCreator) {
return async (req: Request, res: Response) => {
try {
const ctx = await createContext({req, res});

if (!ctx.auth?.user_id) {
return res.status(401).json({
message: 'must be logged in to set a profile picture',
});
}
// check if user is logged in
requireAuth(ctx, res, 'must be logged in to set a profile picture');

if (!req.body) {
return res.status(400).json({
Expand All @@ -41,7 +39,7 @@ export function createProfilePictureHandler(createContext: TContextCreator) {
.set({
picture: fileKey,
})
.where('id', '=', `${ctx.auth.user_id}`)
.where('id', '=', `${ctx.auth?.user_id}`)
.executeTakeFirst();

if (env.NODE_ENV === 'development') {
Expand Down
20 changes: 20 additions & 0 deletions apps/jonogon-core/src/api/http/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {milestoneDetectionQueue} from '../../services/queues/milestoneDetectionQ
import {notificationsSchedulerQueue} from '../../services/queues/notificationsSchedulerQueue.mjs';
import {smsNotificationDispatchQueue} from '../../services/queues/smsNotificationDispatchQueue.mjs';
import basicAuth from 'express-basic-auth';
import {createRespondentImageHandler} from './handlers/respondents/image.mjs';
import {createJobabAttachmentHandler} from './handlers/jobabs/attachment.mjs';

export async function registerHTTPRoutes(
expressApp: Express,
Expand Down Expand Up @@ -51,6 +53,24 @@ export async function registerHTTPRoutes(
createProfilePictureHandler(createContext),
);

app.post(
'/respondents/image',
express.raw({
inflate: true,
limit: 16 * 1024 * 1024,
}),
createRespondentImageHandler(createContext),
);

app.post(
'/jobabs/:id/attachments',
express.raw({
inflate: true,
limit: 16 * 1024 * 1024,
}),
createJobabAttachmentHandler(createContext),
);

if (env.NODE_ENV === 'development') {
app.use('/static/', express.static('/jonogon-static/'));

Expand Down
9 changes: 6 additions & 3 deletions apps/jonogon-core/src/api/trpc/procedures/comments/crud.mts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {scope} from 'scope-utilities';
import {TRPCError} from '@trpc/server';
import {protectedProcedure} from '../../middleware/protected.mjs';
import {sql} from 'kysely';
import { calculateCommentVelocity } from '../../../utility/feed-algorithm.mjs';
import {calculateCommentVelocity} from '../../../utility/feed-algorithm.mjs';

export const countAllComments = publicProcedure
.input(
Expand Down Expand Up @@ -403,10 +403,13 @@ export const createComment = protectedProcedure
.executeTakeFirst();

if (petition) {
const { logScore, newScore } = calculateCommentVelocity(petition?.approved_at, petition.score);
const {logScore, newScore} = calculateCommentVelocity(
petition.approved_at ?? new Date(),
petition.score,
);
await ctx.services.postgresQueryBuilder
.updateTable('petitions')
.set({ score: newScore, log_score: logScore })
.set({score: newScore, log_score: logScore})
.where('id', '=', input.petition_id)
.execute();
await ctx.services.postgresQueryBuilder
Expand Down
Loading

0 comments on commit 635e6c6

Please sign in to comment.