@aurostack/stacks 0.2.1 → 0.2.2
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/package.json +1 -1
- package/templates/nest-api/files/.env.example +11 -4
- package/templates/nest-api/files/src/app.module.ts +7 -5
- package/templates/nest-api/files/src/common/__tests__/origin-builder.spec.ts +24 -0
- package/templates/nest-api/files/src/common/misc/origin-builder.ts +6 -3
- package/templates/nest-api/files/src/common/services/config.service.ts +1 -1
- package/templates/nest-api/files/src/lib/auth.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aurostack/stacks",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Scaffold new projects from house templates: a NestJS API, React app or Turborepo monorepo, and Node or Python workers. Subtractive templates plus a zero-dependency generator.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Aurostack (https://github.com/aurostack-org)",
|
|
@@ -8,7 +8,10 @@ APP_HOST='0.0.0.0'
|
|
|
8
8
|
PORT='5000'
|
|
9
9
|
SERVER_HOST='http://localhost:5000'
|
|
10
10
|
FRONTEND_HOST='http://localhost:3000'
|
|
11
|
-
# Extra
|
|
11
|
+
# Extra origins beyond FRONTEND_HOST, comma separated: trusted by auth in every
|
|
12
|
+
# environment, allowed by CORS in development only. A full origin
|
|
13
|
+
# (`http://localhost:3001`) is used as written; a bare hostname (`example.com`)
|
|
14
|
+
# expands to https://example.com and its auth., app. and admin. subdomains.
|
|
12
15
|
MISC_CORS_ORIGINS=''
|
|
13
16
|
|
|
14
17
|
DATABASE_URL='postgresql://root:postgres@localhost:5432/main'
|
|
@@ -16,9 +19,13 @@ DATABASE_URL='postgresql://root:postgres@localhost:5432/main'
|
|
|
16
19
|
# openssl rand -base64 32
|
|
17
20
|
BETTER_AUTH_SECRET='change-me-openssl-rand-base64-32'
|
|
18
21
|
BETTER_AUTH_URL='http://localhost:5000'
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
+
# Empty: the session cookie belongs to the API's own host, which is what
|
|
23
|
+
# localhost development and a single-origin frontend need. Set a parent domain
|
|
24
|
+
# with a leading dot (`.example.com`) when frontends live on subdomains, to share
|
|
25
|
+
# one login across them; locally, `.lvh.me` does the same with `*.lvh.me` hosts
|
|
26
|
+
# (every subdomain resolves to 127.0.0.1). The domain must cover the host the
|
|
27
|
+
# browser talks to, or the cookie is dropped and every sign-in bounces.
|
|
28
|
+
BETTER_AUTH_COOKIE_DOMAIN=''
|
|
22
29
|
BETTER_AUTH_COOKIE_PREFIX='acme'
|
|
23
30
|
|
|
24
31
|
# Seeded by `yarn db:seed`.
|
|
@@ -81,13 +81,15 @@ import { OriginBuilder } from 'common/misc';
|
|
|
81
81
|
ipAddressHeaders: config.rateLimit.ipHeaders
|
|
82
82
|
},
|
|
83
83
|
// @feature:end
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
84
|
+
// With BETTER_AUTH_COOKIE_DOMAIN set (`.example.com`), the session
|
|
85
|
+
// cookie is shared across subdomains so one login covers every
|
|
86
|
+
// frontend. Empty, it belongs to the API's own host: right for
|
|
87
|
+
// localhost and for a single-origin frontend. A browser drops a
|
|
88
|
+
// cookie whose domain doesn't cover the host that set it.
|
|
87
89
|
cookiePrefix: config.betterAuth.cookiePrefix,
|
|
88
90
|
crossSubDomainCookies: {
|
|
89
|
-
enabled:
|
|
90
|
-
domain: config.betterAuth.cookieDomain
|
|
91
|
+
enabled: Boolean(config.betterAuth.cookieDomain),
|
|
92
|
+
domain: config.betterAuth.cookieDomain || undefined
|
|
91
93
|
},
|
|
92
94
|
database: {
|
|
93
95
|
joins: true
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { OriginBuilder } from 'common/misc/origin-builder';
|
|
2
|
+
|
|
3
|
+
describe('OriginBuilder', () => {
|
|
4
|
+
it('expands a bare hostname into https origins for it and its subdomains', () => {
|
|
5
|
+
expect(OriginBuilder.build('example.com')).toEqual([
|
|
6
|
+
'https://example.com',
|
|
7
|
+
'https://auth.example.com',
|
|
8
|
+
'https://app.example.com',
|
|
9
|
+
'https://admin.example.com'
|
|
10
|
+
]);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('takes an entry with a scheme as one origin, as written', () => {
|
|
14
|
+
expect(OriginBuilder.build('http://localhost:3001, http://localhost:3002/')).toEqual([
|
|
15
|
+
'http://localhost:3001',
|
|
16
|
+
'http://localhost:3002'
|
|
17
|
+
]);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('is empty for an empty list', () => {
|
|
21
|
+
expect(OriginBuilder.build('')).toEqual([]);
|
|
22
|
+
expect(OriginBuilder.build(undefined)).toEqual([]);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Builds a list of origins for CORS configuration
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Builds a list of origins for CORS configuration from a list of entries.
|
|
3
|
+
* A bare hostname (`shop.example.com`) generates https origins for it and its
|
|
4
|
+
* app, auth and admin subdomains. An entry with a scheme
|
|
5
|
+
* (`http://localhost:3001`) is taken as one origin, as written: that is how
|
|
6
|
+
* frontends on other local ports are allowed.
|
|
5
7
|
*
|
|
6
8
|
* @remarks This is mainly for development purposes
|
|
7
9
|
* For frontend developers on the team who cannot run a standalone
|
|
@@ -28,6 +30,7 @@ export class OriginBuilder {
|
|
|
28
30
|
* Origins for a single hostname: the main domain plus each subdomain.
|
|
29
31
|
*/
|
|
30
32
|
fromHost(hostname: string): string[] {
|
|
33
|
+
if (hostname.includes('://')) return [hostname.replace(/\/+$/, '')];
|
|
31
34
|
return [
|
|
32
35
|
`${this.protocol}${hostname}`,
|
|
33
36
|
...this.subdomains.map((sub) => `${this.protocol}${sub}.${hostname}`)
|
|
@@ -172,7 +172,7 @@ namespace Config {
|
|
|
172
172
|
DATABASE_URL: str(),
|
|
173
173
|
BETTER_AUTH_SECRET: str(),
|
|
174
174
|
BETTER_AUTH_URL: str(),
|
|
175
|
-
BETTER_AUTH_COOKIE_DOMAIN:
|
|
175
|
+
BETTER_AUTH_COOKIE_DOMAIN: z.string().default(''),
|
|
176
176
|
BETTER_AUTH_COOKIE_PREFIX: str(),
|
|
177
177
|
SUPERUSER_EMAIL: str(),
|
|
178
178
|
SUPERUSER_PASSWORD: str(),
|
|
@@ -33,8 +33,8 @@ export const auth = betterAuth({
|
|
|
33
33
|
// sessions through this one, so a different cookie name would reject them.
|
|
34
34
|
cookiePrefix: env.BETTER_AUTH_COOKIE_PREFIX,
|
|
35
35
|
crossSubDomainCookies: {
|
|
36
|
-
enabled:
|
|
37
|
-
domain: env.BETTER_AUTH_COOKIE_DOMAIN
|
|
36
|
+
enabled: Boolean(env.BETTER_AUTH_COOKIE_DOMAIN),
|
|
37
|
+
domain: env.BETTER_AUTH_COOKIE_DOMAIN || undefined
|
|
38
38
|
},
|
|
39
39
|
database: {
|
|
40
40
|
joins: true
|