Skip to content

Commit 2af3c09

Browse files
committed
new changes
1 parent 5aa6af4 commit 2af3c09

File tree

5 files changed

+85
-30
lines changed

5 files changed

+85
-30
lines changed

app/public/static/DeleteUrl.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
1-
export async function deleteUrl(hash) {
1+
export async function deleteUrl(hash, authCode) {
22
try {
3-
await fetch(`/${hash}`, {
4-
method: 'DELETE'
3+
const response = await fetch(`/${hash}/${authCode}`, {
4+
method: 'DELETE',
5+
headers: {
6+
'Content-Type': 'application/json'
7+
}
58
});
9+
10+
const result = await response.json();
11+
12+
if (!response.ok) {
13+
throw result.errors;
14+
}
15+
16+
return result;
617
} catch (err) {
7-
console.error('Error eliminando URL:', err.message);
18+
throw err;
819
}
920
}
21+

app/public/static/FetcherUrl.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,30 @@ export async function fetchAllUrls() {
5050
deleteBtn.className = 'delete-btn';
5151
deleteBtn.textContent = 'Eliminar';
5252
deleteBtn.onclick = async () => {
53-
await deleteUrl(item.hash);
54-
fetchAllUrls();
53+
const authCode = document.getElementById('authCode').value;
54+
55+
// Limpia errores anteriores
56+
document.querySelectorAll('.error-message').forEach(e => e.remove());
57+
document.querySelectorAll('.form-group').forEach(e => e.classList.remove('error'));
58+
59+
try {
60+
await deleteUrl(item.hash, authCode);
61+
fetchAllUrls();
62+
} catch (errors) {
63+
errors.forEach(error => {
64+
const el = document.getElementById(error.field);
65+
if (el) {
66+
const div = document.createElement('div');
67+
div.className = 'error-message';
68+
div.textContent = error.message;
69+
el.parentNode.appendChild(div);
70+
el.parentNode.classList.add('error');
71+
}
72+
});
73+
}
5574
};
5675

76+
5777
// Insertamos el span y el botón dentro del div
5878
contentWrapper.appendChild(span);
5979
contentWrapper.appendChild(deleteBtn);

app/routes/routes.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ router.get('/', getDashboard);
1414

1515
router.post('/api/shorten', async (req: Request, res: Response): Promise<void> => {
1616
try {
17-
const { originalUrl, authCode } = req.body;
18-
const result = await dashboardService.createShortUrl(originalUrl, authCode);
17+
const { originalUrl, authCode, customHash } = req.body;
18+
const result = await dashboardService.createShortUrl(originalUrl, authCode, customHash);
1919

2020
res.status(200).json(result);
2121
} catch (error) {
@@ -54,15 +54,23 @@ router.get('/:hash', async (req: Request, res: Response) => {
5454
}
5555
});
5656

57-
router.delete('/:hash', async (req: Request, res: Response) => {
58-
const { hash } = req.params;
57+
router.delete('/:hash/:authCode', async (req: Request, res: Response): Promise<void> => {
58+
const { hash, authCode } = req.params;
59+
60+
if (!authCode) {
61+
res.status(400).json({ errors: [{ field: 'authCode', message: 'AuthCode is required' }] });
62+
return;
63+
}
64+
5965
try {
60-
await dashboardService.deleteUrlByHash(hash);
61-
res.status(204).send(); // No content
66+
await dashboardService.deleteUrlByHash(hash, authCode);
67+
res.status(200).json({ message: 'URL eliminada correctamente' });
6268
} catch (error) {
63-
res.status(500).json({ error: 'Error al eliminar la URL' });
69+
res.status(500).json({ errors: [{ field: 'general', message: 'Server Error Internal' }] });
6470
}
6571
});
6672

6773

74+
75+
6876
export default router;

app/service/dashboardService.ts

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { ILink, errorResponse } from '../types/TypeLink.ts';
44

55
const HASH_LENGTH = 8;
66

7-
export async function createShortUrl(originalUrl: string, authCode?: string): Promise<ILink> {
7+
export async function createShortUrl(originalUrl: string, authCode?: string, customHash?: string): Promise<ILink> {
88
const errors: errorResponse = [];
99

1010
// Validación de URL
@@ -28,7 +28,8 @@ export async function createShortUrl(originalUrl: string, authCode?: string): Pr
2828
throw errors;
2929
}
3030

31-
const hash = nanoid(HASH_LENGTH);
31+
// Usa el customHash si está, sino genera uno aleatorio
32+
const hash = customHash || nanoid(HASH_LENGTH);
3233
const createdAt = new Date();
3334

3435
await sql.query(`
@@ -44,12 +45,11 @@ export async function createShortUrl(originalUrl: string, authCode?: string): Pr
4445
createdAt
4546
]);
4647

47-
48-
4948
return { originalUrl, hash, createdAt };
5049
}
5150

5251

52+
5353
export async function getAllUrls(): Promise<ILink[]> {
5454
try {
5555

@@ -62,7 +62,7 @@ export async function getAllUrls(): Promise<ILink[]> {
6262
created_at as "createdAt"
6363
FROM links
6464
`);
65-
65+
6666

6767
// Si el resultado tiene un valor, devuelve solo la URL como string
6868
return result.rows || [];
@@ -99,28 +99,42 @@ export async function getUrlByHash(hash: string): Promise<string | null> {
9999
FROM links
100100
WHERE hash = $1
101101
`, [hash]);
102-
102+
103103
// Si el resultado tiene un valor, devuelve solo la URL como string
104104
return result.rows[0]?.originalUrl || null;
105105
} catch (error) {
106-
console.error('Error obteniendo URL por hash:', error);
107106
return null;
108107
}
109108
}
110109

111110
// Delete URL by hash
112-
export async function deleteUrlByHash(hash: string): Promise<void> {
113-
try {
114-
await sql.query(`
115-
DELETE FROM links
116-
WHERE hash = $1
117-
`, [hash]);
118-
} catch (error) {
119-
console.error('Error eliminando URL por hash:', error);
111+
export async function deleteUrlByHash(hash: string, authCode: string): Promise<void> {
112+
const errors: errorResponse = [];
113+
114+
// Validación manual de authCode
115+
const { rows } = await sql.query(`
116+
SELECT 1
117+
FROM auth
118+
WHERE code = $1
119+
`, [authCode]);
120+
121+
if (rows.length === 0) {
122+
errors.push({ field: 'authCode', message: 'Invalid AuthCode' });
123+
}
124+
125+
if (errors.length > 0) {
126+
throw errors;
120127
}
128+
129+
// Eliminación de la URL
130+
await sql.query(`
131+
DELETE FROM links
132+
WHERE hash = $1
133+
`, [hash]);
121134
}
122135

123136

137+
124138
export const dashboardService = {
125139
createShortUrl,
126140
getAllUrls,

app/views/dashboard.ejs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,16 @@
3737
<div class="shortener-container">
3838
<h1 class="shortener-title">ACORTADOR DE URL</h1>
3939
<form id="urlShortenerForm">
40+
<!-- General Message Errors -->
41+
<div id="generalError" class="error-message"></div>
4042
<div class="form-group">
4143
<label for="originalUrl">URL ORIGINAL</label>
4244
<input type="text" id="originalUrl" name="originalUrl" placeholder="https://ejemplo.com/larga/url" />
4345
</div>
4446

4547
<div class="form-group">
4648
<label for="customHash">HASH PERSONALIZADO</label>
47-
<input type="text" id="customHash" name="customHash" placeholder="mi-enlace" pattern="[a-zA-Z0-9-]+"
48-
title="Solo letras, números y guiones" />
49+
<input type="text" id="customHash" name="customHash" placeholder="mi-enlace" />
4950
</div>
5051

5152
<div class="form-group">

0 commit comments

Comments
 (0)