Skip to content

Commit

Permalink
Merge pull request #9 from TakumaKira/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
TakumaKira authored Dec 6, 2024
2 parents af51048 + 483d149 commit 6b0cc2c
Show file tree
Hide file tree
Showing 77 changed files with 37,223 additions and 3,241 deletions.
8 changes: 6 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
{
"extends": ["next/core-web-vitals", "next/typescript"]
}
"extends": [
"next/core-web-vitals",
"next/typescript",
"plugin:storybook/recommended"
]
}
41 changes: 41 additions & 0 deletions .github/workflows/deploy-storybook.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Deploy Storybook to S3

on:
push:
branches:
- main
workflow_dispatch:

jobs:
deploy:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Build Storybook
run: npm run build-storybook

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ vars.AWS_REGION }}

- name: Deploy to S3
run: |
aws s3 sync storybook-static s3://${{ vars.AWS_S3_BUCKET }} --delete
- name: Invalidate CloudFront
run: |
aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} --paths "/*"
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,13 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts

*storybook.log

# amplify
.amplify
amplify_outputs*
amplifyconfiguration*

# misc
memo.txt
63 changes: 63 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { StorybookConfig } from "@storybook/nextjs";

const config: StorybookConfig = {
stories: [
"../stories/**/*.mdx",
"../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)",
],
addons: [
"@storybook/addon-onboarding",
"@storybook/addon-essentials",
"@chromatic-com/storybook",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/nextjs",
options: {},
},
staticDirs: [
{
from: '../app/fonts',
to: 'app/fonts',
},
],
webpackFinal(config) {
// Grab the existing rule that handles SVG imports
const fileLoaderRule = config?.module?.rules?.find((rule) =>
typeof rule === 'object' && typeof rule?.test === 'object' && 'test' in rule.test && typeof rule.test.test === 'function' && rule.test.test('.svg'),
)

if (config?.module?.rules && typeof fileLoaderRule === 'object' && fileLoaderRule !== null) {
let not: any[] = []
if (typeof fileLoaderRule.resourceQuery === 'object' && 'not' in fileLoaderRule.resourceQuery && Array.isArray(fileLoaderRule.resourceQuery.not)) {
not = fileLoaderRule.resourceQuery.not
}
config.module.rules.push(
// Reapply the existing rule, but only for svg imports ending in ?url
{
...fileLoaderRule,
test: /\.svg$/i,
resourceQuery: /url/, // *.svg?url
},
// Convert all other *.svg imports to React components
{
test: /\.svg$/i,
issuer: fileLoaderRule.issuer,
resourceQuery: { not: [...not, /url/] }, // exclude if *.svg?url
use: {
loader: '@svgr/webpack',
options: {
icon: true,
},
},
},
)

// Modify the file loader rule to ignore *.svg, since we have it handled now.
fileLoaderRule.exclude = /\.svg$/i
}

return config
},
};
export default config;
52 changes: 52 additions & 0 deletions .storybook/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { Preview } from "@storybook/react";

import './storybook-body-style.scss';

const projectViewports = {
desktop: {
name: 'Desktop',
styles: {
width: '1280px',
height: '832px',
},
},
tablet: {
name: 'Tablet',
styles: {
width: '834px',
height: '1194px',
},
},
mobile: {
name: 'Mobile',
styles: {
width: '390px',
height: '844px',
},
},
};

const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
backgrounds: {
default: 'Light',
values: [
{ name: 'Light', value: '#F0F0E3' },
{ name: 'Dark', value: '#38343F' },
],
},
viewport: {
viewports: {
...projectViewports,
},
},
},
};

export default preview;
3 changes: 3 additions & 0 deletions .storybook/storybook-body-style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.sb-show-main,body {
transition: none !important;
}
136 changes: 115 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,130 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
# Takuma's Portfolio

## Getting Started
## How to run

First, run the development server:
This project uses postgres database. By getting some config data from the database, I as a maintainer can manage some of the contents without modifying the source code and redeploying the application. I leverage Server Component to let Next.js server get the config data directly from the database and render it on the page. These config data are embedded in the HTML as a script tag and no API calls happen on the client side, which is more secure than using normal API.

I use npm as package manager for this project, because it works well in any case including CIs.

### Install prisma cli

I use [Prisma](https://www.prisma.io) to manage the database schema and migrations. So you need to install prisma cli to run some commands.

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
cd prisma # Go into the prisma layer directory
npx prisma
```

### Generate types for prisma client

You need to generate types using prisma cli to correctly reference types of schema in your IDE. First, you need to install dependencies for the prisma layer.

```bash
npm install
```

Then, run generate command.

```bash
npx prisma generate
```

### Install dependencies for the project

```bash
cd .. # Back to the project root
npm install # This requires prisma layer correctly generated before hand
```

### Prepare database

You need to create a PostgreSQL database that is accessible from an AWS Lambda function you'll create in the future step.

### Deploy prisma migrations

You need to setup your database a bit more. If you create your database without public endpoint (which is secure and recommended way), you need to spinup an EC2 instance in the same VPC as the database to use as a bastion host and establish VPN tunnel to connect the database from your local terminal.

```bash
ssh -L <port_number_i_want_to_use_on_my_local_and_i_recommand_5432_here>:<rds_database_endpoint>:<rds_database_port_number> ec2-user@<bastion_host_PUBLIC_ip> -i <path_to_ssh_key_pair_you_got_from_your_bastion_host_ec2_instance.pem>
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
Now, you should be able to access your database on AWS with host `localhost`. Then, create a database named `portfolio`. Connect to the database and run the following SQL query.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
```sql
CREATE DATABASE portfolio;
```

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
Migrate the schema to the postgres database using prisma cli.

```bash
CONFIG_DATABASE_URL=postgresql://<your-database-user>:<your-database-user-password>@localhost:5432/portfolio npx prisma migrate deploy
```

## Learn More
Notice you need to encode `<your-database-user-password>` with percent encoding to escape special characters in the password string.

To learn more about Next.js, take a look at the following resources:
Now you have a database works for this project.

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
### Insert config

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
Provide the config data to the database, otherwise the application will output console errors and some parts of the page will not work as expected.

## Deploy on Vercel
```sql
INSERT INTO "public"."Config" ("id", "name", "type", "value") VALUES
(1, 'name', 'STRING', 'Takuma'),
(2, 'repository_url', 'STRING', 'https://github.com/TakumaKira/portfolio'),
(3, 'storybook_url', 'STRING', 'https://takuma-portfolio-storybook.kirakiraworx.com'),
(4, 'cpsaf_certification_url', 'STRING', 'https://www.certible.com/badge/33141297-d6b6-4dff-9d43-f36452d85d5c'),
(5, 'figma_url', 'STRING', 'https://www.figma.com/design/Hcj8I0Y6umFS5mymgsgVKp/Takuma''s-Portfolio-202411');
```

Now, your database is ready and you can close the VPC tunnel.

### Deploy to Amplify

Create a new Ampify project and connect this repository.

It will trigger deployment configured with `/amplify.yml`. It includes deployment of the prisma layer as a Lambda layer, which is also required to run sandbox you can use in the future step.

When the first deployment has done successfully, you need to add some environment variables onto the Amplify project and the backend Lambda function deployed in the `backend` step of `/amplify.yml`.

For the Amplify project, add the following on the page:

```text
PRISMA_LAMBDA_LAYER_ARN: arn:aws:lambda:<your-resource-region>:<your-aws-account-number>:layer:portfolio-prisma:<latest-lambda-layer-version-number>
```

For the backend Lambda function, add:

```text
CONFIG_DATABASE_URL: postgresql://<your-database-user>:<your-database-user-password>@localhost:5432/portfolio
PRISMA_QUERY_ENGINE_LIBRARY: /opt/nodejs/node_modules/portfolio-prisma/node_modules/.prisma/client/libquery_engine-rhel-openssl-1.0.x.so.node
```

Notice you need to encode `<your-database-user-password>` with percent encoding to escape special characters in the password string.

And you'd better use `RDS database connections` configuration on the Lambda function to grant appropriate security group to the function and the database. This would resolve database connection timeout if happened.

Finally, you should be able to access the working web app on the deployed URL as expected.

### Run Next.js locally

```bash
npm run dev
```

### Run Amplify backend functions sandbox for development

```bash
PRISMA_LAMBDA_LAYER_ARN=arn:aws:lambda:<your-resource-region>:<your-aws-account-number>:layer:portfolio-prisma:<latest-lambda-layer-version-number> npx ampx sandbox
```

Notice you also need to add some environment variables for the sandbox Lambda function.

```text
CONFIG_DATABASE_URL: postgresql://<your-database-user>:<your-database-user-password>@localhost:5432/portfolio
PRISMA_QUERY_ENGINE_LIBRARY: /opt/nodejs/node_modules/portfolio-prisma/node_modules/.prisma/client/libquery_engine-rhel-openssl-1.0.x.so.node
```

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Notice you need to encode `<your-database-user-password>` with percent encoding to escape special characters in the password string.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
And you'd better use `RDS database connections` configuration on the Lambda function to grant appropriate security group to the function and the database. This would resolve database connection timeout if happened.
44 changes: 44 additions & 0 deletions amplify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
version: 1
backend:
phases:
build:
commands:
- echo "Using amplify.yml on repository root"
# Deploy prisma layer as a lambda layer
- cd prisma
- npm ci --cache .npm --prefer-offline
- npx prisma && npx prisma generate
- cd ..
- mkdir -p nodejs/node_modules/portfolio-prisma
- cp -r prisma/* nodejs/node_modules/portfolio-prisma/
- zip -r portfolio-prisma.zip nodejs
- aws lambda publish-layer-version --layer-name portfolio-prisma --zip-file fileb://portfolio-prisma.zip
# Deploy backend
- npm ci --cache .npm --prefer-offline
- npx ampx pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID --debug
frontend:
phases:
preBuild:
commands:
# Generate prisma client
- cd prisma
- npm ci --cache .npm --prefer-offline
- npx prisma && npx prisma generate
- cd ..
# Install frontend dependencies
- npm install
test:
commands:
- npm run test:ci
build:
commands:
- npm run build
artifacts:
baseDirectory: .next
files:
- "**/*"
cache:
paths:
- .next/cache/**/*
- .npm/**/*
- node_modules/**/*
11 changes: 11 additions & 0 deletions amplify/auth/resource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { defineAuth } from '@aws-amplify/backend';

/**
* Define and configure your auth resource
* @see https://docs.amplify.aws/gen2/build-a-backend/auth
*/
export const auth = defineAuth({
loginWith: {
email: true,
},
});
Loading

0 comments on commit 6b0cc2c

Please sign in to comment.