Supabase login/register entegrasyonu

This commit is contained in:
2025-11-12 22:23:47 +03:00
parent 7d2d9a54e9
commit 73ad410535
10 changed files with 502 additions and 49 deletions

View File

@@ -1,16 +1,37 @@
import 'dotenv/config';
import express from 'express';
import cors from 'cors';
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import { tmpdir } from 'os';
import { join } from 'path';
import { promises as fs } from 'fs';
import { v4 as uuidV4 } from 'uuid';
import Epub from 'epub-gen';
const requiredEnv = ['SUPABASE_URL', 'SUPABASE_SERVICE_ROLE_KEY', 'JWT_SECRET'];
requiredEnv.forEach((key) => {
if (!process.env[key]) {
console.error(`Missing required environment variable: ${key}`);
process.exit(1);
}
});
const app = express();
const PORT = process.env.PORT || 4000;
const ORIGIN = process.env.CLIENT_ORIGIN || 'http://localhost:5173';
const allowedOrigins = ORIGIN.split(',').map((origin) => origin.trim());
app.use(cors({ origin: ORIGIN, credentials: true }));
const USERS_TABLE = process.env.SUPABASE_USERS_TABLE || 'users';
const JWT_SECRET = process.env.JWT_SECRET;
import { supabase } from './src/services/supabaseClient.js';
app.use(
cors({
origin: allowedOrigins,
credentials: true,
}),
);
app.use(express.json({ limit: '10mb' }));
const sanitizeHtml = (text = '') =>
@@ -22,6 +43,145 @@ const sanitizeHtml = (text = '') =>
.replace(/'/g, ''')
.replace(/\n/g, '<br/>');
const createToken = (user) =>
jwt.sign(
{
sub: user.id,
email: user.email,
username: user.username,
},
JWT_SECRET,
{ expiresIn: '7d' },
);
const mapUserRecord = (record) => ({
id: record.id,
email: record.email,
name: record.name,
username: record.username,
});
const authMiddleware = (req, res, next) => {
const header = req.headers.authorization || '';
const token = header.startsWith('Bearer ') ? header.slice(7) : null;
if (!token) {
return res.status(401).json({ message: 'Yetkisiz erişim' });
}
try {
const payload = jwt.verify(token, JWT_SECRET);
req.user = payload;
return next();
} catch (error) {
return res.status(401).json({ message: 'Oturum süresi doldu, lütfen tekrar giriş yap.' });
}
};
const authRouter = express.Router();
authRouter.post('/register', async (req, res) => {
const { name, email, password } = req.body || {};
if (!name || !email || !password) {
return res.status(400).json({ message: 'Ad, email ve şifre gereklidir.' });
}
const normalizedEmail = email.trim().toLowerCase();
const username = normalizedEmail.split('@')[0];
const { data: existingUser, error: existingError } = await supabase
.from(USERS_TABLE)
.select('id')
.eq('email', normalizedEmail)
.maybeSingle();
if (existingError && existingError.code !== 'PGRST116') {
return res.status(500).json({ message: 'Kullanıcı kontrolü başarısız.' });
}
if (existingUser) {
return res.status(409).json({ message: 'Bu email adresi ile zaten bir hesap mevcut.' });
}
const passwordHash = await bcrypt.hash(password, 10);
const { data: createdUser, error: insertError } = await supabase
.from(USERS_TABLE)
.insert({
name: name.trim(),
email: normalizedEmail,
username,
password_hash: passwordHash,
})
.select('id,email,name,username')
.single();
if (insertError || !createdUser) {
return res.status(500).json({ message: 'Kullanıcı oluşturulamadı.' });
}
const token = createToken(createdUser);
return res.status(201).json({ token, user: mapUserRecord(createdUser) });
});
authRouter.post('/login', async (req, res) => {
const { email, password } = req.body || {};
if (!email || !password) {
return res.status(400).json({ message: 'Email ve şifre gereklidir.' });
}
const normalizedEmail = email.trim().toLowerCase();
const { data: userRecord, error: fetchError } = await supabase
.from(USERS_TABLE)
.select('id,email,name,username,password_hash')
.eq('email', normalizedEmail)
.maybeSingle();
if (fetchError && fetchError.code !== 'PGRST116') {
return res.status(500).json({ message: 'Giriş işlemi başarısız.' });
}
if (!userRecord) {
return res.status(401).json({ message: 'Email veya şifre hatalı.' });
}
const validPassword = await bcrypt.compare(password, userRecord.password_hash);
if (!validPassword) {
return res.status(401).json({ message: 'Email veya şifre hatalı.' });
}
const token = createToken(userRecord);
return res.json({ token, user: mapUserRecord(userRecord) });
});
authRouter.get('/me', authMiddleware, async (req, res) => {
const { data: userRecord, error } = await supabase
.from(USERS_TABLE)
.select('id,email,name,username')
.eq('id', req.user.sub)
.single();
if (error || !userRecord) {
return res.status(404).json({ message: 'Kullanıcı bulunamadı.' });
}
return res.json({ user: mapUserRecord(userRecord) });
});
authRouter.post('/logout', (_req, res) => {
return res.json({ message: ıkış yapıldı.' });
});
authRouter.post('/forgot-password', async (req, res) => {
const { email } = req.body || {};
if (!email) {
return res.status(400).json({ message: 'Email gereklidir.' });
}
// Supabase Auth kullanılmadığı için burada sadece bilgilendirici bir cevap döndürüyoruz.
return res.json({
message:
'Şifre sıfırlama talebin alındı. Bu demo ortamında e-posta gönderimi aktif değildir.',
});
});
app.use('/auth', authRouter);
app.post('/generate-epub', async (req, res) => {
const { text, meta, cover } = req.body || {};
if (!text || !text.trim()) {