chore: remediate pygienium audit findings

Dead code: 77 verified-unused exports, files (BackArrow, MenuBars,
cookies.ts, db/create.ts, schemas/comment.ts, security-headers.ts) and
12 unused dependencies removed
Comments: ~370 RESTATE comments stripped across 53 files; 2 verbose
blocks tightened; dead commented-out config removed
Complexity: bulkUpsert extracted into 11 per-entity helpers (CCN 156->~10);
login formHandler split into 3 submitters (CCN 63->~5); account page render
split into 8 section components (CCN 40); updatePost SQL builder rebuilt;
assert*Owned consolidated behind generic assertOwnedBy
Defensive guards: 4 redundant rethrow/nullish guards removed
This commit is contained in:
2026-08-11 13:36:18 -04:00
parent 33ca9213f2
commit 898c891bd5
76 changed files with 1437 additions and 2600 deletions

View File

@@ -6,105 +6,6 @@ import { z } from "zod";
* Schemas for post creation, updating, querying, and interactions
*/
// ============================================================================
// Post Category and Status
// ============================================================================
/**
* Post category enum (deprecated but kept for backward compatibility)
*/
export const postCategorySchema = z.enum(["blog", "project"]);
// ============================================================================
// Post Creation and Updates
// ============================================================================
/**
* Create new post schema
*/
export const createPostSchema = z.object({
title: z
.string()
.min(1, "Title is required")
.max(200, "Title must be under 200 characters"),
subtitle: z
.string()
.max(300, "Subtitle must be under 300 characters")
.optional(),
body: z.string().min(1, "Post body is required"),
banner_photo: z.string().url("Must be a valid URL").optional(),
published: z.boolean().default(false),
category: postCategorySchema.default("blog"),
attachments: z.string().optional()
});
/**
* Update post schema (partial updates)
*/
export const updatePostSchema = z.object({
postId: z.number(),
title: z.string().min(1).max(200).optional(),
subtitle: z.string().max(300).optional(),
body: z.string().min(1).optional(),
banner_photo: z.string().url().optional(),
published: z.boolean().optional(),
attachments: z.string().optional()
});
/**
* Delete post schema
*/
export const deletePostSchema = z.object({
postId: z.number()
});
// ============================================================================
// Post Queries and Filtering
// ============================================================================
/**
* Post sort mode enum
* Defines available sorting options for blog posts
*/
export const postSortModeSchema = z.enum([
"newest",
"oldest",
"most_liked",
"most_read",
"most_comments"
]);
/**
* Post query input schema
* Accepts optional filters (pipe-separated tags) and sort mode
*/
export const postQueryInputSchema = z.object({
/**
* Pipe-separated list of tags to filter by
* e.g., "tech|design|javascript"
* Empty string or undefined means no filter
*/
filters: z.string().optional(),
/**
* Sort mode for posts
* Defaults to "newest" if not specified
*/
sortBy: postSortModeSchema.default("newest")
});
/**
* Get single post by ID or slug
*/
export const getPostSchema = z
.object({
postId: z.number().optional(),
slug: z.string().optional()
})
.refine((data) => data.postId || data.slug, {
message: "Either postId or slug must be provided"
});
// ============================================================================
// Post Interactions
// ============================================================================
@@ -116,55 +17,8 @@ export const incrementPostReadSchema = z.object({
postId: z.number()
});
/**
* Like/unlike post
*/
export const togglePostLikeSchema = z.object({
postId: z.number()
});
// ============================================================================
// Tag Management
// ============================================================================
/**
* Add tags to post
*/
export const addTagsToPostSchema = z.object({
postId: z.number(),
tags: z
.array(z.string().min(1).max(50))
.min(1, "At least one tag is required")
});
/**
* Remove tag from post
*/
export const removeTagFromPostSchema = z.object({
tagId: z.number()
});
/**
* Update post tags (replaces all tags)
*/
export const updatePostTagsSchema = z.object({
postId: z.number(),
tags: z.array(z.string().min(1).max(50))
});
// ============================================================================
// Type Exports
// ============================================================================
export type PostCategory = z.infer<typeof postCategorySchema>;
export type CreatePostInput = z.infer<typeof createPostSchema>;
export type UpdatePostInput = z.infer<typeof updatePostSchema>;
export type DeletePostInput = z.infer<typeof deletePostSchema>;
export type PostSortMode = z.infer<typeof postSortModeSchema>;
export type PostQueryInput = z.infer<typeof postQueryInputSchema>;
export type GetPostInput = z.infer<typeof getPostSchema>;
export type IncrementPostReadInput = z.infer<typeof incrementPostReadSchema>;
export type TogglePostLikeInput = z.infer<typeof togglePostLikeSchema>;
export type AddTagsToPostInput = z.infer<typeof addTagsToPostSchema>;
export type RemoveTagFromPostInput = z.infer<typeof removeTagFromPostSchema>;
export type UpdatePostTagsInput = z.infer<typeof updatePostTagsSchema>;

View File

@@ -1,116 +0,0 @@
/**
* Comment API Validation Schemas
*
* Zod schemas for comment-related tRPC procedures:
* - Comment creation, updating, deletion
* - Comment reactions
* - Comment sorting and filtering
*/
import { z } from "zod";
// ============================================================================
// Comment CRUD Operations
// ============================================================================
/**
* Create new comment schema
*/
export const createCommentSchema = z.object({
body: z
.string()
.min(1, "Comment cannot be empty")
.max(5000, "Comment too long"),
post_id: z.number(),
parent_comment_id: z.number().optional()
});
/**
* Update comment schema
*/
export const updateCommentSchema = z.object({
commentId: z.number(),
body: z
.string()
.min(1, "Comment cannot be empty")
.max(5000, "Comment too long")
});
/**
* Delete comment schema
*/
export const deleteCommentSchema = z.object({
commentId: z.number(),
deletionType: z.enum(["user", "admin", "database"]).optional()
});
/**
* Get comments for post schema
*/
export const getCommentsSchema = z.object({
postId: z.number(),
sortBy: z.enum(["newest", "oldest", "highest_rated", "hot"]).default("newest")
});
// ============================================================================
// Comment Reactions
// ============================================================================
/**
* Valid reaction types
*/
export const reactionTypeSchema = z.enum([
"tears",
"blank",
"tongue",
"cry",
"heartEye",
"angry",
"moneyEye",
"sick",
"upsideDown",
"worried"
]);
/**
* Add/remove reaction to comment
*/
export const toggleCommentReactionSchema = z.object({
commentId: z.number(),
reactionType: reactionTypeSchema
});
/**
* Get reactions for comment
*/
export const getCommentReactionsSchema = z.object({
commentId: z.number()
});
// ============================================================================
// Comment Sorting
// ============================================================================
/**
* Valid comment sorting modes
*/
export const commentSortSchema = z
.enum(["newest", "oldest", "highest_rated", "hot"])
.default("newest");
// ============================================================================
// Type Exports
// ============================================================================
export type CommentSortMode = z.infer<typeof commentSortSchema>;
export type ReactionType = z.infer<typeof reactionTypeSchema>;
export type CreateCommentInput = z.infer<typeof createCommentSchema>;
export type UpdateCommentInput = z.infer<typeof updateCommentSchema>;
export type DeleteCommentInput = z.infer<typeof deleteCommentSchema>;
export type GetCommentsInput = z.infer<typeof getCommentsSchema>;
export type ToggleCommentReactionInput = z.infer<
typeof toggleCommentReactionSchema
>;
export type GetCommentReactionsInput = z.infer<
typeof getCommentReactionsSchema
>;

View File

@@ -7,71 +7,10 @@ import { z } from "zod";
* Use these schemas for validating database inputs and outputs in tRPC procedures
*/
// ============================================================================
// User Schemas
// ============================================================================
/**
* Full User schema matching database structure
*/
export const userSchema = z.object({
id: z.string(),
email: z.string().email().nullable().optional(),
email_verified: z.number(),
password_hash: z.string().nullable().optional(),
display_name: z.string().nullable().optional(),
provider: z.enum(["email", "google", "github"]).nullable().optional(),
image: z.string().url().nullable().optional(),
apple_user_string: z.string().nullable().optional(),
database_name: z.string().nullable().optional(),
database_token: z.string().nullable().optional(),
database_url: z.string().nullable().optional(),
db_destroy_date: z.string().nullable().optional(),
created_at: z.string(),
updated_at: z.string()
});
/**
* User creation input (for registration)
*/
export const createUserSchema = z.object({
email: z.string().email().optional(),
password: z.string().min(8).optional(),
display_name: z.string().min(1).max(50).optional(),
provider: z.enum(["email", "google", "github"]).optional(),
image: z.string().url().optional()
});
/**
* User update input (partial updates)
*/
export const updateUserSchema = z.object({
email: z.string().email().optional(),
display_name: z.string().min(1).max(50).optional(),
image: z.string().url().optional()
});
// ============================================================================
// Post Schemas
// ============================================================================
/**
* Full Post schema matching database structure
*/
export const postSchema = z.object({
id: z.number(),
category: z.enum(["blog", "project"]),
title: z.string(),
subtitle: z.string().optional(),
body: z.string(),
banner_photo: z.string().optional(),
date: z.string(),
published: z.boolean(),
author_id: z.string(),
reads: z.number(),
attachments: z.string().optional()
});
/**
* Post creation input
*/
@@ -97,47 +36,6 @@ export const updatePostSchema = z.object({
attachments: z.string().optional()
});
/**
* Post with aggregated data
*/
export const postWithCommentsAndLikesSchema = postSchema.extend({
total_likes: z.number(),
total_comments: z.number()
});
// ============================================================================
// Comment Schemas
// ============================================================================
/**
* Full Comment schema matching database structure
*/
export const commentSchema = z.object({
id: z.number(),
body: z.string(),
post_id: z.number(),
parent_comment_id: z.number().optional(),
date: z.string(),
edited: z.boolean(),
commenter_id: z.string()
});
/**
* Comment creation input
*/
export const createCommentSchema = z.object({
body: z.string().min(1).max(5000),
post_id: z.number(),
parent_comment_id: z.number().optional()
});
/**
* Comment update input
*/
export const updateCommentSchema = z.object({
body: z.string().min(1).max(5000)
});
// ============================================================================
// CommentReaction Schemas
// ============================================================================
@@ -160,94 +58,6 @@ export const reactionTypeSchema = z.enum([
"downVote"
]);
/**
* Full CommentReaction schema matching database structure
*/
export const commentReactionSchema = z.object({
id: z.number(),
type: reactionTypeSchema,
comment_id: z.number(),
user_id: z.string()
});
/**
* Comment reaction creation input
*/
export const createCommentReactionSchema = z.object({
type: reactionTypeSchema,
comment_id: z.number()
});
// ============================================================================
// PostLike Schemas
// ============================================================================
/**
* Full PostLike schema matching database structure
*/
export const postLikeSchema = z.object({
id: z.number(),
user_id: z.string(),
post_id: z.number()
});
/**
* PostLike creation input
*/
export const createPostLikeSchema = z.object({
post_id: z.number()
});
// ============================================================================
// Tag Schemas
// ============================================================================
/**
* Full Tag schema matching database structure
*/
export const tagSchema = z.object({
id: z.number(),
value: z.string(),
post_id: z.number()
});
/**
* Tag creation input
*/
export const createTagSchema = z.object({
value: z.string().min(1).max(50),
post_id: z.number()
});
/**
* PostWithTags schema
*/
export const postWithTagsSchema = postSchema.extend({
tags: z.array(tagSchema)
});
// ============================================================================
// Connection Schemas
// ============================================================================
/**
* Full Connection schema matching database structure
*/
export const connectionSchema = z.object({
id: z.number(),
user_id: z.string(),
connection_id: z.string(),
post_id: z.number().optional()
});
/**
* Connection creation input
*/
export const createConnectionSchema = z.object({
connection_id: z.string(),
post_id: z.number().optional()
});
// ============================================================================
// Common Query Schemas
// ============================================================================
@@ -259,26 +69,6 @@ export const idSchema = z.object({
id: z.number()
});
export const userIdSchema = z.object({
userId: z.string()
});
export const postIdSchema = z.object({
postId: z.number()
});
export const commentIdSchema = z.object({
commentId: z.number()
});
/**
* Pagination schema
*/
export const paginationSchema = z.object({
limit: z.number().min(1).max(100).default(10),
offset: z.number().min(0).default(0)
});
// ============================================================================
// Additional Database Router Schemas
// ============================================================================
@@ -343,10 +133,6 @@ export const getUserByIdSchema = z.object({
id: z.string()
});
export const getUserPublicDataSchema = z.object({
id: z.string()
});
export const updateUserImageSchema = z.object({
id: z.string(),
imageURL: z.string()
@@ -365,15 +151,6 @@ export const updateUserEmailSchema = z.object({
export type ReactionType = z.infer<typeof reactionTypeSchema>;
export type CreatePostInput = z.infer<typeof createPostSchema>;
export type UpdatePostInput = z.infer<typeof updatePostSchema>;
export type CreateCommentInput = z.infer<typeof createCommentSchema>;
export type UpdateCommentInput = z.infer<typeof updateCommentSchema>;
export type CreateCommentReactionInput = z.infer<
typeof createCommentReactionSchema
>;
export type CreatePostLikeInput = z.infer<typeof createPostLikeSchema>;
export type CreateTagInput = z.infer<typeof createTagSchema>;
export type CreateConnectionInput = z.infer<typeof createConnectionSchema>;
export type PaginationInput = z.infer<typeof paginationSchema>;
export type GetPostByIdInput = z.infer<typeof getPostByIdSchema>;
export type GetPostByTitleInput = z.infer<typeof getPostByTitleSchema>;
export type GetCommentsByPostIdInput = z.infer<

View File

@@ -65,11 +65,6 @@ export const loginUserSchema = z.object({
rememberMe: z.boolean().optional().default(false)
});
/**
* OAuth provider schema
*/
export const oauthProviderSchema = z.enum(["google", "github"]);
// ============================================================================
// Profile Management Schemas
// ============================================================================
@@ -168,20 +163,12 @@ export const deleteAccountSchema = z.object({
password: z.string().min(1, "Password is required to delete account")
});
/**
* Email verification schema
*/
export const verifyEmailSchema = z.object({
token: z.string().min(1)
});
// ============================================================================
// Type Exports
// ============================================================================
export type RegisterUserInput = z.infer<typeof registerUserSchema>;
export type LoginUserInput = z.infer<typeof loginUserSchema>;
export type OAuthProvider = z.infer<typeof oauthProviderSchema>;
export type UpdateEmailInput = z.infer<typeof updateEmailSchema>;
export type UpdateDisplayNameInput = z.infer<typeof updateDisplayNameSchema>;
export type UpdateProfileImageInput = z.infer<typeof updateProfileImageSchema>;
@@ -192,4 +179,3 @@ export type RequestPasswordResetInput = z.infer<
>;
export type ResetPasswordInput = z.infer<typeof resetPasswordSchema>;
export type DeleteAccountInput = z.infer<typeof deleteAccountSchema>;
export type VerifyEmailInput = z.infer<typeof verifyEmailSchema>;