mirror of
https://github.com/zen-browser/rices.git
synced 2025-07-07 17:05:40 +02:00
feat: enhance slug generation and validation
- Add robust error handling for `generateSlug`, throwing `BadRequestException` on invalid input. - Improve error messages to provide clearer feedback in case of failures.
This commit is contained in:
parent
6609122f8a
commit
2b8909c17f
5 changed files with 81 additions and 12 deletions
|
@ -44,6 +44,7 @@
|
||||||
"@supabase/supabase-js": "^2.47.10",
|
"@supabase/supabase-js": "^2.47.10",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
"@types/jest": "^29.5.14",
|
"@types/jest": "^29.5.14",
|
||||||
|
"@types/leo-profanity": "^1.5.4",
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "^20.3.1",
|
||||||
"@types/supertest": "^6.0.2",
|
"@types/supertest": "^6.0.2",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
|
@ -53,6 +54,7 @@
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-plugin-prettier": "^5.0.0",
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
|
"leo-profanity": "^1.7.0",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.0",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"supertest": "^7.0.0",
|
"supertest": "^7.0.0",
|
||||||
|
|
|
@ -35,6 +35,13 @@ export class RicesService {
|
||||||
throw new BadRequestException('The request body must be a string.');
|
throw new BadRequestException('The request body must be a string.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.validateJsonStructure(content);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
} catch (e) {
|
||||||
|
throw new BadRequestException('Invalid json request');
|
||||||
|
}
|
||||||
|
|
||||||
// Validate lengths
|
// Validate lengths
|
||||||
if (name.length > 75) {
|
if (name.length > 75) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
|
@ -49,13 +56,11 @@ export class RicesService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse version and OS from User-Agent
|
// Parse version and OS from User-Agent
|
||||||
const userAgentRegex = /ZenBrowser\/(\d+\.\d+\.\d+) \((.+)\)/;
|
const userAgentRegex = /ZenBrowser\/(\d+\.\d+\.\d.\d+) \((.+)\)/;
|
||||||
const match = userAgent.match(userAgentRegex);
|
const match = userAgent.match(userAgentRegex);
|
||||||
|
|
||||||
if (!match) {
|
if (!match) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException('Invalid request');
|
||||||
'Invalid User-Agent format. Expected format: ZenBrowser/<version> (<OS>).',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const [, version, os] = match;
|
const [, version, os] = match;
|
||||||
|
@ -80,9 +85,16 @@ export class RicesService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const slug = `${generateSlug(name)}-${uuidv4()}`;
|
let slug: string;
|
||||||
const token = uuidv4();
|
try {
|
||||||
|
slug = `${generateSlug(name)}-${uuidv4()}`;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
} catch (e) {
|
||||||
|
// If generateSlug throws an error, rethrow as a BadRequestException
|
||||||
|
throw new BadRequestException(`Invalid name provided`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = uuidv4();
|
||||||
const encodedContent = Buffer.from(content).toString('base64');
|
const encodedContent = Buffer.from(content).toString('base64');
|
||||||
|
|
||||||
const metadata = {
|
const metadata = {
|
||||||
|
@ -154,13 +166,18 @@ export class RicesService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse version and OS from User-Agent
|
// Parse version and OS from User-Agent
|
||||||
const userAgentRegex = /ZenBrowser\/(\d+\.\d+\.\d+) \((.+)\)/;
|
const userAgentRegex = /ZenBrowser\/(\d+\.\d+\.\d.\d+) \((.+)\)/;
|
||||||
const match = userAgent.match(userAgentRegex);
|
const match = userAgent.match(userAgentRegex);
|
||||||
|
|
||||||
if (!match) {
|
if (!match) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException('Invalid request');
|
||||||
'Invalid User-Agent format. Expected format: ZenBrowser/<version> (<OS>).',
|
}
|
||||||
);
|
|
||||||
|
try {
|
||||||
|
this.validateJsonStructure(content);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
} catch (e) {
|
||||||
|
throw new BadRequestException('Invalid json request');
|
||||||
}
|
}
|
||||||
|
|
||||||
const [, version, os] = match;
|
const [, version, os] = match;
|
||||||
|
@ -280,4 +297,39 @@ export class RicesService {
|
||||||
throw new Error('Failed to remove rice by moderation');
|
throw new Error('Failed to remove rice by moderation');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateJsonStructure(jsonString: string): boolean {
|
||||||
|
const requiredKeys: string[] = [
|
||||||
|
'userChrome',
|
||||||
|
'userContent',
|
||||||
|
'enabledMods',
|
||||||
|
'preferences',
|
||||||
|
'workspaceThemes',
|
||||||
|
];
|
||||||
|
|
||||||
|
let json: Record<string, unknown>;
|
||||||
|
|
||||||
|
// Validate JSON string
|
||||||
|
try {
|
||||||
|
json = JSON.parse(jsonString);
|
||||||
|
} catch {
|
||||||
|
throw new BadRequestException('Invalid JSON string.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the parsed JSON is an object
|
||||||
|
if (typeof json !== 'object' || json === null) {
|
||||||
|
throw new BadRequestException('The parsed JSON is not a valid object.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for missing keys
|
||||||
|
const missingKeys = requiredKeys.filter((key) => !(key in json));
|
||||||
|
|
||||||
|
if (missingKeys.length > 0) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
`The JSON is missing the following required keys: ${missingKeys.join(', ')}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import * as leoProfanity from 'leo-profanity';
|
||||||
|
|
||||||
export function generateSlug(name: string): string {
|
export function generateSlug(name: string): string {
|
||||||
// Ensure the input is a string and trim whitespace
|
// Ensure the input is a string and trim whitespace
|
||||||
if (typeof name !== 'string') {
|
if (typeof name !== 'string') {
|
||||||
|
@ -5,6 +7,9 @@ export function generateSlug(name: string): string {
|
||||||
}
|
}
|
||||||
const sanitizedInput = name.trim();
|
const sanitizedInput = name.trim();
|
||||||
|
|
||||||
|
// Configure the profanity filter
|
||||||
|
leoProfanity.loadDictionary('en'); // Ensure the dictionary is loaded
|
||||||
|
|
||||||
// Replace accented characters with their unaccented counterparts
|
// Replace accented characters with their unaccented counterparts
|
||||||
const normalized = sanitizedInput
|
const normalized = sanitizedInput
|
||||||
.normalize('NFD')
|
.normalize('NFD')
|
||||||
|
@ -21,5 +26,15 @@ export function generateSlug(name: string): string {
|
||||||
throw new Error('Generated slug is empty');
|
throw new Error('Generated slug is empty');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Split the slug into individual words
|
||||||
|
const words = slug.split('-');
|
||||||
|
|
||||||
|
// Check each word for inappropriate content
|
||||||
|
words.forEach((word) => {
|
||||||
|
if (leoProfanity.check(word)) {
|
||||||
|
throw new Error(`The word "${word}" is inappropriate.`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return slug;
|
return slug;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ POST {{baseUrl}}/rices
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
X-Zen-Rice-Name: cool-zenrice-aurora2
|
X-Zen-Rice-Name: cool-zenrice-aurora2
|
||||||
X-Zen-Rice-Author: jhon@doe.com
|
X-Zen-Rice-Author: jhon@doe.com
|
||||||
User-Agent: ZenBrowser/1.0.0 (EndeavourOS x86_64)
|
User-Agent: ZenBrowser/1.0.0.0 (EndeavourOS x86_64)
|
||||||
|
|
||||||
{
|
{
|
||||||
"userChrome": "",
|
"userChrome": "",
|
||||||
|
|
|
@ -7,7 +7,7 @@ Content-Type: application/json
|
||||||
x-zen-rices-token: {{previous_token}}
|
x-zen-rices-token: {{previous_token}}
|
||||||
X-Zen-Rice-Name: cool-zenrice-aurora2
|
X-Zen-Rice-Name: cool-zenrice-aurora2
|
||||||
X-Zen-Rice-Author: jhon@doe.com
|
X-Zen-Rice-Author: jhon@doe.com
|
||||||
User-Agent: ZenBrowser/1.0.0 (EndeavourOS x86_64)
|
User-Agent: ZenBrowser/1.0.0.0 (EndeavourOS x86_64)
|
||||||
|
|
||||||
{
|
{
|
||||||
"userChrome": "",
|
"userChrome": "",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue