lol
This commit is contained in:
parent
b8b84a8902
commit
5892c00ae7
2
app.js
2
app.js
@ -4,6 +4,7 @@ const userRoutes = require('./src/routes/v1/userRoutes');
|
|||||||
const loginRoutes = require('./src/routes/v1/authRoutes');
|
const loginRoutes = require('./src/routes/v1/authRoutes');
|
||||||
const carsRoutes = require('./src/routes/v1/carsRoutes');
|
const carsRoutes = require('./src/routes/v1/carsRoutes');
|
||||||
const collectionRoutes = require('./src/routes/v1/collectionRoutes');
|
const collectionRoutes = require('./src/routes/v1/collectionRoutes');
|
||||||
|
const postRoutes = require('./src/routes/v1/postsRoutes');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
@ -27,6 +28,7 @@ app.use('/api/v1/users', userRoutes);
|
|||||||
app.use('/api/v1/login', loginRoutes);
|
app.use('/api/v1/login', loginRoutes);
|
||||||
app.use('/api/v1/cars', carsRoutes);
|
app.use('/api/v1/cars', carsRoutes);
|
||||||
app.use('/api/v1/collections', collectionRoutes);
|
app.use('/api/v1/collections', collectionRoutes);
|
||||||
|
app.use('/api/v1/posts', postRoutes);
|
||||||
|
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
res.status(404).json({ message: 'Route not found' });
|
res.status(404).json({ message: 'Route not found' });
|
||||||
|
|||||||
@ -5,27 +5,23 @@ const user = require('../models/user');
|
|||||||
|
|
||||||
exports.createCar = async (req, res) => {
|
exports.createCar = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
// 1) Determine imagePath:
|
// 1) Multer must have put the file under req.file
|
||||||
let imagePath;
|
if (!req.file) {
|
||||||
|
return res.status(400).json({ message: "Image file is required." });
|
||||||
if (req.file) {
|
|
||||||
// Native flow (Android/iOS) → Multerput the file under /uploads/cars/<filename>
|
|
||||||
imagePath = `/uploads/cars/${req.file.filename}`;
|
|
||||||
} else if (req.body.image) {
|
|
||||||
// Web flow → Client sent a JSON { image: "<uri>" }
|
|
||||||
// We trust that URI (could be a data URL or blob URL) and save it directly
|
|
||||||
imagePath = req.body.image;
|
|
||||||
} else {
|
|
||||||
return res.status(400).json({ message: "Image file (or image URL) is required." });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Extract other fields (make, model, year, modifications)
|
// 2) Validate text fields
|
||||||
const { make, model, year, modifications } = req.body;
|
const { make, model, year, modifications } = req.body;
|
||||||
if (!make || !model || !year || !modifications) {
|
if (!make || !model || !year || !modifications) {
|
||||||
return res.status(400).json({ message: "All fields are required." });
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ message: "make, model, year, and modifications are required." });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3) Create the Car document
|
// 3) Build image path (leading slash because /uploads is served statically)
|
||||||
|
const imagePath = `/uploads/cars/${req.file.filename}`;
|
||||||
|
|
||||||
|
// 4) Create the Car document
|
||||||
const newCar = await Car.create({
|
const newCar = await Car.create({
|
||||||
image: imagePath,
|
image: imagePath,
|
||||||
make: make.trim(),
|
make: make.trim(),
|
||||||
@ -34,13 +30,15 @@ exports.createCar = async (req, res) => {
|
|||||||
modifications: modifications.trim(),
|
modifications: modifications.trim(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// 4) Find the user ID from req.user
|
// 5) Find the logged-in user's ID
|
||||||
const userId = req.user.userId || req.user.id || req.user._id;
|
const userId = req.user.userId || req.user.id || req.user._id;
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
return res.status(400).json({ message: "User ID not found in token payload." });
|
return res
|
||||||
|
.status(400)
|
||||||
|
.json({ message: "User ID not found in token payload." });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5) Find or create the user’s Collection
|
// 6) Find or create that user’s Collection
|
||||||
let userCollection = await Collection.findOne({ user_id: userId });
|
let userCollection = await Collection.findOne({ user_id: userId });
|
||||||
if (!userCollection) {
|
if (!userCollection) {
|
||||||
userCollection = await Collection.create({
|
userCollection = await Collection.create({
|
||||||
@ -52,7 +50,16 @@ exports.createCar = async (req, res) => {
|
|||||||
await userCollection.save();
|
await userCollection.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6) Return success
|
let user = await user.findById(userId);
|
||||||
|
if (!user) {
|
||||||
|
return res.status(404).json({ message: "User not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// update number of cars in the collection
|
||||||
|
user.number_of_cars = userCollection.cars.length;
|
||||||
|
await user.save();
|
||||||
|
|
||||||
|
// 7) Return success
|
||||||
return res.status(201).json({
|
return res.status(201).json({
|
||||||
message: "Car added successfully",
|
message: "Car added successfully",
|
||||||
car: newCar,
|
car: newCar,
|
||||||
|
|||||||
136
src/controllers/postsController.js
Normal file
136
src/controllers/postsController.js
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
const Post = require('../models/post');
|
||||||
|
const User = require('../models/user');
|
||||||
|
|
||||||
|
exports.createPost = async (req, res) => {
|
||||||
|
try {
|
||||||
|
if (!req.file) {
|
||||||
|
return res.status(400).json({ message: "Image file is required." });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Grab any text fields you sent in the body
|
||||||
|
// (e.g. title, content, tags—whatever your schema expects)
|
||||||
|
const { title, content } = req.body;
|
||||||
|
|
||||||
|
// 2. Ensure the upload succeeded
|
||||||
|
|
||||||
|
// 3. Build a URL/path for where the image lives
|
||||||
|
// Using the same path logic as in your multer storage
|
||||||
|
// E.g. if you serve /uploads as static:
|
||||||
|
// app.use("/uploads", express.static(path.join(__dirname, "../uploads")));
|
||||||
|
const imageUrl = `/uploads/posts/${req.file.filename}`;
|
||||||
|
|
||||||
|
// 4. Grab the authenticated user ID that your authMiddleware set
|
||||||
|
const userId = req.user.userId || req.user.id || req.user._id;
|
||||||
|
if (!userId) {
|
||||||
|
return res
|
||||||
|
.status(401)
|
||||||
|
.json({ message: "User not authenticated." });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Create and save the post document
|
||||||
|
const newPost = new Post({
|
||||||
|
user_id: userId,
|
||||||
|
content,
|
||||||
|
image: imageUrl,
|
||||||
|
});
|
||||||
|
await newPost.save();
|
||||||
|
|
||||||
|
// 6. Return the newly created post
|
||||||
|
return res.status(201).json(newPost);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error in createPost:", err);
|
||||||
|
return res.status(500).json({
|
||||||
|
message: "Server error creating post",
|
||||||
|
error: err.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getPosts = async (req, res) => {
|
||||||
|
try {
|
||||||
|
// 1) Get userId from req.user (set by authMiddleware)
|
||||||
|
const userId = req.user.userId || req.user.id || req.user._id;
|
||||||
|
if (!userId) {
|
||||||
|
return res.status(401).json({ message: "User not authenticated." });
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await User.findById(userId).select("username");
|
||||||
|
|
||||||
|
console.log("Fetching posts for user ID:", userId);
|
||||||
|
// 2) Fetch that user's posts, populate author info, sort newest first
|
||||||
|
const posts = await Post.find({ user_id: userId }).sort({ timestamp: -1 })
|
||||||
|
|
||||||
|
console.log("Retrieved posts:", posts);
|
||||||
|
|
||||||
|
if (!posts || posts.length === 0) {
|
||||||
|
return res.status(404).json({ message: "No posts found." });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) Build full URL for each image
|
||||||
|
const host = req.get("host");
|
||||||
|
const protocol = req.protocol;
|
||||||
|
const postsWithFullUrl = posts.map(post => {
|
||||||
|
// post.image is something like "/uploads/posts/<filename>"
|
||||||
|
const fullImageUrl = `${protocol}://${host}${post.image}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
_id: post._id,
|
||||||
|
content: post.content,
|
||||||
|
image: fullImageUrl,
|
||||||
|
createdAt: post.timestamp,
|
||||||
|
username: user,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4) Send back the array
|
||||||
|
return res.status(200).json(postsWithFullUrl);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error retrieving posts:", error);
|
||||||
|
return res.status(500).json({
|
||||||
|
message: "Server error while retrieving posts",
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getLatestPosts = async (req, res) => {
|
||||||
|
try {
|
||||||
|
// 1) Get the latest 10 posts, sorted by createdAt descending
|
||||||
|
const posts = await Post.find()
|
||||||
|
.sort({ timestamp: -1 })
|
||||||
|
.limit(10)
|
||||||
|
.populate("user_id", "username")
|
||||||
|
|
||||||
|
if (!posts || posts.length === 0) {
|
||||||
|
return res.status(404).json({ message: "No posts found." });
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Retrieved latest posts:", posts);
|
||||||
|
|
||||||
|
// 2) Build full URL for each image
|
||||||
|
const host = req.get("host");
|
||||||
|
const protocol = req.protocol;
|
||||||
|
const postsWithFullUrl = posts.map(post => {
|
||||||
|
const fullImageUrl = `${protocol}://${host}${post.image}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: post.content,
|
||||||
|
image: fullImageUrl,
|
||||||
|
createdAt: post.timestamp,
|
||||||
|
username: post.user_id.username,
|
||||||
|
profile_pic: post.user_id.profile_pic,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3) Send back the array
|
||||||
|
return res.status(200).json(postsWithFullUrl);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error retrieving latest posts:", error);
|
||||||
|
return res.status(500).json({
|
||||||
|
message: "Server error while retrieving latest posts",
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/models/post.js
Normal file
26
src/models/post.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
const { Schema, model } = require('mongoose');
|
||||||
|
|
||||||
|
const postSchema = new Schema(
|
||||||
|
{
|
||||||
|
user_id: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'User',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
trim: true,
|
||||||
|
maxlength: [500, 'Post content cannot exceed 500 characters']
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
timestamp: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = model('Post', postSchema, 'posts');
|
||||||
@ -50,9 +50,6 @@ const userSchema = new Schema(
|
|||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
garage: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
posts: {
|
posts: {
|
||||||
type: String,
|
type: String,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ const authMiddleware = require('../../middleware/authMiddleware');
|
|||||||
|
|
||||||
const storage = multer.diskStorage({
|
const storage = multer.diskStorage({
|
||||||
destination: (req, file, cb) => {
|
destination: (req, file, cb) => {
|
||||||
|
// Ensure this directory exists on disk: "./uploads/cars"
|
||||||
cb(null, path.join(__dirname, "../../../uploads/cars"));
|
cb(null, path.join(__dirname, "../../../uploads/cars"));
|
||||||
},
|
},
|
||||||
filename: (req, file, cb) => {
|
filename: (req, file, cb) => {
|
||||||
@ -14,11 +15,12 @@ const storage = multer.diskStorage({
|
|||||||
if (file.mimetype === "image/png") ext = ".png";
|
if (file.mimetype === "image/png") ext = ".png";
|
||||||
else if (file.mimetype === "image/jpeg") ext = ".jpg";
|
else if (file.mimetype === "image/jpeg") ext = ".jpg";
|
||||||
|
|
||||||
const uniqueName = Date.now() + ext;
|
// Use a timestamp as filename: e.g. "1627890123456.png"
|
||||||
cb(null, uniqueName);
|
cb(null, `${Date.now()}${ext}`);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const allowedMimeTypes = new Set(["image/jpeg", "image/png", "image/jpg"]);
|
const allowedMimeTypes = new Set(["image/jpeg", "image/png", "image/jpg"]);
|
||||||
|
|
||||||
const upload = multer({
|
const upload = multer({
|
||||||
@ -32,7 +34,6 @@ const upload = multer({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
router.post('/addCar', authMiddleware, upload.single("image"), createCar);
|
router.post('/addCar', authMiddleware, upload.single("image"), createCar);
|
||||||
|
|
||||||
router.get('/getCollection', authMiddleware, getCollection);
|
router.get('/getCollection', authMiddleware, getCollection);
|
||||||
|
|||||||
43
src/routes/v1/postsRoutes.js
Normal file
43
src/routes/v1/postsRoutes.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const { createPost, getPosts, getLatestPosts } = require('../../controllers/postsController');
|
||||||
|
const multer = require("multer");
|
||||||
|
const path = require("path");
|
||||||
|
const authMiddleware = require('../../middleware/authMiddleware');
|
||||||
|
|
||||||
|
|
||||||
|
const storage = multer.diskStorage({
|
||||||
|
destination: (req, file, cb) => {
|
||||||
|
// Ensure this directory exists on disk: "./uploads/posts"
|
||||||
|
cb(null, path.join(__dirname, "../../../uploads/posts"));
|
||||||
|
},
|
||||||
|
filename: (req, file, cb) => {
|
||||||
|
let ext = ".jpg";
|
||||||
|
if (file.mimetype === "image/png") ext = ".png";
|
||||||
|
else if (file.mimetype === "image/jpeg") ext = ".jpg";
|
||||||
|
|
||||||
|
cb(null, `${Date.now()}${ext}`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const allowedMimeTypes = new Set(["image/jpeg", "image/png", "image/jpg"]);
|
||||||
|
|
||||||
|
const upload = multer({
|
||||||
|
storage,
|
||||||
|
limits: { fileSize: 5 * 1024 * 1024 },
|
||||||
|
fileFilter: (req, file, cb) => {
|
||||||
|
if (!allowedMimeTypes.has(file.mimetype)) {
|
||||||
|
return cb(new Error("Only JPG/JPEG/PNG images are allowed"), false);
|
||||||
|
}
|
||||||
|
cb(null, true);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
router.post('/createPost', authMiddleware, upload.single("image"), createPost);
|
||||||
|
|
||||||
|
router.get('/getPosts', authMiddleware, getPosts);
|
||||||
|
|
||||||
|
router.get('/getLatestPosts', getLatestPosts);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
BIN
uploads/cars/1749242596413.jpg
Normal file
BIN
uploads/cars/1749242596413.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 175 KiB |
BIN
uploads/posts/1749279281769.jpg
Normal file
BIN
uploads/posts/1749279281769.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 454 KiB |
BIN
uploads/posts/1749279310017.jpg
Normal file
BIN
uploads/posts/1749279310017.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 454 KiB |
BIN
uploads/posts/1749279365619.jpg
Normal file
BIN
uploads/posts/1749279365619.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 454 KiB |
BIN
uploads/posts/1749279458888.jpg
Normal file
BIN
uploads/posts/1749279458888.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 454 KiB |
BIN
uploads/posts/1749282223166.jpg
Normal file
BIN
uploads/posts/1749282223166.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 218 KiB |
Loading…
x
Reference in New Issue
Block a user