next_on_rails 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +591 -0
- data/Rakefile +32 -0
- data/app/controllers/next_on_rails/application_controller.rb +21 -0
- data/app/controllers/next_on_rails/devise_token_auth_override/concerns/current_resource_serializer.rb +15 -0
- data/app/controllers/next_on_rails/devise_token_auth_override/confirmations_controller.rb +4 -0
- data/app/controllers/next_on_rails/devise_token_auth_override/omniauth_callbacks_controller.rb +7 -0
- data/app/controllers/next_on_rails/devise_token_auth_override/passwords_controller.rb +23 -0
- data/app/controllers/next_on_rails/devise_token_auth_override/registrations_controller.rb +33 -0
- data/app/controllers/next_on_rails/devise_token_auth_override/sessions_controller.rb +27 -0
- data/app/controllers/next_on_rails/devise_token_auth_override/token_validations_controller.rb +13 -0
- data/app/controllers/next_on_rails/devise_token_auth_override/unlocks_controller.rb +23 -0
- data/app/controllers/next_on_rails/merger_controller.rb +36 -0
- data/app/serializers/next_on_rails/active_model_errors_serializer.rb +45 -0
- data/app/serializers/next_on_rails/errors_serializer.rb +16 -0
- data/config/routes.rb +12 -0
- data/lib/generators/nor/backend/backend_generator.rb +129 -0
- data/lib/generators/nor/backend/templates/Procfile +2 -0
- data/lib/generators/nor/backend/templates/application_controller.rb +2 -0
- data/lib/generators/nor/backend/templates/cors.rb +23 -0
- data/lib/generators/nor/backend/templates/current_user_serializer.rb.tt +8 -0
- data/lib/generators/nor/backend/templates/user.rb.tt +8 -0
- data/lib/generators/nor/backend/templates/user_migration.rb.tt +53 -0
- data/lib/generators/nor/backend/templates/user_serializer.rb.tt +11 -0
- data/lib/generators/nor/frontend/frontend_generator.rb +60 -0
- data/lib/generators/nor/frontend/templates/frontend/components/flash.js +29 -0
- data/lib/generators/nor/frontend/templates/frontend/components/inputs.js +69 -0
- data/lib/generators/nor/frontend/templates/frontend/components/login-form.js +23 -0
- data/lib/generators/nor/frontend/templates/frontend/components/registration-form.js +49 -0
- data/lib/generators/nor/frontend/templates/frontend/next-on-rails.config.js +3 -0
- data/lib/generators/nor/frontend/templates/frontend/next.config.js +20 -0
- data/lib/generators/nor/frontend/templates/frontend/pages/_app.js +3 -0
- data/lib/generators/nor/frontend/templates/frontend/pages/index.js +57 -0
- data/lib/generators/nor/frontend/templates/frontend/server.js +31 -0
- data/lib/generators/nor/frontend/templates/frontend/stylesheets/application.scss +2 -0
- data/lib/generators/nor/install/USAGE +30 -0
- data/lib/generators/nor/install/install_generator.rb +8 -0
- data/lib/generators/nor/scaffold/USAGE +10 -0
- data/lib/generators/nor/scaffold/scaffold_generator.rb +53 -0
- data/lib/generators/nor/scaffold/templates/controller.rb.tt +40 -0
- data/lib/generators/nor/scaffold/templates/frontend/components/details.js.tt +20 -0
- data/lib/generators/nor/scaffold/templates/frontend/components/form.js.tt +17 -0
- data/lib/generators/nor/scaffold/templates/frontend/pages/crud.js.tt +123 -0
- data/lib/generators/nor/scaffold/templates/frontend/pages/edit.js.tt +55 -0
- data/lib/generators/nor/scaffold/templates/frontend/pages/index.js.tt +88 -0
- data/lib/generators/nor/scaffold/templates/frontend/pages/new.js.tt +51 -0
- data/lib/generators/nor/scaffold/templates/frontend/pages/show.js.tt +43 -0
- data/lib/next_on_rails.rb +12 -0
- data/lib/next_on_rails/engine.rb +5 -0
- data/lib/next_on_rails/version.rb +3 -0
- data/lib/tasks/next_on_rails_tasks.rake +4 -0
- metadata +235 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
# Be sure to restart your server when you modify this file.
|
2
|
+
|
3
|
+
# Avoid CORS issues when API is called from the frontend app.
|
4
|
+
# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.
|
5
|
+
|
6
|
+
# Read more: https://github.com/cyu/rack-cors
|
7
|
+
|
8
|
+
Rails.application.config.middleware.insert_before 0, Rack::Cors do
|
9
|
+
allow do
|
10
|
+
origins 'localhost:3001'
|
11
|
+
|
12
|
+
resource '*',
|
13
|
+
headers: :any,
|
14
|
+
methods: [:get, :post, :put, :patch, :delete, :options, :head],
|
15
|
+
expose: [
|
16
|
+
DeviseTokenAuth.headers_names[:'access-token'],
|
17
|
+
DeviseTokenAuth.headers_names[:client],
|
18
|
+
DeviseTokenAuth.headers_names[:expiry],
|
19
|
+
DeviseTokenAuth.headers_names[:uid],
|
20
|
+
DeviseTokenAuth.headers_names[:'token-type']
|
21
|
+
]
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class <%= user_class %> < ApplicationRecord
|
2
|
+
# Include default devise modules. Others available are:
|
3
|
+
# :timeoutable
|
4
|
+
devise :database_authenticatable, :registerable,
|
5
|
+
:recoverable, :rememberable, :trackable, :validatable,
|
6
|
+
:confirmable, :lockable, :omniauthable
|
7
|
+
include DeviseTokenAuth::Concerns::<%= user_class %>
|
8
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class DeviseTokenAuthCreateUsers < ActiveRecord::Migration[5.2]
|
2
|
+
def change
|
3
|
+
create_table(:<%= user_class.constantize.table_name %>) do |t|
|
4
|
+
## User Info
|
5
|
+
t.string :email
|
6
|
+
|
7
|
+
## Required
|
8
|
+
t.string :provider, null: false, default: 'email'
|
9
|
+
t.string :uid, null: false, default: ''
|
10
|
+
|
11
|
+
## Database authenticatable
|
12
|
+
t.string :encrypted_password, null: false, default: ''
|
13
|
+
|
14
|
+
## Recoverable
|
15
|
+
t.string :reset_password_token
|
16
|
+
t.datetime :reset_password_sent_at
|
17
|
+
t.boolean :allow_password_change, default: false
|
18
|
+
|
19
|
+
## Rememberable
|
20
|
+
t.datetime :remember_created_at
|
21
|
+
|
22
|
+
## Trackable
|
23
|
+
t.integer :sign_in_count, default: 0, null: false
|
24
|
+
t.datetime :current_sign_in_at
|
25
|
+
t.datetime :last_sign_in_at
|
26
|
+
t.string :current_sign_in_ip
|
27
|
+
t.string :last_sign_in_ip
|
28
|
+
|
29
|
+
## Confirmable
|
30
|
+
t.string :confirmation_token
|
31
|
+
t.datetime :confirmed_at
|
32
|
+
t.datetime :confirmation_sent_at
|
33
|
+
t.string :unconfirmed_email # Only if using reconfirmable
|
34
|
+
|
35
|
+
## Lockable
|
36
|
+
t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
|
37
|
+
t.string :unlock_token # Only if unlock strategy is :email or :both
|
38
|
+
t.datetime :locked_at
|
39
|
+
|
40
|
+
## Tokens
|
41
|
+
t.text :tokens
|
42
|
+
|
43
|
+
t.timestamps
|
44
|
+
end
|
45
|
+
|
46
|
+
add_index :users, :email, unique: true
|
47
|
+
add_index :users, :uid, unique: true
|
48
|
+
add_index :users, [:uid, :provider], unique: true
|
49
|
+
add_index :users, :reset_password_token, unique: true
|
50
|
+
add_index :users, :confirmation_token, unique: true
|
51
|
+
add_index :users, :unlock_token, unique: true
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class <%= user_class %>Serializer
|
2
|
+
include FastJsonapi::ObjectSerializer
|
3
|
+
|
4
|
+
set_type :<%= user_class.underscore %>
|
5
|
+
set_id :id
|
6
|
+
attributes :email
|
7
|
+
|
8
|
+
# link :<%= user_class.underscore %>_url do |<%= user_class.underscore %>|
|
9
|
+
# Rails.application.routes.url_helpers.<%= user_class.underscore %>_url(<%= user_class.underscore %>, host: 'http://localhost:3000')
|
10
|
+
# end
|
11
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Nor
|
2
|
+
class FrontendGenerator < Rails::Generators::Base
|
3
|
+
attr_reader :frontend_path
|
4
|
+
source_root File.expand_path('templates', __dir__)
|
5
|
+
|
6
|
+
def initialize(args, *options)
|
7
|
+
super
|
8
|
+
@frontend_path = File.join(destination_root, 'frontend')
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_next_app
|
12
|
+
if revoke? && frontend_exist?
|
13
|
+
say_status :remove, relative_to_original_destination_root(frontend_path)
|
14
|
+
FileUtils.remove_dir(frontend_path, force: true)
|
15
|
+
elsif frontend_exist?
|
16
|
+
say_status('skipped', 'Create Next app: directory frontend already exist!')
|
17
|
+
else
|
18
|
+
run 'yarn create next-app frontend'
|
19
|
+
inside frontend_path do
|
20
|
+
gsub_file 'package.json', 'create-next-example-app', 'next-on-rails-frontend'
|
21
|
+
gsub_file 'package.json', '"dev": "next dev"', '"dev": "node server.js"'
|
22
|
+
if `which json`.present?
|
23
|
+
run "json -I -f package.json -e 'this.license=\"MIT\"'"
|
24
|
+
run "json -I -f package.json -e 'this.private=true'"
|
25
|
+
run "json -I -f package.json -e 'this.version=\"0.1.0\"'"
|
26
|
+
end
|
27
|
+
remove_file 'components/head.js'
|
28
|
+
remove_file 'components/nav.js'
|
29
|
+
remove_file 'pages/index.js'
|
30
|
+
remove_file 'next.config.js'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def install_dependencies
|
36
|
+
return if revoke?
|
37
|
+
|
38
|
+
inside frontend_path do
|
39
|
+
run 'yarn add next-compose-plugins @zeit/next-sass node-sass bootstrap compression express inflection lodash nookies nprogress next-on-rails next@8.1.0 react@16.8.6 react-dom@16.8.6'
|
40
|
+
run 'yarn add --dev babel-eslint eslint eslint-config-standard eslint-plugin-import eslint-plugin-node eslint-plugin-promise eslint-plugin-react eslint-plugin-standard prettier sass-lint'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def install_templates
|
45
|
+
return if revoke?
|
46
|
+
|
47
|
+
directory 'frontend'
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
|
52
|
+
def frontend_exist?
|
53
|
+
File.exist?(frontend_path)
|
54
|
+
end
|
55
|
+
|
56
|
+
def revoke?
|
57
|
+
behavior == :revoke
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { useFlash } from 'next-on-rails/utils'
|
2
|
+
|
3
|
+
const Flash = () => {
|
4
|
+
const { flash } = useFlash()
|
5
|
+
if (flash) {
|
6
|
+
return Object.entries(flash)
|
7
|
+
.filter(([key, value]) => value && value !== '')
|
8
|
+
.map(([key, value]) => (
|
9
|
+
<div key={key} className={`alert alert-${alertClass(key)}`}>
|
10
|
+
{value}
|
11
|
+
</div>
|
12
|
+
))
|
13
|
+
} else {
|
14
|
+
return null
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
const alertClass = flashType => {
|
19
|
+
switch (flashType) {
|
20
|
+
case 'notice':
|
21
|
+
return 'success'
|
22
|
+
case 'alert':
|
23
|
+
return 'danger'
|
24
|
+
default:
|
25
|
+
return 'info'
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
export default Flash
|
@@ -0,0 +1,69 @@
|
|
1
|
+
import { dasherize, titleize } from 'inflection'
|
2
|
+
|
3
|
+
const Input = ({ name, value, label, error }) => {
|
4
|
+
const id = `input-${dasherize(name).toLowerCase()}`
|
5
|
+
const className = `form-control ${error ? 'is-invalid' : null}`
|
6
|
+
if (label === undefined) label = titleize(name)
|
7
|
+
return (
|
8
|
+
<div className="form-group">
|
9
|
+
{label ? <label htmlFor={id}>{label}</label> : null}
|
10
|
+
<input type="text" id={id} className={className} name={name} defaultValue={value} />
|
11
|
+
{error ? <div className="invalid-feedback">{error}</div> : null}
|
12
|
+
</div>
|
13
|
+
)
|
14
|
+
}
|
15
|
+
|
16
|
+
const TextArea = ({ name, value, label, error }) => {
|
17
|
+
const id = `input-${dasherize(name).toLowerCase()}`
|
18
|
+
const className = `form-control ${error ? 'is-invalid' : null}`
|
19
|
+
if (label === undefined) label = titleize(name)
|
20
|
+
return (
|
21
|
+
<div className="form-group">
|
22
|
+
{label ? <label htmlFor={id}>{label}</label> : null}
|
23
|
+
<textarea id={id} className={className} name={name} defaultValue={value}></textarea>
|
24
|
+
{error ? <div className="invalid-feedback">{error}</div> : null}
|
25
|
+
</div>
|
26
|
+
)
|
27
|
+
}
|
28
|
+
|
29
|
+
const CheckBox = ({ name, value, label, error }) => {
|
30
|
+
const id = `input-${dasherize(name).toLowerCase()}`
|
31
|
+
const className = `form-check-input ${error ? 'is-invalid' : null}`
|
32
|
+
if (label === undefined) label = titleize(name)
|
33
|
+
return (
|
34
|
+
<div className="form-group form-check">
|
35
|
+
<input type="hidden" name={name} defaultValue={false} />
|
36
|
+
<input type="checkbox" id={id} className={className} name={name} defaultChecked={value} />
|
37
|
+
{error ? <div className="invalid-feedback">{error}</div> : null}
|
38
|
+
{label ? (
|
39
|
+
<label htmlFor={id} className="form-post-label">
|
40
|
+
{label}
|
41
|
+
</label>
|
42
|
+
) : null}
|
43
|
+
</div>
|
44
|
+
)
|
45
|
+
}
|
46
|
+
|
47
|
+
const Select = ({ name, value, label, error, collection, selectPlaceholder = 'Select one...' }) => {
|
48
|
+
const id = `input-${dasherize(name).toLowerCase()}`
|
49
|
+
const className = `form-control ${error ? 'is-invalid' : null}`
|
50
|
+
if (label === undefined) label = titleize(name)
|
51
|
+
return (
|
52
|
+
<div className="form-group">
|
53
|
+
{label ? <label htmlFor={id}>{label}</label> : null}
|
54
|
+
<select id={id} className={className} name={name} defaultValue={value}>
|
55
|
+
{selectPlaceholder ? <option>{selectPlaceholder}</option> : null}
|
56
|
+
{collection.map(([value, display]) => (
|
57
|
+
<option key={value} value={value}>
|
58
|
+
{display}
|
59
|
+
</option>
|
60
|
+
))}
|
61
|
+
</select>
|
62
|
+
{error ? <div className="invalid-feedback">{error}</div> : null}
|
63
|
+
</div>
|
64
|
+
)
|
65
|
+
}
|
66
|
+
|
67
|
+
const HiddenIdField = ({ id }) => (id ? <input type="hidden" name="id" defaultValue={id} /> : null)
|
68
|
+
|
69
|
+
export { Input, TextArea, CheckBox, Select, HiddenIdField }
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import { useLoginForm } from 'next-on-rails/devise'
|
2
|
+
|
3
|
+
const LoginForm = ({ successCallback, errorCallback }) => {
|
4
|
+
const login = useLoginForm(successCallback, errorCallback)
|
5
|
+
|
6
|
+
return (
|
7
|
+
<form onSubmit={login}>
|
8
|
+
<div className="form-group">
|
9
|
+
<label htmlFor="form-login-email">Email</label>
|
10
|
+
<input id="form-login-email" type="text" name="email" className="form-control" />
|
11
|
+
</div>
|
12
|
+
<div className="form-group">
|
13
|
+
<label htmlFor="form-login-password">Password</label>
|
14
|
+
<input id="form-login-password" type="password" name="password" className="form-control" />
|
15
|
+
</div>
|
16
|
+
<button type="submit" className="btn btn-primary">
|
17
|
+
Login
|
18
|
+
</button>
|
19
|
+
</form>
|
20
|
+
)
|
21
|
+
}
|
22
|
+
|
23
|
+
export default LoginForm
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import { useRegistrationForm } from 'next-on-rails/devise'
|
2
|
+
|
3
|
+
const RegistrationForm = ({ successCallback, errorCallback }) => {
|
4
|
+
const [register, getError] = useRegistrationForm(successCallback, errorCallback)
|
5
|
+
|
6
|
+
return (
|
7
|
+
<form onSubmit={register}>
|
8
|
+
<div className="form-group">
|
9
|
+
<label htmlFor="form-registration-email">Email</label>
|
10
|
+
<input
|
11
|
+
id="form-registration-email"
|
12
|
+
type="text"
|
13
|
+
name="email"
|
14
|
+
className={`form-control ${getError('email') ? 'is-invalid' : null}`}
|
15
|
+
/>
|
16
|
+
{getError('email') ? <div className="invalid-feedback">{getError('email')}</div> : null}
|
17
|
+
</div>
|
18
|
+
<div className="form-group">
|
19
|
+
<label htmlFor="form-registration-password">Password</label>
|
20
|
+
<input
|
21
|
+
id="form-registration-password"
|
22
|
+
type="password"
|
23
|
+
name="password"
|
24
|
+
className={`form-control ${getError('password') ? 'is-invalid' : null}`}
|
25
|
+
defaultValue="qwerty"
|
26
|
+
/>
|
27
|
+
{getError('password') ? <div className="invalid-feedback">{getError('password')}</div> : null}
|
28
|
+
</div>
|
29
|
+
<div className="form-group">
|
30
|
+
<label htmlFor="form-registration-password-confirmation">Confirm Password</label>
|
31
|
+
<input
|
32
|
+
id="form-registration-password-confirmation"
|
33
|
+
type="password"
|
34
|
+
name="password_confirmation"
|
35
|
+
className={`form-control ${getError('password') ? 'is-invalid' : null}`}
|
36
|
+
defaultValue="qwerty"
|
37
|
+
/>
|
38
|
+
{getError('password_confirmation') ? (
|
39
|
+
<div className="invalid-feedback">{getError('password_confirmation')}</div>
|
40
|
+
) : null}
|
41
|
+
</div>
|
42
|
+
<button type="submit" className="btn btn-primary">
|
43
|
+
Register
|
44
|
+
</button>
|
45
|
+
</form>
|
46
|
+
)
|
47
|
+
}
|
48
|
+
|
49
|
+
export default RegistrationForm
|
@@ -0,0 +1,20 @@
|
|
1
|
+
const withPlugins = require('next-compose-plugins')
|
2
|
+
const withSass = require('@zeit/next-sass')
|
3
|
+
// const optimizedImages = require('next-optimized-images')
|
4
|
+
|
5
|
+
module.exports = withPlugins(
|
6
|
+
[
|
7
|
+
[withSass, {}]
|
8
|
+
// [optimizedImages, {}],
|
9
|
+
// [exportPathMap, {
|
10
|
+
// '/': { page: '/' }
|
11
|
+
// }]
|
12
|
+
],
|
13
|
+
{
|
14
|
+
webpack: config => {
|
15
|
+
// Fixes npm packages that depend on `fs` module
|
16
|
+
config.node = { fs: 'empty' }
|
17
|
+
return config
|
18
|
+
}
|
19
|
+
}
|
20
|
+
)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
import { useCurrentUser } from 'next-on-rails/current-user'
|
2
|
+
import { useLogout } from 'next-on-rails/devise'
|
3
|
+
import { getDetailFromResponseError, useFlash } from 'next-on-rails/utils'
|
4
|
+
|
5
|
+
import Flash from '../components/flash'
|
6
|
+
import LoginForm from '../components/login-form'
|
7
|
+
import RegistrationForm from '../components/registration-form'
|
8
|
+
|
9
|
+
const Home = props => {
|
10
|
+
const { setFlash } = useFlash()
|
11
|
+
const logout = useLogout()
|
12
|
+
const { currentUser } = useCurrentUser()
|
13
|
+
|
14
|
+
const loginSuccessCallback = response => {
|
15
|
+
setFlash({ notice: `Welcome ${response.data.email}!` })
|
16
|
+
}
|
17
|
+
|
18
|
+
const loginErrorCallback = error => {
|
19
|
+
setFlash({ alert: getDetailFromResponseError(error) })
|
20
|
+
}
|
21
|
+
|
22
|
+
const registrationSuccessCallback = response => {
|
23
|
+
setFlash({
|
24
|
+
notice: `Welcome ${response.data.email}! An email to confirm your account has been sent to this email address.`
|
25
|
+
})
|
26
|
+
}
|
27
|
+
|
28
|
+
return (
|
29
|
+
<div className="container pt-4">
|
30
|
+
<Flash />
|
31
|
+
<h1>Welcome to Next On Rails!</h1>
|
32
|
+
|
33
|
+
<img src="https://media.giphy.com/media/12NUbkX6p4xOO4/giphy.gif" alt="Magic" />
|
34
|
+
|
35
|
+
<p>{currentUser ? `Hi, ${currentUser.email}` : 'You are not logged'}</p>
|
36
|
+
|
37
|
+
{currentUser ? (
|
38
|
+
<button className="btn btn-primary" onClick={logout}>
|
39
|
+
Logout
|
40
|
+
</button>
|
41
|
+
) : (
|
42
|
+
<div className="row">
|
43
|
+
<div className="col">
|
44
|
+
<h6>Login</h6>
|
45
|
+
<LoginForm successCallback={loginSuccessCallback} errorCallback={loginErrorCallback} />
|
46
|
+
</div>
|
47
|
+
<div className="col">
|
48
|
+
<h6>Register</h6>
|
49
|
+
<RegistrationForm successCallback={registrationSuccessCallback} />
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
)}
|
53
|
+
</div>
|
54
|
+
)
|
55
|
+
}
|
56
|
+
|
57
|
+
export default Home
|
@@ -0,0 +1,31 @@
|
|
1
|
+
const express = require('express')
|
2
|
+
const next = require('next')
|
3
|
+
const compression = require('compression')
|
4
|
+
|
5
|
+
const dev = process.env.NODE_ENV !== 'production'
|
6
|
+
const app = next({ dev })
|
7
|
+
const handle = app.getRequestHandler()
|
8
|
+
|
9
|
+
app
|
10
|
+
.prepare()
|
11
|
+
.then(() => {
|
12
|
+
const server = express()
|
13
|
+
server.use(compression())
|
14
|
+
|
15
|
+
// server.get('/post/:id', (req, res) => {
|
16
|
+
// app.render(req, res, '/post', { id: req.params.id })
|
17
|
+
// })
|
18
|
+
|
19
|
+
server.get('*', (req, res) => {
|
20
|
+
return handle(req, res)
|
21
|
+
})
|
22
|
+
|
23
|
+
server.listen(3001, err => {
|
24
|
+
if (err) throw err
|
25
|
+
console.log('> Ready on http://localhost:3001')
|
26
|
+
})
|
27
|
+
})
|
28
|
+
.catch(ex => {
|
29
|
+
console.error(ex.stack)
|
30
|
+
process.exit(1)
|
31
|
+
})
|