Skip to content

Commit 84f7a26

Browse files
committed
Prepare deploy via serverless
1 parent 51bbd39 commit 84f7a26

15 files changed

+632
-35
lines changed

.env

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
HTTPS=true
2-
REACT_APP_API_HOST=https://v2.velog.io/
2+
REACT_APP_API_HOST=https://v2dev.velog.io/graphql
3+
PUBLIC_URL=https://static.velog.io/

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,10 @@ npm-debug.log*
2222
yarn-debug.log*
2323
yarn-error.log*
2424

25-
/dist
25+
/dist
26+
27+
# package directories
28+
jspm_packages
29+
30+
# Serverless directories
31+
.serverless

config/env.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
'use strict';
2-
31
const fs = require('fs');
42
const path = require('path');
53
const paths = require('./paths');
@@ -10,7 +8,7 @@ delete require.cache[require.resolve('./paths')];
108
const NODE_ENV = process.env.NODE_ENV;
119
if (!NODE_ENV) {
1210
throw new Error(
13-
'The NODE_ENV environment variable is required but was not specified.'
11+
'The NODE_ENV environment variable is required but was not specified.',
1412
);
1513
}
1614

@@ -35,7 +33,7 @@ dotenvFiles.forEach(dotenvFile => {
3533
require('dotenv-expand')(
3634
require('dotenv').config({
3735
path: dotenvFile,
38-
})
36+
}),
3937
);
4038
}
4139
});
@@ -77,7 +75,7 @@ function getClientEnvironment(publicUrl) {
7775
// This should only be used as an escape hatch. Normally you would put
7876
// images into the `src` and `import` them in code to get their paths.
7977
PUBLIC_URL: publicUrl,
80-
}
78+
},
8179
);
8280
// Stringify all values so we can feed into Webpack DefinePlugin
8381
const stringified = {
@@ -87,7 +85,12 @@ function getClientEnvironment(publicUrl) {
8785
}, {}),
8886
};
8987

90-
return { raw, stringified };
88+
const stringifiedForServerless = Object.keys(raw).reduce((env, key) => {
89+
env[`process.env.${key}`] = JSON.stringify(raw[key]);
90+
return env;
91+
}, {});
92+
93+
return { raw, stringified, stringifiedForServerless };
9194
}
9295

9396
module.exports = getClientEnvironment;

config/paths.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ module.exports = {
8282
publicUrl: getPublicUrl(resolveApp('package.json')),
8383
servedPath: getServedPath(resolveApp('package.json')),
8484
ssrIndexJs: resolveApp('src/index.server.ts'),
85+
ssrServerlessEntry: resolveApp('src/serverless.ts'),
8586
ssrBuild: resolveApp('dist'),
87+
ssrServerlessBuild: resolveApp('.webpack'),
8688
};
8789

8890
module.exports.moduleFileExtensions = moduleFileExtensions;

config/webpack.config.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
2-
31
const fs = require('fs');
42
const path = require('path');
53
const webpack = require('webpack');

config/webpack.config.lambda.js renamed to config/webpack.config.serverless.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
process.env.BABEL_ENV = 'production';
2+
process.env.NODE_ENV = 'production';
3+
process.env.REACT_APP_SSR = 'enabled';
4+
5+
const CopyPlugin = require('copy-webpack-plugin');
16
const nodeExternals = require('webpack-node-externals');
27
const paths = require('./paths');
38
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
@@ -8,6 +13,7 @@ const cssModuleRegex = /\.module\.css$/;
813
const sassRegex = /\.(scss|sass)$/;
914
const sassModuleRegex = /\.module\.(scss|sass)$/;
1015
const path = require('path');
16+
const serverlessWebpack = require('serverless-webpack');
1117

1218
const imageInlineSizeLimit = parseInt(
1319
process.env.IMAGE_INLINE_SIZE_LIMIT || '10000',
@@ -17,12 +23,13 @@ const publicUrl = paths.servedPath.slice(0, -1);
1723
const env = getClientEnvironment(publicUrl);
1824

1925
module.exports = {
20-
mode: 'production',
21-
entry: paths.ssrIndexJs,
26+
mode: serverlessWebpack.lib.webpack.isLocal ? 'development' : 'production',
27+
entry: serverlessWebpack.lib.entries,
2228
target: 'node',
2329
output: {
24-
path: paths.ssrBuild,
25-
filename: 'server.js',
30+
libraryTarget: 'commonjs2',
31+
path: paths.ssrServerlessBuild,
32+
filename: '[name].js',
2633
publicPath: paths.servedPath,
2734
},
2835
module: {
@@ -134,12 +141,18 @@ module.exports = {
134141
.filter(ext => true || !ext.includes('ts')),
135142
},
136143
plugins: [
137-
new webpack.DefinePlugin(env.stringified),
144+
new webpack.DefinePlugin(env.stringifiedForServerless),
138145
new webpack.NormalModuleReplacementPlugin(
139146
/codemirror/,
140147
path.resolve(paths.appSrc, 'lib/replacedModule.ts'),
141148
),
142149
new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
150+
new CopyPlugin([
151+
{
152+
from: path.resolve(paths.appBuild, 'loadable-stats.json'),
153+
to: 'build',
154+
},
155+
]),
143156
],
144157
optimization: {
145158
minimize: false,

docker/redis/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FROM bitnami/redis:5.0
2+
ENV ALLOW_EMPTY_PASSWORD=yes

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@testing-library/jest-dom": "^4.2.4",
1717
"@testing-library/react": "^9.3.2",
1818
"@testing-library/user-event": "^7.1.2",
19+
"@types/aws-lambda": "^8.10.39",
1920
"@types/codemirror": "^0.0.81",
2021
"@types/date-fns": "^2.6.0",
2122
"@types/ioredis": "^4.14.3",
@@ -43,6 +44,7 @@
4344
"@typescript-eslint/eslint-plugin": "^2.8.0",
4445
"@typescript-eslint/parser": "^2.8.0",
4546
"apollo-boost": "^0.4.7",
47+
"aws-lambda": "^1.0.4",
4648
"axios": "^0.19.0",
4749
"babel-eslint": "10.0.3",
4850
"babel-jest": "^24.9.0",
@@ -52,6 +54,7 @@
5254
"camelcase": "^5.3.1",
5355
"case-sensitive-paths-webpack-plugin": "2.2.0",
5456
"codemirror": "^5.49.2",
57+
"copy-webpack-plugin": "^5.1.1",
5558
"css-loader": "3.2.0",
5659
"date-fns": "^2.8.1",
5760
"dotenv": "8.2.0",
@@ -116,6 +119,7 @@
116119
"resolve-url-loader": "3.1.1",
117120
"sass-loader": "8.0.0",
118121
"semver": "6.3.0",
122+
"serverless-webpack": "^5.3.1",
119123
"snakecase-keys": "^3.1.0",
120124
"strip-markdown": "^3.1.1",
121125
"style-loader": "1.0.0",

serverless.yml

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Welcome to Serverless!
2+
#
3+
# This file is the main config file for your service.
4+
# It's very minimal at this point and uses default values.
5+
# You can always add more config options for more control.
6+
# We've included some commented out config examples here.
7+
# Just uncomment any of them to get that config option.
8+
#
9+
# For full config options, check the docs:
10+
# docs.serverless.com
11+
#
12+
# Happy Coding!
13+
14+
service: velog-v2-ssr
15+
16+
plugins:
17+
- serverless-webpack
18+
19+
# app and org for use with dashboard.serverless.com
20+
app: velog-v2-ssr
21+
org: velopert
22+
23+
# You can pin your service to only deploy with a specific Serverless version
24+
# Check out our docs for more details
25+
# frameworkVersion: "=X.X.X"
26+
27+
provider:
28+
name: aws
29+
runtime: nodejs12.x
30+
region: ap-northeast-2
31+
# you can overwrite defaults here
32+
# stage: dev
33+
# region: us-east-1
34+
35+
# you can add statements to the Lambda function's IAM Role here
36+
# iamRoleStatements:
37+
# - Effect: "Allow"
38+
# Action:
39+
# - "s3:ListBucket"
40+
# Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ] }
41+
# - Effect: "Allow"
42+
# Action:
43+
# - "s3:PutObject"
44+
# Resource:
45+
# Fn::Join:
46+
# - ""
47+
# - - "arn:aws:s3:::"
48+
# - "Ref" : "ServerlessDeploymentBucket"
49+
# - "/*"
50+
51+
# you can define service wide environment variables here
52+
environment:
53+
REDIS_HOST: ${ssm:/velog-v2/redis-host}
54+
DEBUG: ioredis:*
55+
56+
vpc:
57+
securityGroupIds:
58+
- sg-007a5a395dbdcef1f
59+
- sg-f588c99d
60+
- sg-0fd14591289bb3212
61+
subnetIds:
62+
- subnet-02c6112f71f5148c7
63+
- subnet-0ebc43e6ab298c646
64+
# you can add packaging information here
65+
#package:
66+
# include:
67+
# - include-me.js
68+
# - include-me-dir/**
69+
# exclude:
70+
# - exclude-me.js
71+
# - exclude-me-dir/**
72+
73+
# package:
74+
# exclude:
75+
# - node_modules/**
76+
77+
package:
78+
include:
79+
- build/loadable-stats.json
80+
81+
custom:
82+
webpack:
83+
webpackConfig: './config/webpack.config.serverless.js'
84+
includeModules: true
85+
86+
functions:
87+
render:
88+
handler: src/serverless.handler
89+
events:
90+
- http:
91+
method: ANY
92+
path: /
93+
- http:
94+
method: ANY
95+
path: /{any+}
96+
97+
# The following are a few example events you can configure
98+
# NOTE: Please make sure to change your handler code to work with those events
99+
# Check the event documentation for details
100+
# events:
101+
# - http:
102+
# path: users/create
103+
# method: get
104+
# - websocket: $connect
105+
# - s3: ${env:BUCKET}
106+
# - schedule: rate(10 minutes)
107+
# - sns: greeter-topic
108+
# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
109+
# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx
110+
# - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
111+
# - iot:
112+
# sql: "SELECT * FROM 'some_topic'"
113+
# - cloudwatchEvent:
114+
# event:
115+
# source:
116+
# - "aws.ec2"
117+
# detail-type:
118+
# - "EC2 Instance State-change Notification"
119+
# detail:
120+
# state:
121+
# - pending
122+
# - cloudwatchLog: '/aws/lambda/hello'
123+
# - cognitoUserPool:
124+
# pool: MyUserPool
125+
# trigger: PreSignUp
126+
# - alb:
127+
# listenerArn: arn:aws:elasticloadbalancing:us-east-1:XXXXXX:listener/app/my-load-balancer/50dc6c495c0c9188/
128+
# priority: 1
129+
# conditions:
130+
# host: example.com
131+
# path: /hello
132+
133+
# Define function environment variables here
134+
# environment:
135+
# variable2: value2
136+
137+
# you can add CloudFormation resource templates here
138+
#resources:
139+
# Resources:
140+
# NewResource:
141+
# Type: AWS::S3::Bucket
142+
# Properties:
143+
# BucketName: my-new-bucket
144+
# Outputs:
145+
# NewOutput:
146+
# Description: "Description for the output"
147+
# Value: "Some output value"

src/index copy.css

Whitespace-only changes.

src/server/CacheManager.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import Redis from 'ioredis';
22
import checkCacheRule from './checkCacheRule';
33

4-
const redis = new Redis();
4+
const redis = new Redis({
5+
host: process.env.REDIS_HOST || 'locahost',
6+
});
57

68
function createCacheKey(url: string) {
79
return `ssr:${url}`;

src/server/sampleRender.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import React from 'react';
2+
import ReactDOMServer from 'react-dom/server';
3+
import CacheManager from './CacheManager';
4+
5+
const cacheManager = new CacheManager();
6+
7+
export default async function sampleRender() {
8+
const rendered = ReactDOMServer.renderToString(<div>Hello React!</div>);
9+
return rendered;
10+
}

src/server/serverRender.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const serverRender = async ({ url, loggedIn, cookie }: SSROption) => {
4747
const client = new ApolloClient({
4848
ssrMode: true,
4949
link: createHttpLink({
50-
uri: 'http://localhost:5000/graphql',
50+
uri: 'https://v2dev.velog.io/graphql',
5151
fetch: fetch as any,
5252
headers: {
5353
cookie,
@@ -58,7 +58,10 @@ const serverRender = async ({ url, loggedIn, cookie }: SSROption) => {
5858

5959
const context = {};
6060
const sheet = new ServerStyleSheet();
61-
const extractor = new ChunkExtractor({ statsFile });
61+
const extractor = new ChunkExtractor({
62+
statsFile,
63+
publicPath: 'https://static.velog.io',
64+
});
6265

6366
const Root = (
6467
<ChunkExtractorManager extractor={extractor}>

0 commit comments

Comments
 (0)