├── .eslintignore ├── .eslintrc.json ├── .github ├── CODEOWNERS └── workflows │ └── node-ci.yml ├── .gitignore ├── .gitpod.yml ├── .husky └── pre-commit ├── .lintstagedrc.js ├── .prettierignore ├── .prettierrc ├── CONTRIBUTE.md ├── LICENSE.md ├── README.md ├── apps ├── 25--5-clock │ ├── .dockerignore │ ├── .gitignore │ ├── client │ │ ├── .babelrc.json │ │ └── index.jsx │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── styles.css │ ├── rollup.config.js │ ├── sample.env │ └── server.js ├── american-british-translator │ ├── .babelrc │ ├── .dockerignore │ ├── .gitignore │ ├── assertion-analyser.js │ ├── components │ │ ├── american-only.js │ │ ├── american-to-british-spelling.js │ │ ├── american-to-british-titles.js │ │ ├── british-only.js │ │ └── translator.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── index.js │ │ └── style.css │ ├── routes │ │ ├── api.js │ │ └── fcctesting.js │ ├── sample.env │ ├── server.js │ ├── test-runner.js │ ├── tests │ │ ├── 1_unit-tests.js │ │ └── 2_functional-tests.js │ └── views │ │ └── index.html ├── anonymous-message-board │ ├── .dockerignore │ ├── Dockerfile │ ├── assertion-analyser.js │ ├── controllers │ │ ├── replyHandler.js │ │ └── threadHandler.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── style.css │ ├── routes │ │ ├── api.js │ │ └── fcctesting.js │ ├── sample.env │ ├── server.js │ ├── test-runner.js │ ├── tests │ │ └── 2_functional-tests.js │ └── views │ │ ├── board.html │ │ ├── index.html │ │ └── thread.html ├── bar-chart │ ├── .dockerignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── script.js │ │ └── styles.css │ ├── sample.env │ └── server.js ├── build-a-pinterest-clone │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── client │ │ ├── .babelrc.json │ │ ├── ajax-functions.js │ │ ├── index.jsx │ │ ├── navbar.jsx │ │ └── pic.jsx │ ├── config │ │ ├── auth.js │ │ └── passport.js │ ├── controllers │ │ └── appHandler.js │ ├── models │ │ ├── pics.js │ │ └── users.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── style.css │ ├── rollup.config.js │ ├── routes │ │ └── index.js │ ├── sample.env │ ├── server.js │ └── views │ │ └── index.html ├── cash-register │ ├── .dockerignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── script.js │ │ └── styles.css │ ├── sample.env │ └── server.js ├── chart-the-stock-market │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── index.html │ │ ├── main.css │ │ └── stocks.js │ └── sample.env ├── choropleth-map │ ├── .dockerignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── script.js │ │ └── styles.css │ ├── sample.env │ └── server.js ├── drum-machine │ ├── .dockerignore │ ├── .gitignore │ ├── client │ │ ├── .babelrc.json │ │ └── index.jsx │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── styles.css │ ├── rollup.config.js │ ├── sample.env │ └── server.js ├── exercise-tracker │ ├── .dockerignore │ ├── Dockerfile │ ├── models │ │ ├── exercises.js │ │ └── users.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── style.css │ ├── routes │ │ └── api.js │ ├── sample.env │ ├── server.js │ ├── tests │ │ └── 2_functional-tests.js │ └── views │ │ └── index.html ├── file-metadata-microservice │ ├── .dockerignore │ ├── .gitconfig │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── style.css │ ├── sample.env │ ├── server.js │ ├── tests │ │ ├── 2_functional-tests.js │ │ └── camperbot.png │ └── views │ │ └── index.html ├── forum-proxy │ ├── .dockerignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── sample.env │ ├── server.mjs │ ├── tests │ │ └── functional.mjs │ └── views │ │ └── index.html ├── fruit-search │ ├── .dockerignore │ ├── app │ │ ├── controllers │ │ │ └── fruitController.js │ │ ├── fruits.json │ │ └── routes │ │ │ └── fruitRoutes.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── index.html │ │ └── styles.css │ ├── sample.env │ └── server.js ├── heat-map │ ├── .dockerignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── script.js │ │ └── styles.css │ ├── sample.env │ └── server.js ├── image-search-abstraction-layer │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── api.js │ ├── app.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── imageSearch.js │ │ ├── index.html │ │ └── styles.css │ └── sample.env ├── issue-tracker │ ├── .dockerignore │ ├── Dockerfile │ ├── assertion-analyser.js │ ├── models │ │ └── issue.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── style.css │ ├── routes │ │ ├── api.js │ │ └── fcctesting.js │ ├── sample.env │ ├── server.js │ ├── test-runner.js │ ├── tests │ │ └── 2_functional-tests.js │ └── views │ │ ├── index.html │ │ └── issue.html ├── javascript-calculator │ ├── .dockerignore │ ├── .gitignore │ ├── client │ │ ├── .babelrc.json │ │ └── index.jsx │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── styles.css │ ├── rollup.config.js │ ├── sample.env │ └── server.js ├── manage-a-book-trading-club │ ├── .dockerignore │ ├── Dockerfile │ ├── README.md │ ├── app │ │ ├── app.js │ │ ├── controllers │ │ │ ├── booksController.js │ │ │ ├── count-incoming.js │ │ │ ├── requestsController.js │ │ │ ├── sessionsController.js │ │ │ ├── tradesController.js │ │ │ └── usersController.js │ │ ├── helpers │ │ │ ├── books.js │ │ │ └── users.js │ │ ├── models │ │ │ ├── Book.js │ │ │ ├── Request.js │ │ │ ├── Trade.js │ │ │ └── User.js │ │ ├── public │ │ │ ├── css │ │ │ │ ├── bootstrap-social.css │ │ │ │ ├── font-awesome.css │ │ │ │ └── style.css │ │ │ └── vendor │ │ │ │ └── font-awesome │ │ │ │ ├── HELP-US-OUT.txt │ │ │ │ ├── css │ │ │ │ ├── font-awesome.css │ │ │ │ └── font-awesome.min.css │ │ │ │ ├── fonts │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ └── fontawesome-webfont.woff2 │ │ │ │ ├── less │ │ │ │ ├── animated.less │ │ │ │ ├── bordered-pulled.less │ │ │ │ ├── core.less │ │ │ │ ├── fixed-width.less │ │ │ │ ├── font-awesome.less │ │ │ │ ├── icons.less │ │ │ │ ├── larger.less │ │ │ │ ├── list.less │ │ │ │ ├── mixins.less │ │ │ │ ├── path.less │ │ │ │ ├── rotated-flipped.less │ │ │ │ ├── screen-reader.less │ │ │ │ ├── stacked.less │ │ │ │ └── variables.less │ │ │ │ └── scss │ │ │ │ ├── _animated.scss │ │ │ │ ├── _bordered-pulled.scss │ │ │ │ ├── _core.scss │ │ │ │ ├── _fixed-width.scss │ │ │ │ ├── _icons.scss │ │ │ │ ├── _larger.scss │ │ │ │ ├── _list.scss │ │ │ │ ├── _mixins.scss │ │ │ │ ├── _path.scss │ │ │ │ ├── _rotated-flipped.scss │ │ │ │ ├── _screen-reader.scss │ │ │ │ ├── _stacked.scss │ │ │ │ ├── _variables.scss │ │ │ │ └── font-awesome.scss │ │ ├── routes.js │ │ └── views │ │ │ ├── books │ │ │ ├── _add-book-form.pug │ │ │ ├── _select.pug │ │ │ ├── index.pug │ │ │ ├── my-books.pug │ │ │ └── selectBooks.pug │ │ │ ├── error.pug │ │ │ ├── layout.pug │ │ │ ├── login.pug │ │ │ ├── navbar.pug │ │ │ ├── requests │ │ │ ├── _select-gives.pug │ │ │ ├── _select-takes.pug │ │ │ ├── _show.pug │ │ │ ├── index.pug │ │ │ ├── new.pug │ │ │ └── select-books.pug │ │ │ ├── trades │ │ │ ├── _show.pug │ │ │ └── index.pug │ │ │ └── users │ │ │ ├── edit.pug │ │ │ ├── index.pug │ │ │ └── show.pug │ ├── config │ │ ├── passport.js │ │ └── session.js │ ├── db │ │ ├── connection.js │ │ └── seeds.js │ ├── package-lock.json │ ├── package.json │ ├── sample.env │ ├── server.js │ └── test │ │ ├── authorization.test.js │ │ ├── env │ │ ├── mocha.opts │ │ ├── navbar.test.js │ │ ├── requests.test.js │ │ ├── testSetup.js │ │ ├── things.test.js │ │ └── user.test.js ├── markdown-previewer │ ├── .dockerignore │ ├── .gitignore │ ├── client │ │ ├── .babelrc.json │ │ └── index.jsx │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── styles.css │ ├── rollup.config.js │ ├── sample.env │ └── server.js ├── metric-imperial-converter │ ├── .dockerignore │ ├── assertion-analyser.js │ ├── controllers │ │ └── convertHandler.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── style.css │ ├── routes │ │ ├── api.js │ │ └── fcctesting.js │ ├── sample.env │ ├── server.js │ ├── test-runner.js │ ├── tests │ │ ├── 1_unit-tests.js │ │ └── 2_functional-tests.js │ └── views │ │ └── index.html ├── nightlife-coordination-app │ └── README.md ├── p2p-video-chat-application │ ├── .dockerignore │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── index.html │ │ └── style.css │ ├── rollup.config.js │ ├── sample.env │ ├── server.js │ └── src │ │ └── client.js ├── palindrome-checker │ ├── .dockerignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── script.js │ │ └── styles.css │ ├── sample.env │ └── server.js ├── personal-library │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── assertion-analyser.js │ ├── db │ │ └── BookModel.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── client.js │ │ └── style.css │ ├── routes │ │ ├── api.js │ │ └── fcctesting.js │ ├── sample.env │ ├── server.js │ ├── test-runner.js │ ├── tests │ │ └── 2_functional-tests.js │ └── views │ │ └── index.html ├── personal-portfolio │ ├── .dockerignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── styles.css │ ├── sample.env │ └── server.js ├── pokeapi-proxy │ ├── .dockerignore │ ├── Dockerfile │ ├── README.md │ ├── api │ │ ├── pokemon │ │ │ ├── pokemon.handlers.mjs │ │ │ ├── pokemon.middleware.mjs │ │ │ └── pokemon.routes.mjs │ │ └── utils │ │ │ └── cache.mjs │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── index.html │ │ └── style.css │ ├── sample.env │ └── server.mjs ├── product-landing-page │ ├── .dockerignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── styles.css │ ├── sample.env │ └── server.js ├── random-quote-machine │ ├── .dockerignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── script.js │ │ └── styles.css │ ├── sample.env │ └── server.js ├── request-header-parser-microservice │ ├── .dockerignore │ ├── .gitconfig │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── style.css │ ├── sample.env │ ├── server.js │ ├── tests │ │ └── 2_functional-tests.js │ └── views │ │ └── index.html ├── roman-numeral-converter │ ├── .dockerignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── script.js │ │ └── styles.css │ ├── sample.env │ └── server.js ├── rpg-creature-api │ ├── .dockerignore │ ├── README.md │ ├── data │ │ └── creatures.json │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── index.html │ │ └── style.css │ ├── routes │ │ └── api.js │ ├── sample.env │ └── server.js ├── rpg-creature-search-app │ ├── .dockerignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── script.js │ │ └── styles.css │ ├── sample.env │ └── server.js ├── scatterplot-graph │ ├── .dockerignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── script.js │ │ └── styles.css │ ├── sample.env │ └── server.js ├── secure-real-time-multiplayer-game │ ├── .babelrc │ ├── .dockerignore │ ├── assertion-analyser.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── Collectible.mjs │ │ ├── Player.mjs │ │ ├── canvas-data.mjs │ │ ├── controls.mjs │ │ ├── game.mjs │ │ └── style.css │ ├── routes │ │ └── fcctesting.js │ ├── sample.env │ ├── server.js │ ├── test-runner.js │ ├── tests │ │ ├── 1_unit-tests.js │ │ └── 2_functional-tests.js │ └── views │ │ └── index.html ├── stock-price-checker-proxy │ ├── .dockerignore │ ├── Dockerfile │ ├── README.md │ ├── api │ │ └── v1.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── index.html │ │ └── style.css │ ├── sample.env │ └── server.js ├── stock-price-checker │ ├── .dockerignore │ ├── Dockerfile │ ├── assertion-analyser.js │ ├── controllers │ │ └── stockHandler.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── script.js │ │ └── style.css │ ├── routes │ │ ├── api.js │ │ └── fcctesting.js │ ├── sample.env │ ├── server.js │ ├── test-runner.js │ ├── tests │ │ └── 2_functional-tests.js │ └── views │ │ └── index.html ├── sudoku-solver │ ├── .babelrc │ ├── .dockerignore │ ├── .gitignore │ ├── assertion-analyser.js │ ├── controllers │ │ ├── puzzle-strings.js │ │ └── sudoku-solver.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── index.js │ │ └── style.css │ ├── routes │ │ ├── api.js │ │ └── fcctesting.js │ ├── sample.env │ ├── server.js │ ├── test-runner.js │ ├── tests │ │ ├── 1_unit-tests.js │ │ └── 2_functional-tests.js │ └── views │ │ └── index.html ├── survey-form │ ├── .dockerignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── styles.css │ ├── sample.env │ └── server.js ├── technical-documentation-page │ ├── .dockerignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── styles.css │ ├── sample.env │ └── server.js ├── telephone-number-validator │ ├── .dockerignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── script.js │ │ └── styles.css │ ├── sample.env │ └── server.js ├── timestamp-microservice │ ├── .dockerignore │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── style.css │ ├── sample.env │ ├── server.js │ ├── tests │ │ └── 2_functional-tests.js │ └── views │ │ └── index.html ├── treemap-diagram │ ├── .dockerignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── script.js │ │ └── styles.css │ ├── sample.env │ └── server.js ├── tribute-page │ ├── .dockerignore │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── styles.css │ ├── sample.env │ └── server.js ├── twitch-proxy │ ├── .dockerignore │ ├── Dockerfile │ ├── README.md │ ├── config.js │ ├── kill-node-process-cron.js │ ├── package-lock.json │ ├── package.json │ ├── sample.env │ ├── server.js │ ├── static-data │ │ ├── helix │ │ │ ├── games.json │ │ │ ├── index.js │ │ │ ├── streams.json │ │ │ └── users.json │ │ ├── kraken.json │ │ └── update.js │ ├── update-api-key.mjs │ ├── utilities │ │ ├── blacklist.js │ │ ├── legacy-data-handler.js │ │ ├── logger.js │ │ ├── outbound-reqs-limiter.js │ │ ├── req-validate.js │ │ └── static-data-handler.js │ └── views │ │ └── index.html ├── url-shortener-microservice │ ├── .dockerignore │ ├── Dockerfile │ ├── controllers │ │ └── urlHandler.js │ ├── models │ │ ├── counters.js │ │ └── urlEntries.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ └── style.css │ ├── sample.env │ ├── server.js │ ├── tests │ │ └── 2_functional-tests.js │ └── views │ │ └── index.html ├── voting-app │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── app.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── chartjs-2.6.0.min.js │ │ ├── jquery-3.1.0.min.js │ │ ├── main.css │ │ └── main.js │ ├── sample.env │ └── views │ │ ├── footer.ejs │ │ ├── head.ejs │ │ ├── index.ejs │ │ ├── login.ejs │ │ ├── mypolls.ejs │ │ ├── navigation.ejs │ │ ├── newpoll.ejs │ │ ├── signup.ejs │ │ └── singlepoll.ejs └── weather-proxy │ ├── .dockerignore │ ├── Dockerfile │ ├── README.md │ ├── data │ ├── cache.json │ ├── cities.json │ └── imgLinks.json │ ├── images │ ├── 01d.png │ ├── 01n.png │ ├── 02d.png │ ├── 02n.png │ ├── 03d.png │ ├── 03n.png │ ├── 04d.png │ ├── 04n.png │ ├── 09d.png │ ├── 09n.png │ ├── 10d.png │ ├── 10n.png │ ├── 11d.png │ ├── 11n.png │ ├── 13d.png │ ├── 13n.png │ ├── 50d.png │ └── 50n.png │ ├── package-lock.json │ ├── package.json │ ├── sample.env │ ├── server.js │ └── views │ └── index.html ├── docker-compose.yml ├── jest.config.js ├── package-lock.json ├── package.json ├── port-map.json ├── renovate.json ├── sample.env ├── scripts ├── copy-dockerignore.js └── create-caddyfile.js ├── shared.Dockerfile ├── shared.dockerignore └── test ├── jest-utils.js ├── ports.test.js └── twitch-proxy.test.js /.eslintignore: -------------------------------------------------------------------------------- 1 | # Ignore minified libraries 2 | apps/voting-app/public/*.min.js 3 | # Ignore bundles 4 | apps/**/public/bundle.js 5 | # Ignore test runner files? 6 | apps/**/assertion-analyser.js 7 | apps/**/test-runner.js 8 | apps/**/fcctesting.js 9 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------- 2 | # CODEOWNERS - For automated review request for 3 | # high impact files. 4 | # 5 | # Important: The order in this file cascades. 6 | # 7 | # https://help.github.com/articles/about-codeowners 8 | # ------------------------------------------------- 9 | 10 | # ------------------------------------------------- 11 | # All files are owned by dev team 12 | # ------------------------------------------------- 13 | 14 | * @freecodecamp/dev-team 15 | 16 | # --- Owned by none (negate rule above) --- 17 | 18 | *.md 19 | package.json 20 | package-lock.json 21 | 22 | # ------------------------------------------------- 23 | # All files in the root are owned by dev team 24 | # ------------------------------------------------- 25 | 26 | /* @freecodecamp/dev-team 27 | 28 | # --- Owned by none (negate rule above) --- 29 | 30 | /package.json 31 | /package-lock.json 32 | 33 | # ------------------------------------------------- 34 | # Files that need attention from Staff 35 | # ------------------------------------------------- 36 | 37 | # README, LICENSE, etc. 38 | /*.md @freeCodeCamp/staff 39 | -------------------------------------------------------------------------------- /.github/workflows/node-ci.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | ci: 9 | name: Lint / Build / Test 10 | runs-on: ubuntu-latest 11 | 12 | strategy: 13 | matrix: 14 | node-version: [18] 15 | fail-fast: false 16 | 17 | steps: 18 | - name: Checkout Source Files 19 | uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2 20 | 21 | - name: Use Node.js v${{ matrix.node-version }} 22 | uses: actions/setup-node@7c12f8017d5436eb855f1ed4399f037a36fbd9e8 # v2 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | 26 | - name: Install Dependencies 27 | run: npm ci 28 | 29 | - name: Lint Files 30 | run: npm run lint 31 | 32 | - name: Create .env File For CI Builds 33 | run: cp sample.env .env 34 | 35 | - name: Start Projects 36 | run: | 37 | npm run build -- --no-cache 38 | npm start 39 | 40 | - name: Sleep For 30 Seconds 41 | run: sleep 30 42 | 43 | - name: Run Tests 44 | run: npm run test 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.env 2 | node_modules 3 | .DS_Store 4 | .vscode/* 5 | apps/stock-price-checker-proxy/cache/ 6 | apps/twitch-proxy/.data/ 7 | apps/twitch-proxy/.logs/ 8 | Caddyfile 9 | .eslintcache -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: gitpod/workspace-mongodb 2 | ports: 3 | - port: 27017 4 | onOpen: ignore 5 | - port: 50000-50200 6 | onOpen: ignore 7 | # Set to public for tests to have access across ports 8 | # Technically, could be configured with `credentials: true` in fetch calls too 9 | visibility: public 10 | tasks: 11 | - before: | 12 | export DEMO_APPS_DOMAIN=$(gp url) 13 | - init: | 14 | cp sample.env .env 15 | npm ci 16 | mkdir /workspace/log 17 | mongod --fork --dbpath /data/db --logpath /workspace/log/mongod.log 18 | command: | 19 | npm i -g pm2 20 | for file in apps/**/sample.env; do cp $file ${file/sample/}; done; 21 | npm run start 22 | vscode: 23 | extensions: 24 | - dbaeumer.vscode-eslint 25 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore minified libraries 2 | apps/voting-app/public/*.min.js 3 | # Ignore bundles 4 | apps/**/public/bundle.js 5 | # Ignore test runner files? 6 | apps/**/assertion-analyser.js 7 | apps/**/test-runner.js 8 | apps/**/fcctesting.js 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": true, 4 | "singleQuote": true, 5 | "jsxSingleQuote": true, 6 | "tabWidth": 2, 7 | "trailingComma": "none", 8 | "arrowParens": "avoid" 9 | } 10 | -------------------------------------------------------------------------------- /CONTRIBUTE.md: -------------------------------------------------------------------------------- 1 | Note: See https://github.com/freeCodeCamp/demo-projects/issues/403 2 | -------------------------------------------------------------------------------- /apps/25--5-clock/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/25--5-clock/.gitignore: -------------------------------------------------------------------------------- 1 | public/bundle.js -------------------------------------------------------------------------------- /apps/25--5-clock/client/.babelrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { "targets": "> 0.25%, not dead" }], 4 | "@babel/preset-react" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /apps/25--5-clock/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 12 | 13 | FCC: 25 + 5 Clock 14 | 15 | 16 |
17 | 18 | 22 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /apps/25--5-clock/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "25--5-clock", 3 | "version": "0.0.1", 4 | "description": "Front end project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "build": "rollup -c", 9 | "prepare": "npm run build" 10 | }, 11 | "dependencies": { 12 | "dotenv": "16.0.0", 13 | "express": "4.21.2" 14 | }, 15 | "devDependencies": { 16 | "@babel/core": "7.27.4", 17 | "@babel/preset-env": "7.27.2", 18 | "@babel/preset-react": "7.27.1", 19 | "@rollup/plugin-babel": "6.0.4", 20 | "@rollup/plugin-commonjs": "23.0.7", 21 | "@rollup/plugin-node-resolve": "15.3.1", 22 | "@rollup/plugin-replace": "5.0.7", 23 | "@rollup/plugin-terser": "0.2.0", 24 | "rollup": "2.79.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /apps/25--5-clock/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import babel from '@rollup/plugin-babel'; 4 | import replace from '@rollup/plugin-replace'; 5 | import terser from '@rollup/plugin-terser'; 6 | 7 | export default { 8 | input: 'client/index.jsx', 9 | output: { 10 | file: 'public/bundle.js', 11 | format: 'iife', 12 | name: 'Client' 13 | }, 14 | plugins: [ 15 | resolve({ moduleDirectories: ['node_modules'] }), 16 | replace({ 17 | preventAssignment: true, 18 | 'process.env.NODE_ENV': JSON.stringify('production') 19 | }), 20 | commonjs({ 21 | include: /node_modules/ 22 | }), 23 | babel({ babelHelpers: 'bundled' }), 24 | terser() 25 | ] 26 | }; 27 | -------------------------------------------------------------------------------- /apps/25--5-clock/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/25--5-clock/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/american-british-translator/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } 4 | -------------------------------------------------------------------------------- /apps/american-british-translator/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/american-british-translator/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/* 2 | .env 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /apps/american-british-translator/components/american-to-british-titles.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'mr.': 'mr', 3 | 'mrs.': 'mrs', 4 | 'ms.': 'ms', 5 | 'mx.': 'mx', 6 | 'dr.': 'dr', 7 | 'prof.': 'prof' 8 | }; 9 | -------------------------------------------------------------------------------- /apps/american-british-translator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "american-british-english-translator", 3 | "version": "2.0.0", 4 | "description": "Quality Assurance 5: American / British English Translator", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "mocha --timeout 5000 --require @babel/register --recursive --exit --ui tdd tests/" 9 | }, 10 | "dependencies": { 11 | "@babel/core": "7.27.4", 12 | "@babel/preset-env": "7.27.2", 13 | "@babel/register": "7.27.1", 14 | "body-parser": "1.20.3", 15 | "chai": "4.5.0", 16 | "chai-http": "4.4.0", 17 | "cors": "2.8.5", 18 | "dotenv": "14.3.2", 19 | "express": "4.21.2", 20 | "mocha": "9.2.2" 21 | }, 22 | "license": "MIT" 23 | } 24 | -------------------------------------------------------------------------------- /apps/american-british-translator/public/index.js: -------------------------------------------------------------------------------- 1 | const translateHandler = async () => { 2 | const textArea = document.getElementById('text-input'); 3 | const localeArea = document.getElementById('locale-select'); 4 | const errorArea = document.getElementById('error-msg'); 5 | const translatedArea = document.getElementById('translated-sentence'); 6 | 7 | errorArea.innerText = ''; 8 | translatedArea.innerText = ''; 9 | 10 | const data = await fetch('/api/translate', { 11 | method: 'POST', 12 | headers: { 13 | Accept: 'application/json', 14 | 'Content-type': 'application/json' 15 | }, 16 | body: JSON.stringify({ text: textArea.value, locale: localeArea.value }) 17 | }); 18 | 19 | const parsed = await data.json(); 20 | if (parsed.error) { 21 | errorArea.innerText = JSON.stringify(parsed); 22 | return; 23 | } 24 | 25 | translatedArea.innerHTML = parsed.translation; 26 | return; 27 | }; 28 | 29 | document 30 | .getElementById('translate-btn') 31 | .addEventListener('click', translateHandler); 32 | -------------------------------------------------------------------------------- /apps/american-british-translator/public/style.css: -------------------------------------------------------------------------------- 1 | code { 2 | background: #d0d0d5; 3 | } 4 | 5 | .container { 6 | display: flex; 7 | flex-direction: column; 8 | } 9 | 10 | .form-container { 11 | max-width: 95%; 12 | } 13 | 14 | textarea { 15 | width: 100%; 16 | } 17 | 18 | #output-container, 19 | #solution-container, 20 | #text-input, 21 | #locale-select, 22 | input[type='button'] { 23 | font-size: 1.1em; 24 | } 25 | 26 | #output-container { 27 | width: 80%; 28 | display: flex; 29 | flex-direction: column; 30 | } 31 | 32 | #solution-container { 33 | font-weight: bold; 34 | } 35 | 36 | #solution-container { 37 | margin-left: 20px; 38 | } 39 | 40 | #translated-sentence { 41 | margin-top: 10px; 42 | font-weight: normal; 43 | } 44 | 45 | #error-msg { 46 | font-weight: normal; 47 | color: red; 48 | } 49 | 50 | .highlight { 51 | color: green; 52 | } 53 | 54 | #text-input { 55 | padding: 10px; 56 | } 57 | 58 | @media (min-width: 800px) { 59 | .container { 60 | flex-direction: row; 61 | } 62 | 63 | .form-container { 64 | align-self: flex-end; 65 | } 66 | 67 | #output-container { 68 | flex-direction: row; 69 | margin-top: 0; 70 | margin-left: 20px; 71 | } 72 | 73 | #solution-container { 74 | width: 75%; 75 | margin-top: 0; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /apps/american-british-translator/routes/api.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * 4 | * Complete the API routing below 5 | * 6 | * 7 | */ 8 | 9 | 'use strict'; 10 | 11 | const Translator = require('../components/translator.js'); 12 | 13 | module.exports = function (app) { 14 | app.route('/api/translate').post((req, res) => { 15 | let text = req.body.text; 16 | if (!('text' in req.body) || !req.body.locale) { 17 | return res.json({ error: 'Required field(s) missing' }); 18 | } 19 | 20 | if (text === '') { 21 | return res.json({ error: 'No text to translate' }); 22 | } 23 | 24 | let translation; 25 | switch (req.body.locale) { 26 | case 'american-to-british': 27 | translation = Translator.translateAmericanToBritish(text, true); 28 | break; 29 | case 'british-to-american': 30 | translation = Translator.translateBritishToAmerican(text, true); 31 | break; 32 | default: 33 | return res.json({ error: 'Invalid value for locale field' }); 34 | } 35 | 36 | if (translation === text) { 37 | translation = 'Everything looks good to me!'; 38 | } 39 | 40 | return res.json({ text, translation }); 41 | }); 42 | }; 43 | -------------------------------------------------------------------------------- /apps/american-british-translator/sample.env: -------------------------------------------------------------------------------- 1 | # Environment Config 2 | 3 | # store your secrets and config variables in here 4 | # only invited collaborators will be able to see your .env values 5 | 6 | # note: .env is a shell file so there can't be spaces around '=' 7 | 8 | PORT=3000 9 | # NODE_ENV=test -------------------------------------------------------------------------------- /apps/anonymous-message-board/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/anonymous-message-board/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bullseye-slim 2 | 3 | WORKDIR /app 4 | 5 | # Copy over all the files in the project directory to /app early 6 | # for rollup bundling 7 | COPY . . 8 | 9 | ENV PORT=3000 10 | ENV DB_URI=${ANONYMOUS_MESSAGE_BOARD_DB_URI} 11 | 12 | RUN npm ci 13 | 14 | CMD ["npm", "start"] 15 | -------------------------------------------------------------------------------- /apps/anonymous-message-board/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "anonymous-message-board", 3 | "version": "1.0.0", 4 | "description": "Information Security 2: Anonymous Message Board", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "PORT=3005 mocha --exit --ui tdd tests/" 9 | }, 10 | "dependencies": { 11 | "body-parser": "1.20.3", 12 | "chai": "4.5.0", 13 | "chai-http": "4.4.0", 14 | "cors": "2.8.5", 15 | "dotenv": "14.3.2", 16 | "express": "4.21.2", 17 | "helmet": "3.23.3", 18 | "mocha": "9.2.2", 19 | "mongodb": "3.7.4", 20 | "sanitize-html": "2.17.0" 21 | }, 22 | "license": "MIT" 23 | } 24 | -------------------------------------------------------------------------------- /apps/anonymous-message-board/public/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0px; 3 | padding: 0px; 4 | } 5 | 6 | body { 7 | padding: 10px; 8 | font-size: 1.2rem; 9 | } 10 | 11 | .replies { 12 | margin-left: 50px; 13 | } 14 | 15 | .reply { 16 | border: 1px dotted black; 17 | margin: 5px; 18 | padding: 5px; 19 | } 20 | 21 | .newReply { 22 | margin: 10px 5px 10px 5px; 23 | } 24 | 25 | .thread { 26 | border: 2px solid black; 27 | padding: 5px; 28 | margin-bottom: 5px; 29 | } 30 | 31 | .id { 32 | font-size: 10.5px; 33 | } 34 | 35 | #submitNewThread { 36 | margin: 50px; 37 | } 38 | -------------------------------------------------------------------------------- /apps/anonymous-message-board/routes/api.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * 4 | * Complete the API routing below 5 | * 6 | * 7 | */ 8 | 9 | 'use strict'; 10 | 11 | const ThreadHandler = require('../controllers/threadHandler.js'); 12 | const ReplyHandler = require('../controllers/replyHandler.js'); 13 | 14 | module.exports = function (app) { 15 | const threadHandler = new ThreadHandler(); 16 | const replyHandler = new ReplyHandler(); 17 | 18 | app 19 | .route('/api/threads/:board') 20 | .get(threadHandler.threadList) 21 | .post(threadHandler.newThread) 22 | .put(threadHandler.reportThread) 23 | .delete(threadHandler.deleteThread); 24 | 25 | app 26 | .route('/api/replies/:board') 27 | .get(replyHandler.replyList) 28 | .post(replyHandler.newReply) 29 | .put(replyHandler.reportReply) 30 | .delete(replyHandler.deleteReply); 31 | }; 32 | -------------------------------------------------------------------------------- /apps/anonymous-message-board/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | DB_URI="mongodb://localhost:27017/message-board" 3 | # NODE_ENV=test 4 | -------------------------------------------------------------------------------- /apps/bar-chart/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/bar-chart/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 |
United States GDP
11 |
12 |
13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /apps/bar-chart/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bar-chart", 3 | "version": "0.0.1", 4 | "description": "Data Visualization project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "dotenv": "16.0.0", 11 | "express": "4.21.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/bar-chart/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/bar-chart/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.status(200).send({ msg: 'pong' }); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/build-a-pinterest-clone/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/build-a-pinterest-clone/.gitignore: -------------------------------------------------------------------------------- 1 | public/bundle.js -------------------------------------------------------------------------------- /apps/build-a-pinterest-clone/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bullseye-slim 2 | 3 | WORKDIR /app 4 | 5 | # Copy over all the files in the project directory to /app early 6 | # for rollup bundling 7 | COPY . . 8 | 9 | ENV PORT=3000 10 | ENV GITHUB_KEY=${BUILD_A_PINTEREST_CLONE_GITHUB_KEY} 11 | ENV GITHUB_SECRET=${BUILD_A_PINTEREST_CLONE_GITHUB_SECRET} 12 | ENV DB_URI=${BUILD_A_PINTEREST_CLONE_DB_URI} 13 | ENV APP_URL=${BUILD_A_PINTEREST_CLONE_APP_URL} 14 | 15 | RUN npm ci 16 | 17 | CMD ["npm", "start"] 18 | -------------------------------------------------------------------------------- /apps/build-a-pinterest-clone/README.md: -------------------------------------------------------------------------------- 1 | # Build a Pinterest clone 2 | 3 | ## User stories: 4 | 5 | 1. As an unauthenticated user, I can login with Github. 6 | 1. As an authenticated user, I can link to images. 7 | 1. As an authenticated user, I can delete images that I've linked to. 8 | 1. As an authenticated user, I can see a Pinterest-style wall of all the images I've linked to. 9 | 1. As an unauthenticated user, I can browse other users' walls of images. 10 | 1. As an authenticated user, if I upload an image that is broken, it will be replaced by a placeholder image. (can use jQuery broken image detection) 11 | 12 | ## Usage: 13 | 14 | Log in with github and add some nice pic links. 15 | There is a bonus picture liking feature. 16 | 17 | ## Tools: 18 | 19 | - Mongoose 20 | - React (no Flux) 21 | - Bootstrap 22 | - Masonry 23 | - ~~SCSS~~ CSS 24 | - passport, github oAuth 25 | - Babelify express middleware 26 | 27 | ## TODO 28 | 29 | Manage loading states for single images, and like. 30 | 31 | ## Notes 32 | 33 | It supports front-end `require()` via **express-babelify-middleware**, 34 | which allows bundling and transpiling react components and helpers in the server, on client request. 35 | -------------------------------------------------------------------------------- /apps/build-a-pinterest-clone/client/.babelrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { "targets": "> 0.25%, not dead" }], 4 | "@babel/preset-react" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /apps/build-a-pinterest-clone/config/auth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | githubAuth: { 5 | clientID: process.env.GITHUB_KEY, 6 | clientSecret: process.env.GITHUB_SECRET, 7 | callbackURL: process.env.APP_URL + 'auth/github/callback' 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /apps/build-a-pinterest-clone/models/pics.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var mongoose = require('mongoose'); 4 | var Schema = mongoose.Schema; 5 | 6 | var Pic = new Schema({ 7 | url: { type: String, required: true }, 8 | description: String, 9 | date: { type: Date, default: Date.now }, 10 | ownerId: { type: Schema.Types.ObjectId, required: true, ref: 'User' }, 11 | likers: [Schema.Types.ObjectId] 12 | }); 13 | 14 | module.exports = mongoose.model('Pic', Pic); 15 | -------------------------------------------------------------------------------- /apps/build-a-pinterest-clone/models/users.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var mongoose = require('mongoose'); 4 | var Schema = mongoose.Schema; 5 | 6 | var User = new Schema({ 7 | github: { 8 | id: String, 9 | displayName: String, 10 | username: String, 11 | imageUrl: String 12 | } 13 | }); 14 | 15 | module.exports = mongoose.model('User', User); 16 | -------------------------------------------------------------------------------- /apps/build-a-pinterest-clone/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import babel from '@rollup/plugin-babel'; 4 | import replace from '@rollup/plugin-replace'; 5 | import terser from '@rollup/plugin-terser'; 6 | 7 | export default { 8 | input: 'client/index.jsx', 9 | output: { 10 | file: 'public/bundle.js', 11 | format: 'iife', 12 | name: 'Client' 13 | }, 14 | plugins: [ 15 | resolve({ moduleDirectories: ['node_modules'] }), 16 | replace({ 17 | preventAssignment: true, 18 | 'process.env.NODE_ENV': JSON.stringify('production') 19 | }), 20 | commonjs({ 21 | include: /node_modules/ 22 | }), 23 | babel({ babelHelpers: 'bundled' }), 24 | terser() 25 | ] 26 | }; 27 | -------------------------------------------------------------------------------- /apps/build-a-pinterest-clone/sample.env: -------------------------------------------------------------------------------- 1 | GITHUB_KEY="foo" 2 | GITHUB_SECRET="bar" 3 | DB_URI="mongodb://localhost:27017/pinterest" 4 | APP_URL=http://localhost:3000 5 | NODE_ENV=DEV 6 | PORT=3000 7 | -------------------------------------------------------------------------------- /apps/build-a-pinterest-clone/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pic-terest 7 | 13 | 14 | 19 | 20 | 21 |
22 | 26 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /apps/cash-register/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/cash-register/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cash-register", 3 | "version": "0.0.1", 4 | "description": "JavaScript Algorithms and Data Structures project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "dotenv": "16.0.0", 11 | "express": "4.21.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/cash-register/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/cash-register/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/chart-the-stock-market/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/chart-the-stock-market/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/* 2 | .env 3 | node_modules/ 4 | .DS_Store -------------------------------------------------------------------------------- /apps/chart-the-stock-market/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bullseye-slim 2 | 3 | WORKDIR /app 4 | 5 | # Copy over all the files in the project directory to /app early 6 | # for rollup bundling 7 | COPY . . 8 | 9 | ENV PORT=3000 10 | ENV APIKEY=${CHART_THE_STOCK_MARKET_APIKEY} 11 | 12 | RUN npm ci 13 | 14 | CMD ["npm", "start"] 15 | -------------------------------------------------------------------------------- /apps/chart-the-stock-market/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stocks", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "dotenv": "14.3.2", 13 | "express": "4.21.2", 14 | "socket.io": "4.8.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/chart-the-stock-market/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | 3 | # API key for https://www.alphavantage.co/ 4 | APIKEY= -------------------------------------------------------------------------------- /apps/choropleth-map/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/choropleth-map/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FCC: D3 Choropleth Map 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |

United States Educational Attainment

13 |
14 | Percentage of adults age 25 and older with a bachelor's degree or higher 15 | (2010-2014) 16 |
17 | 18 |
19 | Source: 20 | USDA Economic Research Service 24 |
25 |
26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /apps/choropleth-map/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "choropleth-map", 3 | "version": "0.0.1", 4 | "description": "Data Visualization project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "dotenv": "16.0.0", 11 | "express": "4.21.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/choropleth-map/public/styles.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | body { 8 | display: flex; 9 | justify-content: center; 10 | font-family: 'Arial'; 11 | } 12 | 13 | #main { 14 | width: 1000px; 15 | display: flex; 16 | padding-top: 1rem; 17 | flex-direction: column; 18 | align-items: center; 19 | } 20 | 21 | .counties { 22 | fill: none; 23 | } 24 | 25 | .states { 26 | fill: none; 27 | stroke: #fff; 28 | stroke-linejoin: round; 29 | } 30 | 31 | div.tooltip { 32 | position: absolute; 33 | padding: 10px; 34 | font: 12px Arial; 35 | background: rgba(255, 255, 204, 0.9); 36 | box-shadow: 1px 1px 10px rgba(128, 128, 128, 0.6); 37 | border: 0px; 38 | border-radius: 2px; 39 | pointer-events: none; 40 | } 41 | 42 | #title { 43 | font-size: 3.5rem; 44 | } 45 | 46 | #description { 47 | text-align: center; 48 | } 49 | 50 | #source { 51 | align-self: flex-end; 52 | margin-top: 1rem; 53 | } 54 | 55 | a { 56 | text-decoration: none; 57 | } 58 | -------------------------------------------------------------------------------- /apps/choropleth-map/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/choropleth-map/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.status(200).send({ msg: 'pong' }); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/drum-machine/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/drum-machine/.gitignore: -------------------------------------------------------------------------------- 1 | public/bundle.js -------------------------------------------------------------------------------- /apps/drum-machine/client/.babelrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { "targets": "> 0.25%, not dead" }], 4 | "@babel/preset-react" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /apps/drum-machine/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | FCC: Drum Machine 11 | 12 | 13 |
14 | 15 | 19 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /apps/drum-machine/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "drum-machine", 3 | "version": "0.0.1", 4 | "description": "Front end project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "build": "rollup -c", 9 | "prepare": "npm run build" 10 | }, 11 | "dependencies": { 12 | "dotenv": "16.0.0", 13 | "express": "4.21.2" 14 | }, 15 | "devDependencies": { 16 | "@babel/core": "7.27.4", 17 | "@babel/preset-env": "7.27.2", 18 | "@babel/preset-react": "7.27.1", 19 | "@rollup/plugin-babel": "6.0.4", 20 | "@rollup/plugin-commonjs": "23.0.7", 21 | "@rollup/plugin-node-resolve": "15.3.1", 22 | "@rollup/plugin-replace": "5.0.7", 23 | "@rollup/plugin-terser": "0.2.0", 24 | "rollup": "2.79.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /apps/drum-machine/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import babel from '@rollup/plugin-babel'; 4 | import replace from '@rollup/plugin-replace'; 5 | import terser from '@rollup/plugin-terser'; 6 | 7 | export default { 8 | input: 'client/index.jsx', 9 | output: { 10 | file: 'public/bundle.js', 11 | format: 'iife', 12 | name: 'Client' 13 | }, 14 | plugins: [ 15 | resolve({ moduleDirectories: ['node_modules'] }), 16 | replace({ 17 | preventAssignment: true, 18 | 'process.env.NODE_ENV': JSON.stringify('production') 19 | }), 20 | commonjs({ 21 | include: /node_modules/ 22 | }), 23 | babel({ babelHelpers: 'bundled' }), 24 | terser() 25 | ] 26 | }; 27 | -------------------------------------------------------------------------------- /apps/drum-machine/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/drum-machine/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/exercise-tracker/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/exercise-tracker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bullseye-slim 2 | 3 | WORKDIR /app 4 | 5 | # Copy over all the files in the project directory to /app early 6 | # for rollup bundling 7 | COPY . . 8 | 9 | ENV PORT=3000 10 | ENV DB_URI=${EXERCISE_TRACKER_DB_URI} 11 | 12 | RUN npm ci 13 | 14 | CMD ["npm", "start"] 15 | -------------------------------------------------------------------------------- /apps/exercise-tracker/models/exercises.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | 4 | const Exercises = new Schema({ 5 | description: { 6 | type: String, 7 | required: true, 8 | maxlength: [20, 'description too long'] 9 | }, 10 | duration: { 11 | type: Number, 12 | required: true, 13 | min: [1, 'duration too short'] 14 | }, 15 | date: { 16 | type: Date, 17 | default: Date.now 18 | }, 19 | username: String, 20 | userId: { 21 | type: String, 22 | ref: 'Users', 23 | index: true 24 | } 25 | }); 26 | 27 | // add current date to the exercise instance if necessary 28 | Exercises.pre('save', function (next) { 29 | if (!this.date) { 30 | this.date = Date.now(); 31 | } 32 | 33 | next(); 34 | }); 35 | 36 | module.exports = mongoose.model('Exercises', Exercises); 37 | -------------------------------------------------------------------------------- /apps/exercise-tracker/models/users.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | 4 | const Users = new Schema({ 5 | username: { 6 | type: String, 7 | required: true, 8 | unique: true, 9 | maxlength: [30, 'username too long'] 10 | } 11 | }); 12 | 13 | module.exports = mongoose.model('Users', Users); 14 | -------------------------------------------------------------------------------- /apps/exercise-tracker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fcc-exercise-tracker", 3 | "version": "0.1.0", 4 | "description": "A REST API project, part of Free Code Camp's curriculum", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "PORT=3005 mocha --exit --ui tdd tests/" 9 | }, 10 | "dependencies": { 11 | "chai": "4.5.0", 12 | "chai-http": "4.4.0", 13 | "cors": "2.8.5", 14 | "dotenv": "14.3.2", 15 | "express": "4.21.2", 16 | "mocha": "9.2.2", 17 | "mongodb": "3.7.4", 18 | "mongoose": "5.13.22" 19 | }, 20 | "license": "MIT" 21 | } 22 | -------------------------------------------------------------------------------- /apps/exercise-tracker/public/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | font-family: sans-serif; 7 | margin: 25px; 8 | color: #222; 9 | background-color: #f5f5f5; 10 | } 11 | 12 | h1 { 13 | font-weight: bold; 14 | } 15 | 16 | .bold { 17 | font-weight: bold; 18 | } 19 | 20 | p { 21 | max-width: 900px; 22 | } 23 | 24 | form { 25 | margin-bottom: 25px; 26 | padding: 15px; 27 | background-color: #87d37c; 28 | display: inline-block; 29 | width: 100%; 30 | max-width: 340px; 31 | border-radius: 5px; 32 | } 33 | 34 | input { 35 | display: block; 36 | margin-bottom: 10px; 37 | padding: 5px; 38 | width: 100%; 39 | border: 1px solid lightgrey; 40 | border-radius: 3px; 41 | font-size: 16px; 42 | } 43 | 44 | input[type='submit'] { 45 | font-size: 16px; 46 | border-radius: 3px; 47 | background-color: #e4f1fe; 48 | border: 1px solid grey; 49 | box-shadow: 2px 2px #999; 50 | cursor: pointer; 51 | } 52 | 53 | input[type='submit']:hover { 54 | background-color: #fffec4; 55 | } 56 | 57 | code { 58 | font-family: monospace; 59 | font-size: 1.2em; 60 | background-color: #fffec4; 61 | padding: 2px; 62 | } 63 | -------------------------------------------------------------------------------- /apps/exercise-tracker/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | DB_URI="mongodb://localhost:27017/exercise-tracker" 3 | -------------------------------------------------------------------------------- /apps/file-metadata-microservice/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/file-metadata-microservice/.gitconfig: -------------------------------------------------------------------------------- 1 | [core] 2 | excludesfile = /etc/.gitignore-global 3 | -------------------------------------------------------------------------------- /apps/file-metadata-microservice/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "file_metadata", 3 | "version": "0.0.1", 4 | "description": "API project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "PORT=3005 mocha --exit --ui tdd tests/" 9 | }, 10 | "dependencies": { 11 | "chai": "4.5.0", 12 | "chai-http": "4.4.0", 13 | "cors": "2.8.5", 14 | "dotenv": "14.3.2", 15 | "express": "5.0.0-beta.3", 16 | "mocha": "9.2.2", 17 | "multer": "1.4.4" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://hyperdev.com/#!/project/welcome-project" 22 | }, 23 | "keywords": [ 24 | "node", 25 | "hyperdev", 26 | "express" 27 | ], 28 | "license": "MIT" 29 | } 30 | -------------------------------------------------------------------------------- /apps/file-metadata-microservice/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Roboto', sans-serif; 3 | font-size: 16px; 4 | color: #222; 5 | background-color: #ecf0f1; 6 | text-align: center; 7 | } 8 | 9 | .container { 10 | padding: 0; 11 | margin-top: 40px; 12 | } 13 | 14 | .footer { 15 | margin-top: 60px; 16 | } 17 | 18 | a { 19 | color: #2574a9; 20 | } 21 | 22 | input { 23 | display: block; 24 | position: relative; 25 | margin: 10px auto; 26 | } 27 | input#button { 28 | width: 230px; 29 | } 30 | .view { 31 | position: relative; 32 | margin: auto; 33 | margin-top: 40px; 34 | border: 1px solid #aaa; 35 | padding: 20px; 36 | width: 60%; 37 | min-width: 400px; 38 | } 39 | -------------------------------------------------------------------------------- /apps/file-metadata-microservice/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/file-metadata-microservice/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('dotenv').config(); 4 | var express = require('express'); 5 | var cors = require('cors'); 6 | 7 | var multer = require('multer'); 8 | // here on HyperDev the fs is read only, 9 | // You have to upload the file to memory 10 | var storage = multer.memoryStorage(); 11 | var upload = multer({ storage: storage }); 12 | 13 | var app = express(); 14 | 15 | app.use(cors()); 16 | app.use('/public', express.static(process.cwd() + '/public')); 17 | 18 | app.get('/', function (req, res) { 19 | res.sendFile(process.cwd() + '/views/index.html'); 20 | }); 21 | 22 | app.get('/status/ping', (req, res) => { 23 | res.send({ msg: 'pong' }).status(200); 24 | }); 25 | 26 | // using 'multer' middleware... 27 | app.post('/api/fileanalyse', upload.single('upfile'), function (req, res) { 28 | res.json({ 29 | name: req.file.originalname, 30 | type: req.file.mimetype, 31 | size: req.file.size 32 | }); 33 | }); 34 | 35 | // 404-NOT FOUND Middleware 36 | app.use(function (req, res) { 37 | res.status(404); 38 | res.type('txt').send('Not found'); 39 | }); 40 | 41 | const portNum = process.env.PORT || 3000; 42 | 43 | app.listen(portNum, function () { 44 | console.log('Listening on port ' + portNum); 45 | }); 46 | 47 | module.exports = app; 48 | -------------------------------------------------------------------------------- /apps/file-metadata-microservice/tests/2_functional-tests.js: -------------------------------------------------------------------------------- 1 | const chaiHttp = require('chai-http'); 2 | const chai = require('chai'); 3 | const assert = chai.assert; 4 | const server = require('../server'); 5 | 6 | chai.use(chaiHttp); 7 | 8 | suite('Functional Tests', function () { 9 | this.timeout(5000); 10 | test('File upload', function (done) { 11 | chai 12 | .request(server) 13 | .post('/api/fileanalyse') 14 | .attach('upfile', './tests/camperbot.png') 15 | .end(function (err, res) { 16 | assert.equal(res.status, 200); 17 | assert.equal(res.body.name, 'camperbot.png'); 18 | assert.equal(res.body.type, 'image/png'); 19 | assert.equal(res.body.size, 20446); 20 | done(); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /apps/file-metadata-microservice/tests/camperbot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/file-metadata-microservice/tests/camperbot.png -------------------------------------------------------------------------------- /apps/file-metadata-microservice/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | File Metadata 6 | 11 | 16 | 17 | 18 | 19 | 20 |
21 |

API Project: File Metadata Microservice

22 |

Usage:

23 |

Please Upload a File ...

24 |
25 |

26 |
31 | 32 | 33 |
34 |
35 |
36 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /apps/forum-proxy/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/forum-proxy/README.md: -------------------------------------------------------------------------------- 1 | # Forum Proxy 2 | -------------------------------------------------------------------------------- /apps/forum-proxy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fcc-forum-proxy", 3 | "version": "0.0.1", 4 | "description": "A proxy for the fCC forum", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.mjs", 8 | "test": "PORT=3005 mocha --exit --ui tdd tests/" 9 | }, 10 | "dependencies": { 11 | "chai": "4.5.0", 12 | "chai-http": "4.4.0", 13 | "cors": "2.8.5", 14 | "dotenv": "14.3.2", 15 | "express": "4.21.2", 16 | "mocha": "9.2.2", 17 | "node-fetch": "3.3.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/forum-proxy/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/forum-proxy/server.mjs: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'url'; 2 | import { dirname, join } from 'path'; 3 | 4 | import 'dotenv/config'; 5 | import cors from 'cors'; 6 | import fetch from 'node-fetch'; 7 | import express from 'express'; 8 | 9 | const app = express(); 10 | 11 | const __dirname = dirname(fileURLToPath(new URL(import.meta.url))); 12 | 13 | app.get('/', (req, res) => { 14 | res.sendFile(join(__dirname, './views/index.html')); 15 | }); 16 | 17 | app.get('/status/ping', (req, res) => { 18 | res.send({ msg: 'pong' }).status(200); 19 | }); 20 | 21 | app.use(cors({ optionsSuccessStatus: 200 })); 22 | app.get('/latest', (req, res, next) => { 23 | fetch('https://forum.freecodecamp.org/latest.json') 24 | .then(response => response.json()) 25 | .then(data => { 26 | res.json(data); 27 | }) 28 | .catch(err => next(err)); 29 | }); 30 | 31 | app.use((req, res) => res.status(404).send('not found')); 32 | 33 | app.use((err, req, res) => res.status(500).json(err)); 34 | 35 | const portNum = process.env.PORT || 3000; 36 | 37 | app.listen(portNum, function () { 38 | console.log(`Your app is listening on port ${portNum}`); 39 | }); 40 | 41 | export default app; 42 | -------------------------------------------------------------------------------- /apps/forum-proxy/tests/functional.mjs: -------------------------------------------------------------------------------- 1 | import chaiHttp from 'chai-http'; 2 | import chai from 'chai'; 3 | 4 | import server from '../server.mjs'; 5 | 6 | const assert = chai.assert; 7 | 8 | chai.use(chaiHttp); 9 | 10 | suite('Functional Tests', function () { 11 | this.timeout(5000); 12 | test('Get forum data', function (done) { 13 | chai 14 | .request(server) 15 | .get('/latest') 16 | .end(function (err, res) { 17 | assert.equal(res.status, 200); 18 | assert.isArray(res.body.users); 19 | done(); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /apps/forum-proxy/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | fcc forum proxy 5 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |

18 | https://forum-proxy.freecodecamp.rocks/latest => 19 | https://forum.freecodecamp.org/latest.json 20 |

21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /apps/fruit-search/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/fruit-search/app/controllers/fruitController.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | const getFruits = () => { 4 | const data = fs.readFileSync('app/fruits.json', 'utf8'); 5 | return JSON.parse(data); 6 | }; 7 | 8 | const fetchFruits = (req, res) => { 9 | try { 10 | const fruits = getFruits(); 11 | const query = req.query.q ? req.query.q.toLowerCase() : ''; 12 | 13 | const filteredFruits = fruits.filter(fruit => 14 | fruit.name.toLowerCase().includes(query) 15 | ); 16 | 17 | res.json(filteredFruits); 18 | } catch (error) { 19 | res.status(500).json({ error: 'Failed to load fruits' }); 20 | } 21 | }; 22 | 23 | module.exports = { fetchFruits }; 24 | -------------------------------------------------------------------------------- /apps/fruit-search/app/routes/fruitRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const { fetchFruits } = require('../controllers/fruitController'); 3 | 4 | const router = express.Router(); 5 | 6 | router.get('/fruits', fetchFruits); 7 | 8 | module.exports = router; 9 | -------------------------------------------------------------------------------- /apps/fruit-search/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fruit-search", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "cors": "^2.8.5", 14 | "dotenv": "^16.4.7", 15 | "express": "^4.21.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /apps/fruit-search/public/styles.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | font-size: 16px; 4 | } 5 | 6 | *, 7 | *:before, 8 | *:after { 9 | box-sizing: inherit; 10 | } 11 | 12 | body { 13 | font-family: Lato, sans-serif; 14 | background-color: #0a0a23; 15 | } 16 | 17 | main { 18 | margin: auto; 19 | display: flex; 20 | flex-direction: column; 21 | justify-content: center; 22 | align-items: center; 23 | } 24 | 25 | h1, 26 | h2, 27 | h3, 28 | p, 29 | code, 30 | pre, 31 | ul { 32 | color: #fff; 33 | } 34 | 35 | .usage { 36 | padding: 15px; 37 | background-color: #0a0a23; 38 | max-width: 95%; 39 | border: 1px solid #fff; 40 | } 41 | 42 | p { 43 | margin-bottom: 1em; 44 | line-height: 1.5rem; 45 | } 46 | 47 | code { 48 | padding: 1px 4px; 49 | overflow-wrap: anywhere; 50 | background-color: #3b3b4f; 51 | color: #dfdfe2; 52 | } 53 | 54 | ul { 55 | margin: 1em; 56 | padding-left: 20px; 57 | } 58 | 59 | li { 60 | margin-bottom: 0.5em; 61 | } 62 | 63 | @media (min-width: 768px) { 64 | .usage { 65 | max-width: 70%; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /apps/fruit-search/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 -------------------------------------------------------------------------------- /apps/fruit-search/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const cors = require('cors'); 4 | const path = require('path'); 5 | const fruitRoutes = require('./app/routes/fruitRoutes'); 6 | 7 | const app = express(); 8 | app.use(cors()); 9 | const PORT = process.env.PORT || 3000; 10 | 11 | app.use(express.static(path.join(process.cwd(), 'public'))); 12 | 13 | app.get('/status/ping', (req, res) => { 14 | res.send({ msg: 'pong' }).status(200); 15 | }); 16 | 17 | app.use('/api', fruitRoutes); 18 | 19 | app.listen(PORT, () => { 20 | console.log(`Server is running on port ${PORT}`); 21 | }); 22 | -------------------------------------------------------------------------------- /apps/heat-map/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/heat-map/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | FCC: D3 Heat Map 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /apps/heat-map/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "heat-map", 3 | "version": "0.0.1", 4 | "description": "Data Visualization project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "dotenv": "16.0.0", 11 | "express": "4.21.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/heat-map/public/styles.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Open+Sans); 2 | body { 3 | font-family: 'Open Sans', sans-serif; 4 | font-weight: 400; 5 | color: black; 6 | } 7 | body .d3-tip { 8 | background-color: rgba(0, 0, 0, 0.8); 9 | border-radius: 6px; 10 | color: white; 11 | text-align: center; 12 | padding: 0.5em; 13 | } 14 | section { 15 | width: auto; 16 | overflow: visible; 17 | margin-left: auto; 18 | margin-right: auto; 19 | font-size: 16px; 20 | } 21 | section heading { 22 | text-align: center; 23 | } 24 | section svg { 25 | stroke-width: 1; 26 | stroke: black; 27 | } 28 | section .y-axis text, 29 | section .x-axis text, 30 | section .legend text { 31 | stroke-width: 0; 32 | } 33 | section .map { 34 | stroke-width: 0; 35 | } 36 | section .map rect:hover { 37 | stroke-width: 2; 38 | } 39 | -------------------------------------------------------------------------------- /apps/heat-map/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/heat-map/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.status(200).send({ msg: 'pong' }); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/image-search-abstraction-layer/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/image-search-abstraction-layer/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/* 2 | .env 3 | node_modules/ 4 | .DS_Store -------------------------------------------------------------------------------- /apps/image-search-abstraction-layer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bullseye-slim 2 | 3 | WORKDIR /app 4 | 5 | # Copy over all the files in the project directory to /app early 6 | # for rollup bundling 7 | COPY . . 8 | 9 | ENV PORT=3000 10 | ENV MONGO_URI=${IMAGE_SEARCH_ABSTRACTION_LAYER_MONGO_URI} 11 | ENV CSEID=${IMAGE_SEARCH_ABSTRACTION_LAYER_CSEID} 12 | ENV APIKEY=${IMAGE_SEARCH_ABSTRACTION_LAYER_APIKEY} 13 | 14 | RUN npm ci 15 | 16 | CMD ["npm", "start"] 17 | -------------------------------------------------------------------------------- /apps/image-search-abstraction-layer/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | require('dotenv').config(); 3 | var mongo = require('mongodb').MongoClient; 4 | 5 | var app = express(); 6 | 7 | app.use(express.static('public')); 8 | 9 | const api = require('./api.js'); 10 | 11 | var portNum = process.env.PORT || 3000; 12 | 13 | mongo.connect( 14 | process.env.MONGO_URI, 15 | { useUnifiedTopology: true }, 16 | function (err, client) { 17 | if (err) throw err; 18 | var db = client.db(); 19 | console.log('Connected to MongoDB'); 20 | api(app, db); 21 | app.listen(portNum, () => { 22 | console.log(`Listening on port ${portNum}`); 23 | }); 24 | } 25 | ); 26 | -------------------------------------------------------------------------------- /apps/image-search-abstraction-layer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "imagesearch", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node app.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/moT01/FCC-Image-Search-Abstraction-Layer.git" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "dotenv": "14.3.2", 17 | "express": "4.21.2", 18 | "google-images": "2.1.0", 19 | "moment": "2.30.1", 20 | "mongodb": "3.7.4" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /apps/image-search-abstraction-layer/public/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 50px; 3 | } 4 | 5 | hr { 6 | margin: 50px 0; 7 | } 8 | -------------------------------------------------------------------------------- /apps/image-search-abstraction-layer/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | 3 | MONGO_URI="mongodb://localhost:27017/image-search-abstraction-layer" 4 | 5 | # keys for google-images package: 6 | # https://www.npmjs.com/package/google-images 7 | CSEID= 8 | APIKEY= 9 | -------------------------------------------------------------------------------- /apps/issue-tracker/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/issue-tracker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bullseye-slim 2 | 3 | WORKDIR /app 4 | 5 | # Copy over all the files in the project directory to /app early 6 | # for rollup bundling 7 | COPY . . 8 | 9 | ENV PORT=3000 10 | ENV DB=${ISSUE_TRACKER_DB} 11 | 12 | RUN npm ci 13 | 14 | CMD ["npm", "start"] 15 | -------------------------------------------------------------------------------- /apps/issue-tracker/models/issue.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const IssueSchema = new mongoose.Schema( 4 | { 5 | project: { type: String, required: true, index: true }, 6 | issue_title: { type: String, required: true }, 7 | issue_text: { type: String, required: true }, 8 | created_by: { type: String, required: true }, 9 | assigned_to: { type: String, default: '' }, 10 | status_text: { type: String, default: '' }, 11 | open: { type: Boolean, default: true } 12 | }, 13 | { 14 | timestamps: { 15 | createdAt: 'created_on', 16 | updatedAt: 'updated_on' 17 | } 18 | } 19 | ); 20 | 21 | // Hide project and __v fields from JSON 22 | // Ref: https://stackoverflow.com/a/17063594/1420506 23 | IssueSchema.set('toJSON', { 24 | transform: function (doc, ret) { 25 | delete ret.project; 26 | delete ret.__v; 27 | return ret; 28 | } 29 | }); 30 | 31 | const IssueModel = mongoose.model('Issue', IssueSchema); 32 | 33 | exports.IssueModel = IssueModel; 34 | -------------------------------------------------------------------------------- /apps/issue-tracker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fcc-issue-tracker", 3 | "version": "1.0.0", 4 | "description": "Issue Tracker Boilerplate", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "PORT=3005 mocha --exit --ui tdd tests/" 9 | }, 10 | "dependencies": { 11 | "body-parser": "1.20.3", 12 | "chai": "4.5.0", 13 | "chai-http": "4.4.0", 14 | "cors": "2.8.5", 15 | "dotenv": "14.3.2", 16 | "express": "4.21.2", 17 | "mocha": "9.2.2", 18 | "mongodb": "3.7.4", 19 | "mongoose": "5.13.22" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/freeCodeCamp/boilerplate-project-issuetracker" 24 | }, 25 | "keywords": [ 26 | "node", 27 | "hyperdev", 28 | "express" 29 | ], 30 | "license": "MIT" 31 | } 32 | -------------------------------------------------------------------------------- /apps/issue-tracker/public/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0px; 3 | padding: 0px; 4 | } 5 | 6 | body { 7 | padding: 10px; 8 | } 9 | 10 | .issue { 11 | margin: 20px; 12 | padding: 5px; 13 | width: 60%; 14 | border: 2px solid black; 15 | } 16 | 17 | .closed { 18 | background-color: lightgrey; 19 | } 20 | 21 | .id { 22 | font-size: 10.5px; 23 | } 24 | -------------------------------------------------------------------------------- /apps/issue-tracker/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | DB="mongodb://localhost:27017/issue-tracker" 3 | # NODE_ENV=test 4 | -------------------------------------------------------------------------------- /apps/javascript-calculator/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/javascript-calculator/.gitignore: -------------------------------------------------------------------------------- 1 | public/bundle.js -------------------------------------------------------------------------------- /apps/javascript-calculator/client/.babelrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { "targets": "> 0.25%, not dead" }], 4 | "@babel/preset-react" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /apps/javascript-calculator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | FCC: Javascript Calculator 7 | 8 | 9 |
10 | 11 | 15 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /apps/javascript-calculator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javascript-calculator", 3 | "version": "0.0.1", 4 | "description": "Front end project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "build": "rollup -c", 9 | "prepare": "npm run build" 10 | }, 11 | "dependencies": { 12 | "dotenv": "16.0.0", 13 | "express": "4.21.2" 14 | }, 15 | "devDependencies": { 16 | "@babel/core": "7.27.4", 17 | "@babel/preset-env": "7.27.2", 18 | "@babel/preset-react": "7.27.1", 19 | "@rollup/plugin-babel": "6.0.4", 20 | "@rollup/plugin-commonjs": "23.0.7", 21 | "@rollup/plugin-node-resolve": "15.3.1", 22 | "@rollup/plugin-replace": "5.0.7", 23 | "@rollup/plugin-terser": "0.2.0", 24 | "rollup": "2.79.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /apps/javascript-calculator/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import babel from '@rollup/plugin-babel'; 4 | import replace from '@rollup/plugin-replace'; 5 | import terser from '@rollup/plugin-terser'; 6 | 7 | export default { 8 | input: 'client/index.jsx', 9 | output: { 10 | file: 'public/bundle.js', 11 | format: 'iife', 12 | name: 'Client' 13 | }, 14 | plugins: [ 15 | resolve({ moduleDirectories: ['node_modules'] }), 16 | replace({ 17 | preventAssignment: true, 18 | 'process.env.NODE_ENV': JSON.stringify('production') 19 | }), 20 | commonjs({ 21 | include: /node_modules/ 22 | }), 23 | babel({ babelHelpers: 'bundled' }), 24 | terser() 25 | ] 26 | }; 27 | -------------------------------------------------------------------------------- /apps/javascript-calculator/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/javascript-calculator/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bullseye-slim 2 | 3 | WORKDIR /app 4 | 5 | # Copy over all the files in the project directory to /app early 6 | # for rollup bundling 7 | COPY . . 8 | 9 | ENV PORT=3000 10 | ENV GITHUB_KEY=${MANAGE_A_BOOK_TRADING_CLUB_GITHUB_KEY} 11 | ENV GITHUB_SECRET=${MANAGE_A_BOOK_TRADING_CLUB_GITHUB_SECRET} 12 | ENV MONGO_URI=${MANAGE_A_BOOK_TRADING_CLUB_MONGO_URI} 13 | ENV APP_URL=${MANAGE_A_BOOK_TRADING_CLUB_APP_URL} 14 | 15 | RUN npm ci 16 | 17 | CMD ["npm", "start"] 18 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/README.md: -------------------------------------------------------------------------------- 1 | # Manage a Book Trading Club 2 | 3 | ## User stories: 4 | 5 | 1. I can view all books posted by every user. 6 | 1. I can add a new book. 7 | 1. I can update my settings to store my full name, city, and state. 8 | 1. I can propose a trade and wait for the other user to accept the trade. 9 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/controllers/count-incoming.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function fromBooks(books, user) { 4 | if (!user) return 0; 5 | return 5; 6 | } 7 | 8 | function fromRequests(requests, user) { 9 | if (!user) return 0; 10 | const requestsForUsersBooks = requests.filter(request => 11 | request.takes.map(book => book.owner.id).includes(user.id) 12 | ); 13 | return requestsForUsersBooks.length; 14 | } 15 | 16 | module.exports = { 17 | fromBooks, 18 | fromRequests 19 | }; 20 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/controllers/tradesController.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // const Request = require('../models/Request'); 4 | // const Book = require('../models/Book'); 5 | const Trade = require('../models/Trade'); 6 | 7 | function index(req, res, next) { 8 | Trade.find({}) 9 | .populate('requester accepter') 10 | .sort({ _id: -1 }) 11 | .then(trades => { 12 | res.render('trades/index', { 13 | trades, 14 | title: 'All Trades', 15 | user: req.user, 16 | active: 'trades', 17 | messages: req.flash('info') 18 | }); 19 | }) 20 | .catch(next); 21 | } 22 | 23 | function create(req, res, next) { 24 | const { requester, gives, takes } = req.locals.request; 25 | 26 | Trade.create({ 27 | // eslint-disable-next-line no-underscore-dangle 28 | accepter: req.user._id, 29 | requester, 30 | gives, 31 | takes 32 | }) 33 | .then(trade => { 34 | if (!trade) next('Could not create trade'); 35 | delete req.locals.request; 36 | req.flash('info', { success: 'Trade successfuly created' }); 37 | res.redirect('/trades'); 38 | }) 39 | .catch(next); 40 | } 41 | 42 | module.exports = { 43 | index, 44 | create 45 | }; 46 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/helpers/books.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Book = require('../models/Book'); 4 | 5 | module.exports = (function BookHelpers() { 6 | function handleCreatedRequest(created) { 7 | return Book.update( 8 | { _id: { $in: created.takes } }, 9 | { $push: { requests: created._id } }, 10 | { multi: true } 11 | ); 12 | } 13 | 14 | return { handleCreatedRequest }; 15 | })(); 16 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/helpers/users.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const User = require('../models/User'); 4 | 5 | module.exports = (function UserHelpers() { 6 | function handleCreatedRequest(created) { 7 | return User.update( 8 | { books: { $in: created.takes } }, 9 | { $push: { receivedRequests: created._id } }, 10 | { multi: true } 11 | ); 12 | } 13 | 14 | return { handleCreatedRequest }; 15 | })(); 16 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/models/Book.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mongoose = require('mongoose'); 4 | 5 | const Schema = mongoose.Schema; 6 | 7 | const bookSchema = new Schema({ 8 | name: { 9 | type: String, 10 | required: true 11 | }, 12 | description: String, 13 | owner: { 14 | type: Schema.Types.ObjectId, 15 | ref: 'User', 16 | required: true 17 | }, 18 | requests: [ 19 | { 20 | type: Schema.Types.ObjectId, 21 | ref: 'Request', 22 | required: true 23 | } 24 | ] 25 | }); 26 | 27 | // bookSchema.methods.getTakeRequests = function getTakeRequests() { 28 | // // eslint-disable-next-line no-underscore-dangle 29 | // return Request.find({ takes: this._id }); 30 | // }; 31 | // 32 | // bookSchema.methods.getGiveRequests = function getGiveRequests() { 33 | // // eslint-disable-next-line no-underscore-dangle 34 | // return Request.find({ gives: this._id }); 35 | // }; 36 | 37 | // Mocha --watch breaks the require cache and mongoose will throw 38 | // an error trying to initialize an model twice, so ignore this error 39 | try { 40 | mongoose.model('Book', bookSchema); 41 | // eslint-disable-next-line no-empty 42 | } catch (err) {} 43 | 44 | module.exports = mongoose.model('Book'); 45 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/models/Request.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mongoose = require('mongoose'); 4 | 5 | const Schema = mongoose.Schema; 6 | 7 | const requestSchema = new Schema({ 8 | description: String, 9 | requester: { 10 | type: Schema.Types.ObjectId, 11 | ref: 'User', 12 | required: true 13 | }, 14 | takes: [ 15 | { 16 | type: Schema.Types.ObjectId, 17 | ref: 'Book', 18 | required: true 19 | } 20 | ], 21 | gives: [ 22 | { 23 | type: Schema.Types.ObjectId, 24 | ref: 'Book', 25 | required: true 26 | } 27 | ] 28 | }); 29 | 30 | try { 31 | mongoose.model('Request', requestSchema); 32 | // eslint-disable-next-line no-empty 33 | } catch (err) {} 34 | 35 | module.exports = mongoose.model('Request'); 36 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/models/Trade.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mongoose = require('mongoose'); 4 | 5 | const Schema = mongoose.Schema; 6 | 7 | const tradeSchema = new Schema({ 8 | description: String, 9 | accepter: { 10 | type: Schema.Types.ObjectId, 11 | ref: 'User', 12 | required: true 13 | }, 14 | requester: { 15 | type: Schema.Types.ObjectId, 16 | ref: 'User', 17 | required: true 18 | }, 19 | takes: [ 20 | { 21 | name: { 22 | type: String, 23 | required: true 24 | }, 25 | description: String 26 | } 27 | ], 28 | gives: [ 29 | { 30 | name: { 31 | type: String, 32 | required: true 33 | }, 34 | description: String 35 | } 36 | ] 37 | }); 38 | 39 | try { 40 | mongoose.model('Trade', tradeSchema); 41 | // eslint-disable-next-line no-empty 42 | } catch (err) {} 43 | 44 | module.exports = mongoose.model('Trade'); 45 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/HELP-US-OUT.txt: -------------------------------------------------------------------------------- 1 | I hope you love Font Awesome. If you've found it useful, please do me a favor and check out my latest project, 2 | Fort Awesome (https://fortawesome.com). It makes it easy to put the perfect icons on your website. Choose from our awesome, 3 | comprehensive icon sets or copy and paste your own. 4 | 5 | Please. Check it out. 6 | 7 | -Dave Gandy 8 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/manage-a-book-trading-club/app/public/vendor/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/manage-a-book-trading-club/app/public/vendor/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/manage-a-book-trading-club/app/public/vendor/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/manage-a-book-trading-club/app/public/vendor/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/manage-a-book-trading-club/app/public/vendor/font-awesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/less/animated.less: -------------------------------------------------------------------------------- 1 | // Animated Icons 2 | // -------------------------- 3 | 4 | .@{fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .@{fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/less/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: 0.2em 0.25em 0.15em; 6 | border: solid 0.08em @fa-border-color; 7 | border-radius: 0.1em; 8 | } 9 | 10 | .@{fa-css-prefix}-pull-left { 11 | float: left; 12 | } 13 | .@{fa-css-prefix}-pull-right { 14 | float: right; 15 | } 16 | 17 | .@{fa-css-prefix} { 18 | &.@{fa-css-prefix}-pull-left { 19 | margin-right: 0.3em; 20 | } 21 | &.@{fa-css-prefix}-pull-right { 22 | margin-left: 0.3em; 23 | } 24 | } 25 | 26 | /* Deprecated as of 4.4.0 */ 27 | .pull-right { 28 | float: right; 29 | } 30 | .pull-left { 31 | float: left; 32 | } 33 | 34 | .@{fa-css-prefix} { 35 | &.pull-left { 36 | margin-right: 0.3em; 37 | } 38 | &.pull-right { 39 | margin-left: 0.3em; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/less/core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal @fa-font-size-base / @fa-line-height-base 7 | FontAwesome; // shortening font declaration 8 | font-size: inherit; // can't have font-size inherit on line above, so need to override 9 | text-rendering: auto; // optimizelegibility throws things off #1094 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | } 13 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/less/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/less/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import 'variables.less'; 7 | @import 'mixins.less'; 8 | @import 'path.less'; 9 | @import 'core.less'; 10 | @import 'larger.less'; 11 | @import 'fixed-width.less'; 12 | @import 'list.less'; 13 | @import 'bordered-pulled.less'; 14 | @import 'animated.less'; 15 | @import 'rotated-flipped.less'; 16 | @import 'stacked.less'; 17 | @import 'icons.less'; 18 | @import 'screen-reader.less'; 19 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/less/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { 11 | font-size: 2em; 12 | } 13 | .@{fa-css-prefix}-3x { 14 | font-size: 3em; 15 | } 16 | .@{fa-css-prefix}-4x { 17 | font-size: 4em; 18 | } 19 | .@{fa-css-prefix}-5x { 20 | font-size: 5em; 21 | } 22 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/less/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { 9 | position: relative; 10 | } 11 | } 12 | .@{fa-css-prefix}-li { 13 | position: absolute; 14 | left: -@fa-li-width; 15 | width: @fa-li-width; 16 | top: (2em / 14); 17 | text-align: center; 18 | &.@{fa-css-prefix}-lg { 19 | left: (-@fa-li-width + (4em / 14)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/less/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}'); 7 | src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') 8 | format('embedded-opentype'), 9 | url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') 10 | format('woff2'), 11 | url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') 12 | format('woff'), 13 | url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') 14 | format('truetype'), 15 | url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') 16 | format('svg'); 17 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 18 | font-weight: normal; 19 | font-style: normal; 20 | } 21 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/less/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { 5 | .fa-icon-rotate(90deg, 1); 6 | } 7 | .@{fa-css-prefix}-rotate-180 { 8 | .fa-icon-rotate(180deg, 2); 9 | } 10 | .@{fa-css-prefix}-rotate-270 { 11 | .fa-icon-rotate(270deg, 3); 12 | } 13 | 14 | .@{fa-css-prefix}-flip-horizontal { 15 | .fa-icon-flip(-1, 1, 0); 16 | } 17 | .@{fa-css-prefix}-flip-vertical { 18 | .fa-icon-flip(1, -1, 2); 19 | } 20 | 21 | // Hook for IE8-9 22 | // ------------------------- 23 | 24 | :root .@{fa-css-prefix}-rotate-90, 25 | :root .@{fa-css-prefix}-rotate-180, 26 | :root .@{fa-css-prefix}-rotate-270, 27 | :root .@{fa-css-prefix}-flip-horizontal, 28 | :root .@{fa-css-prefix}-flip-vertical { 29 | filter: none; 30 | } 31 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/less/screen-reader.less: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { 5 | .sr-only(); 6 | } 7 | .sr-only-focusable { 8 | .sr-only-focusable(); 9 | } 10 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/less/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, 13 | .@{fa-css-prefix}-stack-2x { 14 | position: absolute; 15 | left: 0; 16 | width: 100%; 17 | text-align: center; 18 | } 19 | .@{fa-css-prefix}-stack-1x { 20 | line-height: inherit; 21 | } 22 | .@{fa-css-prefix}-stack-2x { 23 | font-size: 2em; 24 | } 25 | .@{fa-css-prefix}-inverse { 26 | color: @fa-inverse; 27 | } 28 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/scss/_animated.scss: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .#{$fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/scss/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | padding: 0.2em 0.25em 0.15em; 6 | border: solid 0.08em $fa-border-color; 7 | border-radius: 0.1em; 8 | } 9 | 10 | .#{$fa-css-prefix}-pull-left { 11 | float: left; 12 | } 13 | .#{$fa-css-prefix}-pull-right { 14 | float: right; 15 | } 16 | 17 | .#{$fa-css-prefix} { 18 | &.#{$fa-css-prefix}-pull-left { 19 | margin-right: 0.3em; 20 | } 21 | &.#{$fa-css-prefix}-pull-right { 22 | margin-left: 0.3em; 23 | } 24 | } 25 | 26 | /* Deprecated as of 4.4.0 */ 27 | .pull-right { 28 | float: right; 29 | } 30 | .pull-left { 31 | float: left; 32 | } 33 | 34 | .#{$fa-css-prefix} { 35 | &.pull-left { 36 | margin-right: 0.3em; 37 | } 38 | &.pull-right { 39 | margin-left: 0.3em; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/scss/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix} { 5 | display: inline-block; 6 | font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} 7 | FontAwesome; // shortening font declaration 8 | font-size: inherit; // can't have font-size inherit on line above, so need to override 9 | text-rendering: auto; // optimizelegibility throws things off #1094 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | } 13 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/scss/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .#{$fa-css-prefix}-2x { 11 | font-size: 2em; 12 | } 13 | .#{$fa-css-prefix}-3x { 14 | font-size: 3em; 15 | } 16 | .#{$fa-css-prefix}-4x { 17 | font-size: 4em; 18 | } 19 | .#{$fa-css-prefix}-5x { 20 | font-size: 5em; 21 | } 22 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/scss/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: $fa-li-width; 7 | list-style-type: none; 8 | > li { 9 | position: relative; 10 | } 11 | } 12 | .#{$fa-css-prefix}-li { 13 | position: absolute; 14 | left: -$fa-li-width; 15 | width: $fa-li-width; 16 | top: (2em / 14); 17 | text-align: center; 18 | &.#{$fa-css-prefix}-lg { 19 | left: -$fa-li-width + (4em / 14); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/scss/_path.scss: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); 7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') 8 | format('embedded-opentype'), 9 | url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') 10 | format('woff2'), 11 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') 12 | format('woff'), 13 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') 14 | format('truetype'), 15 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') 16 | format('svg'); 17 | // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 18 | font-weight: normal; 19 | font-style: normal; 20 | } 21 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/scss/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { 5 | @include fa-icon-rotate(90deg, 1); 6 | } 7 | .#{$fa-css-prefix}-rotate-180 { 8 | @include fa-icon-rotate(180deg, 2); 9 | } 10 | .#{$fa-css-prefix}-rotate-270 { 11 | @include fa-icon-rotate(270deg, 3); 12 | } 13 | 14 | .#{$fa-css-prefix}-flip-horizontal { 15 | @include fa-icon-flip(-1, 1, 0); 16 | } 17 | .#{$fa-css-prefix}-flip-vertical { 18 | @include fa-icon-flip(1, -1, 2); 19 | } 20 | 21 | // Hook for IE8-9 22 | // ------------------------- 23 | 24 | :root .#{$fa-css-prefix}-rotate-90, 25 | :root .#{$fa-css-prefix}-rotate-180, 26 | :root .#{$fa-css-prefix}-rotate-270, 27 | :root .#{$fa-css-prefix}-flip-horizontal, 28 | :root .#{$fa-css-prefix}-flip-vertical { 29 | filter: none; 30 | } 31 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/scss/_screen-reader.scss: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { 5 | @include sr-only(); 6 | } 7 | .sr-only-focusable { 8 | @include sr-only-focusable(); 9 | } 10 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/scss/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .#{$fa-css-prefix}-stack-1x, 13 | .#{$fa-css-prefix}-stack-2x { 14 | position: absolute; 15 | left: 0; 16 | width: 100%; 17 | text-align: center; 18 | } 19 | .#{$fa-css-prefix}-stack-1x { 20 | line-height: inherit; 21 | } 22 | .#{$fa-css-prefix}-stack-2x { 23 | font-size: 2em; 24 | } 25 | .#{$fa-css-prefix}-inverse { 26 | color: $fa-inverse; 27 | } 28 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/public/vendor/font-awesome/scss/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import 'variables'; 7 | @import 'mixins'; 8 | @import 'path'; 9 | @import 'core'; 10 | @import 'larger'; 11 | @import 'fixed-width'; 12 | @import 'list'; 13 | @import 'bordered-pulled'; 14 | @import 'animated'; 15 | @import 'rotated-flipped'; 16 | @import 'stacked'; 17 | @import 'icons'; 18 | @import 'screen-reader'; 19 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/views/books/_add-book-form.pug: -------------------------------------------------------------------------------- 1 | form.form-horizontal(method='post' action='/books/create') 2 | h3.col-sm-offset-2 Add Book for #{user.username} 3 | .form-group 4 | label.col-sm-2.control-label(for='bookName') Title 5 | .col-sm-10 6 | input#bookName.form-control( 7 | type='text' 8 | name='bookName' 9 | required) 10 | .form-group 11 | label.col-sm-2.control-label(for='bookDescription') Description 12 | .col-sm-10 13 | input#bookDescription.form-control( 14 | type='text' 15 | name='bookDescription' 16 | placeholder='Author, condition...') 17 | .form-group 18 | .col-sm-offset-2.col-sm-10 19 | button.btn.btn-default(type='submit') Add Book to Exchange 20 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/views/books/_select.pug: -------------------------------------------------------------------------------- 1 | ul.list-group 2 | each book in books 3 | li.list-group-item 4 | if (user || {}).id === book.owner.id 5 | a.btn.btn-danger.list-item-delete(title=`Delete "${book.name}" from your collection` href=`/books/${book.id}/delete`) × 6 | .row 7 | - const checked = (selected || []).includes(book.id) 8 | - const isOwner = (user || {}).id === book.owner.id 9 | input.col-xs-2(class=`${isOwner ? 'gives' : 'takes'}` checked=checked type='checkbox' id=`book${book.id}` name=`book${book.id}`) 10 | label.col-xs-10(for=`book${book.id}`) 11 | +requestsBadge(book) 12 | h4.list-group-item-heading= book.name 13 | span.pull-right: +requestersLinks(book) 14 | p.list-group-item-text= book.description 15 | p.text-muted.small 16 | = 'from ' 17 | +userLink(book.owner) 18 | = ` in ${book.owner.city}` 19 | else 20 | li.list-group-item: h4.list-group-item-heading.text-center #{noItems} 21 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/views/books/index.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | form(action='/requests/new/books' method='post') 4 | .panel.panel-default 5 | .panel-heading: h2.text-center #{title} available for trade 6 | include _select 7 | .panel-footer 8 | .row 9 | if user 10 | span.nav-pills.col-xs-offset-2 11 | button.btn.btn-default(type='submit') New Request 12 | a.btn.btn-default(href='/books/my') Add Book 13 | else 14 | a.btn.btn-default.col-xs-offset-2(href='/login') Login to Add Books and Submit Requests 15 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/views/books/my-books.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | include _add-book-form 4 | form(action='/requests/new/gives' method='post') 5 | .panel.panel-default 6 | .panel-heading: h2.text-center #{user.username}'s Books available for trade 7 | include _select 8 | if books.length 9 | .panel-footer 10 | .row 11 | button.btn.btn-default.col-xs-offset-2(type='submit') New Request 12 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/views/books/selectBooks.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | form(action=`/requests/new/${type}` method='post') 4 | .panel.panel-default 5 | .panel-heading: h2.text-center #{title} select what to trade 6 | include _select 7 | .panel-footer 8 | .row 9 | if (books.length) 10 | button.btn.btn-default.col-xs-offset-2(type='submit') Continue 11 | else 12 | a.btn.btn-default.center-block(href='/books/my') Add Books 13 | 14 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/views/error.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | block content 3 | h1= message 4 | h2= error.status 5 | pre #{error.stack} 6 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/views/login.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | block content 3 | .login-btns 4 | a.btn.btn-lg.btn-block.btn-social.btn-github(href='/auth/github' alt='login with github') 5 | span.fa.fa-github 6 | | Sign in with Github 7 | // 8 | a.btn.btn-lg.btn-block.btn-social.btn-google(href='/auth/google' alt='login with google') 9 | span.fa.fa-google 10 | | Sign in with Google 11 | 12 | // 13 | a.btn.btn-lg.btn-block.btn-social.btn-linkedin(href='/auth/linkedin' alt='login with linkedin') 14 | span.fa.fa-linkedin 15 | | Sign in with LinkedIn -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/views/requests/_select-gives.pug: -------------------------------------------------------------------------------- 1 | ul.list-group 2 | each book in request.gives 3 | li.list-group-item 4 | .row 5 | - const checked = (selectedGives || []).includes(book.id) 6 | input.col-xs-2(class='takes' checked=checked type='checkbox' id=`give${book.id}` name=`give${book.id}`) 7 | label.col-xs-10(for=`give${book.id}`) 8 | +requestsBadge(book) 9 | h4.list-group-item-heading= book.name 10 | p.list-group-item-text= book.description 11 | p.text-muted.small 12 | = 'from ' 13 | +userLink(book.owner) 14 | = ` in ${book.owner.city}` 15 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/views/requests/_select-takes.pug: -------------------------------------------------------------------------------- 1 | ul.list-group 2 | each book in request.takes 3 | li.list-group-item 4 | .row 5 | - const checked = (selectedTakes || []).includes(book.id) 6 | input.col-xs-2(class='gives' checked=checked type='checkbox' id=`take${book.id}` name=`take${book.id}`) 7 | label.col-xs-10(for=`take${book.id}`) 8 | +requestsBadge(book) 9 | h4.list-group-item-heading= book.name 10 | p.list-group-item-text= book.description 11 | p.text-muted.small 12 | = 'from ' 13 | +userLink(book.owner) 14 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/views/requests/_show.pug: -------------------------------------------------------------------------------- 1 | .row 2 | .col-sm-6 3 | h5.request-btns-push-left 4 | span: +userLink(request.requester) 5 | span= ' wants to give:' 6 | ul.list-group 7 | each book in request.gives 8 | li.list-group-item 9 | small: +requestsBadge(book) 10 | h4= book.name 11 | p= book.description 12 | .col-sm-6 13 | h5 14 | = 'and wants to take:' 15 | ul.list-group 16 | each book in request.takes 17 | li.list-group-item 18 | small: +requestsBadge(book) 19 | h4 20 | = `${book.name}` 21 | small.text-muted 22 | |  from  23 | span: +userLink(book.owner) 24 | p= book.description 25 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/views/requests/index.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | mixin requestButtons(user, request) 4 | if user 5 | ul.nav.nav-pills 6 | - bookOwners = request.takes.map(book => book.owner._id.toString()) 7 | if bookOwners && bookOwners.includes(user.id) 8 | li: a.success(href=`/requests/${request._id.toString()}/select`) Select 9 | li: a.warning(href=`/requests/${request._id.toString()}/reject`) Reject 10 | if user.id === request.requester.id 11 | li: a.warning(href=`/requests/${request._id.toString()}/cancel`) Cancel Request 12 | 13 | block content 14 | .panel.panel-default 15 | .panel-heading: h2.text-center #{title} #{subtitle} 16 | ul.list-group 17 | each request in requests 18 | li.list-group-item 19 | .well 20 | include _show 21 | .request-btns 22 | +requestButtons(user, request) 23 | else 24 | li.list-group-item: h4.list-group-item-heading.text-center There are currently no requests 25 | .panel-footer.text-center 26 | if user 27 | a.btn.btn-default(href='/requests/new') New Request 28 | else 29 | a.btn.btn-default(href='/login') Login to Submit Requests 30 | 31 | 32 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/views/requests/select-books.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | form(action=`/requests/${request._id.toString()}/accept` method='post') 4 | .panel.panel-default 5 | .panel-heading: h2.text-center #{title} select what to trade 6 | .panel-body 7 | .row 8 | .col-sm-6 9 | h5.request-btns-push-left 10 | span: +userLink(request.requester) 11 | span= ' wants to give you:' 12 | include _select-gives 13 | .col-sm-6 14 | h5 15 | = 'and wants to take from you:' 16 | include _select-takes 17 | .panel-footer 18 | .row 19 | button.btn.btn-default.center-block(type='submit') Accept Trade 20 | 21 | 22 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/views/trades/_show.pug: -------------------------------------------------------------------------------- 1 | .row 2 | .col-sm-6 3 | h5.request-btns-push-left 4 | span: +userLink(trade.accepter) 5 | span= ' gets:' 6 | ul.list-group 7 | each book in trade.gives 8 | li.list-group-item 9 | h4= book.name 10 | p= book.description 11 | .col-sm-6 12 | h5 13 | span: +userLink(trade.requester) 14 | span= ' gets:' 15 | ul.list-group 16 | each book in trade.takes 17 | li.list-group-item 18 | h4= book.name 19 | p= book.description 20 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/views/trades/index.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block content 4 | .panel.panel-default 5 | .panel-heading: h2.text-center #{title} #{subtitle} 6 | ul.list-group 7 | each trade in trades 8 | li.list-group-item 9 | .well 10 | include _show 11 | else 12 | li.list-group-item: h4.list-group-item-heading.text-center There are currently no trades 13 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/views/users/index.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | h1 Users 4 | ul.list-group 5 | each user in users 6 | li.list-group-item 7 | h4.list-group-item-heading: a(href=`/users/${user.id}`) #{user.username} 8 | p.list-group-item-text= `City: ${user.city}` 9 | p.list-group-item-text 10 | if (user.books || {}).length 11 | span.label.label-info= `Books: ${user.books.length}` 12 | if (user.receivedRequests || {}).length 13 | span.label.label-warning= `Incoming Requests: ${user.receivedRequests.length}` 14 | if (user.trades || {}).length 15 | span.label.label-success= `Trades: ${user.trades.length}` 16 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/app/views/users/show.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | block content 3 | form.form-horizontal 4 | .form-group 5 | h3.col-sm-offset-2 #{showUser.username}'s profile 6 | .form-group 7 | label.col-sm-2.control-label Username: 8 | .col-sm-10 9 | p.form-control-static #{showUser.username} 10 | .form-group 11 | label.col-sm-2.control-label Full Name: 12 | .col-sm-10 13 | p.form-control-static #{showUser.fullName} 14 | .form-group 15 | label.col-sm-2.control-label City: 16 | .col-sm-10 17 | p.form-control-static #{showUser.city} 18 | .form-group 19 | label.col-sm-2.control-label State: 20 | .col-sm-10 21 | p.form-control-static #{showUser.state} 22 | .form-group 23 | label.col-sm-2.control-label Address: 24 | .col-sm-10 25 | p.form-control-static #{showUser.address} 26 | .form-group 27 | ul.nav.nav-pills.col-sm-offset-2 28 | li: a.btn.btn-default(href=`/users/${showUser.id}/books`)= `${showUser.username}'s Books` 29 | if (user || {}).id === showUser.id 30 | li: a.btn.btn-default(href=`/users/edit`) Edit Profile 31 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/config/session.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const session = require('express-session'); 4 | const connectMongo = require('connect-mongo'); 5 | const connection = require('../db/connection'); 6 | 7 | const db = connection(process.env.MONGO_URI); 8 | const MongoStore = connectMongo(session); 9 | 10 | module.exports = session({ 11 | secret: 'kfsvvxbbf', 12 | resave: false, 13 | saveUninitialized: false, 14 | store: new MongoStore({ mongooseConnection: db }) 15 | }); 16 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/db/connection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mongoose = require('mongoose'); 4 | 5 | // Set up mongoose connection 6 | function connection(dbUrl) { 7 | mongoose.Promise = global.Promise; 8 | mongoose.connect(dbUrl, { 9 | useNewUrlParser: true, 10 | useUnifiedTopology: true 11 | }); 12 | const db = mongoose.connection; 13 | db.on('error', console.error); 14 | return db; 15 | } 16 | 17 | module.exports = connection; 18 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "book-exchange", 3 | "version": "0.1.0", 4 | "description": "Trade books with other users", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "mocha --exit --ui tdd test/*.test.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/Motardo/book-exchange.git" 13 | }, 14 | "author": "Nathan Wilson ", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/Motardo/book-exchange/issues" 18 | }, 19 | "homepage": "https://github.com/Motardo/book-exchange#readme", 20 | "devDependencies": { 21 | "chai": "4.5.0", 22 | "chai-as-promised": "7.1.2", 23 | "chai-dom": "1.12.1", 24 | "chai-http": "4.4.0", 25 | "mocha": "9.2.2" 26 | }, 27 | "dependencies": { 28 | "body-parser": "1.20.3", 29 | "connect-flash": "0.1.1", 30 | "connect-mongo": "3.2.0", 31 | "cookie-parser": "1.4.7", 32 | "dotenv": "14.3.2", 33 | "express": "4.21.2", 34 | "express-session": "1.18.1", 35 | "font-awesome": "4.7.0", 36 | "helmet": "3.23.3", 37 | "mongoose": "5.13.22", 38 | "morgan": "1.10.0", 39 | "passport": "0.5.2", 40 | "passport-github": "1.1.0", 41 | "pug": "3.0.3" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/sample.env: -------------------------------------------------------------------------------- 1 | GITHUB_KEY="foo" 2 | GITHUB_SECRET="bar" 3 | GOOGLE_KEY="foo" 4 | GOOGLE_SECRET="bar" 5 | MONGO_URI="mongodb://localhost:27017/book-club" 6 | APP_URL="http://localhost:3000" 7 | PORT=3000 8 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const dotenv = require('dotenv'); 4 | 5 | dotenv.config(); 6 | 7 | const app = require('./app/app'); 8 | 9 | const portNum = process.env.PORT || 3000; 10 | 11 | app.listen(portNum, () => { 12 | console.log(`server listening on port ${portNum}`); 13 | }); 14 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/test/env: -------------------------------------------------------------------------------- 1 | APP_URL='http://localhost:3002/' 2 | PORT=3002 3 | GOOGLE_KEY=foo 4 | GOOGLE_SECRET=bar 5 | GITHUB_KEY=foo 6 | GITHUB_SECRET=bar 7 | MONGO_URI=mongodb://localhost:27017/thingexchangetest 8 | 9 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter list 2 | --watch-extensions pug 3 | --bail 4 | --timeout 5s 5 | -------------------------------------------------------------------------------- /apps/manage-a-book-trading-club/test/user.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { User, describe, it, testData } = require('./testSetup'); 4 | 5 | const testUsers = testData.users; 6 | 7 | describe('User', () => { 8 | it('should eventually count all the test users', () => 9 | User.count({}).should.eventually.equal(testUsers.length)); 10 | }); 11 | -------------------------------------------------------------------------------- /apps/markdown-previewer/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/markdown-previewer/.gitignore: -------------------------------------------------------------------------------- 1 | public/bundle.js -------------------------------------------------------------------------------- /apps/markdown-previewer/client/.babelrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { "targets": "> 0.25%, not dead" }], 4 | "@babel/preset-react" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /apps/markdown-previewer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 13 | FCC: Simple React Markdown Previewer 14 | 15 | 16 |
17 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /apps/markdown-previewer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "markdown-previewer", 3 | "version": "0.0.1", 4 | "description": "Front end project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "build": "rollup -c", 9 | "prepare": "npm run build" 10 | }, 11 | "dependencies": { 12 | "dotenv": "16.0.0", 13 | "express": "4.21.2" 14 | }, 15 | "devDependencies": { 16 | "@babel/core": "7.27.4", 17 | "@babel/preset-env": "7.27.2", 18 | "@babel/preset-react": "7.27.1", 19 | "@rollup/plugin-babel": "6.0.4", 20 | "@rollup/plugin-commonjs": "23.0.7", 21 | "@rollup/plugin-node-resolve": "15.3.1", 22 | "@rollup/plugin-replace": "5.0.7", 23 | "@rollup/plugin-terser": "0.2.0", 24 | "rollup": "2.79.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /apps/markdown-previewer/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import babel from '@rollup/plugin-babel'; 4 | import replace from '@rollup/plugin-replace'; 5 | import terser from '@rollup/plugin-terser'; 6 | 7 | export default { 8 | input: 'client/index.jsx', 9 | output: { 10 | file: 'public/bundle.js', 11 | format: 'iife', 12 | name: 'Client' 13 | }, 14 | plugins: [ 15 | resolve({ moduleDirectories: ['node_modules'] }), 16 | replace({ 17 | preventAssignment: true, 18 | 'process.env.NODE_ENV': JSON.stringify('production') 19 | }), 20 | commonjs({ 21 | include: /node_modules/ 22 | }), 23 | babel({ babelHelpers: 'bundled' }), 24 | terser() 25 | ] 26 | }; 27 | -------------------------------------------------------------------------------- /apps/markdown-previewer/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/markdown-previewer/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/metric-imperial-converter/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/metric-imperial-converter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fcc-imperial-metric-converter-boilerplate", 3 | "version": "1.0.0", 4 | "description": "Metric / Imperial Unit Converter", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "PORT=3005 mocha --exit --ui tdd tests/" 9 | }, 10 | "dependencies": { 11 | "body-parser": "1.20.3", 12 | "chai": "4.5.0", 13 | "chai-http": "4.4.0", 14 | "cors": "2.8.5", 15 | "dotenv": "14.3.2", 16 | "express": "4.21.2", 17 | "mocha": "9.2.2" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/freeCodeCamp/boilerplate-project-metricimpconverter" 22 | }, 23 | "keywords": [ 24 | "node", 25 | "freeCodeCamp", 26 | "express" 27 | ], 28 | "license": "MIT" 29 | } 30 | -------------------------------------------------------------------------------- /apps/metric-imperial-converter/public/style.css: -------------------------------------------------------------------------------- 1 | code { 2 | background-color: whitesmoke; 3 | } 4 | 5 | section, 6 | header { 7 | margin: 50px; 8 | } 9 | -------------------------------------------------------------------------------- /apps/metric-imperial-converter/routes/api.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * 4 | * Complete the API routing below 5 | * 6 | * 7 | */ 8 | 9 | 'use strict'; 10 | 11 | const ConvertHandler = require('../controllers/convertHandler.js'); 12 | 13 | module.exports = function (app) { 14 | let convertHandler = new ConvertHandler(); 15 | 16 | app.route('/api/convert').get(function (req, res) { 17 | let input = req.query.input; 18 | let initNum = convertHandler.getNum(input); 19 | let initUnit = convertHandler.getUnit(input); 20 | 21 | // Check for invalid inputs 22 | if (!initNum || !initUnit) { 23 | if (!initUnit && initNum) { 24 | return res.send('invalid unit'); 25 | } else if (!initNum && initUnit) { 26 | return res.send('invalid number'); 27 | } 28 | return res.send('invalid number and unit'); 29 | } 30 | 31 | let returnNum = convertHandler.convert(initNum, initUnit); 32 | let returnUnit = convertHandler.getReturnUnit(initUnit); 33 | let toString = convertHandler.getString( 34 | initNum, 35 | initUnit, 36 | returnNum, 37 | returnUnit 38 | ); 39 | 40 | res.json({ initNum, initUnit, returnNum, returnUnit, string: toString }); 41 | }); 42 | }; 43 | -------------------------------------------------------------------------------- /apps/metric-imperial-converter/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | # NODE_ENV=test 3 | -------------------------------------------------------------------------------- /apps/nightlife-coordination-app/README.md: -------------------------------------------------------------------------------- 1 | This project should fulfill all the user stories referenced in [Nightlife Coordination App project](https://www.freecodecamp.org/learn/coding-interview-prep/take-home-projects/build-a-nightlife-coordination-app). The [heroku version](https://whatsgoinontonight.herokuapp.com/) was built using Angular. The repo for the app resides at [https://github.com/clnhll/whatstonight](https://github.com/clnhll/whatstonight). This demo should be a completely new version instead of a duplicate. 2 | -------------------------------------------------------------------------------- /apps/p2p-video-chat-application/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/p2p-video-chat-application/.gitignore: -------------------------------------------------------------------------------- 1 | public/bundle.js -------------------------------------------------------------------------------- /apps/p2p-video-chat-application/README.md: -------------------------------------------------------------------------------- 1 | # P2P Video Chat Application 2 | 3 | ## User stories: 4 | 5 | 1. Upon arriving, the browser will prompt me to access my camera and microphone. 6 | 1. After I give it permission, I am prompted to type in a room name. 7 | 1. Once I type in the room name, a room will be created if no room of that name existed before. 8 | 1. A friend of mine can subsequently go to the same website, type in the same room I entered, and join the same room, then enter into a video chat with me. 9 | 1. If I type in a room name, and there are already two people in that room, I get a notification that the room is full. 10 | 1. Anyone can create or join any room. And there can be any number of rooms, but all of them must have unique names. 11 | 1. I can choose to not permit the site to access my microphone and webcam. If I choose not to do this, or if some other driver problem occurs, I see an error message saying these are required. 12 | 1. When I choose to cancel the room name input step, or if I type in no name, or just spaces, it should again ask me again to type in a valid room name. 13 | 1. If one of the two people in the room get disconnected, they can reconnect to the same room and continue chatting. 14 | -------------------------------------------------------------------------------- /apps/p2p-video-chat-application/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "p2p-video-chat", 3 | "version": "1.0.0", 4 | "description": "A simple socket.io based video chat app", 5 | "main": "server.js", 6 | "scripts": { 7 | "build": "rollup -c", 8 | "start": "node server.js", 9 | "dev": "concurrently \"rollup -c -w\" \"nodemon server.js\"", 10 | "prepare": "npm run build" 11 | }, 12 | "dependencies": { 13 | "dotenv": "14.3.2", 14 | "express": "4.21.2", 15 | "jquery": "3.7.1", 16 | "simple-peer": "9.11.1", 17 | "socket.io": "4.8.1" 18 | }, 19 | "devDependencies": { 20 | "@rollup/plugin-commonjs": "21.1.0", 21 | "@rollup/plugin-node-resolve": "13.3.0", 22 | "@rollup/plugin-terser": "0.2.0", 23 | "concurrently": "7.6.0", 24 | "nodemon": "2.0.22", 25 | "rollup": "2.79.2" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/p2p-video-chat-application/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | P2P Video Chat 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/p2p-video-chat-application/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import terser from '@rollup/plugin-terser'; 4 | 5 | export default { 6 | input: 'src/client.js', 7 | output: { 8 | file: 'public/bundle.js', 9 | format: 'iife', 10 | name: 'Client' 11 | }, 12 | plugins: [ 13 | resolve({ moduleDirectories: ['node_modules'] }), 14 | commonjs({ 15 | include: /node_modules/ 16 | }), 17 | terser() 18 | ] 19 | }; 20 | -------------------------------------------------------------------------------- /apps/p2p-video-chat-application/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/palindrome-checker/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/palindrome-checker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "palindrome-checker", 3 | "version": "0.0.1", 4 | "description": "JavaScript Algorithms and Data Structures project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "dotenv": "16.0.0", 11 | "express": "4.21.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/palindrome-checker/public/script.js: -------------------------------------------------------------------------------- 1 | const userInput = document.getElementById('text-input'); 2 | const checkPalindromeBtn = document.getElementById('check-btn'); 3 | const resultDiv = document.getElementById('result'); 4 | 5 | const checkForPalindrome = input => { 6 | const originalInput = input; // Store for later output 7 | 8 | if (input === '') { 9 | alert('Please input a value'); 10 | return; 11 | } 12 | 13 | // Remove the previous result 14 | resultDiv.replaceChildren(); 15 | 16 | const lowerCaseStr = input.replace(/[^A-Za-z0-9]/gi, '').toLowerCase(); 17 | let resultMsg = `${originalInput} ${ 18 | lowerCaseStr === [...lowerCaseStr].reverse().join('') ? 'is' : 'is not' 19 | } a palindrome.`; 20 | 21 | const pTag = document.createElement('p'); 22 | pTag.className = 'user-input'; 23 | pTag.innerText = resultMsg; 24 | resultDiv.appendChild(pTag); 25 | 26 | // Show the result. 27 | resultDiv.classList.remove('hidden'); 28 | }; 29 | 30 | checkPalindromeBtn.addEventListener('click', () => { 31 | checkForPalindrome(userInput.value); 32 | userInput.value = ''; 33 | }); 34 | 35 | userInput.addEventListener('keydown', e => { 36 | if (e.key === 'Enter') { 37 | checkForPalindrome(userInput.value); 38 | userInput.value = ''; 39 | } 40 | }); 41 | -------------------------------------------------------------------------------- /apps/palindrome-checker/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/palindrome-checker/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/personal-library/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/personal-library/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/* 2 | .env 3 | node_modules/ 4 | -------------------------------------------------------------------------------- /apps/personal-library/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bullseye-slim 2 | 3 | WORKDIR /app 4 | 5 | # Copy over all the files in the project directory to /app early 6 | # for rollup bundling 7 | COPY . . 8 | 9 | ENV PORT=3000 10 | ENV DB=${PERSONAL_LIBRARY_DB} 11 | 12 | RUN npm ci 13 | 14 | CMD ["npm", "start"] 15 | -------------------------------------------------------------------------------- /apps/personal-library/README.md: -------------------------------------------------------------------------------- 1 | # [Personal Library](https://www.freecodecamp.org/learn/quality-assurance/quality-assurance-projects/personal-library) 2 | -------------------------------------------------------------------------------- /apps/personal-library/db/BookModel.js: -------------------------------------------------------------------------------- 1 | const Mongoose = require('mongoose'); 2 | 3 | const BookSchema = new Mongoose.Schema({ 4 | title: String, 5 | comments: Array, 6 | commentcount: Number 7 | }); 8 | 9 | const BookModel = Mongoose.model('book', BookSchema); 10 | 11 | module.exports = { 12 | BookSchema, 13 | BookModel 14 | }; 15 | -------------------------------------------------------------------------------- /apps/personal-library/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fcc-library-example", 3 | "version": "1.0.0", 4 | "description": "example", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "mocha --exit -u tdd ./tests/*.js" 9 | }, 10 | "dependencies": { 11 | "body-parser": "1.20.3", 12 | "chai": "4.5.0", 13 | "chai-http": "4.4.0", 14 | "cors": "2.8.5", 15 | "dotenv": "14.3.2", 16 | "express": "4.21.2", 17 | "mocha": "9.2.2", 18 | "mongodb": "3.7.4", 19 | "mongoose": "5.13.22" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/freeCodeCamp/demo-projects" 24 | }, 25 | "keywords": [ 26 | "node", 27 | "express" 28 | ], 29 | "license": "MIT" 30 | } 31 | -------------------------------------------------------------------------------- /apps/personal-library/public/style.css: -------------------------------------------------------------------------------- 1 | .border { 2 | border-style: solid; 3 | border-width: 1px; 4 | margin: 10px; 5 | } 6 | 7 | #sampleui { 8 | max-width: 450px; 9 | margin-left: 5%; 10 | height: 100%; 11 | } 12 | 13 | #sampleposting { 14 | max-width: 450px; 15 | text-align: center; 16 | margin-left: 5%; 17 | } 18 | 19 | #userstories { 20 | margin-left: 5%; 21 | } 22 | 23 | form { 24 | padding: 5px; 25 | } 26 | 27 | label { 28 | display: block; 29 | font-weight: bold; 30 | } 31 | 32 | input { 33 | margin-bottom: 5px; 34 | } 35 | -------------------------------------------------------------------------------- /apps/personal-library/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | DB="mongodb://localhost:27017/library" 3 | # NODE_ENV=test 4 | -------------------------------------------------------------------------------- /apps/personal-portfolio/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/personal-portfolio/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "portfolio", 3 | "version": "0.0.1", 4 | "description": "Responsive Web Design project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "dotenv": "16.0.0", 11 | "express": "4.21.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/personal-portfolio/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/personal-portfolio/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/pokeapi-proxy/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/pokeapi-proxy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bullseye-slim 2 | 3 | WORKDIR /app 4 | 5 | # Copy over all the files in the project directory to /app early 6 | # for rollup bundling 7 | COPY . . 8 | 9 | ENV PORT=3000 10 | ENV CACHE_TTL_HOURS=${POKEAPI_PROXY_CACHE_TTL_HOURS} 11 | 12 | RUN npm ci 13 | 14 | CMD ["npm", "start"] 15 | -------------------------------------------------------------------------------- /apps/pokeapi-proxy/README.md: -------------------------------------------------------------------------------- 1 | # PokéAPI Proxy 2 | -------------------------------------------------------------------------------- /apps/pokeapi-proxy/api/pokemon/pokemon.routes.mjs: -------------------------------------------------------------------------------- 1 | import { 2 | getPokemonEndpointResources, 3 | getPokemonData 4 | } from './pokemon.handlers.mjs'; 5 | import { checkCache, validateNameOrId } from './pokemon.middleware.mjs'; 6 | import express from 'express'; 7 | const router = express.Router(); 8 | 9 | router.get('/pokemon', checkCache, getPokemonEndpointResources); 10 | 11 | router.get( 12 | '/pokemon/:pokemonIdOrName', 13 | checkCache, 14 | getPokemonEndpointResources, 15 | validateNameOrId, 16 | getPokemonData 17 | ); 18 | 19 | export { router }; 20 | -------------------------------------------------------------------------------- /apps/pokeapi-proxy/api/utils/cache.mjs: -------------------------------------------------------------------------------- 1 | import NodeCache from 'node-cache'; 2 | const cache = new NodeCache({ 3 | stdTTL: process.env.CACHE_TTL_HOURS * 3600, // Convert hours to seconds 4 | checkperiod: 120 5 | }); 6 | 7 | export const getCache = key => cache.get(key); 8 | 9 | export const setCache = (key, data) => cache.set(key, data); 10 | -------------------------------------------------------------------------------- /apps/pokeapi-proxy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pokeapi-proxy", 3 | "version": "1.0.0", 4 | "description": "PokéAPI Proxy With Cache", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.mjs" 9 | }, 10 | "license": "MIT", 11 | "dependencies": { 12 | "axios": "1.9.0", 13 | "cors": "2.8.5", 14 | "dotenv": "16.3.1", 15 | "express": "4.21.2", 16 | "node-cache": "5.1.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /apps/pokeapi-proxy/public/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | font-size: 16px; 4 | } 5 | 6 | *, 7 | *:before, 8 | *:after { 9 | box-sizing: inherit; 10 | } 11 | 12 | body { 13 | font-family: Lato, sans-serif; 14 | background-color: #0a0a23; 15 | } 16 | 17 | main { 18 | margin: auto; 19 | display: flex; 20 | flex-direction: column; 21 | justify-content: center; 22 | align-items: center; 23 | } 24 | 25 | h1, 26 | h2, 27 | h3, 28 | p, 29 | code, 30 | pre, 31 | ul { 32 | color: #fff; 33 | } 34 | 35 | .usage { 36 | padding: 15px; 37 | background-color: #0a0a23; 38 | max-width: 95%; 39 | border: 1px solid #fff; 40 | } 41 | 42 | p { 43 | margin-bottom: 1em; 44 | line-height: 1.5rem; 45 | } 46 | 47 | code { 48 | padding: 1px 4px; 49 | overflow-wrap: anywhere; 50 | background-color: #3b3b4f; 51 | color: #dfdfe2; 52 | } 53 | 54 | ul { 55 | margin: 1em; 56 | padding-left: 20px; 57 | } 58 | 59 | li { 60 | margin-bottom: 0.5em; 61 | } 62 | 63 | @media (min-width: 768px) { 64 | .usage { 65 | max-width: 70%; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /apps/pokeapi-proxy/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | CACHE_TTL_HOURS=12 3 | -------------------------------------------------------------------------------- /apps/product-landing-page/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/product-landing-page/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "product", 3 | "version": "0.0.1", 4 | "description": "Responsive Web Design project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "dotenv": "16.0.0", 11 | "express": "4.21.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/product-landing-page/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/product-landing-page/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/random-quote-machine/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/random-quote-machine/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "random-quote-machine", 3 | "version": "0.0.1", 4 | "description": "Front end project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "dotenv": "16.0.0", 11 | "express": "4.21.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/random-quote-machine/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/random-quote-machine/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/request-header-parser-microservice/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/request-header-parser-microservice/.gitconfig: -------------------------------------------------------------------------------- 1 | [core] 2 | excludesfile = /etc/.gitignore-global 3 | -------------------------------------------------------------------------------- /apps/request-header-parser-microservice/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "request_header", 3 | "version": "0.0.1", 4 | "description": "API project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "PORT=3005 mocha --exit --ui tdd tests/" 9 | }, 10 | "dependencies": { 11 | "chai": "4.5.0", 12 | "chai-http": "4.4.0", 13 | "cors": "2.8.5", 14 | "dotenv": "14.3.2", 15 | "express": "5.0.0-beta.3", 16 | "mocha": "9.2.2" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://hyperdev.com/#!/project/welcome-project" 21 | }, 22 | "keywords": [ 23 | "node", 24 | "hyperdev", 25 | "express" 26 | ], 27 | "license": "MIT" 28 | } 29 | -------------------------------------------------------------------------------- /apps/request-header-parser-microservice/public/style.css: -------------------------------------------------------------------------------- 1 | /****** Main Styling ******/ 2 | 3 | body { 4 | font-family: 'Roboto', sans-serif; 5 | font-size: 16px; 6 | color: #222; 7 | background-color: #fafafa; 8 | text-align: center; 9 | line-height: 1.4em; 10 | } 11 | 12 | .container { 13 | padding: 0; 14 | margin-top: 40px; 15 | } 16 | 17 | h3 { 18 | margin-top: 30px; 19 | } 20 | 21 | .footer { 22 | margin-top: 40px; 23 | } 24 | 25 | code { 26 | font-family: monospace; 27 | padding: 2px; 28 | color: black; 29 | background-color: #fff; 30 | } 31 | 32 | ul { 33 | list-style-type: none; 34 | } 35 | 36 | li { 37 | margin-bottom: 0.5em; 38 | } 39 | 40 | a { 41 | color: #2574a9; 42 | } 43 | -------------------------------------------------------------------------------- /apps/request-header-parser-microservice/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/request-header-parser-microservice/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('dotenv').config(); 4 | var express = require('express'); 5 | var cors = require('cors'); 6 | 7 | var app = express(); 8 | 9 | // allow Cross Origin requests, for testing 10 | app.use(cors()); 11 | 12 | app.use('/public', express.static(process.cwd() + '/public')); 13 | 14 | // get ip infos even if passing through a proxy like here 15 | app.enable('trust proxy'); 16 | 17 | app.route('/').get(function (req, res) { 18 | res.sendFile(process.cwd() + '/views/index.html'); 19 | }); 20 | 21 | app.route('/status/ping').get(function (req, res) { 22 | res.status(200).send({ msg: 'pong' }); 23 | }); 24 | 25 | app.route('/api/whoami').get(function (req, res) { 26 | res.json({ 27 | ipaddress: req.ip, 28 | language: req.headers['accept-language'], 29 | software: req.headers['user-agent'] 30 | }); 31 | }); 32 | 33 | // 404 Not Found Middleware 34 | app.use(function (req, res) { 35 | res.status(404).type('text').send('Not Found'); 36 | }); 37 | 38 | const portNum = process.env.PORT || 3000; 39 | 40 | //Start our server and tests! 41 | app.listen(portNum, function () { 42 | console.log('Listening on port ' + portNum); 43 | }); 44 | 45 | module.exports = app; 46 | -------------------------------------------------------------------------------- /apps/request-header-parser-microservice/tests/2_functional-tests.js: -------------------------------------------------------------------------------- 1 | const chaiHttp = require('chai-http'); 2 | const chai = require('chai'); 3 | const assert = chai.assert; 4 | const server = require('../server'); 5 | 6 | chai.use(chaiHttp); 7 | 8 | suite('Functional Tests', function () { 9 | this.timeout(5000); 10 | test('/api/whoami', function (done) { 11 | chai 12 | .request(server) 13 | .get('/api/whoami') 14 | .set('accept-language', 'dothraki') 15 | .set('user-agent', 'uss enterprise') 16 | .end(function (err, res) { 17 | assert.equal(res.status, 200); 18 | assert.exists(res.body.ipaddress); 19 | assert.isAtLeast(res.body.ipaddress.length, 1); 20 | assert.equal(res.body.language, 'dothraki'); 21 | assert.equal(res.body.software, 'uss enterprise'); 22 | done(); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /apps/roman-numeral-converter/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/roman-numeral-converter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "roman-numeral-converter", 3 | "version": "0.0.1", 4 | "description": "JavaScript Algorithms and Data Structures project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "dotenv": "16.0.0", 11 | "express": "4.21.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/roman-numeral-converter/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/roman-numeral-converter/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/rpg-creature-api/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/rpg-creature-api/README.md: -------------------------------------------------------------------------------- 1 | # RPG Creature API 2 | -------------------------------------------------------------------------------- /apps/rpg-creature-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rpg-creature-api", 3 | "version": "1.0.0", 4 | "description": "RPG Creature API", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node server.js" 9 | }, 10 | "license": "MIT", 11 | "dependencies": { 12 | "cors": "2.8.5", 13 | "dotenv": "16.3.1", 14 | "express": "4.21.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/rpg-creature-api/routes/api.js: -------------------------------------------------------------------------------- 1 | const router = require('express').Router(); 2 | const creatures = require('../data/creatures.json'); 3 | 4 | const minCreatures = creatures.map(c => { 5 | return { id: c.id, name: c.name }; 6 | }); 7 | 8 | const creatureCase = creature => 9 | creature.charAt(0).toUpperCase() + creature.slice(1).toLowerCase(); 10 | 11 | router.get('/creatures', (req, res) => res.json(minCreatures)); 12 | 13 | router.get('/creature/:creatureNameOrId', (req, res) => { 14 | const { creatureNameOrId } = req.params; 15 | 16 | const creatureFound = creatures.find( 17 | c => creatureNameOrId == c.id || c.name === creatureCase(creatureNameOrId) 18 | ); 19 | 20 | return creatureFound 21 | ? res.json(creatureFound) 22 | : res.status(404).send('Invalid creature name or ID'); 23 | }); 24 | 25 | module.exports = router; 26 | -------------------------------------------------------------------------------- /apps/rpg-creature-api/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/rpg-creature-api/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | const cors = require('cors'); 5 | const creatureRouter = require('./routes/api'); 6 | 7 | app.use(express.static('public')); 8 | app.use(cors()); 9 | 10 | app.get('/', (req, res) => { 11 | res.sendFile(__dirname + '/views/index.html'); 12 | }); 13 | 14 | app.get('/status/ping', (req, res) => { 15 | res.send({ msg: 'pong' }).status(200); 16 | }); 17 | 18 | app.use('/api', creatureRouter); 19 | 20 | const portNum = process.env.PORT || 3000; 21 | app.listen(portNum, () => { 22 | console.log(`Listening on port ${portNum}`); 23 | }); 24 | -------------------------------------------------------------------------------- /apps/rpg-creature-search-app/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/rpg-creature-search-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rpg-creature-search-app", 3 | "version": "0.0.1", 4 | "description": "JavaScript Algorithms and Data Structures project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "dotenv": "16.0.0", 11 | "express": "4.21.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/rpg-creature-search-app/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/rpg-creature-search-app/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/scatterplot-graph/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/scatterplot-graph/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | FCC: D3 Scatter Plot 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /apps/scatterplot-graph/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scatterplot-graph", 3 | "version": "0.0.1", 4 | "description": "Data Visualization project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "dotenv": "16.0.0", 11 | "express": "4.21.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/scatterplot-graph/public/styles.css: -------------------------------------------------------------------------------- 1 | .graph { 2 | display: block; 3 | margin: auto; 4 | background-color: white; 5 | } 6 | 7 | body { 8 | font: 10px sans-serif; 9 | width: 100%; 10 | height: 100%; 11 | } 12 | .main { 13 | position: relative; 14 | } 15 | 16 | .axis path, 17 | .axis line { 18 | fill: none; 19 | stroke: #000; 20 | shape-rendering: crispEdges; 21 | } 22 | 23 | .dot { 24 | stroke: #000; 25 | opacity: 0.8; 26 | } 27 | 28 | div.tooltip { 29 | position: absolute; 30 | 31 | padding: 10px; 32 | font: 12px sans-serif; 33 | background: lightsteelblue; 34 | border: 0px; 35 | border-radius: 8px; 36 | pointer-events: none; 37 | } 38 | -------------------------------------------------------------------------------- /apps/scatterplot-graph/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/scatterplot-graph/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/secure-real-time-multiplayer-game/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } 4 | -------------------------------------------------------------------------------- /apps/secure-real-time-multiplayer-game/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/secure-real-time-multiplayer-game/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "secure-real-time-multiplayer-game", 3 | "version": "1.0.0", 4 | "description": "Information Security 5: Secure Real-Time Multiplayer Game", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "PORT=3005 mocha --require @babel/register --recursive --exit --ui tdd tests/" 9 | }, 10 | "dependencies": { 11 | "@babel/core": "7.27.4", 12 | "@babel/preset-env": "7.27.2", 13 | "@babel/register": "7.27.1", 14 | "body-parser": "1.20.3", 15 | "chai": "4.5.0", 16 | "chai-http": "4.4.0", 17 | "cors": "2.8.5", 18 | "dotenv": "14.3.2", 19 | "express": "4.21.2", 20 | "helmet": "3.23.3", 21 | "jsdom": "16.7.0", 22 | "mocha": "9.2.2", 23 | "socket.io": "4.8.1" 24 | }, 25 | "license": "MIT" 26 | } 27 | -------------------------------------------------------------------------------- /apps/secure-real-time-multiplayer-game/public/Collectible.mjs: -------------------------------------------------------------------------------- 1 | class Collectible { 2 | constructor({ x = 10, y = 10, w = 15, h = 15, value = 1, id }) { 3 | this.x = x; 4 | this.y = y; 5 | this.w = w; 6 | this.h = h; 7 | this.value = value; 8 | this.id = id; 9 | } 10 | 11 | draw(context, imgObj) { 12 | if (this.value === 1) { 13 | context.drawImage(imgObj.bronzeCoinArt, this.x, this.y); 14 | } else if (this.value === 2) { 15 | context.drawImage(imgObj.silverCoinArt, this.x, this.y); 16 | } else { 17 | context.drawImage(imgObj.goldCoinArt, this.x, this.y); 18 | } 19 | } 20 | } 21 | 22 | /* 23 | Note: Attempt to export this for use 24 | in server.js 25 | */ 26 | try { 27 | module.exports = Collectible; 28 | } catch (e) {} 29 | 30 | export default Collectible; 31 | -------------------------------------------------------------------------------- /apps/secure-real-time-multiplayer-game/public/canvas-data.mjs: -------------------------------------------------------------------------------- 1 | const canvasWidth = 640; 2 | const canvasHeight = 480; 3 | const playerWidth = 30; 4 | const playerHeight = 30; 5 | const border = 5; // Between edge of canvas and play field 6 | const infoBar = 45; 7 | 8 | const canvasCalcs = { 9 | canvasWidth: canvasWidth, 10 | canvasHeight: canvasHeight, 11 | playFieldMinX: canvasWidth / 2 - (canvasWidth - 10) / 2, 12 | playFieldMinY: canvasHeight / 2 - (canvasHeight - 100) / 2, 13 | playFieldWidth: canvasWidth - border * 2, 14 | playFieldHeight: canvasHeight - infoBar - border * 2, 15 | playFieldMaxX: canvasWidth - playerWidth - border, 16 | playFieldMaxY: canvasHeight - playerHeight - border 17 | }; 18 | 19 | const generateStartPos = (min, max, multiple) => { 20 | return Math.floor(Math.random() * ((max - min) / multiple)) * multiple + min; 21 | }; 22 | 23 | export { generateStartPos, canvasCalcs }; 24 | -------------------------------------------------------------------------------- /apps/secure-real-time-multiplayer-game/public/controls.mjs: -------------------------------------------------------------------------------- 1 | const controls = (player, socket) => { 2 | const getKey = e => { 3 | if (e.keyCode === 87 || e.keyCode === 38) return 'up'; 4 | if (e.keyCode === 83 || e.keyCode === 40) return 'down'; 5 | if (e.keyCode === 65 || e.keyCode === 37) return 'left'; 6 | if (e.keyCode === 68 || e.keyCode === 39) return 'right'; 7 | }; 8 | 9 | document.onkeydown = e => { 10 | let dir = getKey(e); 11 | 12 | if (dir) { 13 | player.moveDir(dir); 14 | 15 | // Pass current player position back to the server 16 | socket.emit('move-player', dir, { x: player.x, y: player.y }); 17 | } 18 | }; 19 | 20 | document.onkeyup = e => { 21 | let dir = getKey(e); 22 | 23 | if (dir) { 24 | player.stopDir(dir); 25 | 26 | // Pass current player position back to the server 27 | socket.emit('stop-player', dir, { x: player.x, y: player.y }); 28 | } 29 | }; 30 | }; 31 | 32 | export default controls; 33 | -------------------------------------------------------------------------------- /apps/secure-real-time-multiplayer-game/public/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 20px; 3 | } 4 | 5 | code { 6 | background: #d0d0d5; 7 | } 8 | 9 | canvas { 10 | margin: auto; 11 | } 12 | 13 | .container { 14 | display: flex; 15 | flex-direction: column; 16 | } 17 | 18 | @media (min-width: 800px) { 19 | .container { 20 | flex-direction: row; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /apps/secure-real-time-multiplayer-game/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | # NODE_ENV=test 3 | -------------------------------------------------------------------------------- /apps/secure-real-time-multiplayer-game/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Secure Real-Time Multiplayer Game 5 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 |
21 |

Secure Real Time Multiplayer Game

22 |
23 |
24 |
25 | 26 |
27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /apps/stock-price-checker-proxy/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/stock-price-checker-proxy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bullseye-slim 2 | 3 | WORKDIR /app 4 | 5 | # Copy over all the files in the project directory to /app early 6 | # for rollup bundling 7 | COPY . . 8 | 9 | ENV PORT=3000 10 | ENV CACHE_TTL_MINUTES=${STOCK_PRICE_CHECKER_PROXY_CACHE_TTL_MINUTES} 11 | ENV IEX_API_KEY=${STOCK_PRICE_CHECKER_PROXY_IEX_API_KEY} 12 | 13 | RUN npm ci 14 | 15 | CMD ["npm", "start"] 16 | -------------------------------------------------------------------------------- /apps/stock-price-checker-proxy/README.md: -------------------------------------------------------------------------------- 1 | # Stock Price Checker Proxy 2 | -------------------------------------------------------------------------------- /apps/stock-price-checker-proxy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stock-checker-proxy", 3 | "version": "1.0.0", 4 | "description": "API Proxy with cache", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "@seald-io/nedb": "2.2.2", 11 | "axios": "1.9.0", 12 | "cors": "2.8.5", 13 | "dotenv": "14.3.2", 14 | "express": "4.21.2" 15 | }, 16 | "repository": { 17 | "url": "https://stock-price-checker-proxy.freecodecamp.rocks" 18 | }, 19 | "author": "Em-Ant", 20 | "license": "MIT", 21 | "keywords": [ 22 | "node", 23 | "express" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /apps/stock-price-checker-proxy/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Stock Price Checker Proxy 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |

Stock Price Checker Proxy

15 |
16 |
17 | Usage: 18 |
19 | GET https://stock-price-checker-proxy.freecodecamp.rocks/v1/stock/[symbol]/quote
21 | Where: 22 |
symbol = msft | goog | aapl | ...
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /apps/stock-price-checker-proxy/public/style.css: -------------------------------------------------------------------------------- 1 | /* styles */ 2 | /* called by your view template */ 3 | 4 | * { 5 | box-sizing: border-box; 6 | } 7 | 8 | body { 9 | font-family: 'Benton Sans', 'Helvetica Neue', helvetica, arial, sans-serif; 10 | margin: 2em; 11 | } 12 | 13 | h1 { 14 | font-style: italic; 15 | color: #373fff; 16 | } 17 | 18 | .bold { 19 | font-weight: bold; 20 | } 21 | 22 | p { 23 | max-width: 600px; 24 | } 25 | -------------------------------------------------------------------------------- /apps/stock-price-checker-proxy/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | CACHE_TTL_MINUTES= 3 | ALPHA_VANTAGE_API_KEY= 4 | -------------------------------------------------------------------------------- /apps/stock-price-checker-proxy/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | const api_v1 = require('./api/v1'); 6 | 7 | app.use(express.static('public')); 8 | 9 | app.use('/v1', api_v1); 10 | 11 | app.get('/', (req, res) => { 12 | res.sendFile(__dirname + '/views/index.html'); 13 | }); 14 | 15 | app.get('/status/ping', (req, res) => { 16 | res.send({ msg: 'pong' }).status(200); 17 | }); 18 | 19 | // eslint-disable-next-line no-unused-vars 20 | app.use((err, req, res, next) => { 21 | if (err) { 22 | console.log(err.message, err.stack); 23 | res.status(500).json({ status: 'internal server error' }); 24 | } 25 | }); 26 | 27 | const portNum = process.env.PORT || 3000; 28 | 29 | app.listen(portNum, () => { 30 | console.log('stock proxy is listening on port ' + portNum); 31 | }); 32 | -------------------------------------------------------------------------------- /apps/stock-price-checker/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/stock-price-checker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bullseye-slim 2 | 3 | WORKDIR /app 4 | 5 | # Copy over all the files in the project directory to /app early 6 | # for rollup bundling 7 | COPY . . 8 | 9 | ENV PORT=3000 10 | ENV DB_URI=${STOCK_PRICE_CHECKER_DB_URI} 11 | 12 | RUN npm ci 13 | 14 | CMD ["npm", "start"] 15 | -------------------------------------------------------------------------------- /apps/stock-price-checker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "//1": "describes your app and its dependencies", 3 | "//2": "https://docs.npmjs.com/files/package.json", 4 | "//3": "updating this file will download and update your packages", 5 | "name": "my-hyperdev-app", 6 | "version": "0.0.1", 7 | "description": "What am I about?", 8 | "main": "server.js", 9 | "scripts": { 10 | "start": "node server.js", 11 | "test": "PORT=3005 mocha --exit --ui tdd tests/" 12 | }, 13 | "dependencies": { 14 | "body-parser": "1.20.3", 15 | "chai": "4.5.0", 16 | "chai-http": "4.4.0", 17 | "cors": "2.8.5", 18 | "dotenv": "14.3.2", 19 | "express": "4.21.2", 20 | "helmet": "3.23.3", 21 | "mocha": "9.2.2", 22 | "mongodb": "3.7.4", 23 | "request": "2.88.2" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "https://hyperdev.com/#!/project/welcome-project" 28 | }, 29 | "keywords": [ 30 | "node", 31 | "hyperdev", 32 | "express" 33 | ], 34 | "license": "MIT" 35 | } 36 | -------------------------------------------------------------------------------- /apps/stock-price-checker/public/script.js: -------------------------------------------------------------------------------- 1 | document.getElementById('testForm2').addEventListener('submit', e => { 2 | e.preventDefault(); 3 | const stock = e.target[0].value; 4 | const checkbox = e.target[1].checked; 5 | fetch(`/api/stock-prices/?stock=${stock}&like=${checkbox}`) 6 | .then(res => res.json()) 7 | .then(data => { 8 | document.getElementById('jsonResult').innerText = JSON.stringify(data); 9 | }); 10 | }); 11 | 12 | document.getElementById('testForm').addEventListener('submit', e => { 13 | e.preventDefault(); 14 | const stock1 = e.target[0].value; 15 | const stock2 = e.target[1].value; 16 | const checkbox = e.target[2].checked; 17 | console.log(stock1, stock2, checkbox); 18 | fetch(`/api/stock-prices?stock=${stock1}&stock=${stock2}&like=${checkbox}`) 19 | .then(res => res.json()) 20 | .then(data => { 21 | document.getElementById('jsonResult').innerText = JSON.stringify(data); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /apps/stock-price-checker/public/style.css: -------------------------------------------------------------------------------- 1 | div#examples { 2 | margin-left: 5%; 3 | margin-top: 5%; 4 | } 5 | 6 | form input { 7 | width: 100px; 8 | } 9 | 10 | hr { 11 | margin: 50px; 12 | } 13 | 14 | hr:last-of-type { 15 | margin-top: 200px; 16 | } 17 | 18 | div#testui { 19 | margin-left: 5%; 20 | } 21 | 22 | div#testui h2 { 23 | text-align: left; 24 | } 25 | -------------------------------------------------------------------------------- /apps/stock-price-checker/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | DB_URI="mongodb://localhost:27017/stock-checker" 3 | # NODE_ENV=test 4 | -------------------------------------------------------------------------------- /apps/sudoku-solver/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } 4 | -------------------------------------------------------------------------------- /apps/sudoku-solver/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/sudoku-solver/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/* 2 | .env 3 | .glitch-assets 4 | node_modules/ -------------------------------------------------------------------------------- /apps/sudoku-solver/controllers/puzzle-strings.js: -------------------------------------------------------------------------------- 1 | const puzzlesAndSolutions = [ 2 | [ 3 | '1.5..2.84..63.12.7.2..5.....9..1....8.2.3674.3.7.2..9.47...8..1..16....926914.37.', 4 | '135762984946381257728459613694517832812936745357824196473298561581673429269145378' 5 | ], 6 | [ 7 | '5..91372.3...8.5.9.9.25..8.68.47.23...95..46.7.4.....5.2.......4..8916..85.72...3', 8 | '568913724342687519197254386685479231219538467734162895926345178473891652851726943' 9 | ], 10 | [ 11 | '..839.7.575.....964..1.......16.29846.9.312.7..754.....62..5.78.8...3.2...492...1', 12 | '218396745753284196496157832531672984649831257827549613962415378185763429374928561' 13 | ], 14 | [ 15 | '.7.89.....5....3.4.2..4..1.5689..472...6.....1.7.5.63873.1.2.8.6..47.1..2.9.387.6', 16 | '473891265851726394926345817568913472342687951197254638734162589685479123219538746' 17 | ], 18 | [ 19 | '82..4..6...16..89...98315.749.157.............53..4...96.415..81..7632..3...28.51', 20 | '827549163531672894649831527496157382218396475753284916962415738185763249374928651' 21 | ] 22 | ]; 23 | 24 | module.exports = puzzlesAndSolutions; 25 | -------------------------------------------------------------------------------- /apps/sudoku-solver/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sudoku-solver", 3 | "version": "2.0.0", 4 | "description": "Quality Assurance 4: Sudoku Solver", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "mocha --timeout 5000 --require @babel/register --recursive --exit --ui tdd tests/" 9 | }, 10 | "dependencies": { 11 | "@babel/core": "7.27.4", 12 | "@babel/preset-env": "7.27.2", 13 | "@babel/register": "7.27.1", 14 | "body-parser": "1.20.3", 15 | "chai": "4.5.0", 16 | "chai-http": "4.4.0", 17 | "cors": "2.8.5", 18 | "dotenv": "14.3.2", 19 | "express": "4.21.2", 20 | "jsdom": "16.7.0", 21 | "mocha": "9.2.2" 22 | }, 23 | "license": "MIT" 24 | } 25 | -------------------------------------------------------------------------------- /apps/sudoku-solver/sample.env: -------------------------------------------------------------------------------- 1 | # Environment Config 2 | 3 | # store your secrets and config variables in here 4 | # only invited collaborators will be able to see your .env values 5 | 6 | # reference these in your code with process.env.SECRET 7 | # note: .env is a shell file so there can't be spaces around '=' 8 | 9 | PORT=3000 10 | # NODE_ENV=test 11 | -------------------------------------------------------------------------------- /apps/survey-form/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/survey-form/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "survey", 3 | "version": "0.0.1", 4 | "description": "Responsive Web Design project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "dotenv": "16.0.0", 11 | "express": "4.21.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/survey-form/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/survey-form/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/technical-documentation-page/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/technical-documentation-page/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tech-doc", 3 | "version": "0.0.1", 4 | "description": "Responsive Web Design project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "dotenv": "16.0.0", 11 | "express": "4.21.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/technical-documentation-page/sample.env: -------------------------------------------------------------------------------- 1 | PORT="" 2 | -------------------------------------------------------------------------------- /apps/technical-documentation-page/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/telephone-number-validator/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/telephone-number-validator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "telephone-number-validator", 3 | "version": "0.0.1", 4 | "description": "JavaScript Algorithms and Data Structures project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "dotenv": "16.0.0", 11 | "express": "4.21.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/telephone-number-validator/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/telephone-number-validator/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/timestamp-microservice/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/timestamp-microservice/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "timestamp", 3 | "version": "0.0.1", 4 | "description": "API project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "PORT=3005 mocha --exit --ui tdd tests/" 9 | }, 10 | "dependencies": { 11 | "chai": "4.5.0", 12 | "chai-http": "4.4.0", 13 | "cors": "2.8.5", 14 | "dotenv": "14.3.2", 15 | "express": "4.21.2", 16 | "mocha": "9.2.2" 17 | }, 18 | "license": "MIT" 19 | } 20 | -------------------------------------------------------------------------------- /apps/timestamp-microservice/public/style.css: -------------------------------------------------------------------------------- 1 | /****** Main Styling ******/ 2 | 3 | body { 4 | font-family: 'Roboto', sans-serif; 5 | font-size: 16px; 6 | color: #222; 7 | background-color: #fafafa; 8 | text-align: center; 9 | line-height: 1.4em; 10 | } 11 | 12 | .container { 13 | padding: 0; 14 | margin-top: 40px; 15 | } 16 | 17 | h3 { 18 | margin-top: 30px; 19 | } 20 | 21 | hr { 22 | margin: 25px; 23 | } 24 | 25 | .footer { 26 | margin-top: 40px; 27 | } 28 | 29 | code { 30 | font-family: monospace; 31 | padding: 2px; 32 | color: black; 33 | background-color: #fff; 34 | } 35 | 36 | ul { 37 | list-style-type: none; 38 | } 39 | 40 | li { 41 | margin-bottom: 0.5em; 42 | } 43 | 44 | a { 45 | color: #2574a9; 46 | } 47 | -------------------------------------------------------------------------------- /apps/timestamp-microservice/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/timestamp-microservice/views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Timestamp Microservice | freecodecamp.org 6 | 11 | 16 | 17 | 18 | 19 | 20 |

Timestamp Microservice

21 |
22 |
23 |

Example Usage:

24 | 28 | 29 |

Example Output:

30 |

31 | {"unix":1451001600000, "utc":"Fri, 25 Dec 2015 00:00:00 GMT"} 34 |

35 |
36 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /apps/treemap-diagram/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/treemap-diagram/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Video Game Data Set | 10 | Movies Data Set | 11 | Kickstarter Data Set 12 |

13 |
14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /apps/treemap-diagram/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "treemap-diagram", 3 | "version": "0.0.1", 4 | "description": "Data Visualization project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "dotenv": "16.0.0", 11 | "express": "4.21.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/treemap-diagram/public/styles.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | margin-top: 0.5em; 5 | padding: 0; 6 | } 7 | 8 | svg { 9 | font: 10px sans-serif; 10 | text-align: center; 11 | display: block; 12 | margin: auto; 13 | } 14 | 15 | a { 16 | text-decoration: none; 17 | } 18 | 19 | body { 20 | text-align: center; 21 | font-family: Arial, Helvetica, sans-serif; 22 | } 23 | 24 | .tile-text { 25 | cursor: default; 26 | } 27 | 28 | #title { 29 | font-size: 45px; 30 | margin-bottom: 10px; 31 | } 32 | 33 | #description { 34 | padding-bottom: 1.5rem; 35 | } 36 | 37 | div.tooltip { 38 | position: absolute; 39 | padding: 10px; 40 | font: 12px Arial; 41 | background: rgba(255, 255, 204, 0.95); 42 | box-shadow: 1px 1px 10px rgba(128, 128, 128, 0.6); 43 | border: 0; 44 | border-radius: 2px; 45 | pointer-events: none; 46 | } 47 | 48 | #legend { 49 | font-size: 15px; 50 | text-align: center; 51 | display: block; 52 | margin: auto; 53 | padding-bottom: 5px; 54 | margin-bottom: 2rem; 55 | margin-top: 1rem; 56 | } 57 | -------------------------------------------------------------------------------- /apps/treemap-diagram/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/treemap-diagram/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/tribute-page/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/tribute-page/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tribute", 3 | "version": "0.0.1", 4 | "description": "Responsive Web Design project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "dotenv": "16.0.0", 11 | "express": "4.21.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/tribute-page/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | -------------------------------------------------------------------------------- /apps/tribute-page/server.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const express = require('express'); 3 | const app = express(); 4 | 5 | app.use(express.static('public')); 6 | 7 | app.get('', (req, res) => { 8 | res.sendFile(`${process.cwd()}/index.html`); 9 | }); 10 | 11 | app.get('/status/ping', (req, res) => { 12 | res.send({ msg: 'pong' }).status(200); 13 | }); 14 | 15 | const portNum = process.env.PORT || 3000; 16 | 17 | app.listen(portNum, () => { 18 | console.log(`Listening on port ${portNum}`); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/twitch-proxy/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/twitch-proxy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bullseye-slim 2 | 3 | WORKDIR /app 4 | 5 | # Copy over all the files in the project directory to /app early 6 | # for rollup bundling 7 | COPY . . 8 | 9 | ENV PORT=3000 10 | ENV TWITCH_CLIENT_ID=${TWITCH_PROXY_TWITCH_CLIENT_ID} 11 | ENV TWITCH_CLIENT_SECRET=${TWITCH_PROXY_TWITCH_CLIENT_SECRET} 12 | 13 | RUN npm ci 14 | 15 | CMD ["npm", "start"] 16 | -------------------------------------------------------------------------------- /apps/twitch-proxy/README.md: -------------------------------------------------------------------------------- 1 | # Twitch Proxy 2 | -------------------------------------------------------------------------------- /apps/twitch-proxy/config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config({ silent: true }); 2 | 3 | module.exports = { 4 | serveStaticData: false, 5 | twitchCID: process.env.TWITCH_CLIENT_ID, 6 | accessTOKEN: process.env.ACCESS_TOKEN, 7 | port: parseInt(process.env.PORT) || 3000, 8 | host: 'https://twitch-proxy.freecodecamp.rocks/', 9 | logLevels: { 10 | _default: 'info', 11 | file: 'warn', 12 | filePath: `${__dirname}/.logs` 13 | }, 14 | validRoutes: { 15 | streams: { q: ['user_id', 'user_login'] }, 16 | users: { q: ['login', 'id'] }, 17 | games: { q: ['id'] } 18 | }, 19 | validLegacyRoutes: ['streams', 'users', 'channels'], 20 | db: { 21 | dbPath: `${__dirname}/.data/db`, 22 | dataExpirationSecs: 5400, 23 | enableCompaction: true, 24 | compactionMillisecs: 2705000 25 | }, 26 | outboundReqsLimiter: { 27 | active: true, 28 | checkIntervalMs: 15000, 29 | maxOBReqsPerInterval: 55 30 | }, 31 | blacklist: { 32 | active: true, 33 | urls: [ 34 | 'www.donotargue.com', 35 | 'www.csulbesports.org', 36 | 'https://www.photoshoptroll.com' 37 | ], 38 | ips: ['78.145.103.20', '107.178.192.90', '204.236.208.181'], 39 | cooldown: 3000 40 | }, 41 | baseApiUrl: 'https://api.twitch.tv' 42 | }; 43 | -------------------------------------------------------------------------------- /apps/twitch-proxy/kill-node-process-cron.js: -------------------------------------------------------------------------------- 1 | const cron = require('node-cron'); 2 | 3 | cron.schedule('0 0 1 * *', () => { 4 | process.kill(1); 5 | }); 6 | -------------------------------------------------------------------------------- /apps/twitch-proxy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fcc-twitch-api-passthrough", 3 | "version": "2.0.0", 4 | "description": "fCC Twitch API passthrough server", 5 | "main": "server.js", 6 | "scripts": { 7 | "prestart": "node kill-node-process-cron.js & node update-api-key.mjs", 8 | "start": "node server.js" 9 | }, 10 | "dependencies": { 11 | "@seald-io/nedb": "2.2.2", 12 | "cors": "2.8.5", 13 | "dotenv": "14.3.2", 14 | "express": "5.0.0-beta.3", 15 | "node-cron": "3.0.3", 16 | "node-fetch": "3.3.0", 17 | "request": "2.88.2", 18 | "winston": "3.17.0", 19 | "winston-daily-rotate-file": "3.10.0" 20 | }, 21 | "license": "MIT" 22 | } 23 | -------------------------------------------------------------------------------- /apps/twitch-proxy/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | TWITCH_CLIENT_ID= 3 | TWITCH_CLIENT_SECRET= 4 | # Expires in ~60 days 5 | ACCESS_TOKEN= 6 | -------------------------------------------------------------------------------- /apps/twitch-proxy/static-data/helix/games.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [ 3 | { 4 | "id": "504689", 5 | "name": "Farming Simulator 19", 6 | "box_art_url": "https://static-cdn.jtvnw.net/ttv-boxart/Farming%20Simulator%2019-{width}x{height}.jpg" 7 | }, 8 | { 9 | "id": "490422", 10 | "name": "StarCraft II", 11 | "box_art_url": "https://static-cdn.jtvnw.net/ttv-boxart/StarCraft%20II-{width}x{height}.jpg" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /apps/twitch-proxy/static-data/helix/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | users: require('./users.json'), 3 | streams: require('./streams.json'), 4 | games: require('./games.json') 5 | }; 6 | -------------------------------------------------------------------------------- /apps/twitch-proxy/update-api-key.mjs: -------------------------------------------------------------------------------- 1 | import { join, dirname } from 'path'; 2 | 3 | import { config } from 'dotenv'; 4 | import fetch from 'node-fetch'; 5 | import { writeFileSync } from 'fs'; 6 | import { fileURLToPath } from 'url'; 7 | 8 | const __dirname = dirname(fileURLToPath(new URL(import.meta.url))); 9 | const envFilePath = join(`${__dirname}/`, '.env'); 10 | config({ path: envFilePath }); 11 | 12 | const { TWITCH_CLIENT_ID, TWITCH_CLIENT_SECRET, PORT } = process.env; 13 | 14 | const updateApiKey = async () => { 15 | const url = `https://id.twitch.tv/oauth2/token?client_id=${TWITCH_CLIENT_ID}&client_secret=${TWITCH_CLIENT_SECRET}&grant_type=client_credentials`; 16 | const keyObj = await fetch(url, { 17 | method: 'POST' 18 | }) 19 | .then(res => res.json()) 20 | .catch(err => console.log(err)); 21 | 22 | const accessToken = keyObj.access_token; 23 | if (!accessToken) { 24 | return console.error('Twitch api did not return an accessToken'); 25 | } 26 | 27 | // Write new file with updated token 28 | writeFileSync( 29 | envFilePath, 30 | `PORT=${PORT} 31 | TWITCH_CLIENT_ID=${TWITCH_CLIENT_ID} 32 | TWITCH_CLIENT_SECRET=${TWITCH_CLIENT_SECRET} 33 | # Expires in ~60 days 34 | ACCESS_TOKEN=${accessToken} 35 | ` 36 | ); 37 | }; 38 | 39 | updateApiKey(); 40 | -------------------------------------------------------------------------------- /apps/twitch-proxy/utilities/blacklist.js: -------------------------------------------------------------------------------- 1 | const log = require('./logger'); 2 | const { 3 | blacklist: { cooldown, urls = [], ips = [], active } 4 | } = require('../config'); 5 | 6 | module.exports = function (req, res, next) { 7 | const ref = req.headers.referer || ''; 8 | if (active && (ips.indexOf(req.ip) > -1 || urls.find(r => ref.match(r)))) { 9 | log.warn(`>> ${ref} - ${req.ip} blacklisted`); 10 | setTimeout(function () { 11 | return res.status(403).send(); 12 | }, parseInt(cooldown) || 2000); 13 | } else { 14 | next(); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /apps/twitch-proxy/utilities/legacy-data-handler.js: -------------------------------------------------------------------------------- 1 | const streamers = require('../static-data/kraken.json'); 2 | 3 | function getStreamersData(type, user) { 4 | if (!streamers[user]) return undefined; 5 | return streamers[user][type]; 6 | } 7 | 8 | module.exports = getStreamersData; 9 | -------------------------------------------------------------------------------- /apps/twitch-proxy/utilities/logger.js: -------------------------------------------------------------------------------- 1 | const { createLogger, format, transports } = require('winston'); 2 | require('winston-daily-rotate-file'); 3 | 4 | const { printf, timestamp, combine } = format; 5 | 6 | const _format = printf(info => { 7 | return `${info.timestamp} [${info.level.toUpperCase()}] ${info.message}`; 8 | }); 9 | 10 | const { 11 | logLevels: { _default, file, filePath } 12 | } = require('../config'); 13 | 14 | const logger = createLogger({ 15 | format: combine(timestamp(), _format), 16 | transports: [new transports.Console({ level: _default })] 17 | }); 18 | 19 | if (file) { 20 | logger.add( 21 | new transports.DailyRotateFile({ 22 | filename: `${filePath}/tw-%DATE%.log`, 23 | datePattern: 'YYYY-MM-DD', 24 | maxSize: '10m', 25 | maxFiles: '2d', 26 | level: file 27 | }) 28 | ); 29 | } 30 | 31 | logger.info( 32 | `logger initialized with console level: ${_default}, file level: ${ 33 | file || 'none' 34 | }` 35 | ); 36 | 37 | module.exports = logger; 38 | -------------------------------------------------------------------------------- /apps/twitch-proxy/utilities/outbound-reqs-limiter.js: -------------------------------------------------------------------------------- 1 | const { 2 | outboundReqsLimiter: { active, maxOBReqsPerInterval, checkIntervalMs } 3 | } = require('../config'); 4 | const log = require('./logger'); 5 | 6 | var outReqCounter = { tstamp: Date.now(), req: 0 }; 7 | var outReqLimiter = function (req) { 8 | if (!active) return false; 9 | var now = Date.now(); 10 | if (now - outReqCounter.tstamp <= checkIntervalMs) { 11 | outReqCounter.req++; 12 | } else { 13 | outReqCounter = { tstamp: Date.now(), req: 1 }; 14 | } 15 | var elapsed = now - outReqCounter.tstamp; 16 | log.info(`** ob rate - ${outReqCounter.req} reqs / ${elapsed} ms`); 17 | if (outReqCounter.req >= maxOBReqsPerInterval) { 18 | const { 19 | headers: { referer }, 20 | ip, 21 | originalUrl 22 | } = req; 23 | log.warn(`>> RATE LIMITER - ${referer} - ${ip} - ${originalUrl}`); 24 | return true; 25 | } 26 | return false; 27 | }; 28 | 29 | module.exports = outReqLimiter; 30 | -------------------------------------------------------------------------------- /apps/twitch-proxy/utilities/static-data-handler.js: -------------------------------------------------------------------------------- 1 | const db = require('../static-data/helix'); 2 | 3 | const checkItem = (item, qs) => { 4 | const keys = Object.keys(qs); 5 | for (let i = 0; i < keys.length; i++) { 6 | const k = keys[i]; 7 | let query = qs[k]; 8 | query = Array.isArray(query) ? query : [query]; 9 | const found = query.indexOf(item[k]) > -1; 10 | if (found) return true; 11 | } 12 | return false; 13 | }; 14 | function staticHandler(type, qs) { 15 | const resp = db[type]; 16 | if (!resp) return; 17 | const data = resp.data.filter(i => checkItem(i, qs)); 18 | return { data }; 19 | } 20 | 21 | module.exports = staticHandler; 22 | -------------------------------------------------------------------------------- /apps/url-shortener-microservice/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/url-shortener-microservice/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bullseye-slim 2 | 3 | WORKDIR /app 4 | 5 | # Copy over all the files in the project directory to /app early 6 | # for rollup bundling 7 | COPY . . 8 | 9 | ENV PORT=3000 10 | ENV DB_URI=${URL_SHORTENER_MICROSERVICE_DB_URI} 11 | 12 | RUN npm ci 13 | 14 | CMD ["npm", "start"] 15 | -------------------------------------------------------------------------------- /apps/url-shortener-microservice/models/counters.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mongoose = require('mongoose'); 4 | const Schema = mongoose.Schema; 5 | 6 | const Counters = new Schema({ 7 | count: { type: Number, default: 1 } 8 | }); 9 | 10 | module.exports = mongoose.model('Counters', Counters, 'counters'); 11 | -------------------------------------------------------------------------------- /apps/url-shortener-microservice/models/urlEntries.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mongoose = require('mongoose'); 4 | const Schema = mongoose.Schema; 5 | 6 | const UrlEntries = new Schema({ 7 | url: { type: String, required: true }, 8 | index: { type: Number, required: true } 9 | }); 10 | 11 | module.exports = mongoose.model('UrlEntries', UrlEntries, 'url_entries'); 12 | -------------------------------------------------------------------------------- /apps/url-shortener-microservice/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shorturl", 3 | "version": "0.0.1", 4 | "description": "API project for freeCodeCamp", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "PORT=3005 mocha --exit --ui tdd tests/" 9 | }, 10 | "dependencies": { 11 | "chai": "4.5.0", 12 | "chai-http": "4.4.0", 13 | "cors": "2.8.5", 14 | "dotenv": "14.3.2", 15 | "express": "4.21.2", 16 | "mocha": "9.2.2", 17 | "mongoose": "5.13.22" 18 | }, 19 | "license": "MIT" 20 | } 21 | -------------------------------------------------------------------------------- /apps/url-shortener-microservice/public/style.css: -------------------------------------------------------------------------------- 1 | /****** Main Styling ******/ 2 | 3 | body { 4 | font-family: 'Roboto', sans-serif; 5 | font-size: 16px; 6 | color: #222; 7 | background-color: #fafafa; 8 | text-align: center; 9 | line-height: 1.4em; 10 | } 11 | 12 | .container { 13 | padding: 0; 14 | margin-top: 40px; 15 | } 16 | 17 | h3 { 18 | margin-top: 30px; 19 | } 20 | 21 | .footer { 22 | margin-top: 40px; 23 | } 24 | 25 | .user-stories { 26 | position: relative; 27 | text-align: justify; 28 | max-width: 700px; 29 | margin: 15px auto; 30 | } 31 | code { 32 | font-family: monospace; 33 | padding: 2px; 34 | color: black; 35 | background-color: #fff; 36 | } 37 | 38 | a { 39 | color: #2574a9; 40 | } 41 | 42 | form { 43 | border: solid 1px black; 44 | border-radius: 5px; 45 | margin: 10px auto; 46 | padding: 20px; 47 | max-width: 600px; 48 | } 49 | 50 | label { 51 | margin-right: 10px; 52 | } 53 | 54 | input { 55 | padding: 5px; 56 | } 57 | 58 | input[type='text'] { 59 | width: 220px; 60 | text-align: center; 61 | } 62 | -------------------------------------------------------------------------------- /apps/url-shortener-microservice/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | DB_URI="mongodb://localhost:27017/url-shortener" 3 | -------------------------------------------------------------------------------- /apps/voting-app/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/voting-app/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/* 2 | .env 3 | node_modules/ 4 | .DS_Store -------------------------------------------------------------------------------- /apps/voting-app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bullseye-slim 2 | 3 | WORKDIR /app 4 | 5 | # Copy over all the files in the project directory to /app early 6 | # for rollup bundling 7 | COPY . . 8 | 9 | ENV PORT=3000 10 | ENV MONGO_URI=${VOTING_APP_MONGO_URI} 11 | 12 | RUN npm ci 13 | 14 | CMD ["npm", "start"] 15 | -------------------------------------------------------------------------------- /apps/voting-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "voteapp", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node app.js" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "body-parser": "1.20.3", 13 | "dotenv": "14.3.2", 14 | "ejs": "3.1.10", 15 | "express": "4.21.2", 16 | "mongodb": "3.7.4" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /apps/voting-app/public/main.js: -------------------------------------------------------------------------------- 1 | const dontToggle = ['INPUT', 'OPTION', 'SELECT']; 2 | 3 | $('.poll-wrap').click(function (e) { 4 | if (!dontToggle.includes(e.target.tagName)) { 5 | $(this).children().last().slideToggle('slow'); 6 | } 7 | }); 8 | 9 | $('select').change(function () { 10 | if ($(this).val() === 'Add option') { 11 | $(this).next().prop('disabled', false); 12 | } else { 13 | $(this).next().prop('disabled', true); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /apps/voting-app/sample.env: -------------------------------------------------------------------------------- 1 | # In order to get the database connection to work, I had to set the node env to 2.2.12 or later on mongo atlas, see link: 2 | # https://stackoverflow.com/questions/49101567/mongodb-atlas-not-authorized-on-admin-to-execute-command/57182045#57182045 3 | 4 | PORT=3000 5 | MONGO_URI="mongodb://localhost:27017/voting-app" 6 | -------------------------------------------------------------------------------- /apps/voting-app/views/footer.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /apps/voting-app/views/head.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Voting App 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /apps/voting-app/views/login.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%- include("head") -%> 5 | 6 | 7 | <%- include("navigation") -%> 8 | 9 |
10 |
11 |
12 | Username:
13 | Password:
14 | 15 |
16 | 17 | Don't have an account? 18 | Sign up
19 | Or login with my default user...
20 | username: you
21 | password: you 22 |
23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /apps/voting-app/views/mypolls.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%- include("head") -%> 5 | 6 | 7 | <%- include("navigation") -%> 8 | 9 |
10 |

My Polls


11 |
12 | <% if (usersPolls) { %> 13 | <% usersPolls.forEach(poll => { %> 14 |

<%= poll['question'] %>

15 | ">Delete 16 | Share 17 | 18 | <% for (var prop in poll['options']) { %> 19 |

<%= prop +':'+ poll['options'][prop] %>

20 | <% } %> 21 |
22 | <% }) %> 23 | <% } %> 24 |
25 |
26 | 27 | <%- include("footer") -%> 28 | 29 | 30 | -------------------------------------------------------------------------------- /apps/voting-app/views/navigation.ejs: -------------------------------------------------------------------------------- 1 |
2 | <% if (user) { %> 3 | 13 | <% } else { %> 14 | 23 | <% } %> 24 |
25 | -------------------------------------------------------------------------------- /apps/voting-app/views/newpoll.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%- include("head") -%> 5 | 6 | 7 | <%- include("navigation") -%> 8 | 9 |
10 |

New Poll


11 |
12 |
13 | Question:
14 | Options (separated by commas):
15 |
16 | 17 |
18 | 19 | Cancel 20 |
21 |
22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /apps/voting-app/views/signup.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%- include("head") -%> 5 | 6 | 7 | <%- include("navigation") -%> 8 | 9 |
10 |
11 |
12 | Username:
13 | Password:
14 | 15 |
16 | 17 | Already have an account? 18 | Log in
19 | Don't feel like signing up?
20 | You can login with my default user...
21 | username: you
22 | password: you 23 |
24 |
25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /apps/weather-proxy/.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /apps/weather-proxy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bullseye-slim 2 | 3 | WORKDIR /app 4 | 5 | # Copy over all the files in the project directory to /app early 6 | # for rollup bundling 7 | COPY . . 8 | 9 | ENV PORT=3000 10 | ENV OPEN_WEATHER_API_KEY=${WEATHER_PROXY_OPEN_WEATHER_API_KEY} 11 | 12 | RUN npm ci 13 | 14 | CMD ["npm", "start"] 15 | -------------------------------------------------------------------------------- /apps/weather-proxy/README.md: -------------------------------------------------------------------------------- 1 | # Weather Proxy 2 | -------------------------------------------------------------------------------- /apps/weather-proxy/data/cache.json: -------------------------------------------------------------------------------- 1 | { 2 | "coord": { 3 | "lon": 139, 4 | "lat": 35 5 | }, 6 | "weather": [ 7 | { 8 | "id": 803, 9 | "main": "Clouds", 10 | "description": "broken clouds", 11 | "icon": "https://cdn.freecodecamp.org/weather-icons/04d.png" 12 | } 13 | ], 14 | "base": "stations", 15 | "main": { 16 | "temp": 28.23, 17 | "pressure": 1011, 18 | "humidity": 74, 19 | "temp_min": 26, 20 | "temp_max": 31 21 | }, 22 | "visibility": 10000, 23 | "wind": { 24 | "speed": 3.6, 25 | "deg": 230 26 | }, 27 | "clouds": { 28 | "all": 75 29 | }, 30 | "dt": 1499396400, 31 | "sys": { 32 | "type": 1, 33 | "id": 7616, 34 | "message": 0.0043, 35 | "country": "JP", 36 | "sunrise": 1499369792, 37 | "sunset": 1499421666 38 | }, 39 | "id": 1851632, 40 | "name": "Shuzenji", 41 | "cod": 200 42 | } 43 | -------------------------------------------------------------------------------- /apps/weather-proxy/data/imgLinks.json: -------------------------------------------------------------------------------- 1 | { 2 | "01d": "https://cdn.freecodecamp.org/weather-icons/01d.png", 3 | "01n": "https://cdn.freecodecamp.org/weather-icons/01n.png", 4 | "02d": "https://cdn.freecodecamp.org/weather-icons/02d.png", 5 | "02n": "https://cdn.freecodecamp.org/weather-icons/02n.png", 6 | "03d": "https://cdn.freecodecamp.org/weather-icons/03d.png", 7 | "03n": "https://cdn.freecodecamp.org/weather-icons/03n.png", 8 | "04d": "https://cdn.freecodecamp.org/weather-icons/04d.png", 9 | "04n": "https://cdn.freecodecamp.org/weather-icons/04n.png", 10 | "09d": "https://cdn.freecodecamp.org/weather-icons/09d.png", 11 | "09n": "https://cdn.freecodecamp.org/weather-icons/09n.png", 12 | "10d": "https://cdn.freecodecamp.org/weather-icons/10d.png", 13 | "10n": "https://cdn.freecodecamp.org/weather-icons/10n.png", 14 | "11d": "https://cdn.freecodecamp.org/weather-icons/11d.png", 15 | "11n": "https://cdn.freecodecamp.org/weather-icons/11n.png", 16 | "13d": "https://cdn.freecodecamp.org/weather-icons/13d.png", 17 | "13n": "https://cdn.freecodecamp.org/weather-icons/13n.png", 18 | "50d": "https://cdn.freecodecamp.org/weather-icons/50d.png", 19 | "50n": "https://cdn.freecodecamp.org/weather-icons/50n.png" 20 | } 21 | -------------------------------------------------------------------------------- /apps/weather-proxy/images/01d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/01d.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/01n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/01n.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/02d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/02d.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/02n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/02n.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/03d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/03d.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/03n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/03n.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/04d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/04d.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/04n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/04n.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/09d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/09d.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/09n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/09n.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/10d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/10d.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/10n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/10n.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/11d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/11d.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/11n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/11n.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/13d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/13d.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/13n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/13n.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/50d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/50d.png -------------------------------------------------------------------------------- /apps/weather-proxy/images/50n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/freeCodeCamp/demo-projects/fc6a98ea7a69bce8540ff6a2b0ea88c8a91bcb4a/apps/weather-proxy/images/50n.png -------------------------------------------------------------------------------- /apps/weather-proxy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fcc-weather-api", 3 | "version": "1.0.0", 4 | "description": "fCC Weather API Passthrough", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "cors": "2.8.5", 11 | "dotenv": "14.3.2", 12 | "express": "4.21.2", 13 | "request": "2.88.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/weather-proxy/sample.env: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | OPEN_WEATHER_API_KEY= 3 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | roots: ['./test/'], 3 | setupFiles: ['dotenv/config'] 4 | }; 5 | 6 | module.exports = config; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@freecodecamp/demo-projects", 3 | "version": "0.0.0", 4 | "engines": { 5 | "node": "18", 6 | "npm": "9" 7 | }, 8 | "description": "Example projects for the freeCodeCamp.org curriculum", 9 | "scripts": { 10 | "lint": "eslint apps test --max-warnings 0 && prettier --check .", 11 | "prebuild": "npm run create-caddyfile && node scripts/copy-dockerignore.js", 12 | "prestart": "npm run create-caddyfile && node scripts/copy-dockerignore.js", 13 | "build": "docker compose build", 14 | "create-caddyfile": "node scripts/create-caddyfile.js", 15 | "start": "docker compose up -d", 16 | "stop": "docker compose down", 17 | "format": "prettier --write .", 18 | "prepare": "husky install", 19 | "test": "jest" 20 | }, 21 | "devDependencies": { 22 | "@types/jest": "^29.5.10", 23 | "axios": "1.9.0", 24 | "eslint-config-prettier": "8.7.0", 25 | "eslint-plugin-react": "7.37.4", 26 | "husky": "8.0.3", 27 | "jest": "29.7.0", 28 | "lint-staged": "15.5.2", 29 | "prettier": "2.8.8" 30 | }, 31 | "dependencies": { 32 | "dotenv": "^16.0.3" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["github>freecodecamp/renovate-config:schedule-weekend"] 3 | } 4 | -------------------------------------------------------------------------------- /scripts/copy-dockerignore.js: -------------------------------------------------------------------------------- 1 | const { copyFileSync } = require('fs'); 2 | const { join } = require('path'); 3 | const portMap = require('../port-map.json'); 4 | 5 | Object.keys(portMap).forEach(name => { 6 | copyFileSync( 7 | join(__dirname, '../shared.dockerignore'), 8 | join(__dirname, `../apps/${name}/.dockerignore`) 9 | ); 10 | }); 11 | -------------------------------------------------------------------------------- /shared.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-bullseye-slim 2 | 3 | WORKDIR /app 4 | 5 | # Copy over all the files in the project directory to /app early 6 | # for rollup bundling 7 | COPY . . 8 | 9 | ENV PORT=3000 10 | 11 | RUN npm ci 12 | 13 | CMD ["npm", "start"] 14 | -------------------------------------------------------------------------------- /shared.dockerignore: -------------------------------------------------------------------------------- 1 | .env 2 | .git 3 | .gitignore 4 | .dockerignore 5 | node_modules 6 | Dockerfile 7 | -------------------------------------------------------------------------------- /test/jest-utils.js: -------------------------------------------------------------------------------- 1 | const baseUrl = port => { 2 | if (process.env.DEMO_APPS_DOMAIN === 'localhost') { 3 | const url = new URL('http://localhost'); 4 | url.port = port; 5 | return url; 6 | } 7 | 8 | if (process.env.GITPOD_HOST) { 9 | const url = new URL( 10 | `https://${port}-${process.env.GITPOD_WORKSPACE_ID}.${process.env.GITPOD_WORKSPACE_CLUSTER_HOST}` 11 | ); 12 | return url; 13 | } 14 | }; 15 | 16 | module.exports = { 17 | baseUrl 18 | }; 19 | --------------------------------------------------------------------------------