├── api ├── .env ├── .gitignore ├── apiCalls.js ├── client.js ├── index.js ├── package-lock.json ├── package.json └── public │ ├── 1645644077808-Dwayne_Johnson_2014_(cropped).jpg │ ├── 1645663438209-images.png │ ├── 1645669245274-3408.jpg │ ├── 1645669454692-3408.jpg │ ├── 1645669461881-3408.jpg │ ├── 1645669784482-young-man-face-cartoon-vector-19116342.jpg │ ├── 1645669803751-young-man-face-cartoon-vector-19116342.jpg │ ├── 1645669816973-download.jfif │ ├── 1645669928543-young-man-face-cartoon-vector-19116342.jpg │ ├── 1645669938298-young-man-face-cartoon-vector-19116342.jpg │ ├── 1645669943936-young-man-face-cartoon-vector-19116342.jpg │ ├── 1645670617279-download.jfif │ └── 1645671618422-3408.jpg ├── database ├── .eslintrc ├── .gitignore ├── .npmignore ├── README.md ├── config │ ├── .checksums │ └── @sanity │ │ ├── data-aspects.json │ │ ├── default-layout.json │ │ ├── default-login.json │ │ ├── form-builder.json │ │ └── vision.json ├── package.json ├── plugins │ └── .gitkeep ├── sanity.json ├── schemas │ ├── post.js │ ├── schema.js │ └── user.js ├── static │ ├── .gitkeep │ └── favicon.ico ├── tsconfig.json └── yarn.lock └── frontend ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── components ├── AlertDismissible.js ├── AllPosts.js ├── App.js ├── CreatePost.js ├── EditProfile.js ├── Login.js ├── Profile.js ├── ProfileItem.js ├── Search.js └── SignUp.js ├── css ├── App.css ├── CreatePost.css ├── Profile.css ├── Search.css ├── SignUp.css └── index.css └── index.js /api/.env: -------------------------------------------------------------------------------- 1 | SANITY_API_TOKEN="" -------------------------------------------------------------------------------- /api/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /api/apiCalls.js: -------------------------------------------------------------------------------- 1 | import sanityClient from "./client.js"; 2 | import { createReadStream } from "fs"; 3 | import { basename } from "path"; 4 | import { nanoid } from "nanoid"; 5 | 6 | const functions = {}; 7 | 8 | functions.createUser = (firstName, lastName, username) => { 9 | return sanityClient.create({ 10 | _type: "user", 11 | first_name: firstName, 12 | last_name: lastName, 13 | username: username, 14 | created_at: new Date(), 15 | }); 16 | }; 17 | 18 | functions.getProfile = (user) => { 19 | return sanityClient.fetch( 20 | `*[_type == "user" && username == $username]{ 21 | ..., 22 | "following": count(following), 23 | "followers": *[_type == "user" && references(^._id)], 24 | photo{ 25 | asset->{ 26 | _id, 27 | url 28 | } 29 | } 30 | }`, 31 | { username: user } 32 | ); 33 | }; 34 | 35 | functions.getUserId = (user) => { 36 | return sanityClient.fetch( 37 | `*[_type == "user" && username == $username]{ 38 | _id 39 | }`, 40 | { username: user } 41 | ); 42 | }; 43 | 44 | functions.createPost = (user, caption, image) => { 45 | return sanityClient.assets 46 | .upload("image", createReadStream(image.path), { 47 | filename: basename(image.path), 48 | }) 49 | .then((data) => 50 | functions.getUserId(user).then((ids) => { 51 | const id = ids[0]._id; 52 | return sanityClient.create({ 53 | _type: "post", 54 | author: { _ref: id }, 55 | photo: { asset: { _ref: data._id } }, 56 | description: caption, 57 | created_at: new Date(), 58 | }); 59 | }) 60 | ); 61 | }; 62 | 63 | functions.getAllPosts = () => { 64 | return sanityClient.fetch(`*[_type == "post"]{ 65 | ..., 66 | "username": author->username, 67 | photo{ 68 | asset->{ 69 | _id, 70 | url 71 | } 72 | } 73 | }`); 74 | }; 75 | 76 | functions.getPostsOfFollowing = (username) => { 77 | return sanityClient.fetch( 78 | `*[_type == "user" && username == $username]{ 79 | following[]->{ 80 | "posts": *[_type == "post" && references(^._id)]{ 81 | ..., 82 | "username": author->username, 83 | photo{ 84 | asset->{ 85 | _id, 86 | url 87 | } 88 | } 89 | } 90 | } 91 | }`, 92 | { username } 93 | ); 94 | }; 95 | 96 | functions.searchForUsername = (text) => { 97 | return sanityClient.fetch( 98 | `*[_type == "user" && username match "${text}*"]{ 99 | ..., 100 | "followers": count(*[_type == "user" && references(^._id)]), 101 | photo{ 102 | asset->{ 103 | _id, 104 | url 105 | } 106 | } 107 | }` 108 | ); 109 | }; 110 | 111 | functions.getPosts = (username) => { 112 | return sanityClient.fetch( 113 | `*[_type == "post" && author->username == $username]{ 114 | ..., 115 | "username": author->username, 116 | photo{ 117 | asset->{ 118 | _id, 119 | url 120 | } 121 | } 122 | }`, 123 | { username } 124 | ); 125 | }; 126 | 127 | functions.updateProfile = (user, first_name, last_name, bio, image) => { 128 | if (image) { 129 | return sanityClient.assets 130 | .upload("image", createReadStream(image.path), { 131 | filename: basename(image.path), 132 | }) 133 | .then((data) => 134 | functions.getUserId(user).then((ids) => 135 | sanityClient 136 | .patch(ids[0]._id) 137 | .set({ 138 | first_name, 139 | last_name, 140 | bio, 141 | photo: { asset: { _ref: data._id } }, 142 | }) 143 | .commit() 144 | ) 145 | ); 146 | } else { 147 | return functions.getUserId(user).then((ids) => 148 | sanityClient 149 | .patch(ids[0]._id) 150 | .set({ 151 | first_name, 152 | last_name, 153 | bio, 154 | }) 155 | .commit() 156 | ); 157 | } 158 | }; 159 | 160 | functions.addFollower = (user, followingId) => { 161 | return functions.getUserId(user).then((ids) => 162 | sanityClient 163 | .patch(ids[0]._id) 164 | .setIfMissing({ following: [] }) 165 | .insert("after", "following[-1]", [ 166 | { _ref: followingId, _key: nanoid(), _type: "reference" }, 167 | ]) 168 | .commit() 169 | ); 170 | }; 171 | 172 | functions.removeFollower = (user, followingId) => { 173 | return functions.getUserId(user).then((ids) => 174 | sanityClient 175 | .patch(ids[0]._id) 176 | .unset([`following[_ref=="${followingId}"]`]) 177 | .commit() 178 | ); 179 | }; 180 | 181 | export default functions; 182 | -------------------------------------------------------------------------------- /api/client.js: -------------------------------------------------------------------------------- 1 | import sanityClient from "@sanity/client"; 2 | import dotenv from "dotenv"; 3 | dotenv.config(); 4 | 5 | export default sanityClient({ 6 | projectId: "l77o0dei", 7 | dataset: "production", 8 | useCdn: false, 9 | apiVersion: "2022-02-22", 10 | token: process.env.SANITY_API_TOKEN, 11 | }); 12 | -------------------------------------------------------------------------------- /api/index.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import bodyParser from "body-parser"; 3 | import multer from "multer"; 4 | import functions from "./apiCalls.js"; 5 | const { 6 | createUser, 7 | getProfile, 8 | createPost, 9 | getAllPosts, 10 | getPostsOfFollowing, 11 | searchForUsername, 12 | getPosts, 13 | updateProfile, 14 | addFollower, 15 | removeFollower, 16 | } = functions; 17 | 18 | const app = express(); 19 | app.use(bodyParser.json()); 20 | app.use(bodyParser.urlencoded({ extended: false })); 21 | 22 | var storage = multer.diskStorage({ 23 | destination: function (req, file, cb) { 24 | cb(null, "public"); 25 | }, 26 | filename: function (req, file, cb) { 27 | cb(null, Date.now() + "-" + file.originalname); 28 | }, 29 | }); 30 | var upload = multer({ storage: storage }); 31 | 32 | app.post("/createUser", (req, res) => { 33 | const body = req.body; 34 | createUser(body.firstName, body.lastName, body.username).then((data) => 35 | res.json(data) 36 | ); 37 | }); 38 | 39 | app.get("/getProfile", (req, res) => { 40 | const user = req.query.user; 41 | getProfile(user).then((data) => res.json(data)); 42 | }); 43 | 44 | app.post("/createPost", upload.single("file"), (req, res) => { 45 | const body = req.body; 46 | createPost(body.user, body.caption, req.file).then((data) => res.json(data)); 47 | }); 48 | 49 | app.get("/getPostsOfFollowing", (req, res) => { 50 | const user = req.query.user; 51 | getPostsOfFollowing(user) 52 | .then((data) => { 53 | var posts = data[0].following; 54 | posts = posts.map((post) => post.posts); 55 | posts = posts.flat(1); 56 | res.json(posts); 57 | }) 58 | .catch((err) => res.json([])); 59 | }); 60 | 61 | app.get("/searchForUsername", (req, res) => { 62 | const text = req.query.text; 63 | searchForUsername(text).then((data) => res.json(data)); 64 | }); 65 | 66 | app.get("/getAllPosts", (req, res) => { 67 | getAllPosts().then((data) => res.json(data)); 68 | }); 69 | 70 | app.get("/getPosts", (req, res) => { 71 | const user = req.query.user; 72 | getPosts(user).then((data) => res.json(data)); 73 | }); 74 | 75 | app.post("/updateProfile", upload.single("file"), (req, res) => { 76 | const body = req.body; 77 | updateProfile( 78 | body.user, 79 | body.first_name, 80 | body.last_name, 81 | body.bio, 82 | req.file 83 | ).then((data) => res.json(data)); 84 | }); 85 | 86 | app.post("/addFollower", (req, res) => { 87 | const body = req.body; 88 | addFollower(body.user, body.id).then((data) => res.json(data)); 89 | }); 90 | 91 | app.delete("/removeFollower", (req, res) => { 92 | const body = req.body; 93 | removeFollower(body.user, body.id).then((data) => res.json(data)); 94 | }); 95 | 96 | app.listen(3001, () => console.log("started")); 97 | -------------------------------------------------------------------------------- /api/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@sanity/client": { 8 | "version": "3.1.0", 9 | "resolved": "https://registry.npmjs.org/@sanity/client/-/client-3.1.0.tgz", 10 | "integrity": "sha512-DtyCtAWNsDgLlEI8y4Zm9L2R1KBe5/BEVQoyD7V2ebScqH9n68nIq4cg//uTjyvvzSwZM2hmV6dd5ZXhvAdk7Q==", 11 | "requires": { 12 | "@sanity/eventsource": "^3.0.1", 13 | "@sanity/generate-help-url": "^2.18.0", 14 | "get-it": "^6.0.0", 15 | "make-error": "^1.3.0", 16 | "object-assign": "^4.1.1", 17 | "rxjs": "^6.0.0" 18 | } 19 | }, 20 | "@sanity/eventsource": { 21 | "version": "3.0.1", 22 | "resolved": "https://registry.npmjs.org/@sanity/eventsource/-/eventsource-3.0.1.tgz", 23 | "integrity": "sha512-xeMzr0wK/1+lawSicDg8yA7mdoNY3SKtr70CCsb1ltWbtYtsgZWjKeqNivNAWofxSU2GN7yU23HPzyk6Tx9fkA==", 24 | "requires": { 25 | "event-source-polyfill": "^1.0.25", 26 | "eventsource": "^1.0.6" 27 | } 28 | }, 29 | "@sanity/generate-help-url": { 30 | "version": "2.18.0", 31 | "resolved": "https://registry.npmjs.org/@sanity/generate-help-url/-/generate-help-url-2.18.0.tgz", 32 | "integrity": "sha512-If8Qkw32LWPes16UzqwUsTLgfxF5d4ACdUvCLMl6grJc/5G8LKPAGCQUuA/d1F4W16yCJVV7Zv31HDRDXJSJkg==" 33 | }, 34 | "@sanity/timed-out": { 35 | "version": "4.0.2", 36 | "resolved": "https://registry.npmjs.org/@sanity/timed-out/-/timed-out-4.0.2.tgz", 37 | "integrity": "sha512-NBDKGj14g9Z+bopIvZcQKWCzJq5JSrdmzRR1CS+iyA3Gm8SnIWBfZa7I3mTg2X6Nu8LQXG0EPKXdOGozLS4i3w==" 38 | }, 39 | "append-field": { 40 | "version": "1.0.0", 41 | "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", 42 | "integrity": "sha1-HjRA6RXwsSA9I3SOeO3XubW0PlY=" 43 | }, 44 | "body-parser": { 45 | "version": "1.19.2", 46 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", 47 | "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", 48 | "requires": { 49 | "bytes": "3.1.2", 50 | "content-type": "~1.0.4", 51 | "debug": "2.6.9", 52 | "depd": "~1.1.2", 53 | "http-errors": "1.8.1", 54 | "iconv-lite": "0.4.24", 55 | "on-finished": "~2.3.0", 56 | "qs": "6.9.7", 57 | "raw-body": "2.4.3", 58 | "type-is": "~1.6.18" 59 | } 60 | }, 61 | "buffer-from": { 62 | "version": "1.1.2", 63 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", 64 | "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" 65 | }, 66 | "busboy": { 67 | "version": "0.2.14", 68 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz", 69 | "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=", 70 | "requires": { 71 | "dicer": "0.2.5", 72 | "readable-stream": "1.1.x" 73 | }, 74 | "dependencies": { 75 | "isarray": { 76 | "version": "0.0.1", 77 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 78 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 79 | }, 80 | "readable-stream": { 81 | "version": "1.1.14", 82 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 83 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 84 | "requires": { 85 | "core-util-is": "~1.0.0", 86 | "inherits": "~2.0.1", 87 | "isarray": "0.0.1", 88 | "string_decoder": "~0.10.x" 89 | } 90 | }, 91 | "string_decoder": { 92 | "version": "0.10.31", 93 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 94 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" 95 | } 96 | } 97 | }, 98 | "bytes": { 99 | "version": "3.1.2", 100 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 101 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" 102 | }, 103 | "capture-stack-trace": { 104 | "version": "1.0.1", 105 | "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", 106 | "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==" 107 | }, 108 | "concat-stream": { 109 | "version": "1.6.2", 110 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", 111 | "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", 112 | "requires": { 113 | "buffer-from": "^1.0.0", 114 | "inherits": "^2.0.3", 115 | "readable-stream": "^2.2.2", 116 | "typedarray": "^0.0.6" 117 | } 118 | }, 119 | "content-type": { 120 | "version": "1.0.4", 121 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 122 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 123 | }, 124 | "core-util-is": { 125 | "version": "1.0.3", 126 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", 127 | "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" 128 | }, 129 | "create-error-class": { 130 | "version": "3.0.2", 131 | "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", 132 | "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", 133 | "requires": { 134 | "capture-stack-trace": "^1.0.0" 135 | } 136 | }, 137 | "debug": { 138 | "version": "2.6.9", 139 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 140 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 141 | "requires": { 142 | "ms": "2.0.0" 143 | } 144 | }, 145 | "decompress-response": { 146 | "version": "6.0.0", 147 | "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", 148 | "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", 149 | "requires": { 150 | "mimic-response": "^3.1.0" 151 | } 152 | }, 153 | "depd": { 154 | "version": "1.1.2", 155 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 156 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 157 | }, 158 | "dicer": { 159 | "version": "0.2.5", 160 | "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz", 161 | "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=", 162 | "requires": { 163 | "readable-stream": "1.1.x", 164 | "streamsearch": "0.1.2" 165 | }, 166 | "dependencies": { 167 | "isarray": { 168 | "version": "0.0.1", 169 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 170 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" 171 | }, 172 | "readable-stream": { 173 | "version": "1.1.14", 174 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 175 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 176 | "requires": { 177 | "core-util-is": "~1.0.0", 178 | "inherits": "~2.0.1", 179 | "isarray": "0.0.1", 180 | "string_decoder": "~0.10.x" 181 | } 182 | }, 183 | "string_decoder": { 184 | "version": "0.10.31", 185 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 186 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" 187 | } 188 | } 189 | }, 190 | "dotenv": { 191 | "version": "16.0.0", 192 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz", 193 | "integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==" 194 | }, 195 | "ee-first": { 196 | "version": "1.1.1", 197 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 198 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 199 | }, 200 | "event-source-polyfill": { 201 | "version": "1.0.25", 202 | "resolved": "https://registry.npmjs.org/event-source-polyfill/-/event-source-polyfill-1.0.25.tgz", 203 | "integrity": "sha512-hQxu6sN1Eq4JjoI7ITdQeGGUN193A2ra83qC0Ltm9I2UJVAten3OFVN6k5RX4YWeCS0BoC8xg/5czOCIHVosQg==" 204 | }, 205 | "eventsource": { 206 | "version": "1.1.0", 207 | "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.1.0.tgz", 208 | "integrity": "sha512-VSJjT5oCNrFvCS6igjzPAt5hBzQ2qPBFIbJ03zLI9SE0mxwZpMw6BfJrbFHm1a141AavMEB8JHmBhWAd66PfCg==", 209 | "requires": { 210 | "original": "^1.0.0" 211 | } 212 | }, 213 | "follow-redirects": { 214 | "version": "1.14.9", 215 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", 216 | "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" 217 | }, 218 | "form-urlencoded": { 219 | "version": "2.0.9", 220 | "resolved": "https://registry.npmjs.org/form-urlencoded/-/form-urlencoded-2.0.9.tgz", 221 | "integrity": "sha512-fWUzNiOnYa126vFAT6TFXd1mhJrvD8IqmQ9ilZPjkLYQfaRreBr5fIUoOpPlWtqaAG64nzoE7u5zSetifab9IA==" 222 | }, 223 | "from2": { 224 | "version": "2.3.0", 225 | "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", 226 | "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", 227 | "requires": { 228 | "inherits": "^2.0.1", 229 | "readable-stream": "^2.0.0" 230 | } 231 | }, 232 | "fs": { 233 | "version": "0.0.1-security", 234 | "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", 235 | "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" 236 | }, 237 | "get-it": { 238 | "version": "6.0.1", 239 | "resolved": "https://registry.npmjs.org/get-it/-/get-it-6.0.1.tgz", 240 | "integrity": "sha512-tZMsIu4VatXy2RT3M1sStmqn5U0JI6JratUi5kNV/SKpF14+2hssT9J+C51HVO8BPc34fuYVO9W9OzqhbZyQ4A==", 241 | "requires": { 242 | "@sanity/timed-out": "^4.0.2", 243 | "create-error-class": "^3.0.2", 244 | "debug": "^2.6.8", 245 | "decompress-response": "^6.0.0", 246 | "follow-redirects": "^1.2.4", 247 | "form-urlencoded": "^2.0.7", 248 | "into-stream": "^3.1.0", 249 | "is-plain-object": "^2.0.4", 250 | "is-retry-allowed": "^1.1.0", 251 | "is-stream": "^1.1.0", 252 | "nano-pubsub": "^1.0.2", 253 | "object-assign": "^4.1.1", 254 | "parse-headers": "^2.0.4", 255 | "progress-stream": "^2.0.0", 256 | "same-origin": "^0.1.1", 257 | "simple-concat": "^1.0.1", 258 | "tunnel-agent": "^0.6.0", 259 | "url-parse": "^1.1.9" 260 | } 261 | }, 262 | "http-errors": { 263 | "version": "1.8.1", 264 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", 265 | "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", 266 | "requires": { 267 | "depd": "~1.1.2", 268 | "inherits": "2.0.4", 269 | "setprototypeof": "1.2.0", 270 | "statuses": ">= 1.5.0 < 2", 271 | "toidentifier": "1.0.1" 272 | } 273 | }, 274 | "iconv-lite": { 275 | "version": "0.4.24", 276 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 277 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 278 | "requires": { 279 | "safer-buffer": ">= 2.1.2 < 3" 280 | } 281 | }, 282 | "inherits": { 283 | "version": "2.0.4", 284 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 285 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 286 | }, 287 | "into-stream": { 288 | "version": "3.1.0", 289 | "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", 290 | "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=", 291 | "requires": { 292 | "from2": "^2.1.1", 293 | "p-is-promise": "^1.1.0" 294 | } 295 | }, 296 | "is-plain-object": { 297 | "version": "2.0.4", 298 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", 299 | "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", 300 | "requires": { 301 | "isobject": "^3.0.1" 302 | } 303 | }, 304 | "is-retry-allowed": { 305 | "version": "1.2.0", 306 | "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", 307 | "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" 308 | }, 309 | "is-stream": { 310 | "version": "1.1.0", 311 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 312 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 313 | }, 314 | "isarray": { 315 | "version": "1.0.0", 316 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 317 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 318 | }, 319 | "isobject": { 320 | "version": "3.0.1", 321 | "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", 322 | "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" 323 | }, 324 | "make-error": { 325 | "version": "1.3.6", 326 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 327 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" 328 | }, 329 | "media-typer": { 330 | "version": "0.3.0", 331 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 332 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 333 | }, 334 | "mime-db": { 335 | "version": "1.51.0", 336 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", 337 | "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" 338 | }, 339 | "mime-types": { 340 | "version": "2.1.34", 341 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", 342 | "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", 343 | "requires": { 344 | "mime-db": "1.51.0" 345 | } 346 | }, 347 | "mimic-response": { 348 | "version": "3.1.0", 349 | "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", 350 | "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" 351 | }, 352 | "minimist": { 353 | "version": "1.2.5", 354 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 355 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" 356 | }, 357 | "mkdirp": { 358 | "version": "0.5.5", 359 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 360 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 361 | "requires": { 362 | "minimist": "^1.2.5" 363 | } 364 | }, 365 | "ms": { 366 | "version": "2.0.0", 367 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 368 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 369 | }, 370 | "multer": { 371 | "version": "1.4.4", 372 | "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4.tgz", 373 | "integrity": "sha512-2wY2+xD4udX612aMqMcB8Ws2Voq6NIUPEtD1be6m411T4uDH/VtL9i//xvcyFlTVfRdaBsk7hV5tgrGQqhuBiw==", 374 | "requires": { 375 | "append-field": "^1.0.0", 376 | "busboy": "^0.2.11", 377 | "concat-stream": "^1.5.2", 378 | "mkdirp": "^0.5.4", 379 | "object-assign": "^4.1.1", 380 | "on-finished": "^2.3.0", 381 | "type-is": "^1.6.4", 382 | "xtend": "^4.0.0" 383 | } 384 | }, 385 | "nano-pubsub": { 386 | "version": "1.0.2", 387 | "resolved": "https://registry.npmjs.org/nano-pubsub/-/nano-pubsub-1.0.2.tgz", 388 | "integrity": "sha1-NM53b3r5WZFbj3rP6N1rnGbzvek=" 389 | }, 390 | "nanoid": { 391 | "version": "3.3.1", 392 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", 393 | "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==" 394 | }, 395 | "object-assign": { 396 | "version": "4.1.1", 397 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 398 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 399 | }, 400 | "on-finished": { 401 | "version": "2.3.0", 402 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 403 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 404 | "requires": { 405 | "ee-first": "1.1.1" 406 | } 407 | }, 408 | "original": { 409 | "version": "1.0.2", 410 | "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", 411 | "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", 412 | "requires": { 413 | "url-parse": "^1.4.3" 414 | } 415 | }, 416 | "p-is-promise": { 417 | "version": "1.1.0", 418 | "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", 419 | "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=" 420 | }, 421 | "parse-headers": { 422 | "version": "2.0.4", 423 | "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.4.tgz", 424 | "integrity": "sha512-psZ9iZoCNFLrgRjZ1d8mn0h9WRqJwFxM9q3x7iUjN/YT2OksthDJ5TiPCu2F38kS4zutqfW+YdVVkBZZx3/1aw==" 425 | }, 426 | "process-nextick-args": { 427 | "version": "2.0.1", 428 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 429 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 430 | }, 431 | "progress-stream": { 432 | "version": "2.0.0", 433 | "resolved": "https://registry.npmjs.org/progress-stream/-/progress-stream-2.0.0.tgz", 434 | "integrity": "sha1-+sY6Cz0R3qy7CWmrzJOyFLzhntU=", 435 | "requires": { 436 | "speedometer": "~1.0.0", 437 | "through2": "~2.0.3" 438 | } 439 | }, 440 | "qs": { 441 | "version": "6.9.7", 442 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", 443 | "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==" 444 | }, 445 | "querystringify": { 446 | "version": "2.2.0", 447 | "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", 448 | "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" 449 | }, 450 | "raw-body": { 451 | "version": "2.4.3", 452 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", 453 | "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", 454 | "requires": { 455 | "bytes": "3.1.2", 456 | "http-errors": "1.8.1", 457 | "iconv-lite": "0.4.24", 458 | "unpipe": "1.0.0" 459 | } 460 | }, 461 | "readable-stream": { 462 | "version": "2.3.7", 463 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 464 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 465 | "requires": { 466 | "core-util-is": "~1.0.0", 467 | "inherits": "~2.0.3", 468 | "isarray": "~1.0.0", 469 | "process-nextick-args": "~2.0.0", 470 | "safe-buffer": "~5.1.1", 471 | "string_decoder": "~1.1.1", 472 | "util-deprecate": "~1.0.1" 473 | } 474 | }, 475 | "requires-port": { 476 | "version": "1.0.0", 477 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 478 | "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" 479 | }, 480 | "rxjs": { 481 | "version": "6.6.7", 482 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", 483 | "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", 484 | "requires": { 485 | "tslib": "^1.9.0" 486 | } 487 | }, 488 | "safe-buffer": { 489 | "version": "5.1.2", 490 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 491 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 492 | }, 493 | "safer-buffer": { 494 | "version": "2.1.2", 495 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 496 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 497 | }, 498 | "same-origin": { 499 | "version": "0.1.1", 500 | "resolved": "https://registry.npmjs.org/same-origin/-/same-origin-0.1.1.tgz", 501 | "integrity": "sha1-wih9MZJXffUXrLvW0UUanDw5FPU=" 502 | }, 503 | "setprototypeof": { 504 | "version": "1.2.0", 505 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 506 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 507 | }, 508 | "simple-concat": { 509 | "version": "1.0.1", 510 | "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", 511 | "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" 512 | }, 513 | "speedometer": { 514 | "version": "1.0.0", 515 | "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-1.0.0.tgz", 516 | "integrity": "sha1-zWccsGdSwivKM3Di8zREC+T8YuI=" 517 | }, 518 | "statuses": { 519 | "version": "1.5.0", 520 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 521 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 522 | }, 523 | "streamsearch": { 524 | "version": "0.1.2", 525 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", 526 | "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" 527 | }, 528 | "string_decoder": { 529 | "version": "1.1.1", 530 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 531 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 532 | "requires": { 533 | "safe-buffer": "~5.1.0" 534 | } 535 | }, 536 | "through2": { 537 | "version": "2.0.5", 538 | "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", 539 | "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", 540 | "requires": { 541 | "readable-stream": "~2.3.6", 542 | "xtend": "~4.0.1" 543 | } 544 | }, 545 | "toidentifier": { 546 | "version": "1.0.1", 547 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 548 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" 549 | }, 550 | "tslib": { 551 | "version": "1.14.1", 552 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 553 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" 554 | }, 555 | "tunnel-agent": { 556 | "version": "0.6.0", 557 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 558 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 559 | "requires": { 560 | "safe-buffer": "^5.0.1" 561 | } 562 | }, 563 | "type-is": { 564 | "version": "1.6.18", 565 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 566 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 567 | "requires": { 568 | "media-typer": "0.3.0", 569 | "mime-types": "~2.1.24" 570 | } 571 | }, 572 | "typedarray": { 573 | "version": "0.0.6", 574 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 575 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" 576 | }, 577 | "unpipe": { 578 | "version": "1.0.0", 579 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 580 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 581 | }, 582 | "url-parse": { 583 | "version": "1.5.10", 584 | "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", 585 | "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", 586 | "requires": { 587 | "querystringify": "^2.1.1", 588 | "requires-port": "^1.0.0" 589 | } 590 | }, 591 | "util-deprecate": { 592 | "version": "1.0.2", 593 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 594 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 595 | }, 596 | "xtend": { 597 | "version": "4.0.2", 598 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 599 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" 600 | } 601 | } 602 | } 603 | -------------------------------------------------------------------------------- /api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node index.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@sanity/client": "^3.1.0", 14 | "body-parser": "^1.19.2", 15 | "dotenv": "^16.0.0", 16 | "fs": "0.0.1-security", 17 | "multer": "^1.4.4", 18 | "nanoid": "^3.3.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /api/public/1645644077808-Dwayne_Johnson_2014_(cropped).jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/api/public/1645644077808-Dwayne_Johnson_2014_(cropped).jpg -------------------------------------------------------------------------------- /api/public/1645663438209-images.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/api/public/1645663438209-images.png -------------------------------------------------------------------------------- /api/public/1645669245274-3408.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/api/public/1645669245274-3408.jpg -------------------------------------------------------------------------------- /api/public/1645669454692-3408.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/api/public/1645669454692-3408.jpg -------------------------------------------------------------------------------- /api/public/1645669461881-3408.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/api/public/1645669461881-3408.jpg -------------------------------------------------------------------------------- /api/public/1645669784482-young-man-face-cartoon-vector-19116342.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/api/public/1645669784482-young-man-face-cartoon-vector-19116342.jpg -------------------------------------------------------------------------------- /api/public/1645669803751-young-man-face-cartoon-vector-19116342.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/api/public/1645669803751-young-man-face-cartoon-vector-19116342.jpg -------------------------------------------------------------------------------- /api/public/1645669816973-download.jfif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/api/public/1645669816973-download.jfif -------------------------------------------------------------------------------- /api/public/1645669928543-young-man-face-cartoon-vector-19116342.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/api/public/1645669928543-young-man-face-cartoon-vector-19116342.jpg -------------------------------------------------------------------------------- /api/public/1645669938298-young-man-face-cartoon-vector-19116342.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/api/public/1645669938298-young-man-face-cartoon-vector-19116342.jpg -------------------------------------------------------------------------------- /api/public/1645669943936-young-man-face-cartoon-vector-19116342.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/api/public/1645669943936-young-man-face-cartoon-vector-19116342.jpg -------------------------------------------------------------------------------- /api/public/1645670617279-download.jfif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/api/public/1645670617279-download.jfif -------------------------------------------------------------------------------- /api/public/1645671618422-3408.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/api/public/1645671618422-3408.jpg -------------------------------------------------------------------------------- /database/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@sanity/eslint-config-studio" 3 | } 4 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /database/.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | /logs 3 | *.log 4 | 5 | # Coverage directory used by tools like istanbul 6 | /coverage 7 | 8 | # Dependency directories 9 | node_modules 10 | 11 | # Compiled sanity studio 12 | /dist 13 | -------------------------------------------------------------------------------- /database/README.md: -------------------------------------------------------------------------------- 1 | # Sanity Clean Content Studio 2 | 3 | Congratulations, you have now installed the Sanity Content Studio, an open source real-time content editing environment connected to the Sanity backend. 4 | 5 | Now you can do the following things: 6 | 7 | - [Read “getting started” in the docs](https://www.sanity.io/docs/introduction/getting-started?utm_source=readme) 8 | - [Join the community Slack](https://slack.sanity.io/?utm_source=readme) 9 | - [Extend and build plugins](https://www.sanity.io/docs/content-studio/extending?utm_source=readme) 10 | -------------------------------------------------------------------------------- /database/config/.checksums: -------------------------------------------------------------------------------- 1 | { 2 | "#": "Used by Sanity to keep track of configuration file checksums, do not delete or modify!", 3 | "@sanity/default-layout": "bb034f391ba508a6ca8cd971967cbedeb131c4d19b17b28a0895f32db5d568ea", 4 | "@sanity/default-login": "6fb6d3800aa71346e1b84d95bbcaa287879456f2922372bb0294e30b968cd37f", 5 | "@sanity/form-builder": "b38478227ba5e22c91981da4b53436df22e48ff25238a55a973ed620be5068aa", 6 | "@sanity/data-aspects": "d199e2c199b3e26cd28b68dc84d7fc01c9186bf5089580f2e2446994d36b3cb6", 7 | "@sanity/vision": "da5b6ed712703ecd04bf4df560570c668aa95252c6bc1c41d6df1bda9b8b8f60" 8 | } 9 | -------------------------------------------------------------------------------- /database/config/@sanity/data-aspects.json: -------------------------------------------------------------------------------- 1 | { 2 | "listOptions": {} 3 | } 4 | -------------------------------------------------------------------------------- /database/config/@sanity/default-layout.json: -------------------------------------------------------------------------------- 1 | { 2 | "toolSwitcher": { 3 | "order": [], 4 | "hidden": [] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /database/config/@sanity/default-login.json: -------------------------------------------------------------------------------- 1 | { 2 | "providers": { 3 | "mode": "append", 4 | "redirectOnSingle": false, 5 | "entries": [] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /database/config/@sanity/form-builder.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": { 3 | "directUploads": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /database/config/@sanity/vision.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultApiVersion": "2021-10-21" 3 | } 4 | -------------------------------------------------------------------------------- /database/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "instaclonetutorial", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "", 6 | "main": "package.json", 7 | "author": "Tim Ruscica ", 8 | "license": "UNLICENSED", 9 | "scripts": { 10 | "start": "sanity start", 11 | "build": "sanity build" 12 | }, 13 | "keywords": [ 14 | "sanity" 15 | ], 16 | "dependencies": { 17 | "@sanity/base": "^2.27.1", 18 | "@sanity/core": "^2.27.0", 19 | "@sanity/default-layout": "^2.27.1", 20 | "@sanity/default-login": "^2.27.0", 21 | "@sanity/desk-tool": "^2.27.1", 22 | "@sanity/eslint-config-studio": "^2.0.0", 23 | "@sanity/vision": "^2.27.1", 24 | "eslint": "^8.6.0", 25 | "prop-types": "^15.7", 26 | "react": "^17.0", 27 | "react-dom": "^17.0", 28 | "styled-components": "^5.2.0" 29 | }, 30 | "devDependencies": {} 31 | } 32 | -------------------------------------------------------------------------------- /database/plugins/.gitkeep: -------------------------------------------------------------------------------- 1 | User-specific packages can be placed here 2 | -------------------------------------------------------------------------------- /database/sanity.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "project": { 4 | "name": "insta_clone_tutorial" 5 | }, 6 | "api": { 7 | "projectId": "l77o0dei", 8 | "dataset": "production" 9 | }, 10 | "plugins": [ 11 | "@sanity/base", 12 | "@sanity/default-layout", 13 | "@sanity/default-login", 14 | "@sanity/desk-tool" 15 | ], 16 | "env": { 17 | "development": { 18 | "plugins": [ 19 | "@sanity/vision" 20 | ] 21 | } 22 | }, 23 | "parts": [ 24 | { 25 | "name": "part:@sanity/base/schema", 26 | "path": "./schemas/schema" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /database/schemas/post.js: -------------------------------------------------------------------------------- 1 | export default { 2 | title: "Post", 3 | name: "post", 4 | type: "document", 5 | fields: [ 6 | { 7 | title: "Photo", 8 | name: "photo", 9 | type: "image", 10 | }, 11 | { 12 | title: "Description", 13 | name: "description", 14 | type: "text", 15 | }, 16 | { 17 | title: "Created At", 18 | name: "created_at", 19 | type: "datetime", 20 | }, 21 | { 22 | title: "Author", 23 | name: "author", 24 | type: "reference", 25 | to: [{ type: "user" }], 26 | }, 27 | ], 28 | }; 29 | -------------------------------------------------------------------------------- /database/schemas/schema.js: -------------------------------------------------------------------------------- 1 | // First, we must import the schema creator 2 | import createSchema from "part:@sanity/base/schema-creator"; 3 | 4 | // Then import schema types from any plugins that might expose them 5 | import schemaTypes from "all:part:@sanity/base/schema-type"; 6 | import user from "./user.js"; 7 | import post from "./post.js"; 8 | 9 | // Then we give our schema to the builder and provide the result to Sanity 10 | export default createSchema({ 11 | // We name our schema 12 | name: "default", 13 | // Then proceed to concatenate our document type 14 | // to the ones provided by any plugins that are installed 15 | types: schemaTypes.concat([user, post]), 16 | }); 17 | -------------------------------------------------------------------------------- /database/schemas/user.js: -------------------------------------------------------------------------------- 1 | export default { 2 | title: "User", 3 | name: "user", 4 | type: "document", 5 | fields: [ 6 | { 7 | title: "First Name", 8 | name: "first_name", 9 | type: "string", 10 | }, 11 | { 12 | title: "Last Name", 13 | name: "last_name", 14 | type: "string", 15 | }, 16 | { 17 | title: "Username", 18 | name: "username", 19 | type: "string", 20 | }, 21 | { 22 | title: "Photo", 23 | name: "photo", 24 | type: "image", 25 | }, 26 | { 27 | title: "Bio", 28 | name: "bio", 29 | type: "text", 30 | }, 31 | { 32 | title: "Following", 33 | name: "following", 34 | type: "array", 35 | of: [ 36 | { 37 | type: "reference", 38 | to: [{ type: "user" }], 39 | }, 40 | ], 41 | validation: (Rule) => Rule.unique(), 42 | }, 43 | { 44 | title: "Created At", 45 | name: "created_at", 46 | type: "datetime", 47 | }, 48 | ], 49 | }; 50 | -------------------------------------------------------------------------------- /database/static/.gitkeep: -------------------------------------------------------------------------------- 1 | Files placed here will be served by the Sanity server under the `/static`-prefix 2 | -------------------------------------------------------------------------------- /database/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/database/static/favicon.ico -------------------------------------------------------------------------------- /database/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // Note: This config is only used to help editors like VS Code understand/resolve 3 | // parts, the actual transpilation is done by babel. Any compiler configuration in 4 | // here will be ignored. 5 | "include": ["./node_modules/@sanity/base/types/**/*.ts", "./**/*.ts", "./**/*.tsx"] 6 | } 7 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 13 | 14 | The page will reload when you make changes.\ 15 | You may also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!** 35 | 36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. 39 | 40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `npm run build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.2", 7 | "@testing-library/react": "^12.1.3", 8 | "@testing-library/user-event": "^13.5.0", 9 | "bootstrap": "^5.1.3", 10 | "react": "^17.0.2", 11 | "react-bootstrap": "^2.1.2", 12 | "react-dom": "^17.0.2", 13 | "react-router-bootstrap": "^0.26.0", 14 | "react-router-dom": "^6.2.1", 15 | "react-scripts": "5.0.0", 16 | "web-vitals": "^2.1.4" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject" 23 | }, 24 | "eslintConfig": { 25 | "extends": [ 26 | "react-app", 27 | "react-app/jest" 28 | ] 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | }, 42 | "proxy": "http://localhost:3001" 43 | } 44 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/frontend/public/logo192.png -------------------------------------------------------------------------------- /frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/frontend/public/logo512.png -------------------------------------------------------------------------------- /frontend/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /frontend/src/components/AlertDismissible.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { Alert } from "react-bootstrap"; 3 | 4 | export default function AlertDismissible({ message, variant, deleteAlert }) { 5 | const [show, setShow] = useState(true); 6 | 7 | if (show) { 8 | return ( 9 | { 12 | deleteAlert(); 13 | setShow(false); 14 | }} 15 | dismissible 16 | > 17 | {message} 18 | 19 | ); 20 | } else { 21 | return null; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /frontend/src/components/AllPosts.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import { Card } from "react-bootstrap"; 4 | 5 | export default function AllPosts({ user }) { 6 | const [allPostsData, setAllPosts] = useState(null); 7 | 8 | useEffect(() => { 9 | if (!user) { 10 | fetch("/getAllPosts") 11 | .then((res) => res.json()) 12 | .then((data) => setAllPosts(data)) 13 | .catch((err) => console.error(err)); 14 | } else { 15 | fetch("/getPostsOfFollowing?user=" + user) 16 | .then((res) => res.json()) 17 | .then((data) => setAllPosts(data)) 18 | .catch((err) => console.error(err)); 19 | } 20 | }, [user]); 21 | 22 | return ( 23 |
24 | {allPostsData ? ( 25 | allPostsData.map((post, index) => ( 26 |
31 | 32 |
33 | 38 |
39 | 40 | 41 | @{post.username} 42 | 43 | {post.description} 44 | 45 | 46 | {post.created_at} 47 | 48 |
49 |
50 | )) 51 | ) : ( 52 |

No posts to display.

53 | )} 54 |
55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /frontend/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { BrowserRouter, Route, Routes, Link } from "react-router-dom"; 3 | import { Navbar, Container, Nav, Button } from "react-bootstrap"; 4 | import { LinkContainer } from "react-router-bootstrap"; 5 | import AllPosts from "./AllPosts"; 6 | import AlertDismissible from "./AlertDismissible"; 7 | import CreatePost from "./CreatePost"; 8 | import Login from "./Login"; 9 | import Profile from "./Profile"; 10 | import Search from "./Search"; 11 | import SignUp from "./SignUp"; 12 | import "../css/App.css"; 13 | 14 | function App() { 15 | const [alert, setAlert] = useState(null); 16 | const [user, setUser] = useState(""); 17 | 18 | return ( 19 |
20 | 21 | 22 | 23 | 24 | Instagram Clone 25 | 26 | 27 | 28 | 39 | 63 | 64 | 65 | 66 | {alert ? ( 67 | setAlert(null)} /> 68 | ) : null} 69 | 70 | } path="/" exact /> 71 | } 73 | path="/login" 74 | /> 75 | } 77 | path="/sign-up" 78 | /> 79 | } 81 | path="/profile/:username" 82 | /> 83 | } path="/search" /> 84 | } 86 | path="/create-post" 87 | /> 88 | 89 | 90 |
91 | ); 92 | } 93 | 94 | export default App; 95 | -------------------------------------------------------------------------------- /frontend/src/components/CreatePost.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { Button, Form } from "react-bootstrap"; 3 | import { useNavigate } from "react-router-dom"; 4 | import "../css/CreatePost.css"; 5 | 6 | export default function CreatePost({ user, setAlert }) { 7 | const [caption, setCaption] = useState(""); 8 | const [file, setFile] = useState(""); 9 | const navigate = useNavigate(); 10 | 11 | useEffect(() => { 12 | if (!user) { 13 | setAlert({ 14 | variant: "danger", 15 | message: "Please sign in to make a post!", 16 | }); 17 | navigate("/login"); 18 | } 19 | }, [user]); 20 | 21 | function uploadFile(e) { 22 | setFile(e.target.files[0]); 23 | } 24 | 25 | function makePost() { 26 | const formData = new FormData(); 27 | formData.append("user", user); 28 | formData.append("caption", caption); 29 | formData.append("file", file); 30 | const requestOptions = { 31 | method: "POST", 32 | body: formData, 33 | }; 34 | fetch("/createPost", requestOptions) 35 | .then((_res) => { 36 | setAlert({ variant: "success", message: "Post created!" }); 37 | navigate("/"); 38 | }) 39 | .catch((err) => setAlert({ variant: "danger", message: err.message })); 40 | } 41 | 42 | return ( 43 |
44 |
45 | 46 | 50 | 51 | 52 | 53 | 54 | 55 | setCaption(e.target.value)} 59 | /> 60 | 61 |
62 | 70 |
71 |
72 |
73 | ); 74 | } 75 | -------------------------------------------------------------------------------- /frontend/src/components/EditProfile.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { Button, Form, Modal } from "react-bootstrap"; 3 | 4 | export default function EditProfile({ 5 | show, 6 | hideCallback, 7 | user, 8 | setAlert, 9 | profileData, 10 | }) { 11 | const [bio, setBio] = useState(""); 12 | const [firstName, setFirstName] = useState(""); 13 | const [lastName, setLastName] = useState(""); 14 | const [file, setFile] = useState(""); 15 | 16 | useEffect(() => { 17 | setFirstName(profileData.first_name); 18 | setLastName(profileData.last_name); 19 | setBio(profileData.bio); 20 | }, [profileData]); 21 | 22 | function updateProfile() { 23 | const formData = new FormData(); 24 | formData.append("file", file); 25 | formData.append("user", user); 26 | formData.append("first_name", firstName); 27 | formData.append("last_name", lastName); 28 | formData.append("bio", bio); 29 | const requestOptions = { 30 | method: "POST", 31 | body: formData, 32 | }; 33 | fetch("/updateProfile", requestOptions) 34 | .then((res) => res.json()) 35 | .then((data) => { 36 | setAlert({ 37 | variant: "success", 38 | message: "Profile updated successfully.", 39 | }); 40 | hideCallback(); 41 | }) 42 | .catch((err) => { 43 | setAlert({ variant: "danger", message: err.message }); 44 | hideCallback(); 45 | }); 46 | } 47 | 48 | return ( 49 | 50 | 51 | Edit Profile 52 | 53 | 54 |
55 | 56 | {profileData.photo && !file ? ( 57 | 58 | ) : ( 59 | 63 | )} 64 | 65 | 66 | setFile(e.target.files[0])} 70 | /> 71 | 72 | 73 | setFirstName(e.target.value)} 78 | /> 79 | 80 | 81 | setLastName(e.target.value)} 86 | /> 87 | 88 | 89 | setBio(e.target.value)} 94 | /> 95 | 96 |
97 | 100 |
101 |
102 |
103 |
104 | ); 105 | } 106 | -------------------------------------------------------------------------------- /frontend/src/components/Login.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { Form, Button } from "react-bootstrap"; 3 | import { useNavigate, Link } from "react-router-dom"; 4 | 5 | export default function Login({ setAlert, setUser }) { 6 | const [username, setUsername] = useState(""); 7 | const navigate = useNavigate(); 8 | 9 | function handleLogin(e) { 10 | fetch("/getProfile?user=" + username) 11 | .then((res) => res.json()) 12 | .then((data) => { 13 | if (data.length > 0) { 14 | setAlert({ variant: "success", message: "Successfully logged in!" }); 15 | setUser(data[0].username); 16 | navigate("/"); 17 | } else { 18 | setAlert({ 19 | variant: "danger", 20 | message: "No user with that name exists!", 21 | }); 22 | } 23 | }) 24 | .catch((err) => setAlert({ variant: "danger", message: err.message })); 25 | } 26 | 27 | return ( 28 |
29 | 30 | Username 31 | { 35 | setUsername(e.target.value); 36 | }} 37 | /> 38 | 39 | Don't have an account? Sign up here 40 | 41 | 42 | 45 |
46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /frontend/src/components/Profile.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { useParams } from "react-router-dom"; 3 | import { Button } from "react-bootstrap"; 4 | import EditProfile from "./EditProfile"; 5 | import "../css/Profile.css"; 6 | 7 | export default function Profile({ user, setAlert }) { 8 | const [profileData, setProfileData] = useState({}); 9 | const [posts, setPosts] = useState({}); 10 | const [following, setFollowing] = useState(false); 11 | const [owner, setOwner] = useState(false); 12 | const [editing, setEditing] = useState(false); 13 | const params = useParams(); 14 | 15 | useEffect(() => { 16 | updateProfile(params.username); 17 | }, [params.username, user]); 18 | 19 | function updateFollowing(profile) { 20 | for (let follower of profile.followers) { 21 | if (follower.username === user) { 22 | setFollowing(true); 23 | return; 24 | } 25 | } 26 | setFollowing(false); 27 | } 28 | 29 | function updateProfile(username) { 30 | fetch("/getProfile?user=" + username) 31 | .then((res) => res.json()) 32 | .then((data) => { 33 | if (data.length === 0) { 34 | setAlert({ 35 | variant: "danger", 36 | message: "Profile does not exist.", 37 | }); 38 | return; 39 | } 40 | fetch("/getPosts?user=" + username) 41 | .then((res) => res.json()) 42 | .then((posts) => { 43 | setProfileData(data[0]); 44 | setPosts(posts); 45 | updateFollowing(data[0]); 46 | setOwner(user === data[0].username); 47 | }); 48 | }) 49 | .catch((err) => console.error(err)); 50 | } 51 | 52 | function followClick() { 53 | if (owner) return; 54 | 55 | if (!following) { 56 | const requestOptions = { 57 | method: "POST", 58 | headers: { 59 | "Content-Type": "application/json", 60 | }, 61 | body: JSON.stringify({ user: user, id: profileData._id }), 62 | }; 63 | fetch("/addFollower", requestOptions) 64 | .then((res) => res.json()) 65 | .then((_data) => updateProfile(params.username)); 66 | } else { 67 | const requestOptions = { 68 | method: "DELETE", 69 | headers: { 70 | "Content-Type": "application/json", 71 | }, 72 | body: JSON.stringify({ user: user, id: profileData._id }), 73 | }; 74 | fetch("/removeFollower", requestOptions) 75 | .then((res) => res.json()) 76 | .then((_data) => updateProfile(params.username)); 77 | } 78 | } 79 | 80 | function hideEditCallback() { 81 | updateProfile(params.username); 82 | setEditing(false); 83 | } 84 | 85 | if (profileData == {}) return null; 86 | 87 | return ( 88 |
89 | 96 |
97 |

@{profileData.username}

98 |
99 | 107 |
108 |

109 | Posts 110 |

111 |

{posts ? posts.length : 0}

112 |
113 |
114 |

115 | Followers 116 |

117 |

{profileData.followers ? profileData.followers.length : 0}

118 |
119 |
120 |

121 | Following 122 |

123 |

{profileData.following ? profileData.following : 0}

124 |
125 |
126 | {user && !owner ? ( 127 | 133 | ) : null} 134 | {user && owner ? ( 135 | 138 | ) : null} 139 |
140 |
141 |
142 |
143 | 144 | {(profileData.first_name ? profileData.first_name : "") + 145 | " " + 146 | (profileData.last_name ? profileData.last_name : "")} 147 | 148 |
149 |
{profileData.bio}
150 |
151 |
152 |
153 |
154 |
155 | {posts && posts.length > 0 156 | ? posts.map((post, idx) => { 157 | return ; 158 | }) 159 | : null} 160 |
161 |
162 |
163 | ); 164 | } 165 | -------------------------------------------------------------------------------- /frontend/src/components/ProfileItem.js: -------------------------------------------------------------------------------- 1 | import { Button, ListGroup } from "react-bootstrap"; 2 | import { useNavigate } from "react-router-dom"; 3 | 4 | export default function ProfileItem({ 5 | username, 6 | first_name, 7 | last_name, 8 | photo, 9 | followers, 10 | }) { 11 | const navigate = useNavigate(); 12 | 13 | return ( 14 | 15 |
16 |
17 | 21 |
22 |

23 | {username} 24 |

25 |

26 | {(first_name ? first_name : "") + 27 | " " + 28 | (last_name ? last_name : "")} 29 |

30 |
31 |
32 |
33 |

34 | {followers} Followers 35 |

36 | 43 |
44 |
45 |
46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /frontend/src/components/Search.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { Form, Button, ListGroup, Card } from "react-bootstrap"; 3 | import ProfileItem from "./ProfileItem"; 4 | import "../css/Search.css"; 5 | 6 | export default function Search() { 7 | const [searchText, updateSearchText] = useState(""); 8 | const [searchResults, updateSearchResults] = useState([]); 9 | 10 | function search() { 11 | fetch("/searchForUsername?text=" + searchText) 12 | .then((res) => res.json()) 13 | .then((data) => updateSearchResults(data)) 14 | .catch((err) => console.error(err)); 15 | } 16 | 17 | return ( 18 |
19 |
20 |
21 | 22 | updateSearchText(e.target.value)} 25 | placeholder="Search for a username" 26 | /> 27 | 28 | 31 |
32 | {searchResults.length > 0 ? ( 33 |
34 | 35 | 36 | {searchResults.map((item, idx) => ( 37 | 38 | ))} 39 | 40 | 41 |
42 | ) : ( 43 |

No Search Results

44 | )} 45 |
46 |
47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /frontend/src/components/SignUp.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { Form, Button } from "react-bootstrap"; 3 | import { useNavigate } from "react-router-dom"; 4 | 5 | export default function SignUp({ setAlert, setUser }) { 6 | const [firstName, setFirstName] = useState(""); 7 | const [lastName, setLastName] = useState(""); 8 | const [username, setUsername] = useState(""); 9 | const navigate = useNavigate(); 10 | 11 | function createAccount(e) { 12 | const requestOptions = { 13 | method: "POST", 14 | headers: { 15 | "Content-Type": "application/json", 16 | }, 17 | body: JSON.stringify({ 18 | firstName: firstName, 19 | lastName: lastName, 20 | username: username, 21 | }), 22 | }; 23 | fetch("/createUser", requestOptions) 24 | .then((res) => { 25 | return res.json(); 26 | }) 27 | .then((data) => { 28 | setAlert({ 29 | variant: "success", 30 | message: "Your account has been created.", 31 | }); 32 | setUser(data.username); 33 | navigate("/"); 34 | }) 35 | .catch((err) => console.error(err)); 36 | } 37 | 38 | function updateUsername(e) { 39 | setUsername(e.target.value); 40 | } 41 | 42 | function updateFirstName(e) { 43 | setFirstName(e.target.value); 44 | } 45 | 46 | function updateLastName(e) { 47 | setLastName(e.target.value); 48 | } 49 | 50 | return ( 51 |
52 | 53 | Username 54 | 59 | 60 | 61 | First Name 62 | 67 | 68 | 69 | Last Name 70 | 75 | 76 | 79 |
80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /frontend/src/css/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /frontend/src/css/CreatePost.css: -------------------------------------------------------------------------------- 1 | .post-form { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | flex-direction: column; 6 | } 7 | 8 | .create-post { 9 | min-width: 30%; 10 | display: flex; 11 | flex-direction: column; 12 | margin-top: 2em; 13 | } 14 | 15 | .post-button-wrapper { 16 | display: flex; 17 | align-items: center; 18 | justify-content: center; 19 | } 20 | 21 | .post-button { 22 | width: 100%; 23 | } 24 | 25 | .post-image { 26 | width: 300px; 27 | height: 300px; 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/css/Profile.css: -------------------------------------------------------------------------------- 1 | .vertical-data { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | flex-direction: column; 6 | } 7 | 8 | .profile { 9 | display: flex; 10 | flex-direction: row; 11 | justify-content: center; 12 | align-items: center; 13 | flex-wrap: wrap; 14 | } 15 | 16 | .profile-banner { 17 | min-width: 30%; 18 | max-width: 500px; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: flex-start; 22 | justify-content: center; 23 | column-gap: 2em; 24 | } 25 | 26 | .profile-data { 27 | display: flex; 28 | flex-direction: row; 29 | column-gap: 2em; 30 | } 31 | 32 | .follow-button { 33 | display: flex; 34 | align-items: center; 35 | } 36 | 37 | #profile-img { 38 | max-width: 100px; 39 | min-width: 100px; 40 | } 41 | 42 | .profile-bio { 43 | display: flex; 44 | flex-direction: column; 45 | } 46 | 47 | .profile-posts-wrapper { 48 | min-width: 30%; 49 | max-width: 500px; 50 | display: flex; 51 | flex-direction: column; 52 | } 53 | 54 | .profile-posts { 55 | display: grid; 56 | grid-template-columns: 1fr 1fr 1fr; 57 | } 58 | 59 | .profile-posts img { 60 | width: 100%; 61 | min-width: 100%; 62 | } 63 | 64 | .break { 65 | flex-basis: 100%; 66 | height: 2em; 67 | } 68 | 69 | .upload-image { 70 | width: 300px; 71 | height: 300px; 72 | } 73 | -------------------------------------------------------------------------------- /frontend/src/css/Search.css: -------------------------------------------------------------------------------- 1 | .search { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | } 6 | 7 | .search-wrapper { 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | justify-content: center; 12 | min-width: 30%; 13 | max-width: 500px; 14 | } 15 | 16 | .search-field { 17 | width: 100%; 18 | } 19 | 20 | .search-form { 21 | display: flex; 22 | margin-top: 2em; 23 | flex-direction: row; 24 | width: 100%; 25 | } 26 | 27 | .search-results-wrapper { 28 | margin-top: 2em; 29 | flex-direction: column; 30 | width: 100%; 31 | } 32 | -------------------------------------------------------------------------------- /frontend/src/css/SignUp.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techwithtim/Instagram-Clone/919cd9bddfca8af107c36480b67d574dae0b6287/frontend/src/css/SignUp.css -------------------------------------------------------------------------------- /frontend/src/css/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | height: 100vh; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | 16 | #root { 17 | height: 100%; 18 | } 19 | 20 | .fill-parent { 21 | width: 100%; 22 | height: 100%; 23 | display: flex; 24 | flex-direction: column; 25 | } 26 | 27 | .center { 28 | height: 100%; 29 | align-items: center; 30 | display: flex; 31 | flex-direction: column; 32 | } 33 | 34 | .center-form { 35 | display: flex; 36 | align-items: center; 37 | justify-content: center; 38 | flex-direction: column; 39 | height: 100%; 40 | } 41 | -------------------------------------------------------------------------------- /frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./css/index.css"; 4 | import "../node_modules/bootstrap/dist/css/bootstrap.min.css"; 5 | import App from "./components/App"; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById("root") 12 | ); 13 | --------------------------------------------------------------------------------