This commit is contained in:
Jonathan Jara 2025-06-07 00:49:01 -07:00
parent b8b84a8902
commit 5892c00ae7
13 changed files with 237 additions and 25 deletions

2
app.js
View File

@ -4,6 +4,7 @@ const userRoutes = require('./src/routes/v1/userRoutes');
const loginRoutes = require('./src/routes/v1/authRoutes');
const carsRoutes = require('./src/routes/v1/carsRoutes');
const collectionRoutes = require('./src/routes/v1/collectionRoutes');
const postRoutes = require('./src/routes/v1/postsRoutes');
const path = require('path');
const app = express();
@ -27,6 +28,7 @@ app.use('/api/v1/users', userRoutes);
app.use('/api/v1/login', loginRoutes);
app.use('/api/v1/cars', carsRoutes);
app.use('/api/v1/collections', collectionRoutes);
app.use('/api/v1/posts', postRoutes);
app.use((req, res, next) => {
res.status(404).json({ message: 'Route not found' });

View File

@ -5,27 +5,23 @@ const user = require('../models/user');
exports.createCar = async (req, res) => {
try {
// 1) Determine imagePath:
let imagePath;
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." });
// 1) Multer must have put the file under req.file
if (!req.file) {
return res.status(400).json({ message: "Image file is required." });
}
// 2) Extract other fields (make, model, year, modifications)
// 2) Validate text fields
const { make, model, year, modifications } = req.body;
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({
image: imagePath,
make: make.trim(),
@ -34,13 +30,15 @@ exports.createCar = async (req, res) => {
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;
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 users Collection
// 6) Find or create that users Collection
let userCollection = await Collection.findOne({ user_id: userId });
if (!userCollection) {
userCollection = await Collection.create({
@ -52,7 +50,16 @@ exports.createCar = async (req, res) => {
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({
message: "Car added successfully",
car: newCar,

View 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
View 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');

View File

@ -50,9 +50,6 @@ const userSchema = new Schema(
type: Number,
default: 0,
},
garage: {
type: String,
},
posts: {
type: String,
}

View File

@ -7,6 +7,7 @@ const authMiddleware = require('../../middleware/authMiddleware');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
// Ensure this directory exists on disk: "./uploads/cars"
cb(null, path.join(__dirname, "../../../uploads/cars"));
},
filename: (req, file, cb) => {
@ -14,11 +15,12 @@ const storage = multer.diskStorage({
if (file.mimetype === "image/png") ext = ".png";
else if (file.mimetype === "image/jpeg") ext = ".jpg";
const uniqueName = Date.now() + ext;
cb(null, uniqueName);
// Use a timestamp as filename: e.g. "1627890123456.png"
cb(null, `${Date.now()}${ext}`);
},
});
const allowedMimeTypes = new Set(["image/jpeg", "image/png", "image/jpg"]);
const upload = multer({
@ -32,7 +34,6 @@ const upload = multer({
},
});
router.post('/addCar', authMiddleware, upload.single("image"), createCar);
router.get('/getCollection', authMiddleware, getCollection);

View 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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB