# Portfolio [![wakatime](https://wakatime.com/badge/user/134d8831-a75e-46b4-80ff-d330f34e449a/project/2b514dd0-4607-4523-a67a-c885733663cc.svg)](https://wakatime.com/badge/user/134d8831-a75e-46b4-80ff-d330f34e449a/project/2b514dd0-4607-4523-a67a-c885733663cc) A massively over-the-top portfolio website to display stats, projects, skills, education and experience. [TOC] ## Background My main reason for working on this project is due to my lack of open source projects, as well as my previous portfolio being heavily outdated. I also didn't want to have to edit the source code when adding a new project/skill or similar, so most of the content on this site is dynamic, and can be edited on the site through a simple, easy to use UI. *Pre-warning: Frontend development is not my forte, don't expect too much* ## Features ### Projects The main part of any portfolio are your projects. As time goes on, you're likely to add more and more projects causing a simple projects page can quickly become large and cluttered. The projects page includes pagination, sorting by date added, project name, if the project is open source, ID in ascending or descending order and even filtering by a certain skill, simply by clicking on it! You can also click "Learn More" on each project to bring up all the details for that project on a single page! Each project has the following information: - Project Name - Short Description - Long/Detailed Description - List of Skills (Technologies/Languages/Frameworks etc) - Open Source - Repository URL - Contributors - Date Added - Image ![](docs/images/Projects.png) ![](docs/images/DetailedProject.png) ![](docs/images/AdminProjects.png) ![](docs/images/EditProject.png) ![](docs/images/CreateProject.png) ### Skills Skills are closely related to projects. Each project can have any number of skills assigned to it. Skills are grouped by their type, which can be anything you desire. For example, skills AWS, Azure, GCP can all be grouped by the type of "Cloud Provider", or you can group programming languages together. On the skills page, it shows how many projects that skill is used in, including a button which can filter the projects by ones that use that specific skill. Each skill has the following information: - Skill Name - Type - Link - Image URL - Badge Colour ![](docs/images/Skills.png) ![](docs/images/AdminSkills.png) ![](docs/images/EditSkills.png) ![](docs/images/CreateSkill.png) ### Education Your education history can be added to display a timeline of your qualifications, showing your most recent at the top, and oldest at the bottom. The start and ending year of each qualification is displayed, as well as the time since it ended. Each education has the following information: - Subject Name - Education Level (GSCE, A-Level, Degree etc) - Location - Grade - Start Year - Ending Year ![](docs/images/Education.png) ![](docs/images/AdminEducation.png) ![](docs/images/CreateEducation.png) ### Experience Experience is used to display previous employment or significant project experience. Experience is displayed on a timeline with the newest at the top. Current positions can also be added by omitting the ending date. Each experience has the following: - Job/Position Title - Company - Description - URL - Icon - Starting Date - Ending Date ![](docs/images/Experience.png) ![](docs/images/AdminExperience.png) ![](docs/images/CreateExperience.png) ### Contacts Should someone wish to contact me, this page contains a range of method of contact, or just generic social media. Each contact has the following: - Title - Text - Link - Base Site Link - Icon - Icon Colour ![](docs/images/Contacts.png) ![](docs/images/AdminContacts.png) ![](docs/images/CreateContact.png) ### Stats Several stats from various sources are displayed on the statistics page, mainly regarding development activity. #### WakaTime WakaTime is the tool I use to track the time I spend developing with in a range of programs and IDEs. It includes: - A graph displaying my time spend developing over the last 30 days - The highest time spend developing in a single day - Daily average in the last week - Time spend developing in the last 7 days - Global WakaTime leaderboard position - Last recorded activity. ![](docs/images/WakaTime.png) #### GitLab/GitHub Stats GitLab is my preferred Git platform for storing and managing my code, so most of my Git related activity will occur there. Both GitLab and GitHub stats include: - How many contributions have been made over the last x consecutive days - How many contributions have been made in the last year - The day in which the most contributions were made (Within the last year) - When the last contribution was ![](docs/images/GitStats.png) ### Passwordless Login Passwordless login allows a user to login to the site using security keys or biometrics on mobile phones using the WebAuthn API. Since I don't own a physical security key, this is currently only tested to work with Touch ID/Face ID on Apple devices. ![](docs/images/WebAuthnLogin.mp4) ### Full Admin Dashboard The admin dashboard contains pages to add, edit and remove each resource though an easy-to-use UI. ![](docs/images/AdminDashboard.png) From the dashboard you can: - Add new projects - Edit existing projects - Delete projects - Add new skills - Edit existing skills - Delete skills - Add education history - Delete education history - Add experience - Remove experience - Add contacts - Remove contacts ### Spotify Status Just a cool small feature to display my currently playing song on Spotify, on the landing page. ![](docs/images/SpotifyStatus.png) ## Setup While this isn't a 100% generic portfolio (some parts are not dynamic and are specific to me), if you wish to use this portfolio as your own, you can fork it, make the required static changes and follow the deployment information below. Before proceeding with the following setup, please ensure the dependencies are installed by running `npm install` as some setup scripts use external packages. ### Requirements - [NodeJS](https://nodejs.org) (I used v14) - [MySQL 5.7 Database](https://dev.mysql.com/downloads/mysql/5.7.html) - [Redis Instance](https://redis.com) - [Spotify Developer Application](https://developer.spotify.com/dashboard/applications) - [WakaTime Account](https://wakatime.com/signup) - [GitLab Account](https://gitlab.com/users/sign_up) - [GitHub Account](https://github.com/signup) ### Database Setup Create a schema in the database called `portfolio` and create a MySQL user that has permission to use the `portfolio` schema. Run the SQL commands in [resources/databaseSchema.sql](./resources/databaseSchema.sql) to create all the required tables. You can then set the following environment variables regarding the database: `MYSQL_HOST` - The IP of the database `MYSQL_DATABASE` - The database schema name, `portfolio` unless using a different name in the previous step `MYSQL_USERNAME` - The username of the user created in the previous step `MYSQL_PASSWORD` - The password of the user created in the previous step `MYSQL_PORT` - The port of the MySQL server, unless manually changed, 3306 ### Redis Setup Create your Redis URI for your instance using the following format: *In most cases, username can be left blank.* `redis://[USERNAME]:[PASSWORD]@[IP]:[PORT]` For example: `redis://:password1234@1.2.3.4:6379` The `REDIS_URI` environment variable can then be set to this value. ### Creating Users Since any user account has full access to the admin dashboard, there is no register functionality on the site itself, regular users have no need to have an account at all. Instead, you can use the create-user script to walk you through some questions about the user. The script will then output a table for each column in the database with the required value for creating this user, as well as an SQL query for you to run to create the user. To use this script, simply run `npm run create-user`. ![](docs/images/CreatingUser.gif) ### Environment Variables The `ENVIRONMENT` variable specifies which environment the app is currently running in. This should be `dev` or `prod`. The only difference this makes is that GitLab, GitHub and WakaTime stats are not cached in `dev`. For details on the MYSQL_* variables, see [Database Setup](#database-setup) For details on the REDIS_* variables, see [Redis Setup](#redis-setup) #### JWT Variables Environment variables which begin with `JWT_` are related to the generation and validation of JSON Web Tokens, used for authentication. `JWT_PRIVATE_KEY` and `JWT_PUBLIC_KEY` are used to sign and verify the signature of tokens. They can be generated using the `npm run generate-jwt-key-pair` script. This script will ask you for the desired key length, I recommend `1024` or `2048` due to the Vercel environment variable size limit (enforced by AWS). You will then be asked the output location of the generated keys. If you select `Console`, the keys will simply be printed into the console, where you can then copy-paste them wherever you need. Or, you can select `Environment Variable File` which will then ask you the filename of the env file, `.env.local` by default. The script will then insert the generated key pair into the environment variable file in the correct format. If the variables exist, they will be replaced with the newly generated keys, or they will be added to the end of the file. ![](docs/images/GeneratingJWTKeyPair.gif) Finally, set the `AUTH_COOKIE_DOMAIN` to the domain you are hosting the portfolio on. For example, `dantarr.dev`. #### Spotify Variables To access the Spotify API to display the currently playing song on the landing page, you will need to create a developer application [here](https://developer.spotify.com/dashboard/applications). On the application dashboard, you can find the Client ID at the top for the `SPOTIFY_CLIENT_ID` variable. Clicking the `SHOW CLIENT SECRET` button will display the Client Secret value for the `SPOTIFY_CLIENT_SECRET` variable. `SPOTIFY_REDIRECT_URI` should be set to `http://localhost` as it is only used when running locally to get initial credentials, which are then refreshed when required. Before the Spotify integration will work, you will need to get an initial access token and refresh token from Spotify. To begin, run `npm run get-spotify-credentials`. This script should open the Spotify authorization page in your browser. Login to Spotify if needed, authorize the app then you should be able to see in the console the SQL query to insert the initial credentials into the database. *Note: Depending on your console, when copying the query which usually spans multiple lines, the new lines may also be included, which must be removed before the query is run* **While it is often best practice to not store access tokens in plain text, I don't feel like this is required in this instance due to the very limited scope of this token (Only `user-read-playback-state`), and limited lifespan of the access token (1 hour, cannot be refreshed without the Client Secret, which is not stored in the database)** #### WakaTime Variable To get the statistics for WakaTime, your API key is needed. This can be found [here](https://wakatime.com/settings/account) under "API Key" at the top. #### GitLab Variables To get your GitLab statistics, there are 3 GitLab related environment variables that are needed. The first is `GITLAB_USERNAME` which is simply the username of your GitLab account. Your GitLab user ID is also required, however, it is not displayed anywhere on the UI. Providing you are logged into GitLab, you can visit `https://gitlab.com/api/v4/users?username=YOUR_USERNAME` in your browser to get your user ID. This value can then be put into the `GITLAB_USER_ID` variable. Finally, you require a personal access token. This token can be created on the [Access Tokens Page](https://gitlab.com/-/profile/personal_access_tokens) in your GitLab settings. The required scopes are `api` and `read_user`. Once created, the personal access token can then be used in the `GITLAB_AUTH_TOKEN` variable. #### GitHub Variables To get your GitHub statistics, there are 2 GitHub related environment variables that are needed. The first is `GITHUB_USERNAME` which is simply the username of your GitHub account. Secondly, you will need a personal access token to use the GitHub GraphQL API. This token can be created from the [Developer Settings Page](https://github.com/settings/tokens). Since this token is used to fetch when your last commit was made, it requires the `repo` and `user` scope. I'm sure it doesn't need the whole of those scopes, and it could be made more specific, however, I have not tested it. This token can then be used within the `GITHUB_AUTH_TOKEN` variable. #### WebAuthn Variable The only WebAuthn variable required is `WEBAUTHN_RP_ID`. This is the "ID" of the relaying party, which is simply the domain (and subdomain, if used) which the portfolio is hosted on. For example, `dantarr.dev`. ### Deployment In production, my portfolio is deployed to [Vercel](https://vercel.com). To run this project on Vercel, simply fork this repo and add a project on Vercel for the forked repo. Add the environment variables needed (Found in [example.env](./example.env)) by going to your Vercel project > Settings > Environment Variables and following the [Environment Variables Instructions](#environment-variables). If you don't want to use Vercel, you can deploy it anywhere that can run Node. Run `npm run build` to build the Next.js project, then run `npm run start` to start the server. The site will then be available at localhost:3000, which you can reverse proxy using something like [NGINX](https://www.nginx.com).