create-ortha-app 0.4.2 → 0.4.3
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/dist/cli.js +38 -6
- package/dist/lib/features.d.ts +21 -19
- package/dist/lib/features.d.ts.map +1 -1
- package/dist/lib/features.js +26 -20
- package/dist/lib/template.d.ts.map +1 -1
- package/dist/lib/template.js +11 -1
- package/package.json +1 -1
- package/templates/default/README.md.tmpl +12 -5
- package/templates/default/apps/admin/src/plugins.spec.ts +1 -0
- package/templates/default/apps/admin/src/plugins.ts +4 -0
- package/templates/default/apps/admin/tsconfig.json +2 -1
- package/templates/default/apps/server/config/copilot-anthropic.ts +22 -0
- package/templates/default/apps/server/config/copilot-openai.ts +21 -0
- package/templates/default/apps/server/config/copilot.ts +61 -0
- package/templates/default/apps/server/config/docs.ts +14 -0
- package/templates/default/apps/server/config/i18n.ts +16 -0
- package/templates/default/apps/server/config/identity.ts +96 -0
- package/templates/default/apps/server/config/mcp.ts +25 -0
- package/templates/default/apps/server/config/media-storage.ts +83 -0
- package/templates/default/apps/server/config/media.ts +67 -0
- package/templates/default/apps/server/config/sso-oidc.ts +34 -0
- package/templates/default/apps/server/ortha.config.ts +55 -449
- package/templates/default/apps/server/src/plugins.spec.ts +1 -0
- package/templates/default/apps/server/src/plugins.ts +16 -6
- package/templates/default/apps/server/tsconfig.spec.json +25 -0
- package/templates/default/env.tmpl +7 -2
- package/templates/default/tsconfig.json +3 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where uploads are written.
|
|
3
|
+
*
|
|
4
|
+
* One block per adapter, and the wizard picks exactly one — so a generated app
|
|
5
|
+
* has the body it chose and nothing else. Swapping backends later means
|
|
6
|
+
* swapping this file, the type on `AppMediaConfig.storage`, and the factory
|
|
7
|
+
* `src/plugins.ts` imports; nothing inside the media package changes.
|
|
8
|
+
*/
|
|
9
|
+
// ortha:if media-local
|
|
10
|
+
import type { LocalStorageConfig } from '@orthacms/media-provider-local';
|
|
11
|
+
|
|
12
|
+
/** Local-filesystem blobs. */
|
|
13
|
+
export function mediaStorage(): LocalStorageConfig {
|
|
14
|
+
return {
|
|
15
|
+
// Point MEDIA_LOCAL_ROOT at a persistent volume in production: a
|
|
16
|
+
// container's own disk is wiped on every deploy.
|
|
17
|
+
rootDir: process.env['MEDIA_LOCAL_ROOT'] ?? './.storage/media'
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
// ortha:end
|
|
21
|
+
// ortha:if media-vercel-blob
|
|
22
|
+
import type { VercelBlobStorageConfig } from '@orthacms/media-provider-vercel-blob';
|
|
23
|
+
import { defined, readEnv } from '@orthacms/utils-server';
|
|
24
|
+
/** Vercel Blob. */
|
|
25
|
+
export function mediaStorage(): VercelBlobStorageConfig {
|
|
26
|
+
return defined({
|
|
27
|
+
// On Vercel the SDK reads BLOB_READ_WRITE_TOKEN itself, so this is only
|
|
28
|
+
// for running the app elsewhere.
|
|
29
|
+
token: readEnv('BLOB_READ_WRITE_TOKEN')
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
// ortha:end
|
|
33
|
+
// ortha:if media-gcs
|
|
34
|
+
import type { GcsStorageConfig } from '@orthacms/media-provider-gcs';
|
|
35
|
+
import { defined, readEnv, readFlag, requireEnv } from '@orthacms/utils-server';
|
|
36
|
+
/** Google Cloud Storage. */
|
|
37
|
+
export function mediaStorage(): GcsStorageConfig {
|
|
38
|
+
return defined({
|
|
39
|
+
bucket: requireEnv('MEDIA_GCS_BUCKET'),
|
|
40
|
+
// Everything else is optional: with no key file and no inline
|
|
41
|
+
// credentials the client uses Application Default Credentials, which is
|
|
42
|
+
// what a GKE or Cloud Run deployment wants.
|
|
43
|
+
projectId: readEnv('MEDIA_GCS_PROJECT_ID'),
|
|
44
|
+
keyFilename: readEnv('MEDIA_GCS_KEY_FILE'),
|
|
45
|
+
signWithIam: readFlag('MEDIA_GCS_SIGN_WITH_IAM', false)
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
// ortha:end
|
|
49
|
+
// ortha:if media-azure
|
|
50
|
+
import type { AzureStorageConfig } from '@orthacms/media-provider-azure';
|
|
51
|
+
import { requireEnv } from '@orthacms/utils-server';
|
|
52
|
+
/** Azure Blob Storage. */
|
|
53
|
+
export function mediaStorage(): AzureStorageConfig {
|
|
54
|
+
return {
|
|
55
|
+
container: requireEnv('MEDIA_AZURE_CONTAINER'),
|
|
56
|
+
connectionString: requireEnv('MEDIA_AZURE_CONNECTION_STRING')
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// ortha:end
|
|
60
|
+
// ortha:if media-s3
|
|
61
|
+
import type { S3StorageConfig } from '@orthacms/media-provider-s3';
|
|
62
|
+
import { defined, readEnv, readFlag, requireEnv } from '@orthacms/utils-server';
|
|
63
|
+
/** S3 or an S3-compatible endpoint — R2, MinIO, Spaces, B2. */
|
|
64
|
+
export function mediaStorage(): S3StorageConfig {
|
|
65
|
+
const accessKeyId = readEnv('MEDIA_S3_ACCESS_KEY_ID');
|
|
66
|
+
const secretAccessKey = readEnv('MEDIA_S3_SECRET_ACCESS_KEY');
|
|
67
|
+
return defined({
|
|
68
|
+
bucket: requireEnv('MEDIA_S3_BUCKET'),
|
|
69
|
+
// `auto` is what R2 expects; AWS needs its real region.
|
|
70
|
+
region: process.env['MEDIA_S3_REGION'] ?? 'auto',
|
|
71
|
+
// Omit for AWS S3 itself; set it for R2, MinIO, Spaces, B2…
|
|
72
|
+
endpoint: readEnv('MEDIA_S3_ENDPOINT'),
|
|
73
|
+
forcePathStyle: readFlag('MEDIA_S3_FORCE_PATH_STYLE', false),
|
|
74
|
+
// Absent means "use the SDK's own provider chain" — an instance role,
|
|
75
|
+
// IRSA, a shared config file. Passing blanks instead would shadow all
|
|
76
|
+
// of that with credentials that cannot sign.
|
|
77
|
+
credentials:
|
|
78
|
+
accessKeyId && secretAccessKey
|
|
79
|
+
? { accessKeyId, secretAccessKey }
|
|
80
|
+
: undefined
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// ortha:end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** Media — the storage backend, plus how downloads and uploads are bounded. */
|
|
2
|
+
import type { MediaPluginConfig } from '@orthacms/media-server';
|
|
3
|
+
// ortha:if media-local
|
|
4
|
+
import type { LocalStorageConfig } from '@orthacms/media-provider-local';
|
|
5
|
+
// ortha:end
|
|
6
|
+
// ortha:if media-s3
|
|
7
|
+
import type { S3StorageConfig } from '@orthacms/media-provider-s3';
|
|
8
|
+
// ortha:end
|
|
9
|
+
// ortha:if media-azure
|
|
10
|
+
import type { AzureStorageConfig } from '@orthacms/media-provider-azure';
|
|
11
|
+
// ortha:end
|
|
12
|
+
// ortha:if media-gcs
|
|
13
|
+
import type { GcsStorageConfig } from '@orthacms/media-provider-gcs';
|
|
14
|
+
// ortha:end
|
|
15
|
+
// ortha:if media-vercel-blob
|
|
16
|
+
import type { VercelBlobStorageConfig } from '@orthacms/media-provider-vercel-blob';
|
|
17
|
+
// ortha:end
|
|
18
|
+
import { readEnv, readPositiveInt } from '@orthacms/utils-server';
|
|
19
|
+
|
|
20
|
+
import { mediaStorage } from './media-storage';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Media settings, plus whatever the storage backend `src/plugins.ts`
|
|
24
|
+
* constructs needs. The two move together: the type below is the one exported
|
|
25
|
+
* by the adapter that file imports.
|
|
26
|
+
*/
|
|
27
|
+
export interface AppMediaConfig extends MediaPluginConfig {
|
|
28
|
+
// ortha:if media-local
|
|
29
|
+
storage: LocalStorageConfig;
|
|
30
|
+
// ortha:end
|
|
31
|
+
// ortha:if media-s3
|
|
32
|
+
storage: S3StorageConfig;
|
|
33
|
+
// ortha:end
|
|
34
|
+
// ortha:if media-azure
|
|
35
|
+
storage: AzureStorageConfig;
|
|
36
|
+
// ortha:end
|
|
37
|
+
// ortha:if media-gcs
|
|
38
|
+
storage: GcsStorageConfig;
|
|
39
|
+
// ortha:end
|
|
40
|
+
// ortha:if media-vercel-blob
|
|
41
|
+
storage: VercelBlobStorageConfig;
|
|
42
|
+
// ortha:end
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Media — the storage backend, plus how downloads and uploads are bounded. */
|
|
46
|
+
export function mediaConfig(): AppMediaConfig {
|
|
47
|
+
return {
|
|
48
|
+
storage: mediaStorage(),
|
|
49
|
+
// Redirect an already-authorized download straight to the storage
|
|
50
|
+
// backend instead of streaming it through the app. Off unless asked
|
|
51
|
+
// for, and only possible on a backend that can sign a URL — the plugin
|
|
52
|
+
// refuses the combination at boot rather than proxying while the
|
|
53
|
+
// operator believes otherwise.
|
|
54
|
+
directServe:
|
|
55
|
+
readEnv('MEDIA_DIRECT_SERVE') === 'signed-url'
|
|
56
|
+
? 'signed-url'
|
|
57
|
+
: 'off',
|
|
58
|
+
directServeTtlSeconds: readPositiveInt(
|
|
59
|
+
'MEDIA_DIRECT_SERVE_TTL_SECONDS',
|
|
60
|
+
300
|
|
61
|
+
),
|
|
62
|
+
maxUploadBytes: readPositiveInt(
|
|
63
|
+
'MEDIA_MAX_UPLOAD_BYTES',
|
|
64
|
+
50 * 1024 * 1024
|
|
65
|
+
)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// ortha:if sso-oidc
|
|
2
|
+
import type { OidcProviderConfig } from '@orthacms/identity-provider-oidc';
|
|
3
|
+
import { defined, readEnv, readFlag } from '@orthacms/utils-server';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The OIDC provider, or nothing.
|
|
7
|
+
*
|
|
8
|
+
* Present only when both values are set: an issuer with no client id becomes a
|
|
9
|
+
* sign-in button that can only fail, and every SSO failure looks the same, so
|
|
10
|
+
* whoever clicks it learns nothing.
|
|
11
|
+
*/
|
|
12
|
+
export function oidcProvider(): (OidcProviderConfig & { name: string }) | undefined {
|
|
13
|
+
const issuer = readEnv('SSO_OIDC_ISSUER');
|
|
14
|
+
const clientId = readEnv('SSO_OIDC_CLIENT_ID');
|
|
15
|
+
if (!issuer || !clientId) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
return defined({
|
|
19
|
+
name: process.env['SSO_OIDC_NAME'] ?? 'oidc',
|
|
20
|
+
issuer,
|
|
21
|
+
clientId,
|
|
22
|
+
clientSecret: readEnv('SSO_OIDC_CLIENT_SECRET'),
|
|
23
|
+
label: readEnv('SSO_OIDC_LABEL'),
|
|
24
|
+
// The only gate on a first sign-in claiming an existing account. A
|
|
25
|
+
// provider that omits the claim — Entra ID, notably — links nobody
|
|
26
|
+
// until an operator asserts that this directory owns the addresses it
|
|
27
|
+
// reports.
|
|
28
|
+
emailVerifiedWhenAbsent: readFlag(
|
|
29
|
+
'SSO_OIDC_EMAIL_VERIFIED_WHEN_ABSENT',
|
|
30
|
+
false
|
|
31
|
+
)
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
// ortha:end
|