1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | Object.defineProperty(exports, "__esModule", { value: true });
|
11 | const path_1 = require("path");
|
12 | const helpers_1 = require("@poppinss/utils/build/helpers");
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | const CONTRACTS_PARTIALS_BASE = './contract/partials';
|
19 |
|
20 |
|
21 |
|
22 | const CONFIG_PARTIALS_BASE = './config/partials';
|
23 |
|
24 |
|
25 |
|
26 | const PROVIDER_PROMPT_CHOICES = [
|
27 | {
|
28 | name: 'lucid',
|
29 | message: 'Lucid',
|
30 | hint: ' (Uses Data Models)',
|
31 | },
|
32 | {
|
33 | name: 'database',
|
34 | message: 'Database',
|
35 | hint: ' (Uses Database QueryBuilder)',
|
36 | },
|
37 | ];
|
38 |
|
39 |
|
40 |
|
41 | const GUARD_PROMPT_CHOICES = [
|
42 | {
|
43 | name: 'web',
|
44 | message: 'Web',
|
45 | hint: ' (Uses sessions for managing auth state)',
|
46 | },
|
47 | {
|
48 | name: 'api',
|
49 | message: 'API tokens',
|
50 | hint: ' (Uses database backed opaque tokens)',
|
51 | },
|
52 | {
|
53 | name: 'basic',
|
54 | message: 'Basic Auth',
|
55 | hint: ' (Uses HTTP Basic auth for authenticating requests)',
|
56 | },
|
57 | ];
|
58 |
|
59 |
|
60 |
|
61 | const TOKENS_PROVIDER_PROMPT_CHOICES = [
|
62 | {
|
63 | name: 'database',
|
64 | message: 'Database',
|
65 | hint: ' (Uses SQL table for storing API tokens)',
|
66 | },
|
67 | {
|
68 | name: 'redis',
|
69 | message: 'Redis',
|
70 | hint: ' (Uses Redis for storing API tokens)',
|
71 | },
|
72 | ];
|
73 |
|
74 |
|
75 |
|
76 |
|
77 | function getStub(...relativePaths) {
|
78 | return (0, path_1.join)(__dirname, 'templates', ...relativePaths);
|
79 | }
|
80 |
|
81 |
|
82 |
|
83 | function makeModel(projectRoot, app, sink, state) {
|
84 | const modelsDirectory = app.resolveNamespaceDirectory('models') || 'app/Models';
|
85 | const modelPath = (0, path_1.join)(modelsDirectory, `${state.modelName}.ts`);
|
86 | const template = new sink.files.MustacheFile(projectRoot, modelPath, getStub('model.txt'));
|
87 | if (template.exists()) {
|
88 | sink.logger.action('create').skipped(`${modelPath} file already exists`);
|
89 | return;
|
90 | }
|
91 | template.apply(state).commit();
|
92 | sink.logger.action('create').succeeded(modelPath);
|
93 | }
|
94 |
|
95 |
|
96 |
|
97 | function makeUsersMigration(projectRoot, app, sink, state) {
|
98 | const migrationsDirectory = app.directoriesMap.get('migrations') || 'database';
|
99 | const migrationPath = (0, path_1.join)(migrationsDirectory, `${Date.now()}_${state.usersTableName}.ts`);
|
100 | const template = new sink.files.MustacheFile(projectRoot, migrationPath, getStub('migrations/auth.txt'));
|
101 | if (template.exists()) {
|
102 | sink.logger.action('create').skipped(`${migrationPath} file already exists`);
|
103 | return;
|
104 | }
|
105 | template.apply(state).commit();
|
106 | sink.logger.action('create').succeeded(migrationPath);
|
107 | }
|
108 |
|
109 |
|
110 |
|
111 | function makeTokensMigration(projectRoot, app, sink, state) {
|
112 | const migrationsDirectory = app.directoriesMap.get('migrations') || 'database';
|
113 | const migrationPath = (0, path_1.join)(migrationsDirectory, `${Date.now()}_${state.tokensTableName}.ts`);
|
114 | const template = new sink.files.MustacheFile(projectRoot, migrationPath, getStub('migrations/api_tokens.txt'));
|
115 | if (template.exists()) {
|
116 | sink.logger.action('create').skipped(`${migrationPath} file already exists`);
|
117 | return;
|
118 | }
|
119 | template.apply(state).commit();
|
120 | sink.logger.action('create').succeeded(migrationPath);
|
121 | }
|
122 |
|
123 |
|
124 |
|
125 | function makeMiddleware(projectRoot, app, sink, state) {
|
126 | const middlewareDirectory = app.resolveNamespaceDirectory('middleware') || 'app/Middleware';
|
127 | |
128 |
|
129 |
|
130 | const authPath = (0, path_1.join)(middlewareDirectory, 'Auth.ts');
|
131 | const authTemplate = new sink.files.MustacheFile(projectRoot, authPath, getStub('middleware/Auth.txt'));
|
132 | if (authTemplate.exists()) {
|
133 | sink.logger.action('create').skipped(`${authPath} file already exists`);
|
134 | }
|
135 | else {
|
136 | authTemplate.apply(state).commit();
|
137 | sink.logger.action('create').succeeded(authPath);
|
138 | }
|
139 | |
140 |
|
141 |
|
142 | const silentAuthPath = (0, path_1.join)(middlewareDirectory, 'SilentAuth.ts');
|
143 | const silentAuthTemplate = new sink.files.MustacheFile(projectRoot, silentAuthPath, getStub('middleware/SilentAuth.txt'));
|
144 | if (silentAuthTemplate.exists()) {
|
145 | sink.logger.action('create').skipped(`${silentAuthPath} file already exists`);
|
146 | }
|
147 | else {
|
148 | silentAuthTemplate.apply(state).commit();
|
149 | sink.logger.action('create').succeeded(silentAuthPath);
|
150 | }
|
151 | }
|
152 |
|
153 |
|
154 |
|
155 | function makeContract(projectRoot, app, sink, state) {
|
156 | const contractsDirectory = app.directoriesMap.get('contracts') || 'contracts';
|
157 | const contractPath = (0, path_1.join)(contractsDirectory, 'auth.ts');
|
158 | const template = new sink.files.MustacheFile(projectRoot, contractPath, getStub('contract/auth.txt'));
|
159 | template.overwrite = true;
|
160 | const partials = {
|
161 | provider: getStub(CONTRACTS_PARTIALS_BASE, `user-provider-${state.provider}.txt`),
|
162 | };
|
163 | state.guards.forEach((guard) => {
|
164 | partials[`${guard}_guard`] = getStub(CONTRACTS_PARTIALS_BASE, `${guard}-guard.txt`);
|
165 | });
|
166 | template.apply(state).partials(partials).commit();
|
167 | sink.logger.action('create').succeeded(contractPath);
|
168 | }
|
169 |
|
170 |
|
171 |
|
172 | function makeConfig(projectRoot, app, sink, state) {
|
173 | const configDirectory = app.directoriesMap.get('config') || 'config';
|
174 | const configPath = (0, path_1.join)(configDirectory, 'auth.ts');
|
175 | const template = new sink.files.MustacheFile(projectRoot, configPath, getStub('config/auth.txt'));
|
176 | template.overwrite = true;
|
177 | const partials = {
|
178 | provider: getStub(CONFIG_PARTIALS_BASE, `user-provider-${state.provider}.txt`),
|
179 | token_provider: getStub(CONFIG_PARTIALS_BASE, `tokens-provider-${state.tokensProvider}.txt`),
|
180 | };
|
181 | state.guards.forEach((guard) => {
|
182 | partials[`${guard}_guard`] = getStub(CONFIG_PARTIALS_BASE, `${guard}-guard.txt`);
|
183 | });
|
184 | template.apply(state).partials(partials).commit();
|
185 | sink.logger.action('create').succeeded(configPath);
|
186 | }
|
187 |
|
188 |
|
189 |
|
190 | async function getProvider(sink) {
|
191 | return sink.getPrompt().choice('Select provider for finding users', PROVIDER_PROMPT_CHOICES, {
|
192 | validate(choice) {
|
193 | return choice && choice.length ? true : 'Select the provider for finding users';
|
194 | },
|
195 | });
|
196 | }
|
197 |
|
198 |
|
199 |
|
200 | async function getTokensProvider(sink) {
|
201 | return sink
|
202 | .getPrompt()
|
203 | .choice('Select the provider for storing API tokens', TOKENS_PROVIDER_PROMPT_CHOICES, {
|
204 | validate(choice) {
|
205 | return choice && choice.length ? true : 'Select the provider for storing API tokens';
|
206 | },
|
207 | });
|
208 | }
|
209 |
|
210 |
|
211 |
|
212 | async function getGuard(sink) {
|
213 | return sink
|
214 | .getPrompt()
|
215 | .multiple('Select which guard you need for authentication (select using space)', GUARD_PROMPT_CHOICES, {
|
216 | validate(choices) {
|
217 | return choices && choices.length
|
218 | ? true
|
219 | : 'Select one or more guards for authenticating users';
|
220 | },
|
221 | });
|
222 | }
|
223 |
|
224 |
|
225 |
|
226 | async function getModelName(sink) {
|
227 | return sink.getPrompt().ask('Enter model name to be used for authentication', {
|
228 | validate(value) {
|
229 | return !!value.trim().length;
|
230 | },
|
231 | });
|
232 | }
|
233 |
|
234 |
|
235 |
|
236 | async function getTableName(sink) {
|
237 | return sink.getPrompt().ask('Enter the database table name to look up users', {
|
238 | validate(value) {
|
239 | return !!value.trim().length;
|
240 | },
|
241 | });
|
242 | }
|
243 |
|
244 |
|
245 |
|
246 | async function getMigrationConsent(sink, tableName) {
|
247 | return sink
|
248 | .getPrompt()
|
249 | .confirm(`Create migration for the ${sink.logger.colors.underline(tableName)} table?`);
|
250 | }
|
251 |
|
252 |
|
253 |
|
254 | async function instructions(projectRoot, app, sink) {
|
255 | const state = {
|
256 | usersTableName: '',
|
257 | tokensTableName: 'api_tokens',
|
258 | tokensSchemaName: 'ApiTokens',
|
259 | usersSchemaName: '',
|
260 | provider: 'lucid',
|
261 | tokensProvider: 'database',
|
262 | guards: [],
|
263 | hasGuard: {
|
264 | web: false,
|
265 | api: false,
|
266 | basic: false,
|
267 | },
|
268 | };
|
269 | state.provider = await getProvider(sink);
|
270 | state.guards = await getGuard(sink);
|
271 | |
272 |
|
273 |
|
274 | state.guards.forEach((guard) => (state.hasGuard[guard] = true));
|
275 | |
276 |
|
277 |
|
278 |
|
279 | if (state.provider === 'lucid') {
|
280 | const modelName = await getModelName(sink);
|
281 | state.modelName = modelName.replace(/(\.ts|\.js)$/, '');
|
282 | state.usersTableName = helpers_1.string.pluralize(helpers_1.string.snakeCase(state.modelName));
|
283 | state.modelReference = helpers_1.string.camelCase(state.modelName);
|
284 | state.modelNamespace = `${app.namespacesMap.get('models') || 'App/Models'}/${state.modelName}`;
|
285 | }
|
286 | else {
|
287 | state.usersTableName = await getTableName(sink);
|
288 | }
|
289 | const usersMigrationConsent = await getMigrationConsent(sink, state.usersTableName);
|
290 | let tokensMigrationConsent = false;
|
291 | |
292 |
|
293 |
|
294 | if (state.hasGuard.api) {
|
295 | state.tokensProvider = await getTokensProvider(sink);
|
296 | if (state.tokensProvider === 'database') {
|
297 | tokensMigrationConsent = await getMigrationConsent(sink, state.tokensTableName);
|
298 | }
|
299 | }
|
300 | |
301 |
|
302 |
|
303 | const camelCaseSchemaName = helpers_1.string.camelCase(`${state.usersTableName}_schema`);
|
304 | state.usersSchemaName = `${camelCaseSchemaName
|
305 | .charAt(0)
|
306 | .toUpperCase()}${camelCaseSchemaName.slice(1)}`;
|
307 | |
308 |
|
309 |
|
310 | if (state.modelName) {
|
311 | makeModel(projectRoot, app, sink, state);
|
312 | }
|
313 | |
314 |
|
315 |
|
316 | if (usersMigrationConsent) {
|
317 | makeUsersMigration(projectRoot, app, sink, state);
|
318 | }
|
319 | |
320 |
|
321 |
|
322 | if (tokensMigrationConsent) {
|
323 | makeTokensMigration(projectRoot, app, sink, state);
|
324 | }
|
325 | |
326 |
|
327 |
|
328 | makeContract(projectRoot, app, sink, state);
|
329 | |
330 |
|
331 |
|
332 | makeConfig(projectRoot, app, sink, state);
|
333 | |
334 |
|
335 |
|
336 | makeMiddleware(projectRoot, app, sink, state);
|
337 | }
|
338 | exports.default = instructions;
|