nitro-web 0.0.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.
- package/.editorconfig +9 -0
- package/.eslintrc.json +86 -0
- package/_example/.env-example +16 -0
- package/_example/client/config.ts +5 -0
- package/_example/client/css/index.css +35 -0
- package/_example/client/fonts/Roboto-Bold.ttf +0 -0
- package/_example/client/fonts/Roboto-BoldItalic.ttf +0 -0
- package/_example/client/fonts/Roboto-Italic.ttf +0 -0
- package/_example/client/fonts/Roboto-Medium.ttf +0 -0
- package/_example/client/fonts/Roboto-MediumItalic.ttf +0 -0
- package/_example/client/fonts/Roboto-Regular.ttf +0 -0
- package/_example/client/fonts/inter-v13-latin-300.woff2 +0 -0
- package/_example/client/fonts/inter-v13-latin-500.woff2 +0 -0
- package/_example/client/fonts/inter-v13-latin-600.woff2 +0 -0
- package/_example/client/fonts/inter-v13-latin-700.woff2 +0 -0
- package/_example/client/fonts/inter-v13-latin-800.woff2 +0 -0
- package/_example/client/fonts/inter-v13-latin-900.woff2 +0 -0
- package/_example/client/fonts/inter-v13-latin-regular.woff2 +0 -0
- package/_example/client/imgs/android-chrome-512x512.png +0 -0
- package/_example/client/imgs/favicon.png +0 -0
- package/_example/client/imgs/icons/calendar.svg +3 -0
- package/_example/client/imgs/icons/email.svg +6 -0
- package/_example/client/imgs/icons/eye-open.svg +4 -0
- package/_example/client/imgs/icons/eye.svg +5 -0
- package/_example/client/imgs/icons/filter.svg +7 -0
- package/_example/client/imgs/icons/left-circle.svg +3 -0
- package/_example/client/imgs/icons/left.svg +3 -0
- package/_example/client/imgs/icons/line-options.svg +5 -0
- package/_example/client/imgs/icons/line.svg +3 -0
- package/_example/client/imgs/icons/person.svg +7 -0
- package/_example/client/imgs/icons/plus-circle.svg +5 -0
- package/_example/client/imgs/icons/plus.svg +5 -0
- package/_example/client/imgs/icons/right-circle.svg +3 -0
- package/_example/client/imgs/icons/right.svg +3 -0
- package/_example/client/imgs/icons/search.svg +3 -0
- package/_example/client/imgs/icons/shield.svg +6 -0
- package/_example/client/imgs/icons/tick-circle-solid.svg +8 -0
- package/_example/client/imgs/icons/tick-circle.svg +6 -0
- package/_example/client/imgs/icons/tick.svg +5 -0
- package/_example/client/imgs/icons/up2-small.svg +4 -0
- package/_example/client/imgs/icons/up2.svg +4 -0
- package/_example/client/imgs/icons/updown.svg +6 -0
- package/_example/client/imgs/icons/v-big-dark.svg +3 -0
- package/_example/client/imgs/icons/v-dark.svg +3 -0
- package/_example/client/imgs/icons/v.svg +3 -0
- package/_example/client/imgs/icons/v2-active.svg +6 -0
- package/_example/client/imgs/icons/x1.svg +4 -0
- package/_example/client/imgs/logo/logo-white.svg +20 -0
- package/_example/client/imgs/logo/logo.svg +20 -0
- package/_example/client/imgs/no-image.jpg +0 -0
- package/_example/client/imgs/user.jpg +0 -0
- package/_example/client/index.html +12 -0
- package/_example/client/index.ts +47 -0
- package/_example/components/auth.api.js +1 -0
- package/_example/components/index.tsx +225 -0
- package/_example/components/partials/layouts.tsx +5 -0
- package/_example/components/settings.api.js +1 -0
- package/_example/server/config.js +120 -0
- package/_example/server/email/welcome.html +27 -0
- package/_example/server/index.js +32 -0
- package/_example/tailwind.config.js +84 -0
- package/_example/tsconfig.json +32 -0
- package/_example/types.d.ts +7 -0
- package/_example/webpack.config.js +4 -0
- package/client/app.js +300 -0
- package/client/css/components.css +84 -0
- package/client/css/fonts.css +67 -0
- package/client/imgs/icons/calendar.svg +3 -0
- package/client/imgs/icons/email.svg +6 -0
- package/client/imgs/icons/eye-open.svg +4 -0
- package/client/imgs/icons/eye.svg +5 -0
- package/client/imgs/icons/filter.svg +7 -0
- package/client/imgs/icons/left-circle.svg +3 -0
- package/client/imgs/icons/left.svg +3 -0
- package/client/imgs/icons/line-options.svg +5 -0
- package/client/imgs/icons/line.svg +3 -0
- package/client/imgs/icons/person.svg +7 -0
- package/client/imgs/icons/plus-circle.svg +5 -0
- package/client/imgs/icons/plus.svg +5 -0
- package/client/imgs/icons/right-circle.svg +3 -0
- package/client/imgs/icons/right.svg +3 -0
- package/client/imgs/icons/search.svg +3 -0
- package/client/imgs/icons/shield.svg +6 -0
- package/client/imgs/icons/tick-circle-solid.svg +8 -0
- package/client/imgs/icons/tick-circle.svg +6 -0
- package/client/imgs/icons/tick.svg +5 -0
- package/client/imgs/icons/up2-small.svg +4 -0
- package/client/imgs/icons/up2.svg +4 -0
- package/client/imgs/icons/updown.svg +6 -0
- package/client/imgs/icons/v-big-dark.svg +3 -0
- package/client/imgs/icons/v-dark.svg +3 -0
- package/client/imgs/icons/v.svg +3 -0
- package/client/imgs/icons/v2-active.svg +6 -0
- package/client/imgs/icons/x1.svg +4 -0
- package/client.js +42 -0
- package/components/auth/auth.api.js +419 -0
- package/components/auth/reset.jsx +88 -0
- package/components/auth/signin.jsx +74 -0
- package/components/auth/signup.jsx +62 -0
- package/components/billing/stripe.api.js +267 -0
- package/components/partials/element/accordion.jsx +82 -0
- package/components/partials/element/avatar.jsx +28 -0
- package/components/partials/element/button.jsx +66 -0
- package/components/partials/element/dropdown.jsx +185 -0
- package/components/partials/element/initials.jsx +56 -0
- package/components/partials/element/message.jsx +124 -0
- package/components/partials/element/modal.jsx +229 -0
- package/components/partials/element/sidebar.jsx +166 -0
- package/components/partials/element/tooltip.jsx +146 -0
- package/components/partials/element/topbar.jsx +25 -0
- package/components/partials/form/checkbox.jsx +74 -0
- package/components/partials/form/drop-handler.jsx +62 -0
- package/components/partials/form/drop.jsx +125 -0
- package/components/partials/form/form-error.jsx +21 -0
- package/components/partials/form/input-color.jsx +77 -0
- package/components/partials/form/input-currency.jsx +133 -0
- package/components/partials/form/input-date.jsx +223 -0
- package/components/partials/form/input.jsx +131 -0
- package/components/partials/form/location.jsx +212 -0
- package/components/partials/form/select.jsx +369 -0
- package/components/partials/form/toggle.jsx +46 -0
- package/components/partials/is-first-render.js +15 -0
- package/components/partials/layout/layout1.jsx +32 -0
- package/components/partials/layout/layout2.jsx +47 -0
- package/components/partials/not-found.jsx +7 -0
- package/components/partials/styleguide.jsx +252 -0
- package/components/settings/settings-account.jsx +143 -0
- package/components/settings/settings-business.jsx +121 -0
- package/components/settings/settings-team--member.jsx +108 -0
- package/components/settings/settings-team.jsx +76 -0
- package/components/settings/settings.api.js +54 -0
- package/package.json +175 -0
- package/readme.md +43 -0
- package/server/email/index.js +192 -0
- package/server/email/partials/email.css +153 -0
- package/server/email/partials/layout1.swig +92 -0
- package/server/email/partials/line.swig +8 -0
- package/server/email/partials/vert-10.swig +8 -0
- package/server/email/partials/vert-15.swig +8 -0
- package/server/email/partials/vert-20.swig +8 -0
- package/server/email/partials/vert-25.swig +8 -0
- package/server/email/partials/vert-30.swig +8 -0
- package/server/email/partials/vert-35.swig +8 -0
- package/server/email/partials/vert-50.swig +8 -0
- package/server/email/reset-password.html +21 -0
- package/server/email/welcome.html +21 -0
- package/server/models/company.js +76 -0
- package/server/models/user.js +45 -0
- package/server/router.js +355 -0
- package/server.js +20 -0
- package/util.js +1145 -0
- package/webpack.config.js +302 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Any component with a .route property is called a page component. Any page component found under /components
|
|
3
|
+
* will be automatically imported and setup by the client router, we're just listing them all here for the sake of example.
|
|
4
|
+
*/
|
|
5
|
+
import {
|
|
6
|
+
Signin,
|
|
7
|
+
Signup,
|
|
8
|
+
ResetInstructions,
|
|
9
|
+
ResetPassword,
|
|
10
|
+
// SettingsAccount,
|
|
11
|
+
// SettingsBusiness,
|
|
12
|
+
// SettingsTeam,
|
|
13
|
+
Styleguide,
|
|
14
|
+
NotFound,
|
|
15
|
+
} from 'nitro-web'
|
|
16
|
+
import config from '../client/config'
|
|
17
|
+
|
|
18
|
+
// Signin page (can be saved onto a seperate file under the components folder)
|
|
19
|
+
export const SigninPage = () => <Signin config={config} />
|
|
20
|
+
SigninPage.route = {
|
|
21
|
+
'/signin': true,
|
|
22
|
+
'/signout': true,
|
|
23
|
+
'meta': { 'title': 'Sign In - Nitro', layout: 2 },
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Signup page
|
|
27
|
+
export const SignupPage = () => <Signup config={config} />
|
|
28
|
+
SignupPage.route = {
|
|
29
|
+
'/signup': true,
|
|
30
|
+
'meta': { 'title': 'Sign Up - Nitro', layout: 2 },
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Reset instructions page
|
|
34
|
+
export const ResetInstructionsPage = () => <ResetInstructions />
|
|
35
|
+
ResetInstructionsPage.route = {
|
|
36
|
+
'/reset': true,
|
|
37
|
+
'meta': { 'title': 'Reset password - Nitro', layout: 2 },
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Reset password page
|
|
41
|
+
export const ResetPasswordPage = () => <ResetPassword />
|
|
42
|
+
ResetPasswordPage.route = {
|
|
43
|
+
'/reset/:token': true,
|
|
44
|
+
'meta': { 'title': 'Reset password - Nitro', layout: 2 },
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// // Settings Account page
|
|
48
|
+
// export const SettingsAccountPage = () => <SettingsAccount />
|
|
49
|
+
// SettingsAccountPage.route = {
|
|
50
|
+
// '/settings/account': ['isUser'],
|
|
51
|
+
// 'meta': { 'title': 'Account Settings - Nitro', layout: 1 },
|
|
52
|
+
// }
|
|
53
|
+
|
|
54
|
+
// // Settings Business page
|
|
55
|
+
// export const SettingsBusinessPage = () => <SettingsBusiness config={config} />
|
|
56
|
+
// SettingsBusinessPage.route = {
|
|
57
|
+
// '/settings/business': ['isUser'],
|
|
58
|
+
// 'meta': { 'title': 'Business Settings - Nitro', layout: 1 },
|
|
59
|
+
// }
|
|
60
|
+
|
|
61
|
+
// // Settings Team page
|
|
62
|
+
// export const SettingsTeamPage = () => <SettingsTeam config={config} />
|
|
63
|
+
// SettingsTeamPage.route = {
|
|
64
|
+
// '/settings/team': ['isUser'],
|
|
65
|
+
// 'meta': { 'title': 'Team Settings - Nitro', layout: 1 },
|
|
66
|
+
// }
|
|
67
|
+
|
|
68
|
+
// Dashboard page
|
|
69
|
+
export function DashboardPage() {
|
|
70
|
+
return (
|
|
71
|
+
<div>
|
|
72
|
+
<h1 className="h1 mb-8">Dashboard</h1>
|
|
73
|
+
<p className="text-gray-700">
|
|
74
|
+
Welcome to Nitro, a modular React/Express base project, styled using Tailwind 🚀.
|
|
75
|
+
</p>
|
|
76
|
+
</div>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
DashboardPage.route = {
|
|
80
|
+
'/': true,
|
|
81
|
+
'meta': { 'title': 'Dashboard - Nitro', layout: 1 },
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Styleguide page
|
|
85
|
+
export const StyleguidePage = () => <Styleguide config={config} />
|
|
86
|
+
StyleguidePage.route = {
|
|
87
|
+
'/styleguide': true,
|
|
88
|
+
'meta': { title: 'Style Guide - Nitro', layout: 1 },
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Not found page
|
|
92
|
+
export const NotFoundPage = () => <NotFound />
|
|
93
|
+
NotFoundPage.route = {
|
|
94
|
+
'*': true,
|
|
95
|
+
'meta': { 'title': 'Nothing found - Nitro', layout: 1 },
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Tailwind UI pricing page example
|
|
99
|
+
export function PricingPage() {
|
|
100
|
+
const tiers = [
|
|
101
|
+
{
|
|
102
|
+
name: 'Hobby',
|
|
103
|
+
id: 'tier-hobby',
|
|
104
|
+
href: '#',
|
|
105
|
+
priceMonthly: '$29',
|
|
106
|
+
description: 'The perfect plan if you\'re just getting started with our product.',
|
|
107
|
+
features: ['25 products', 'Up to 10,000 subscribers', 'Advanced analytics', '24-hour support response time'],
|
|
108
|
+
featured: false,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'Enterprise',
|
|
112
|
+
id: 'tier-enterprise',
|
|
113
|
+
href: '#',
|
|
114
|
+
priceMonthly: '$99',
|
|
115
|
+
description: 'Dedicated support and infrastructure for your company.',
|
|
116
|
+
features: [
|
|
117
|
+
'Unlimited products',
|
|
118
|
+
'Unlimited subscribers',
|
|
119
|
+
'Advanced analytics',
|
|
120
|
+
'Dedicated support representative',
|
|
121
|
+
'Marketing automations',
|
|
122
|
+
'Custom integrations',
|
|
123
|
+
],
|
|
124
|
+
featured: true,
|
|
125
|
+
},
|
|
126
|
+
]
|
|
127
|
+
function classNames(...classes: string[]) {
|
|
128
|
+
return classes.filter(Boolean).join(' ')
|
|
129
|
+
}
|
|
130
|
+
return (
|
|
131
|
+
<div className="relative isolate bg-white py-12 md:py-16 px-6">
|
|
132
|
+
<div aria-hidden="true" className="absolute inset-x-0 -top-3 -z-10 transform-gpu overflow-hidden px-36 blur-3xl">
|
|
133
|
+
<div
|
|
134
|
+
style={{
|
|
135
|
+
clipPath:
|
|
136
|
+
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 85.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%,'
|
|
137
|
+
+' 47.5% 58.3%, 45.2% 34.5%, 27.5% 76.7%, 0.1% 64.9%, 17.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 74.1% 44.1%)',
|
|
138
|
+
}}
|
|
139
|
+
className="mx-auto aspect-[1155/678] w-[72.1875rem] bg-gradient-to-tr from-[#ff80b5] to-[#9089fc] opacity-30"
|
|
140
|
+
/>
|
|
141
|
+
</div>
|
|
142
|
+
<div className="mx-auto max-w-4xl text-center">
|
|
143
|
+
<h2 className="text-base/7 font-semibold text-indigo-600">Pricing</h2>
|
|
144
|
+
<p className="mt-2 text-balance text-5xl font-semibold tracking-tight text-gray-900 sm:text-5xl">
|
|
145
|
+
Choose the right plan for you
|
|
146
|
+
</p>
|
|
147
|
+
</div>
|
|
148
|
+
<p className="mx-auto mt-6 max-w-2xl text-pretty text-center text-lg font-medium text-gray-600 sm:text-xl/8">
|
|
149
|
+
Choose an affordable plan that’s packed with the best features for engaging your audience, creating customer
|
|
150
|
+
loyalty, and driving sales.
|
|
151
|
+
</p>
|
|
152
|
+
<div className="mx-auto mt-16 grid max-w-lg grid-cols-1 items-center gap-y-6 sm:mt-20 sm:gap-y-0 lg:max-w-4xl lg:grid-cols-2">
|
|
153
|
+
{tiers.map((tier, tierIdx) => (
|
|
154
|
+
<div
|
|
155
|
+
key={tier.id}
|
|
156
|
+
className={classNames(
|
|
157
|
+
tier.featured ? 'relative bg-gray-900 shadow-2xl' : 'bg-white/60 sm:mx-8 lg:mx-0',
|
|
158
|
+
tier.featured
|
|
159
|
+
? ''
|
|
160
|
+
: tierIdx === 0
|
|
161
|
+
? 'rounded-t-3xl sm:rounded-b-none lg:rounded-bl-3xl lg:rounded-tr-none'
|
|
162
|
+
: 'sm:rounded-t-none lg:rounded-bl-none lg:rounded-tr-3xl',
|
|
163
|
+
'rounded-3xl p-8 ring-1 ring-gray-900/10 sm:p-10'
|
|
164
|
+
)}
|
|
165
|
+
>
|
|
166
|
+
<h3
|
|
167
|
+
id={tier.id}
|
|
168
|
+
className={classNames(tier.featured ? 'text-indigo-400' : 'text-indigo-600', 'text-base/7 font-semibold')}
|
|
169
|
+
>
|
|
170
|
+
{tier.name}
|
|
171
|
+
</h3>
|
|
172
|
+
<p className="mt-4 flex items-baseline gap-x-2">
|
|
173
|
+
<span
|
|
174
|
+
className={classNames(
|
|
175
|
+
tier.featured ? 'text-white' : 'text-gray-900',
|
|
176
|
+
'text-5xl font-semibold tracking-tight'
|
|
177
|
+
)}
|
|
178
|
+
>
|
|
179
|
+
{tier.priceMonthly}
|
|
180
|
+
</span>
|
|
181
|
+
<span className={classNames(tier.featured ? 'text-gray-400' : 'text-gray-500', 'text-base')}>/month</span>
|
|
182
|
+
</p>
|
|
183
|
+
<p className={classNames(tier.featured ? 'text-gray-300' : 'text-gray-600', 'mt-6 text-base/7')}>
|
|
184
|
+
{tier.description}
|
|
185
|
+
</p>
|
|
186
|
+
<ul
|
|
187
|
+
role="list"
|
|
188
|
+
className={classNames(
|
|
189
|
+
tier.featured ? 'text-gray-300' : 'text-gray-600',
|
|
190
|
+
'mt-8 space-y-3 text-sm/6 sm:mt-10'
|
|
191
|
+
)}
|
|
192
|
+
>
|
|
193
|
+
{tier.features.map((feature) => (
|
|
194
|
+
<li key={feature} className="flex gap-x-3">
|
|
195
|
+
{/* <CheckIcon
|
|
196
|
+
aria-hidden="true"
|
|
197
|
+
className={classNames(tier.featured ? 'text-indigo-400' : 'text-indigo-600', 'h-6 w-5 flex-none')}
|
|
198
|
+
/> */}
|
|
199
|
+
{feature}
|
|
200
|
+
</li>
|
|
201
|
+
))}
|
|
202
|
+
</ul>
|
|
203
|
+
<a
|
|
204
|
+
href={tier.href}
|
|
205
|
+
aria-describedby={tier.id}
|
|
206
|
+
className={classNames(
|
|
207
|
+
tier.featured
|
|
208
|
+
? 'bg-indigo-500 text-white shadow-sm hover:bg-indigo-400 focus-visible:outline-indigo-500'
|
|
209
|
+
: 'text-indigo-600 ring-1 ring-inset ring-indigo-200 hover:ring-indigo-300 focus-visible:outline-indigo-600',
|
|
210
|
+
'mt-8 block rounded-md px-3.5 py-2.5 text-center text-sm font-semibold focus-visible:outline '
|
|
211
|
+
+ 'focus-visible:outline-2 focus-visible:outline-offset-2 sm:mt-10'
|
|
212
|
+
)}
|
|
213
|
+
>
|
|
214
|
+
Get started today
|
|
215
|
+
</a>
|
|
216
|
+
</div>
|
|
217
|
+
))}
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
PricingPage.route = {
|
|
223
|
+
'/pricing': true,
|
|
224
|
+
'meta': { 'title': 'Pricing - Nitro', layout: 1 },
|
|
225
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { settings as default } from '#nitro-web/server.js'
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import 'dotenv/config'
|
|
2
|
+
import { dirname, resolve } from 'path'
|
|
3
|
+
import { fileURLToPath } from 'url'
|
|
4
|
+
import { createRequire } from 'module'
|
|
5
|
+
const _require = createRequire(import.meta.url)
|
|
6
|
+
const projectDir = resolve(dirname(fileURLToPath(import.meta.url)) + '/../') + '/'
|
|
7
|
+
const env = process.env.env || (process.env.NODE_ENV !== 'production' ? 'development' : process.env.NODE_ENV)
|
|
8
|
+
const isNitro = process.env.NITRO
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
inject: 'awsUrl clientUrl currencies countries env googleMapsApiKey stripePublishableKey testEmail',
|
|
12
|
+
|
|
13
|
+
// apiUrl: process.env.originUrl || 'http://localhost:3001',
|
|
14
|
+
clientUrl: process.env.originUrl || 'http://localhost:3000',
|
|
15
|
+
emailFrom: process.env.emailFrom,
|
|
16
|
+
emailReplyTo: process.env.emailReplyTo,
|
|
17
|
+
emailTestMode: process.env.emailTestMode,
|
|
18
|
+
env: env,
|
|
19
|
+
masterPassword: process.env.masterPassword,
|
|
20
|
+
mongoUrl: process.env.mongoUrl,
|
|
21
|
+
testEmail: process.env.testEmail,
|
|
22
|
+
|
|
23
|
+
clientDir: projectDir + 'client/',
|
|
24
|
+
componentsDir: projectDir + 'components/',
|
|
25
|
+
distDir: projectDir + 'client/dist/',
|
|
26
|
+
emailTemplateDir: projectDir + 'server/email/',
|
|
27
|
+
modelsDir: projectDir + 'server/models/',
|
|
28
|
+
projectDir: projectDir,
|
|
29
|
+
tmpDir: projectDir + 'tmp/',
|
|
30
|
+
nitroDir: resolve(isNitro ? projectDir + '../' : projectDir + 'node_modules/nitro-web/') + '/',
|
|
31
|
+
|
|
32
|
+
awsUrl: process.env.awsUrl,
|
|
33
|
+
googleMapsApiKey: process.env.googleMapsApiKey,
|
|
34
|
+
mailgunDomain: process.env.mailgunDomain,
|
|
35
|
+
mailgunKey: process.env.mailgunKey,
|
|
36
|
+
stripePublishableKey: process.env.stripePublishableKey,
|
|
37
|
+
stripeSecretKey: process.env.stripeSecretKey,
|
|
38
|
+
stripeWebhookSecret: process.env.stripeWebhookSecret,
|
|
39
|
+
|
|
40
|
+
monasteryOptions: {
|
|
41
|
+
noDefaults: true,
|
|
42
|
+
nullObjects: true,
|
|
43
|
+
useMilliseconds: true,
|
|
44
|
+
imagePlugin: process.env.awsSecretAccessKey
|
|
45
|
+
? {
|
|
46
|
+
awsBucket: process.env.awsBucket,
|
|
47
|
+
awsRegion: process.env.awsRegion,
|
|
48
|
+
awsAccessKeyId: process.env.awsAccessKeyId,
|
|
49
|
+
awsSecretAccessKey: process.env.awsSecretAccessKey,
|
|
50
|
+
formats: ['png', 'jpg', 'jpeg', 'bmp', 'tiff', 'gif', 'webp'],
|
|
51
|
+
}
|
|
52
|
+
: undefined,
|
|
53
|
+
// show mongod selection error faster in development
|
|
54
|
+
serverSelectionTimeoutMS: env == 'development' ? 3000 : undefined,
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
countries: {
|
|
58
|
+
nz: {
|
|
59
|
+
currency: 'nzd',
|
|
60
|
+
name: 'New Zealand',
|
|
61
|
+
numberFormats: {
|
|
62
|
+
currency: '¤#,##0.00',
|
|
63
|
+
percentage: '¤#,##0.00%',
|
|
64
|
+
},
|
|
65
|
+
dateFormats: {
|
|
66
|
+
full: 'dddd, D MMMM YYYY',
|
|
67
|
+
long: 'D MMMM YYYY',
|
|
68
|
+
medium: 'D/MM/YYYY',
|
|
69
|
+
short: 'D/MM/YY',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
currencies: {
|
|
75
|
+
nzd: {
|
|
76
|
+
name: 'New Zealand Dollar',
|
|
77
|
+
symbol: '$',
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
middleware: {
|
|
82
|
+
isAdmin: (req, res, next) => {
|
|
83
|
+
// Still need to remove cookie matching in favour of uid..
|
|
84
|
+
// E.g. Cookie matching handy for rare issues, e.g. signout > signin (to a different user on another tab)
|
|
85
|
+
let cookieMatch = req.user && (!req.headers.userid || req.user._id.toString() == req.headers.userid)
|
|
86
|
+
if (cookieMatch && req.user.type.match(/admin/)) next()
|
|
87
|
+
else if (req.user && req.user.type.match(/admin/)) res.unauthorized('Invalid cookie, please refresh your browser')
|
|
88
|
+
else if (req.user) res.unauthorized('You are not authorised to make this request.')
|
|
89
|
+
else res.unauthorized('Please sign in first.')
|
|
90
|
+
},
|
|
91
|
+
isCompanyOwner: (req, res, next) => {
|
|
92
|
+
let user = req.user || { companies: [] }
|
|
93
|
+
let cid = req.params.cid
|
|
94
|
+
let company = user.companies.find((o) => o._id.toString() == cid)
|
|
95
|
+
let companyUser = company?.users?.find((o) => o._id.toString() == user._id.toString())
|
|
96
|
+
if (!user._id) return res.unauthorized('Please sign in first.')
|
|
97
|
+
else if (!company || !companyUser) res.unauthorized('You are not authorised to make this request.')
|
|
98
|
+
else if (companyUser.type != 'owner') res.unauthorized('Only owners can make this request.')
|
|
99
|
+
else next()
|
|
100
|
+
},
|
|
101
|
+
isCompanyUser: (req, res, next) => {
|
|
102
|
+
let user = req.user || { companies: [] }
|
|
103
|
+
let cid = req.params.cid
|
|
104
|
+
let company = user.companies.find((o) => o._id.toString() == cid)
|
|
105
|
+
if (!user._id) return res.unauthorized('Please sign in first.')
|
|
106
|
+
else if (!company) res.unauthorized('You are not authorised to make this request.')
|
|
107
|
+
else next()
|
|
108
|
+
},
|
|
109
|
+
isUser: (req, res, next) => {
|
|
110
|
+
// todo: need to double check that uid is always defined
|
|
111
|
+
let uid = req.params.uid
|
|
112
|
+
if (req.user && (typeof uid == 'undefined' || req.user._id.toString() == uid)) next()
|
|
113
|
+
else if (req.user) res.unauthorized('You are not authorised to make this request.')
|
|
114
|
+
else res.unauthorized('Please sign in first.')
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
version: _require(projectDir + (isNitro ? '../' : '') + 'package.json').version,
|
|
119
|
+
isNitro: isNitro,
|
|
120
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!-- You can view templates at http://localhost:3001/email/welcome -->
|
|
2
|
+
<!-- To modify the css, add your own /server/email/partials/email.css file -->
|
|
3
|
+
|
|
4
|
+
<!-- extends references the default nitro layout file -->
|
|
5
|
+
{% extends "partials/layout1.swig" %}
|
|
6
|
+
|
|
7
|
+
<!-- block content is the content of the email -->
|
|
8
|
+
{% block content %}
|
|
9
|
+
|
|
10
|
+
[[ subject = Welcome to Nitro ]]
|
|
11
|
+
<b>%recipient.greet%</b>,<br/>
|
|
12
|
+
<br/>
|
|
13
|
+
Thanks for trying out Nitro (the example)! <br/>
|
|
14
|
+
<br/>
|
|
15
|
+
If you have any feature requests, feedback or questions, please don't hesitate to reach out to us at <a href="mailto:%recipient.replyToEmail%">%recipient.replyToEmail%</a>.<br/>
|
|
16
|
+
<br/>
|
|
17
|
+
<br/>
|
|
18
|
+
<span mc:edit="button">
|
|
19
|
+
<a class="button" href="%recipient.domain%/signin?email=%recipient.email%" target="_blank">
|
|
20
|
+
Account Sign In
|
|
21
|
+
</a>
|
|
22
|
+
</span><br/>
|
|
23
|
+
<br/>
|
|
24
|
+
Thanks,<br/>
|
|
25
|
+
<b>The Nitro Team</b>
|
|
26
|
+
|
|
27
|
+
{% endblock %}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import 'dotenv/config'
|
|
2
|
+
import db from 'monastery'
|
|
3
|
+
import config from './config.js'
|
|
4
|
+
import { setupRouter, setupDefaultModels } from '#nitro-web/server.js'
|
|
5
|
+
|
|
6
|
+
// Setup monastery models
|
|
7
|
+
db.manager(config.mongoUrl, config.monasteryOptions)
|
|
8
|
+
await db.models(config.modelsDir)
|
|
9
|
+
await setupDefaultModels(db)
|
|
10
|
+
|
|
11
|
+
// Catch mongod not running
|
|
12
|
+
if (config.env === 'development') {
|
|
13
|
+
db.onError((err) => console.log(err))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Setup router
|
|
17
|
+
const server = await setupRouter(config)
|
|
18
|
+
|
|
19
|
+
// Start express
|
|
20
|
+
server.listen(process.env.PORT || 3001, '0.0.0.0', async () => {
|
|
21
|
+
// ...success
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
// You can send emails like this:
|
|
25
|
+
// const html = await sendEmail({
|
|
26
|
+
// config: config,
|
|
27
|
+
// data: { name: 'Test' },
|
|
28
|
+
// template: 'welcome',
|
|
29
|
+
// test: true,
|
|
30
|
+
// to: 'test@test.com',
|
|
31
|
+
// })
|
|
32
|
+
// console.log(html)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/** @type {import('tailwindcss').Config} */
|
|
2
|
+
// https://github.com/tailwindlabs/tailwindcss/blob/main/stubs/config.full.js#L889
|
|
3
|
+
import defaultTheme from 'tailwindcss/defaultTheme'
|
|
4
|
+
import colors from 'tailwindcss/colors'
|
|
5
|
+
import { nitroDir } from './server/config'
|
|
6
|
+
import Color from 'color'
|
|
7
|
+
const lighten = (clr, val) => Color(clr).lighten(val).rgb().string()
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
content: {
|
|
11
|
+
relative: true,
|
|
12
|
+
files: [
|
|
13
|
+
'./components/**/*.{ts,tsx}',
|
|
14
|
+
nitroDir + 'components/**/*.{js,jsx}',
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
experimental: {
|
|
18
|
+
optimizeUniversalDefaults: true, // remove unneeded varables from universal selectors
|
|
19
|
+
},
|
|
20
|
+
theme: {
|
|
21
|
+
extend: {
|
|
22
|
+
// Nitro theme variables
|
|
23
|
+
boxShadow: {
|
|
24
|
+
'dropdown-ul': '0 2px 8px 0 rgba(0, 0, 0, 0.05)',
|
|
25
|
+
},
|
|
26
|
+
colors: {
|
|
27
|
+
// Main colors
|
|
28
|
+
'primary': colors.indigo[500],
|
|
29
|
+
'primary-dark': colors.indigo[600],
|
|
30
|
+
'primary-light': colors.indigo[400],
|
|
31
|
+
'primary-hover': lighten(colors.indigo[500], 0.05),
|
|
32
|
+
'secondary': colors.green[500],
|
|
33
|
+
'secondary-dark': colors.green[600],
|
|
34
|
+
'secondary-light': colors.green[400],
|
|
35
|
+
'secondary-hover': lighten(colors.green[500], 0.05),
|
|
36
|
+
'label': colors.gray[900],
|
|
37
|
+
'link': colors.black,
|
|
38
|
+
'link-hover': colors.blue[200],
|
|
39
|
+
'link-focus': colors.blue[200],
|
|
40
|
+
'light': colors.gray[100],
|
|
41
|
+
'dark': colors.gray[900],
|
|
42
|
+
// Alert colors
|
|
43
|
+
'danger': '#ff0000',
|
|
44
|
+
'danger-dark': colors.red[800],
|
|
45
|
+
'info': colors.blue[500],
|
|
46
|
+
'success': colors.green[500],
|
|
47
|
+
// Element colors
|
|
48
|
+
'input': colors.gray[900],
|
|
49
|
+
'input-placeholder': colors.gray[400],
|
|
50
|
+
'input-border': colors.gray[300],
|
|
51
|
+
'dropdown-ul-border': colors.gray[200],
|
|
52
|
+
},
|
|
53
|
+
fontFamily: {
|
|
54
|
+
sans: ['Inter', ...defaultTheme.fontFamily.sans],
|
|
55
|
+
},
|
|
56
|
+
fontSize: {
|
|
57
|
+
// '2xs': ['0.75rem', { lineHeight: '1.5' }],
|
|
58
|
+
// 'xs': ['0.81rem', { lineHeight: '1.5' }],
|
|
59
|
+
// 'sm': ['0.875rem', { lineHeight: '1.5' }],
|
|
60
|
+
// 'base': ['1rem', { lineHeight: '1.5' }],
|
|
61
|
+
// 'lg': ['1.125rem', { lineHeight: '1.75' }],
|
|
62
|
+
// 'xl': ['1.25rem', { lineHeight: '1.75' }],
|
|
63
|
+
// '2xl': ['1.4rem', { lineHeight: '1.75' }],
|
|
64
|
+
// '3xl': ['1.875rem', { lineHeight: '1.75' }],
|
|
65
|
+
|
|
66
|
+
'2xs': ['0.77rem', { lineHeight: '1.5' }], // ~12px
|
|
67
|
+
'xs': ['0.83rem', { lineHeight: '1.5' }], // ~13px
|
|
68
|
+
'sm-label': ['0.87rem', { lineHeight: '1.5' }], // ~13.5px
|
|
69
|
+
'sm': ['0.90rem', { lineHeight: '1.5' }], // ~14px
|
|
70
|
+
'base': ['1rem', { lineHeight: '1.5' }], // 15.5px
|
|
71
|
+
'lg': ['1.16rem', { lineHeight: '1.75' }], // ~18px
|
|
72
|
+
'xl': ['1.29rem', { lineHeight: '1.75' }], // ~20px
|
|
73
|
+
'2xl': ['1.45rem', { lineHeight: '1.75' }], // ~22.4px
|
|
74
|
+
'3xl': ['1.94rem', { lineHeight: '1.75' }], // ~30px
|
|
75
|
+
},
|
|
76
|
+
spacing: {
|
|
77
|
+
'input-before': '0.625rem',
|
|
78
|
+
'input-after': '1.5rem',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
plugins: [],
|
|
83
|
+
}
|
|
84
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"allowJs": true,
|
|
4
|
+
"allowSyntheticDefaultImports": true,
|
|
5
|
+
"downlevelIteration": true,
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"forceConsistentCasingInFileNames": true,
|
|
8
|
+
"isolatedModules": true,
|
|
9
|
+
"jsx": "react-jsx",
|
|
10
|
+
"lib": ["es6", "dom", "dom.iterable", "esnext"],
|
|
11
|
+
"module": "esnext",
|
|
12
|
+
"moduleResolution": "node",
|
|
13
|
+
"noEmit": false,
|
|
14
|
+
"noFallthroughCasesInSwitch": true,
|
|
15
|
+
"noImplicitAny": true,
|
|
16
|
+
"noImplicitReturns": false,
|
|
17
|
+
"noImplicitThis": true,
|
|
18
|
+
"noStrictGenericChecks": false,
|
|
19
|
+
"outDir": "./dist",
|
|
20
|
+
"paths": {
|
|
21
|
+
"nitro-web": [
|
|
22
|
+
"../client.js"
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
"resolveJsonModule": true,
|
|
26
|
+
"skipLibCheck": true,
|
|
27
|
+
"sourceMap": true,
|
|
28
|
+
"strict": true,
|
|
29
|
+
"target": "es6",
|
|
30
|
+
},
|
|
31
|
+
"include": ["client", "components", "types.d.ts"],
|
|
32
|
+
}
|