Profile avatar resize crop eklendi. Hatalar fixlendi.

This commit is contained in:
2025-12-04 21:29:20 +03:00
parent 3d67900557
commit bbc245ced1
9 changed files with 880 additions and 11 deletions

View File

@@ -90,6 +90,7 @@ const FFPROBE_MAX_BUFFER =
Number(process.env.FFPROBE_MAX_BUFFER) > 0
? Number(process.env.FFPROBE_MAX_BUFFER)
: 10 * 1024 * 1024;
const AVATAR_PATH = path.join(__dirname, "..", "client", "src", "assets", "avatar.png");
app.use(cors());
app.use(express.json());
@@ -180,6 +181,19 @@ const { router: authRouter, requireAuth, requireRole, issueMediaToken, verifyTok
app.use(authRouter);
const avatarUpload = multer({
storage: multer.memoryStorage(),
limits: { fileSize: 3 * 1024 * 1024 },
fileFilter: (req, file, cb) => {
const type = (file.mimetype || "").toLowerCase();
const allowed = ["image/png", "image/jpeg", "image/jpg"];
if (!allowed.includes(type)) {
return cb(new Error("INVALID_FILE_TYPE"));
}
cb(null, true);
}
});
buildHealthReport({
ffmpegPath: "ffmpeg",
ffprobePath: FFPROBE_PATH,
@@ -201,6 +215,73 @@ buildHealthReport({
app.get("/api/health", requireAuth, healthRouter(() => healthSnapshot));
// --- Profil bilgisi ---
app.get("/api/profile", requireAuth, (req, res) => {
const username = req.user?.sub || req.user?.username || "user";
const role = req.user?.role || "user";
const avatarExists = fs.existsSync(AVATAR_PATH);
res.json({
username,
role,
avatarExists,
avatarUrl: avatarExists ? "/api/profile/avatar" : null
});
});
app.get("/api/profile/avatar", requireAuth, (req, res) => {
if (!fs.existsSync(AVATAR_PATH)) {
return res.status(404).json({ error: "Avatar bulunamadı" });
}
res.setHeader("Content-Type", "image/png");
res.setHeader("Cache-Control", "no-store");
fs.createReadStream(AVATAR_PATH).pipe(res);
});
app.post(
"/api/profile/avatar",
requireAuth,
(req, res, next) => {
avatarUpload.single("avatar")(req, res, (err) => {
if (err) {
const isSize = err.code === "LIMIT_FILE_SIZE";
const message = isSize
? "Dosya boyutu 3MB'ı aşmamalı."
: "Geçersiz dosya tipi. Sadece jpg, jpeg veya png.";
return res.status(400).json({ error: message });
}
next();
});
},
(req, res) => {
try {
if (!req.file?.buffer) {
return res.status(400).json({ error: "Dosya yüklenemedi" });
}
const buffer = req.file.buffer;
if (!isAllowedImage(buffer)) {
return res.status(400).json({ error: "Sadece jpeg/jpg/png kabul edilir" });
}
if (!isPng(buffer)) {
return res
.status(400)
.json({ error: "Lütfen kırptıktan sonra PNG olarak yükleyin." });
}
ensureDirForFile(AVATAR_PATH);
fs.writeFileSync(AVATAR_PATH, buffer);
res.json({
success: true,
avatarUrl: "/api/profile/avatar"
});
} catch (err) {
console.error("Avatar yükleme hatası:", err);
res.status(500).json({ error: "Avatar kaydedilemedi" });
}
}
);
function tvdbImageUrl(pathSegment) {
if (!pathSegment) return null;
if (pathSegment.startsWith("http")) return pathSegment;
@@ -505,6 +586,25 @@ function sanitizeRelative(relPath) {
return relPath.replace(/^[\\/]+/, "");
}
function isPng(buffer) {
return (
buffer &&
buffer.length >= 8 &&
buffer[0] === 0x89 &&
buffer[1] === 0x50 &&
buffer[2] === 0x4e &&
buffer[3] === 0x47
);
}
function isJpeg(buffer) {
return buffer && buffer.length >= 2 && buffer[0] === 0xff && buffer[1] === 0xd8;
}
function isAllowedImage(buffer) {
return isPng(buffer) || isJpeg(buffer);
}
function determineMediaType({
tracker,
movieMatch,