diff --git a/packages/super-sync-server/package.json b/packages/super-sync-server/package.json index 50d951cca..c44e06284 100644 --- a/packages/super-sync-server/package.json +++ b/packages/super-sync-server/package.json @@ -6,7 +6,8 @@ "scripts": { "build": "tsc", "start": "node dist/index.js", - "dev": "ts-node src/index.ts" + "dev": "ts-node src/index.ts", + "delete-user": "ts-node scripts/delete-user.ts" }, "dependencies": { "@fastify/cors": "^11.1.0", diff --git a/packages/super-sync-server/scripts/delete-user.ts b/packages/super-sync-server/scripts/delete-user.ts new file mode 100644 index 000000000..f5051a50d --- /dev/null +++ b/packages/super-sync-server/scripts/delete-user.ts @@ -0,0 +1,50 @@ +import Database from 'better-sqlite3'; +import * as path from 'path'; +import * as fs from 'fs'; +import { loadConfigFromEnv } from '../src/config'; + +const deleteUser = (email: string) => { + try { + // Load config to get data directory + const config = loadConfigFromEnv(); + const dbPath = path.join(config.dataDir, 'database.sqlite'); + + if (!fs.existsSync(dbPath)) { + console.error(`Database not found at ${dbPath}`); + process.exit(1); + } + + const db = new Database(dbPath); + + // Check if user exists first + const user = db.prepare('SELECT * FROM users WHERE email = ?').get(email); + + if (!user) { + console.log(`User with email "${email}" not found.`); + return; + } + + // Delete user + const info = db.prepare('DELETE FROM users WHERE email = ?').run(email); + + if (info.changes > 0) { + console.log(`Successfully deleted user: ${email}`); + } else { + console.log(`Failed to delete user: ${email}`); + } + } catch (error) { + console.error('Error deleting user:', error); + process.exit(1); + } +}; + +// Get email from command line arguments +const email = process.argv[2]; + +if (!email) { + console.error('Please provide an email address.'); + console.error('Usage: npm run delete-user -- '); + process.exit(1); +} + +deleteUser(email); diff --git a/packages/super-sync-server/src/config.ts b/packages/super-sync-server/src/config.ts index 46b782131..66bc75ad8 100644 --- a/packages/super-sync-server/src/config.ts +++ b/packages/super-sync-server/src/config.ts @@ -77,10 +77,14 @@ export const loadConfigFromEnv = ( // SMTP Configuration if (process.env.SMTP_HOST) { + const port = parseInt(process.env.SMTP_PORT || '587', 10); config.smtp = { host: process.env.SMTP_HOST, - port: parseInt(process.env.SMTP_PORT || '587', 10), - secure: process.env.SMTP_SECURE === 'true', + port, + secure: + process.env.SMTP_SECURE !== undefined + ? process.env.SMTP_SECURE === 'true' + : port === 465, user: process.env.SMTP_USER, pass: process.env.SMTP_PASS, from: process.env.SMTP_FROM || '"SuperSync" ', diff --git a/packages/super-sync-server/src/pages.ts b/packages/super-sync-server/src/pages.ts new file mode 100644 index 000000000..edf47bad2 --- /dev/null +++ b/packages/super-sync-server/src/pages.ts @@ -0,0 +1,47 @@ +import { FastifyInstance } from 'fastify'; +import { verifyEmail } from './auth'; +import { Logger } from './logger'; + +// Error response helper +const errorMessage = (err: unknown): string => + err instanceof Error ? err.message : 'Unknown error'; + +interface VerifyEmailQuery { + token?: string; +} + +export async function pageRoutes(fastify: FastifyInstance) { + fastify.get<{ Querystring: VerifyEmailQuery }>('/verify-email', async (req, reply) => { + try { + const { token } = req.query; + if (!token) { + return reply.status(400).send('Token is required'); + } + + verifyEmail(token); + return reply.type('text/html').send(` + + + Email Verified + + + +
+

Email Verified!

+

Your account has been successfully verified.

+ Return to Login +
+ + + `); + } catch (err) { + Logger.error(`Verification error: ${errorMessage(err)}`); + return reply.status(400).send(`Verification failed: ${errorMessage(err)}`); + } + }); +} diff --git a/packages/super-sync-server/src/server.ts b/packages/super-sync-server/src/server.ts index 9d5ba6d9c..0fc9fb715 100644 --- a/packages/super-sync-server/src/server.ts +++ b/packages/super-sync-server/src/server.ts @@ -8,6 +8,7 @@ import { loadConfigFromEnv, ServerConfig } from './config'; import { Logger } from './logger'; import { initDb } from './db'; import { apiRoutes } from './api'; +import { pageRoutes } from './pages'; import { verifyToken } from './auth'; export { ServerConfig, loadConfigFromEnv }; @@ -162,6 +163,9 @@ export const createServer = ( // API Routes await fastifyServer.register(apiRoutes, { prefix: '/api' }); + // Page Routes + await fastifyServer.register(pageRoutes, { prefix: '/' }); + // WebDAV Handler (Catch-all via hook) // We use a hook because Fastify's router validates HTTP methods and might not support all WebDAV methods fastifyServer.addHook('onRequest', (req, reply, done) => { @@ -170,10 +174,13 @@ export const createServer = ( return; } - // Allow static files to be handled by Fastify + // Allow static files and verify-email route to be handled by Fastify const staticFiles = ['/', '/index.html', '/style.css', '/app.js', '/favicon.ico']; const urlPath = req.url.split('?')[0]; - if (req.method === 'GET' && staticFiles.includes(urlPath)) { + if ( + (req.method === 'GET' && staticFiles.includes(urlPath)) || + urlPath === '/verify-email' + ) { done(); return; }