API güvenliği sağlandı
This commit is contained in:
@@ -8,6 +8,7 @@ SUPABASE_URL=""
|
||||
SUPABASE_SERVICE_ROLE_KEY=""
|
||||
SUPABASE_USERS_TABLE="users"
|
||||
JWT_SECRET="change-me"
|
||||
# Allowed frontend origins (comma-separated, include scheme)
|
||||
CLIENT_ORIGIN="http://localhost:5173"
|
||||
|
||||
# GLM / Anthropic çeviri servisi
|
||||
|
||||
@@ -27,7 +27,32 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
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());
|
||||
const allowedOrigins = ORIGIN.split(',').map((origin) => origin.trim()).filter(Boolean);
|
||||
|
||||
const normalizeOrigin = (value = '') => {
|
||||
try {
|
||||
return new URL(value).origin;
|
||||
} catch (error) {
|
||||
return value.replace(/\/$/, '');
|
||||
}
|
||||
};
|
||||
|
||||
const trustedOrigins = allowedOrigins.map(normalizeOrigin).filter(Boolean);
|
||||
|
||||
const enforceClientOrigin = (req, res, next) => {
|
||||
if (!trustedOrigins.length) {
|
||||
return next();
|
||||
}
|
||||
const header = req.get('origin') || req.get('referer');
|
||||
if (!header) {
|
||||
return res.status(403).json({ message: 'Bu istemciye izin verilmiyor.' });
|
||||
}
|
||||
const requestOrigin = normalizeOrigin(header);
|
||||
if (!requestOrigin || !trustedOrigins.includes(requestOrigin)) {
|
||||
return res.status(403).json({ message: 'Bu istemciye izin verilmiyor.' });
|
||||
}
|
||||
return next();
|
||||
};
|
||||
|
||||
const USERS_TABLE = process.env.SUPABASE_USERS_TABLE || 'users';
|
||||
const JWT_SECRET = process.env.JWT_SECRET;
|
||||
@@ -85,6 +110,7 @@ const authMiddleware = (req, res, next) => {
|
||||
};
|
||||
|
||||
const authRouter = express.Router();
|
||||
authRouter.use(enforceClientOrigin);
|
||||
|
||||
authRouter.post('/register', async (req, res) => {
|
||||
const { name, email, password } = req.body || {};
|
||||
@@ -256,7 +282,7 @@ authRouter.post('/forgot-password', async (req, res) => {
|
||||
|
||||
app.use('/auth', authRouter);
|
||||
|
||||
app.post('/translate', async (req, res) => {
|
||||
app.post('/translate', authMiddleware, async (req, res) => {
|
||||
const { text } = req.body || {};
|
||||
if (!text || !text.trim()) {
|
||||
return res.status(400).json({ message: 'Çevrilecek metin bulunamadı.' });
|
||||
@@ -273,7 +299,7 @@ app.post('/translate', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/generate-epub', async (req, res) => {
|
||||
app.post('/generate-epub', authMiddleware, async (req, res) => {
|
||||
const { text, meta, cover } = req.body || {};
|
||||
if (!text || !text.trim()) {
|
||||
return res.status(400).json({ message: 'text is required' });
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { useAppStore } from '../store/useAppStore';
|
||||
|
||||
const base64ToBlob = (base64, mimeType) => {
|
||||
const binary = atob(base64);
|
||||
const len = binary.length;
|
||||
@@ -26,6 +28,14 @@ const blobToBase64 = (blob) =>
|
||||
|
||||
const API_BASE = import.meta.env.VITE_API_BASE_URL || 'http://localhost:4000';
|
||||
|
||||
const getAuthToken = () => {
|
||||
const { authToken } = useAppStore.getState();
|
||||
if (!authToken) {
|
||||
throw new Error('EPUB oluşturmak için giriş yapmalısın.');
|
||||
}
|
||||
return authToken;
|
||||
};
|
||||
|
||||
const slugify = (value = '') =>
|
||||
value
|
||||
.toLowerCase()
|
||||
@@ -63,9 +73,13 @@ export const createEpubFromOcr = async (text, coverImage, meta = {}) => {
|
||||
filename: resolvedFilename,
|
||||
};
|
||||
|
||||
const token = getAuthToken();
|
||||
const response = await fetch(`${API_BASE}/generate-epub`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
text,
|
||||
meta: {
|
||||
|
||||
@@ -1,13 +1,27 @@
|
||||
import { useAppStore } from '../store/useAppStore';
|
||||
|
||||
const API_BASE = import.meta.env.VITE_API_BASE_URL || 'http://localhost:4000';
|
||||
|
||||
const getAuthToken = () => {
|
||||
const { authToken } = useAppStore.getState();
|
||||
if (!authToken) {
|
||||
throw new Error('Çeviri için giriş yapmalısın.');
|
||||
}
|
||||
return authToken;
|
||||
};
|
||||
|
||||
export const translateChunkToTurkish = async (text) => {
|
||||
if (!text?.trim()) {
|
||||
throw new Error('Çevrilecek metin bulunamadı.');
|
||||
}
|
||||
|
||||
const token = getAuthToken();
|
||||
const response = await fetch(`${API_BASE}/translate`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({ text }),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user