Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion backend/src/modules/admin/admin.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Request, Response } from 'express';
import { BadRequestError } from '../../common/errors/AppError.js';
import {
createAuditLogSchema,
listAuditLogsQuerySchema,
platformStatsResponseSchema,
} from './admin.schema.js';
Expand Down Expand Up @@ -58,7 +59,11 @@ export async function createAuditLogController(
throw new BadRequestError('Unauthorized');
}

const { action, target, metadata } = req.body;
const parsed = createAuditLogSchema.safeParse(req.body);
if (!parsed.success) {
throw new BadRequestError('Invalid audit log payload', parsed.error.issues);
}
const { action, target, metadata } = parsed.data;

const log = await logAuditAction(
auth.sub,
Expand Down
6 changes: 3 additions & 3 deletions backend/src/modules/admin/admin.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ export const createAuditLogSchema = z.object({
action: z.string().min(1).max(255),
target: z.string().optional().nullable(),
metadata: z.record(z.unknown()).default({}),
});
}).strict();

export const listAuditLogsQuerySchema = z.object({
limit: z.coerce.number().int().min(1).max(100).default(20),
offset: z.coerce.number().int().min(0).default(0),
action: z.string().optional(),
actor: z.string().optional(),
});
}).strict();

export const platformStatsResponseSchema = z.object({
totalUsers: z.number(),
Expand All @@ -22,4 +22,4 @@ export const platformStatsResponseSchema = z.object({
totalSubscriptions: z.number(),
totalRefunds: z.number(),
averageTipAmount: z.string(),
});
}).strict();
16 changes: 8 additions & 8 deletions backend/src/modules/admin/config.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,44 +26,44 @@ const stroopsString = z
export const prepareSetFeeSchema = z.object({
/** New fee in basis points. Contract bound: 0–1000 (max 10%). */
feeBps: z.number().int().min(0).max(1000, 'fee_bps must be ≤ 1000 (10%)'),
});
}).strict();

export const prepareSetMinTipAmountSchema = z.object({
/** New minimum tip amount in stroops (>= 0). */
amount: stroopsString,
});
}).strict();

export const prepareSetMinWithdrawalAmountSchema = z.object({
/** New minimum withdrawal amount in stroops (>= 0). */
amount: stroopsString,
});
}).strict();

export const preparePauseSchema = z.object({
/** true = pause, false = unpause. */
paused: z.boolean(),
});
}).strict();

// ── Submit schemas ────────────────────────────────────────────────────────────

export const submitSetFeeSchema = z.object({
feeBps: z.number().int().min(0).max(1000, 'fee_bps must be ≤ 1000 (10%)'),
signedTxXdr: z.string().min(1, 'signedTxXdr is required'),
});
}).strict();

export const submitSetMinTipAmountSchema = z.object({
amount: stroopsString,
signedTxXdr: z.string().min(1, 'signedTxXdr is required'),
});
}).strict();

export const submitSetMinWithdrawalAmountSchema = z.object({
amount: stroopsString,
signedTxXdr: z.string().min(1, 'signedTxXdr is required'),
});
}).strict();

export const submitPauseSchema = z.object({
paused: z.boolean(),
signedTxXdr: z.string().min(1, 'signedTxXdr is required'),
});
}).strict();

// ── Types ─────────────────────────────────────────────────────────────────────

Expand Down
12 changes: 6 additions & 6 deletions backend/src/modules/analytics/analytics.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const analyticsDailyQuerySchema = z.object({
endDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'Must be YYYY-MM-DD format').optional(),
limit: z.coerce.number().int().min(1).max(365).default(30),
offset: z.coerce.number().int().min(0).default(0),
});
}).strict();

export type AnalyticsDailyQuery = z.infer<typeof analyticsDailyQuerySchema>;

Expand All @@ -15,15 +15,15 @@ export const volumeQuerySchema = z.object({
granularity: z.enum(['day', 'week', 'month']).default('day'),
startDate: z.string().datetime({ offset: true }).optional(),
endDate: z.string().datetime({ offset: true }).optional(),
});
}).strict();

export type VolumeQuery = z.infer<typeof volumeQuerySchema>;

/** Query parameters for GET /analytics/top-tippers (issue #1009). */
export const topTippersQuerySchema = z.object({
page: z.coerce.number().int().min(1).default(1),
limit: z.coerce.number().int().min(1).max(100).default(20),
});
}).strict();

export type TopTippersQuery = z.infer<typeof topTippersQuerySchema>;

Expand All @@ -32,13 +32,13 @@ export const activeUsersQuerySchema = z.object({
granularity: z.enum(['day', 'week', 'month']).default('day'),
startDate: z.string().datetime({ offset: true }).optional(),
endDate: z.string().datetime({ offset: true }).optional(),
});
}).strict();

export type ActiveUsersQuery = z.infer<typeof activeUsersQuerySchema>;
/** Path parameters for GET /analytics/creators/:username. */
export const creatorUsernameParamSchema = z.object({
username: z.string().min(1, 'Username is required').max(50),
});
}).strict();

export type CreatorUsernameParam = z.infer<typeof creatorUsernameParamSchema>;

Expand All @@ -47,6 +47,6 @@ export const creatorAnalyticsQuerySchema = z.object({
startDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'Must be YYYY-MM-DD format').optional(),
endDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, 'Must be YYYY-MM-DD format').optional(),
granularity: z.enum(['day', 'week', 'month']).default('day'),
});
}).strict();

export type CreatorAnalyticsQuery = z.infer<typeof creatorAnalyticsQuerySchema>;
6 changes: 3 additions & 3 deletions backend/src/modules/apiKeys/apiKeys.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { z } from "zod";
export const createApiKeySchema = z.object({
scopes: z.array(z.string().min(1)).min(1, "At least one scope is required"),
expiresAt: z.string().datetime().optional(),
});
}).strict();

export const rotateApiKeySchema = z.object({
gracePeriodMinutes: z.coerce.number().int().positive().max(10080).optional(),
});
}).strict();

export const apiKeyIdParamSchema = z.object({
id: z.string().min(1, "API key ID is required"),
});
}).strict();

export type CreateApiKeyInput = z.infer<typeof createApiKeySchema>;
export type RotateApiKeyInput = z.infer<typeof rotateApiKeySchema>;
Expand Down
6 changes: 3 additions & 3 deletions backend/src/modules/auth/auth.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ import { z } from "zod";
export const challengeSchema = z.object({
stellarAddress: z.string().min(1, "Stellar address is required"),
network: z.enum(["TESTNET", "FUTURENET", "MAINNET"]).optional(),
});
}).strict();

export const verifySchema = z.object({
stellarAddress: z.string().min(1, "Stellar address is required"),
signature: z.string().min(1, "Signature is required"),
challenge: z.string().min(1, "Challenge is required"),
network: z.enum(["TESTNET", "FUTURENET", "MAINNET"]).optional(),
});
}).strict();

export const refreshSchema = z.object({
refreshToken: z.string().min(1, "Refresh token is required"),
});
}).strict();

export type ChallengeInput = z.infer<typeof challengeSchema>;
export type VerifyInput = z.infer<typeof verifySchema>;
Expand Down
8 changes: 4 additions & 4 deletions backend/src/modules/credit/credit.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ import { z } from 'zod';

export const creditIdentifierParamSchema = z.object({
identifier: z.string().min(1).max(100),
});
}).strict();

export const userIdParamSchema = z.object({
userId: z.string().min(1),
});
}).strict();

export const recalculateSchema = z.object({
userId: z.string().min(1),
});
}).strict();

export const creditHistoryQuerySchema = z.object({
limit: z.coerce.number().int().min(1).max(100).default(20),
offset: z.coerce.number().int().min(0).default(0),
});
}).strict();

export type CreditIdentifierParam = z.infer<typeof creditIdentifierParamSchema>;
export type UserIdParam = z.infer<typeof userIdParamSchema>;
Expand Down
4 changes: 2 additions & 2 deletions backend/src/modules/discovery/discovery.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { z } from 'zod';
export const trendingQuerySchema = z.object({
limit: z.coerce.number().int().min(1).max(100).default(20),
offset: z.coerce.number().int().min(0).default(0),
});
}).strict();

export const similarQuerySchema = z.object({
limit: z.coerce.number().int().min(1).max(100).default(20),
});
}).strict();
2 changes: 1 addition & 1 deletion backend/src/modules/email/email.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ export const sendEmailSchema = z.object({
html: z.string().max(20_000).optional(),
type: z.string().min(1).max(80).optional(),
metadata: z.record(z.string(), z.unknown()).optional(),
});
}).strict();

export type SendEmailInput = z.infer<typeof sendEmailSchema>;
8 changes: 4 additions & 4 deletions backend/src/modules/goals/goals.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ export const createGoalSchema = z.object({
title: z.string().min(1).max(200),
targetStroops: z.string().regex(/^\d+$/, 'Must be a positive integer string'),
deadline: z.string().datetime({ offset: true }).optional(),
});
}).strict();

export const updateGoalSchema = z.object({
title: z.string().min(1).max(200).optional(),
targetStroops: z.string().regex(/^\d+$/, 'Must be a positive integer string').optional(),
deadline: z.string().datetime({ offset: true }).nullable().optional(),
status: z.enum(['ACTIVE', 'CANCELLED']).optional(),
});
}).strict();

export const goalIdSchema = z.object({
id: z.string().min(1),
});
}).strict();

export const goalListQuerySchema = z.object({
limit: z.coerce.number().int().min(1).max(100).default(20),
offset: z.coerce.number().int().min(0).default(0),
status: z.enum(['ACTIVE', 'COMPLETED', 'CANCELLED', 'EXPIRED']).optional(),
});
}).strict();

export type CreateGoalInput = z.infer<typeof createGoalSchema>;
export type UpdateGoalInput = z.infer<typeof updateGoalSchema>;
Expand Down
4 changes: 2 additions & 2 deletions backend/src/modules/ipfs/ipfs.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ export const cidParamSchema = z.object({
.trim()
.min(1, "CID cannot be empty")
.regex(cidRegex, "Invalid IPFS CID format"),
});
}).strict();

/**
* Zod schema for gateway query parameters.
*/
export const gatewayQuerySchema = z.object({
gateway: z.string().url("Invalid gateway URL format").optional(),
});
}).strict();

export type CidParamInput = z.infer<typeof cidParamSchema>;
export type GatewayQueryInput = z.infer<typeof gatewayQuerySchema>;
Loading