Home > Net >  How to create public shareable link for private user resources in MERN application
How to create public shareable link for private user resources in MERN application

Time:05-29

I am creating a movie web application using MERN stack where a user can sign up and login. Users can also add the movies they like to their respective list.

Now, I want to add a feature where the user can share their created list with anyone through a shareable link just like how we give access to files on google drive without the person requiring to login.

Please give me some insights on how this can be achieved.

Model of the database I want to share.

const mongoose = require("mongoose");

const FavoritesSchema = mongoose.Schema({
    user: {
        type: mongoose.Schema.Types.ObjectId,
        ref: "user",
    },
    imdbID: {
        type: String,
        require: true,
    },
    Poster: {
        type: String,
        require: true,
    },
    Title: {
        type: String,
        require: true,
    },
    Type: {
        type: String,
        require: true,
    },
    Year: {
        type: String,
        require: true,
    },
    date: {
        type: Date,
        default: Date.now,
    },
});

module.exports = mongoose.model("favorites", FavoritesSchema);

server.js

const express = require("express");
const dotenv = require("dotenv");
const connectDB = require("./config/db");
const path = require("path");

const cors = require("cors");
const axios = require("axios");

dotenv.config();

const app = express();

connectDB();
app.use(cors());

app.all("*", (req, res, next) => {
    res.header("Access-Control-Allow-Origin", "https://localhost:3000");
    next();
});

app.use(express.json({ extended: false }));

app.use("/api/users", require("./routes/users"));
app.use("/api/auth", require("./routes/auth"));
app.use("/api/mylist", require("./routes/mylist"));

app.get("/api/data/:searchValue", async (req, res) => {
    const { searchValue } = req.params;
    console.log(searchValue);
    const response = await axios.get(
        `http://www.omdbapi.com/?apikey=${process.env.REACT_APP_CLIENT_SECRET}&s=${searchValue}`
    );
    console.log(response.data);
    return res.send(response.data);
});

app.get("/api/data/:searchValue/:currentPage", async (req, res) => {
    const { searchValue, currentPage } = req.params;
    console.log(searchValue);
    const response = await axios.get(
        `http://www.omdbapi.com/?apikey=${process.env.REACT_APP_CLIENT_SECRET}&s=${searchValue}&page=${currentPage}`
    );
    console.log(response.data);
    return res.send(response.data);
});
//FOR PRODUCTION

__dirname = path.resolve();
app.use("/uploads", express.static(path.join(__dirname, "/uploads")));

if (process.env.NODE_ENV === "production") {
    app.use(express.static(path.join(__dirname, "frontend/build")));

    app.get("*", (req, res) =>
        res.sendFile(path.resolve(__dirname, "frontend", "build", "index.html"))
    );
}

const PORT = process.env.PORT || 5000;

app.listen(
    PORT,
    console.log(`server started in ${process.env.NODE_ENV} mode on port ${PORT}`)
);

Users list route. routes/mylist.js

const express = require("express");
const router = express.Router();

const auth = require("../middleware/auth");

const { body, validationResult } = require("express-validator");

const User = require("../models/User");

const Favorite = require("../models/Favorites");

// @route GET api/mylist
// @get a users all lists of movies
// @access Private
router.get("/", auth, async (req, res) => {
    try {
        const favorites = await Favorite.find({ user: req.user.id }).sort({
            date: -1,
        });
        res.json(favorites);
        // console.log(favorites);
    } catch (error) {
        console.error(error.message);
        res.status(500).send("Server Error");
    }
    // res.send("Get the user all lists of movies");
});

// @route POST api/mylist
// @get a users all lists of movies
// @access Private
router.post(
    "/",
    [
        auth,

        [
            body("Poster", "Poster is required").not().isEmpty(),
            body("Title", "imdbid is required").not().isEmpty(),
            body("Type", "Type is required").not().isEmpty(),
            body("Year", "Year is required").not().isEmpty(),
            body("imdbID", "imdbid is required").not().isEmpty(),
        ],
    ],
    async (req, res) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(400).json({ errors: errors.array() });
        }

        const { Poster, Title, Type, Year, imdbID } = req.body;

        try {
            const newFavorite = new Favorite({
                imdbID,
                Poster,
                Type,
                Year,
                Title,
                user: req.user.id,
            });
            const favorite = await newFavorite.save();
            console.log(newFavorite);
            res.json(favorite);
        } catch (error) {
            console.error(error.message);
            res.status(500).send("Server Error");
        }
        // res.send("Add a movie to mylist ");
    }
);

router.delete(
    "/:id",
    [auth, [body("_id", "_id is required").not().isEmpty()]],
    async (req, res) => {
        try {
            let favorite = await Favorite.findById(req.params.id);

            if (!favorite) return res.status(401).json({ msg: "Movie not found" });

            if (favorite.user.toString() !== req.user.id) {
                return res.status(401).json({ msg: "Not Authorized" });
            }

            await Favorite.findByIdAndRemove(req.params.id);

            res.json({ msg: "Movie Removed" });
            console.log("hey");
        } catch (error) {
            console.error(error.message);
            res.status(500).send("Server Error");
        }
        // res.send("Add a movie to mylist ");
    }
);

module.exports = router;

CodePudding user response:

You have to add String type array to your mongoose scheme.When user add films to own list these films links will push your array.after that you simply can share it anywere if you want. add to your mongoose scheme list: [String]

CodePudding user response:

This is how I will do it.

Generate a unique id that you will use to identify the favorite list (I will use the UUID package https://www.npmjs.com/package/uuid)

Say I generated a unique id, I need to associate it to a favorite list in the DB.

I will make use of the node-cache library (https://www.npmjs.com/package/node-cache).

For example, the id of my favorite list is 62477be0e5372c02a82b2983

const NodeCache = require( "node-cache" );
const { v4: uuidv4 } = require('uuid');

const favoriteCache = new NodeCache();

const favoriteListId = "62477be0e5372c02a82b2983";
const shareableCode = uuidv4(); // ⇨ '1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed'

favoriteCache.set(shareableCode,favoriteListId);

In the code above, I have saved in the cache a unique id and set its value to the id of the favorite list.

Now I want to retrieve the list using the code.

router.get("/shared/:code", auth, async (req, res) => {
    const {code} = req.params;
    const listId = favoriteCache.get(code);
    if (!listId) return res.status(400).send('invalid share code');
    const list = await Favorite.find({_id: listId});
    res.json(list);
})

If you restart your app the codes will be removed from the cache, you can make use of the db to save the code to make them persistent. But If you are looking to have the sharing codes last for a few hours, then this is one of the ways you can do it. You can also set a third parameter to the set function of the cache to assign an expiration time.

  • Related