zkt25/sk1/backend/routes/profiles.js

187 lines
6.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import express from "express";
import fetch from "node-fetch";
import bcrypt from "bcryptjs";
import { User } from "../models/User.js";
import { requireAuth } from "../middleware/auth.js";
const router = express.Router();
// GET /api/profiles/me
router.get("/me", requireAuth, async (req, res) => {
const user = await User.findById(req.user.id).select("-password -__v");
if (!user) return res.status(404).json({ error: "User not found" });
res.json({
email: user.email,
role: user.role,
profile: user.profile,
});
});
// NEW: GET any profile by its ID
router.get("/:id", async (req, res) => {
try {
const user = await User.findById(req.params.id).select("-password -__v");
if (!user) return res.status(404).json({ error: "Profile not found" });
return res.json({
email: user.email,
role: user.role,
profile: user.profile,
});
} catch (err) {
return res.status(500).json({ error: err.message });
}
});
// POST /api/profiles (create or update profile)
// normal user: only name/address/password
// pro user: full profile (field, rate, address, lat, lon, published)
router.post("/", requireAuth, async (req, res) => {
try {
const user = await User.findById(req.user.id);
if (req.user.role === "pro") {
const {
firstName,
lastName,
phone,
field,
rate,
address,
description,
picture,
published,
} = req.body;
// pure publish toggle?
if (
req.body.hasOwnProperty("published") &&
Object.keys(req.body).length === 1
) {
// disallow publishing unless all fields are set
if (published) {
const pf = (await User.findById(req.user.id)).profile;
if (
!pf.firstName ||
!pf.lastName ||
!pf.phone ||
!pf.field ||
pf.rate == null ||
!pf.address ||
!pf.description || // ← require description
!pf.picture // ← require picture
) {
return res.status(400).json({
error:
"Complete firstName, lastName, phone, field, rate, address, description & picture before publishing.",
});
}
}
user.profile.published = !!published;
await user.save();
return res.json({ success: true });
}
// full update → geocode + persist all fields
const geoRes = await fetch(
`https://maps.googleapis.com/maps/api/geocode/json` +
`?address=${encodeURIComponent(address)}` +
`&key=${process.env.GOOGLE_PLACES_API_KEY}`
);
const geoData = await geoRes.json();
console.log(
"🗺️ geoData.results:",
JSON.stringify(geoData.results, null, 2)
);
if (geoData.status !== "OK" || !geoData.results.length) {
return res.status(400).json({ error: "Invalid address" });
}
// prefer a true citylevel result, then region, then country, then anything
const place =
geoData.results.find((r) => r.types.includes("locality")) ||
geoData.results.find((r) =>
r.types.includes("administrative_area_level_2")
) ||
geoData.results.find((r) =>
r.types.includes("administrative_area_level_1")
) ||
geoData.results[0];
const { lat, lng } = place.geometry.location;
// first try the locality component
const cityComp = place.address_components.find((c) =>
c.types.includes("locality")
);
let rawCity;
if (cityComp) {
rawCity = cityComp.long_name;
} else {
// use the penultimate segment of formatted_address, e.g.
// ["Hlavná","Old Town","Košice","Slovakia"] → "Košice"
const parts = (place.formatted_address || "")
.split(",")
.map((s) => s.trim());
rawCity = parts.length > 1 ? parts[parts.length - 2] : parts[0];
}
// normalize: strip accents + lowercase
const city = rawCity
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "")
.toLowerCase();
// now persist everything
user.profile.firstName = firstName;
user.profile.lastName = lastName;
user.profile.phone = phone;
user.profile.field = field;
user.profile.rate = rate;
user.profile.address = address;
user.profile.description = description; // ← save description
user.profile.picture = picture; // ← save picture
user.profile.city = city;
user.profile.location = {
type: "Point",
coordinates: [lng, lat],
};
user.profile.published = !!published;
} else {
// ─── Normal user profile update ──────────────────────────────────────
// Allow them to save firstName, lastName, phone, and (optionally) password
const { firstName, lastName, phone, password } = req.body;
const u = await User.findById(req.user.id);
if (!u) return res.status(404).json({ error: "User not found" });
if (firstName !== undefined) u.profile.firstName = firstName;
if (lastName !== undefined) u.profile.lastName = lastName;
if (phone !== undefined) u.profile.phone = phone;
// if they submitted a new password, hash + save it
if (password) {
u.password = await bcrypt.hash(password, 10);
}
await u.save();
return res.json({ success: true, profile: u.profile });
}
await user.save();
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// DELETE /api/profiles/me → unpublish pro or delete normal account
router.delete("/me", requireAuth, async (req, res) => {
try {
// Delete the entire user account
await User.findByIdAndDelete(req.user.id);
return res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
export default router;