nails-boilerplate 2.0.10 → 2.0.13
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.
- package/README.md +419 -345
- package/lib/database_connector.js +1 -0
- package/lib/model_v2.js +10 -0
- package/lib/nails.js +7 -1
- package/lib/sequelize_connector.js +4 -2
- package/package.json +6 -6
- package/templates/bin/start.sh +1 -1
- package/templates/config/db.js +6 -9
- package/templates/default/bin/promote.sh +20 -0
- package/templates/default/bin/rollout.sh +74 -0
- package/templates/default/bin/server.js +6 -0
- package/templates/default/bin/start.sh +16 -0
- package/templates/default/common/readme_fetcher.js +4 -0
- package/templates/default/config/db.js +19 -0
- package/templates/default/config/mimes.js +59 -0
- package/templates/default/config/routes.js +38 -0
- package/templates/default/config/service.js +45 -0
- package/templates/default/config/ssl/certificate.pem +22 -0
- package/templates/default/config/ssl/csr.csr +17 -0
- package/templates/default/config/ssl/key.pem +28 -0
- package/templates/default/config/ssl/private_key.pem +30 -0
- package/templates/default/config/ssl/public_key.pem +9 -0
- package/templates/default/package-lock.json +9048 -0
- package/templates/default/package.json +43 -0
- package/templates/default/public/README.xml +332 -0
- package/templates/default/public/css/styles.css +17 -0
- package/templates/default/public/download.jpg +0 -0
- package/templates/default/public/favicon.ico +0 -0
- package/templates/default/public/index.html +9 -0
- package/templates/default/public/js/client.js +1 -0
- package/templates/default/server/controllers/home_controller.js +34 -0
- package/templates/default/server/models/User.js +18 -0
- package/templates/default/server/views/home/index.ejs +14 -0
- package/templates/default/server/views/partials/javascripts.ejs +1 -0
- package/templates/default/server/views/partials/reactapp.ejs +1 -0
- package/templates/default/server/views/partials/styles.ejs +3 -0
- package/templates/default/spec/User.test.js +20 -0
- package/templates/default/spec/home_controller.test.js +28 -0
- package/templates/default/spec/setupTests.js +0 -0
- package/templates/default/src/AboutPage.jsx +9 -0
- package/templates/default/src/HomePage.jsx +9 -0
- package/templates/default/src/Layout.jsx +78 -0
- package/templates/default/src/ReadmePage.jsx +7 -0
- package/templates/default/src/app.jsx +29 -0
- package/templates/default/src/components/ReadmeLoader.jsx +13 -0
- package/templates/default/src/styles/appstyles.css +3 -0
- package/templates/default/vite.config.ts +42 -0
- package/templates/public/README.xml +72 -13
- package/templates/spec/User.test.js +1 -0
- package/templates/spec/home_controller.test.js +1 -0
- package/templates/vite.config.ts +4 -1
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
|
|
4
|
+
|
|
5
|
+
import Layout from './Layout';
|
|
6
|
+
import Home from './HomePage';
|
|
7
|
+
import About from './AboutPage';
|
|
8
|
+
import Readme from './ReadmePage';
|
|
9
|
+
|
|
10
|
+
import './styles/appstyles.css';
|
|
11
|
+
|
|
12
|
+
function App() {
|
|
13
|
+
console.log("rendering app");
|
|
14
|
+
return (
|
|
15
|
+
<Router>
|
|
16
|
+
<Routes>
|
|
17
|
+
<Route path="/" element={<Layout />}>
|
|
18
|
+
<Route index element={<Home />} />
|
|
19
|
+
<Route path="about" element={<About />} />
|
|
20
|
+
<Route path="readme" element={<Readme />} />
|
|
21
|
+
</Route>
|
|
22
|
+
</Routes>
|
|
23
|
+
</Router>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const container = document.getElementById('AppRoot');
|
|
28
|
+
const root = createRoot(container);
|
|
29
|
+
root.render(<App />);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useEffect, useState } from "react"
|
|
2
|
+
import { fetchReadme } from "@common/readme_fetcher";
|
|
3
|
+
|
|
4
|
+
export default function ReadmeLoader() {
|
|
5
|
+
const [readmeHtmlSnippet, setReadmeHtmlSnippet] = useState(null);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
fetchReadme().then(setReadmeHtmlSnippet);
|
|
8
|
+
}, []);
|
|
9
|
+
return (
|
|
10
|
+
<div dangerouslySetInnerHTML={{__html: readmeHtmlSnippet}}>
|
|
11
|
+
</div>
|
|
12
|
+
)
|
|
13
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/// <reference types="vitest" />
|
|
2
|
+
|
|
3
|
+
import react from '@vitejs/plugin-react'
|
|
4
|
+
import { defineConfig } from 'vite'
|
|
5
|
+
import path from 'path'
|
|
6
|
+
|
|
7
|
+
// https://vitejs.dev/config/
|
|
8
|
+
export default defineConfig({
|
|
9
|
+
publicDir: false,
|
|
10
|
+
define: {
|
|
11
|
+
'process.env': {}
|
|
12
|
+
},
|
|
13
|
+
plugins: [
|
|
14
|
+
react(),
|
|
15
|
+
],
|
|
16
|
+
test: {
|
|
17
|
+
globals: true,
|
|
18
|
+
// environment: 'jsdom',
|
|
19
|
+
// setupFiles: './spec/setupTests.js',
|
|
20
|
+
env: {
|
|
21
|
+
NAILS_RELEASE_STAGE: "test",
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
build: {
|
|
25
|
+
lib: {
|
|
26
|
+
entry: './src/app.jsx', // Your library's main entry file
|
|
27
|
+
name: 'NailsReactApp',
|
|
28
|
+
fileName: 'nails-react-app',
|
|
29
|
+
},
|
|
30
|
+
outDir: './public/dist',
|
|
31
|
+
},
|
|
32
|
+
esbuild: {
|
|
33
|
+
minifyIdentifiers: false,
|
|
34
|
+
keepNames: true,
|
|
35
|
+
},
|
|
36
|
+
resolve: {
|
|
37
|
+
alias: {
|
|
38
|
+
'@common': path.resolve(__dirname, './common'), // Alias '@' to the 'common' directory
|
|
39
|
+
'@server': path.resolve(__dirname, './server'), // Alias '@' to the 'server' directory
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
})
|
|
@@ -20,7 +20,7 @@ off to a good start.</p>
|
|
|
20
20
|
|
|
21
21
|
npm install
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
npm start
|
|
24
24
|
</code></pre>
|
|
25
25
|
<h2 id="gettingtoknowyournailsservice">Getting to know your Nails service</h2>
|
|
26
26
|
<p>For your convenience, here is a quick outline of the main components of a nails service.
|
|
@@ -51,7 +51,9 @@ above example, PORT will be overridden to 80 if NODE</em>ENV is set to PROD.</p>
|
|
|
51
51
|
<p>While most of these values don't need to be changed, feel free to add custom
|
|
52
52
|
fields. The resulting config will be available to your service through the nails
|
|
53
53
|
module:</p>
|
|
54
|
-
<pre><code class="js language-js">
|
|
54
|
+
<pre><code class="js language-js">import nails from 'nails-boilerplate';
|
|
55
|
+
|
|
56
|
+
const service_config = nails.config
|
|
55
57
|
</code></pre>
|
|
56
58
|
<p>If the config contains a custom field,</p>
|
|
57
59
|
<pre><code class="js language-js">export default {
|
|
@@ -68,7 +70,7 @@ module:</p>
|
|
|
68
70
|
<code>[method, path, options]</code></p>
|
|
69
71
|
<p><strong>method</strong> is a string defining the HTTP request method of the route. Supported
|
|
70
72
|
methods are <em>GET</em>, <em>PUT</em>, <em>POST</em>, <em>DELETE</em>, and <em>ALL</em>. All is a special case
|
|
71
|
-
which matches all HTTP request methods.</p>
|
|
73
|
+
which matches all HTTP request methods. Lastly, <em>WS</em> routes will handle WebSocket connections.</p>
|
|
72
74
|
<p><strong>path</strong> is a string or regular expression which matches the path of the
|
|
73
75
|
incoming request. If <em>path</em> is a string, then the request must match exactly*.
|
|
74
76
|
You can use route parameters to dynamically match parts of the path and assign
|
|
@@ -109,7 +111,7 @@ to dynamically route your request to the appropriate handler.</li>
|
|
|
109
111
|
<h4 id="dbjs">db.js</h4>
|
|
110
112
|
<p>Quickly configure your database connection here. Nails comes pre-configured to
|
|
111
113
|
use the sequelize connector, giving your models sequelize support. The initial setup
|
|
112
|
-
uses
|
|
114
|
+
uses a <em>sqlite3</em> database file <code>config/development.db</code> and an in-memory database in the test environment. Change the address to change the location and
|
|
113
115
|
version of your desired sql database. Check out <a href="https://sequelize.org">Sequelize</a>
|
|
114
116
|
for more info.</p>
|
|
115
117
|
<p>Alternatively, you can configure a connection to MongoDB using the mongoose_connector.js.
|
|
@@ -160,6 +162,9 @@ implicitly routed to their respective parent controllers.</p>
|
|
|
160
162
|
['get', './relative/path', {action: 'actionB'}],
|
|
161
163
|
// Routes requests to /users/relative/path
|
|
162
164
|
['get', 'relative/path', {action: 'actionB'}],
|
|
165
|
+
// If no action is provided, the last path segment
|
|
166
|
+
// is used as the action name.
|
|
167
|
+
['get', 'actionC']
|
|
163
168
|
]
|
|
164
169
|
|
|
165
170
|
// Handles requests to /absolute/path
|
|
@@ -167,6 +172,9 @@ implicitly routed to their respective parent controllers.</p>
|
|
|
167
172
|
|
|
168
173
|
// Handles requests to /users/relative/path
|
|
169
174
|
actionB(request, response, params) {}
|
|
175
|
+
|
|
176
|
+
// Handles requests to /users/actionC
|
|
177
|
+
actionC(request, response, params) {}
|
|
170
178
|
}
|
|
171
179
|
</code></pre>
|
|
172
180
|
<h3 id="actions">Actions</h3>
|
|
@@ -207,6 +215,26 @@ params object:</p>
|
|
|
207
215
|
<h4 id="response">Response</h4>
|
|
208
216
|
<p>The response object provided by <em>express.js</em>. The <em>#render()</em> method has been
|
|
209
217
|
overridden to allow for the rendering of views by name.</p>
|
|
218
|
+
<h4 id="jsonactions">JSON Actions</h4>
|
|
219
|
+
<p>JSON actions only return JSON objects in the response instead of text or HTML. These actions are ideal for building an API Server. There are three ways to designate JSON Actions:</p>
|
|
220
|
+
<h5 id="expressresponseobject">Express Response Object</h5>
|
|
221
|
+
<pre><code class="js language-js"> response.json({your: 'jsonresponse'})
|
|
222
|
+
</code></pre>
|
|
223
|
+
<p>Simply use the express Response object directly</p>
|
|
224
|
+
<h5 id="jsonroutes">JSON Routes</h5>
|
|
225
|
+
<p>You can configure an individual route to respond with JSON by setting the <code>json</code> option to <code>true</code>.</p>
|
|
226
|
+
<pre><code class="js language-js">['get', '/your/json/route', {json: true}],
|
|
227
|
+
</code></pre>
|
|
228
|
+
<h5 id="apicontrollers">API Controllers</h5>
|
|
229
|
+
<p>By setting <code>json</code> to <code>true</code>, all actions in a controller will respond with JSON.</p>
|
|
230
|
+
<pre><code class="js language-js">class YourApiController extends nails.Controller {
|
|
231
|
+
json = true;
|
|
232
|
+
|
|
233
|
+
action(params, request, response) {
|
|
234
|
+
return {your: 'jsonresponse'};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
</code></pre>
|
|
210
238
|
<h2 id="model">Model</h2>
|
|
211
239
|
<p>Models are programmatic representations of data you wish to persist in a
|
|
212
240
|
database. The constructor for Model accepts two arguments: the <code>modelName</code> and an
|
|
@@ -219,15 +247,25 @@ extending an instance of the <code>Model</code> class provided by Nails:</p>
|
|
|
219
247
|
<pre><code class="js language-js">// const Model = require("nails-boilerplate").Model;
|
|
220
248
|
import nails from 'nails-boilerplate';
|
|
221
249
|
import {DataTypes} from 'sequelize';
|
|
222
|
-
|
|
250
|
+
schema = {
|
|
223
251
|
name: {type: DataTypes.STRING, allowNull: false},
|
|
224
252
|
email: {type: DataTypes.STRING, allowNull: false}
|
|
225
253
|
};
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
254
|
+
|
|
255
|
+
options = {
|
|
256
|
+
indexes: [
|
|
257
|
+
{
|
|
258
|
+
unique: true,
|
|
259
|
+
fields: ['email'],
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
export default class User extends new Model("User", {schema, options}) {
|
|
265
|
+
someHelperMethod() {
|
|
266
|
+
// This method will be available on all instances of User and is
|
|
267
|
+
// an ideal way to simplify data manipulation.
|
|
268
|
+
}
|
|
231
269
|
};
|
|
232
270
|
</code></pre>
|
|
233
271
|
<h3 id="mongoosemodels">Mongoose Models</h3>
|
|
@@ -244,6 +282,17 @@ export default class User extends new Model("User", {schema: userSchema}) {
|
|
|
244
282
|
</code></pre>
|
|
245
283
|
<p>The <code>schema</code> option for Mongoose Models accepts a schema field that is used
|
|
246
284
|
to define how documents are stored in MongoDB.</p>
|
|
285
|
+
<h3 id="modellibrary">Model Library</h3>
|
|
286
|
+
<p>Nails will store all instantialized models in a single object called <code>MODELS</code>. By accessing these models via the library, you can avoid circular dependencies and ensure all models have been fully initialized.</p>
|
|
287
|
+
<pre><code class="js language-js">class User extends nails.Model("User", {schema, options}) {
|
|
288
|
+
// A helper method which depends on anoher model using the
|
|
289
|
+
// Nails Model Library rather than directly importing the model.
|
|
290
|
+
async findFriends() {
|
|
291
|
+
return nails.MODELS.Friend.findByUserId(this.id);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
</code></pre>
|
|
295
|
+
<p>This design pattern is not always necessary, but will help avoid circular dependencies.</p>
|
|
247
296
|
<h3 id="databaseconnectors">Database Connectors</h3>
|
|
248
297
|
<p>Database connectors are intermediaries which define how a Model interacts with
|
|
249
298
|
a database. Database connector modules need to export two methods:</p>
|
|
@@ -257,11 +306,21 @@ a database. Ideally, interfaces will define save() and find() methods, but
|
|
|
257
306
|
these methods and their implementations are up to the individual connector.</li>
|
|
258
307
|
</ul>
|
|
259
308
|
<h2 id="view">View</h2>
|
|
260
|
-
<p>Views are
|
|
261
|
-
Nails comes prepackaged with
|
|
262
|
-
If no template engine is specified in the service config, Nails will
|
|
309
|
+
<p>Views are dynamic templates used to render an html response for a browser.
|
|
310
|
+
Nails comes prepackaged with EJS templates.
|
|
311
|
+
If no template engine is specified in the service config, Nails will default to
|
|
263
312
|
EJS. Nails will always attempt to autorender your views unless a response has
|
|
264
313
|
already been sent to the client.</p>
|
|
314
|
+
<h3 id="reactfrontendwithvite">React Frontend with Vite</h3>
|
|
315
|
+
<p>This project uses Vite to build the frontend React application. The source files for the React app are located in the <code>src</code> directory. The server is pre-configured to serve the built React application.</p>
|
|
316
|
+
<p>To rebuild the frontend application, you can run the following command:</p>
|
|
317
|
+
<pre><code class="bash language-bash">npm run build
|
|
318
|
+
</code></pre>
|
|
319
|
+
<h2 id="testing">Testing</h2>
|
|
320
|
+
<p>This project uses <a href="https://vitest.dev/">Vitest</a> for running tests. All test files are located in the <code>spec</code> folder. To run the tests, use the following command:</p>
|
|
321
|
+
<pre><code class="bash language-bash">npm test
|
|
322
|
+
</code></pre>
|
|
323
|
+
<p>For more information on how to write tests with Vitest, please refer to the <a href="https://vitest.dev/guide/">official documentation</a>.</p>
|
|
265
324
|
<p>Stay tuned as nails evolves:</p>
|
|
266
325
|
<ul>
|
|
267
326
|
<li>Server/client redirects</li>
|
package/templates/vite.config.ts
CHANGED