first commit
This commit is contained in:
193
backend/src/routes/contacts.ts
Normal file
193
backend/src/routes/contacts.ts
Normal file
@@ -0,0 +1,193 @@
|
||||
import { Hono } from 'hono';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
import { z } from 'zod';
|
||||
import { db, contacts, emailSubscribers } from '../db/index.js';
|
||||
import { eq, desc } from 'drizzle-orm';
|
||||
import { requireAuth } from '../lib/auth.js';
|
||||
import { generateId, getNow } from '../lib/utils.js';
|
||||
|
||||
const contactsRouter = new Hono();
|
||||
|
||||
const createContactSchema = z.object({
|
||||
name: z.string().min(2),
|
||||
email: z.string().email(),
|
||||
message: z.string().min(10),
|
||||
});
|
||||
|
||||
const subscribeSchema = z.object({
|
||||
email: z.string().email(),
|
||||
name: z.string().optional(),
|
||||
});
|
||||
|
||||
const updateContactSchema = z.object({
|
||||
status: z.enum(['new', 'read', 'replied']),
|
||||
});
|
||||
|
||||
// Submit contact form (public)
|
||||
contactsRouter.post('/', zValidator('json', createContactSchema), async (c) => {
|
||||
const data = c.req.valid('json');
|
||||
const now = getNow();
|
||||
const id = generateId();
|
||||
|
||||
const newContact = {
|
||||
id,
|
||||
name: data.name,
|
||||
email: data.email,
|
||||
message: data.message,
|
||||
status: 'new' as const,
|
||||
createdAt: now,
|
||||
};
|
||||
|
||||
await (db as any).insert(contacts).values(newContact);
|
||||
|
||||
return c.json({ message: 'Message sent successfully' }, 201);
|
||||
});
|
||||
|
||||
// Subscribe to newsletter (public)
|
||||
contactsRouter.post('/subscribe', zValidator('json', subscribeSchema), async (c) => {
|
||||
const data = c.req.valid('json');
|
||||
|
||||
// Check if already subscribed
|
||||
const existing = await (db as any)
|
||||
.select()
|
||||
.from(emailSubscribers)
|
||||
.where(eq((emailSubscribers as any).email, data.email))
|
||||
.get();
|
||||
|
||||
if (existing) {
|
||||
if (existing.status === 'unsubscribed') {
|
||||
// Resubscribe
|
||||
await (db as any)
|
||||
.update(emailSubscribers)
|
||||
.set({ status: 'active' })
|
||||
.where(eq((emailSubscribers as any).id, existing.id));
|
||||
|
||||
return c.json({ message: 'Successfully resubscribed' });
|
||||
}
|
||||
return c.json({ message: 'Already subscribed' });
|
||||
}
|
||||
|
||||
const now = getNow();
|
||||
const id = generateId();
|
||||
|
||||
const newSubscriber = {
|
||||
id,
|
||||
email: data.email,
|
||||
name: data.name || null,
|
||||
status: 'active' as const,
|
||||
createdAt: now,
|
||||
};
|
||||
|
||||
await (db as any).insert(emailSubscribers).values(newSubscriber);
|
||||
|
||||
return c.json({ message: 'Successfully subscribed' }, 201);
|
||||
});
|
||||
|
||||
// Unsubscribe from newsletter (public)
|
||||
contactsRouter.post('/unsubscribe', zValidator('json', z.object({ email: z.string().email() })), async (c) => {
|
||||
const { email } = c.req.valid('json');
|
||||
|
||||
const existing = await (db as any)
|
||||
.select()
|
||||
.from(emailSubscribers)
|
||||
.where(eq((emailSubscribers as any).email, email))
|
||||
.get();
|
||||
|
||||
if (!existing) {
|
||||
return c.json({ error: 'Email not found' }, 404);
|
||||
}
|
||||
|
||||
await (db as any)
|
||||
.update(emailSubscribers)
|
||||
.set({ status: 'unsubscribed' })
|
||||
.where(eq((emailSubscribers as any).id, existing.id));
|
||||
|
||||
return c.json({ message: 'Successfully unsubscribed' });
|
||||
});
|
||||
|
||||
// Get all contacts (admin)
|
||||
contactsRouter.get('/', requireAuth(['admin', 'organizer']), async (c) => {
|
||||
const status = c.req.query('status');
|
||||
|
||||
let query = (db as any).select().from(contacts);
|
||||
|
||||
if (status) {
|
||||
query = query.where(eq((contacts as any).status, status));
|
||||
}
|
||||
|
||||
const result = await query.orderBy(desc((contacts as any).createdAt)).all();
|
||||
|
||||
return c.json({ contacts: result });
|
||||
});
|
||||
|
||||
// Get single contact (admin)
|
||||
contactsRouter.get('/:id', requireAuth(['admin', 'organizer']), async (c) => {
|
||||
const id = c.req.param('id');
|
||||
|
||||
const contact = await (db as any)
|
||||
.select()
|
||||
.from(contacts)
|
||||
.where(eq((contacts as any).id, id))
|
||||
.get();
|
||||
|
||||
if (!contact) {
|
||||
return c.json({ error: 'Contact not found' }, 404);
|
||||
}
|
||||
|
||||
return c.json({ contact });
|
||||
});
|
||||
|
||||
// Update contact status (admin)
|
||||
contactsRouter.put('/:id', requireAuth(['admin', 'organizer']), zValidator('json', updateContactSchema), async (c) => {
|
||||
const id = c.req.param('id');
|
||||
const data = c.req.valid('json');
|
||||
|
||||
const existing = await (db as any)
|
||||
.select()
|
||||
.from(contacts)
|
||||
.where(eq((contacts as any).id, id))
|
||||
.get();
|
||||
|
||||
if (!existing) {
|
||||
return c.json({ error: 'Contact not found' }, 404);
|
||||
}
|
||||
|
||||
await (db as any)
|
||||
.update(contacts)
|
||||
.set({ status: data.status })
|
||||
.where(eq((contacts as any).id, id));
|
||||
|
||||
const updated = await (db as any)
|
||||
.select()
|
||||
.from(contacts)
|
||||
.where(eq((contacts as any).id, id))
|
||||
.get();
|
||||
|
||||
return c.json({ contact: updated });
|
||||
});
|
||||
|
||||
// Delete contact (admin)
|
||||
contactsRouter.delete('/:id', requireAuth(['admin']), async (c) => {
|
||||
const id = c.req.param('id');
|
||||
|
||||
await (db as any).delete(contacts).where(eq((contacts as any).id, id));
|
||||
|
||||
return c.json({ message: 'Contact deleted successfully' });
|
||||
});
|
||||
|
||||
// Get all subscribers (admin)
|
||||
contactsRouter.get('/subscribers/list', requireAuth(['admin', 'marketing']), async (c) => {
|
||||
const status = c.req.query('status');
|
||||
|
||||
let query = (db as any).select().from(emailSubscribers);
|
||||
|
||||
if (status) {
|
||||
query = query.where(eq((emailSubscribers as any).status, status));
|
||||
}
|
||||
|
||||
const result = await query.orderBy(desc((emailSubscribers as any).createdAt)).all();
|
||||
|
||||
return c.json({ subscribers: result });
|
||||
});
|
||||
|
||||
export default contactsRouter;
|
||||
Reference in New Issue
Block a user