Skip to content

Commit 43b9568

Browse files
committed
chore: wip
1 parent 4862a89 commit 43b9568

File tree

10 files changed

+157
-16
lines changed

10 files changed

+157
-16
lines changed

bun.lockb

1.38 KB
Binary file not shown.

routes/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ await route.post('/email/subscribe', 'Actions/SubscriberEmailAction')
1717
await route.post('/login', 'Actions/LoginAction')
1818

1919
await route.email('/welcome')
20-
await route.health() // adds an `/api/health` route
20+
await route.health() // adds a GET `/api/health` route
2121

2222
// await route.group('/some-path', async () => {...})
2323
// await route.action('/example') // equivalent to `route.get('/example', 'ExampleAction')`

storage/framework/core/actions/src/cdn/invalidate-cache.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// TODO: port over logic from meemalabs/cloudfront to invalidate cache
2-
// buddy cloud invalidate-cache --paths /index.html /about.html
2+
// buddy cloud --invalidate-cache --paths /index.html /about.html
33

44
// import { CloudFrontClient, CreateInvalidationCommand } from '@aws-sdk/client-cloudfront'
55

storage/framework/core/buddy/src/commands/cloud.ts

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import process from 'node:process'
2+
import { CloudFrontClient, CreateInvalidationCommand } from '@aws-sdk/client-cloudfront'
23
import { intro, italic, log, outro, prompts, runCommand, runCommandSync, underline } from '@stacksjs/cli'
34
import {
45
addJumpBox,
@@ -9,6 +10,7 @@ import {
910
deleteParameterStore,
1011
deleteStacksBuckets,
1112
deleteStacksFunctions,
13+
getCloudFrontDistributionId,
1214
getJumpBoxInstanceId,
1315
} from '@stacksjs/cloud'
1416
import { path as p } from '@stacksjs/path'
@@ -24,6 +26,8 @@ export function cloud(buddy: CLI) {
2426
remove: 'Removes the Stacks Cloud. In case it fails, try again',
2527
optimizeCost: 'Removes certain resources that may be re-applied at a later time',
2628
cleanUp: 'Removes all resources that were retained during the cloud deletion',
29+
invalidateCache: 'Invalidates the CloudFront cache',
30+
paths: 'The paths to invalidate',
2731
project: 'Target a specific project',
2832
verbose: 'Enable verbose output',
2933
}
@@ -32,13 +36,15 @@ export function cloud(buddy: CLI) {
3236
.command('cloud', descriptions.cloud)
3337
.option('--ssh', descriptions.ssh, { default: false })
3438
.option('--connect', descriptions.ssh, { default: false })
39+
.option('--invalidate-cache', descriptions.invalidateCache, { default: false })
40+
.option('--paths [paths]', descriptions.paths)
3541
.option('-p, --project [project]', descriptions.project, { default: false })
3642
.option('--verbose', descriptions.verbose, { default: false })
3743
.action(async (options: CloudCliOptions) => {
3844
log.debug('Running `buddy cloud` ...', options)
45+
const startTime = performance.now()
3946

4047
if (options.ssh || options.connect) {
41-
const startTime = performance.now()
4248
const jumpBoxId = await getJumpBoxInstanceId()
4349
const result = await runCommand(`aws ssm start-session --target ${jumpBoxId}`, {
4450
...options,
@@ -59,7 +65,49 @@ export function cloud(buddy: CLI) {
5965
process.exit(ExitCode.Success)
6066
}
6167

62-
log.info('Not implemented yet. Please use the --ssh (or --connect) flag to connect to the Stacks Cloud.')
68+
if (options.invalidateCache) {
69+
const { confirm } = await prompts({
70+
name: 'confirm',
71+
type: 'confirm',
72+
message: 'Would you like to invalidate the CDN (CloudFront) cache?',
73+
})
74+
75+
if (!confirm) {
76+
await outro('Exited', { startTime, useSeconds: true })
77+
process.exit(ExitCode.Success)
78+
}
79+
80+
log.info('Invalidating the CloudFront cache...')
81+
82+
const cloudfront = new CloudFrontClient()
83+
const distributionId = await getCloudFrontDistributionId()
84+
85+
const params = {
86+
DistributionId: distributionId,
87+
InvalidationBatch: {
88+
CallerReference: `${Date.now()}`,
89+
Paths: {
90+
Quantity: 1,
91+
Items: [
92+
'/*',
93+
/* more items */
94+
],
95+
},
96+
},
97+
}
98+
99+
const command = new CreateInvalidationCommand(params)
100+
101+
cloudfront.send(command).then(
102+
(data) => console.log(data),
103+
(err) => console.log(err, err.stack),
104+
)
105+
106+
await outro('Exited', { startTime, useSeconds: true })
107+
process.exit(ExitCode.Success)
108+
}
109+
110+
log.info('Not implemented yet. Read more about `buddy cloud` here: https://stacksjs.org/docs/cloud')
63111
process.exit(ExitCode.Success)
64112
})
65113

@@ -373,6 +421,61 @@ export function cloud(buddy: CLI) {
373421
process.exit(ExitCode.Success)
374422
})
375423

424+
buddy
425+
.command('cloud:invalidate-cache', descriptions.invalidateCache)
426+
.option('--paths [paths]', descriptions.paths, { default: false })
427+
.option('-p, --project [project]', descriptions.project, { default: false })
428+
.option('--verbose', descriptions.verbose, { default: false })
429+
.action(async (options: CloudCliOptions) => {
430+
log.debug('Running `buddy cloud:invalidate-cache` ...', options)
431+
432+
const startTime = await intro('buddy cloud:invalidate-cache')
433+
434+
const { confirm } = await prompts({
435+
name: 'confirm',
436+
type: 'confirm',
437+
message: 'Would you like to invalidate the CloudFront cache?',
438+
})
439+
440+
if (!confirm) {
441+
await outro('Exited', { startTime, useSeconds: true })
442+
process.exit(ExitCode.Success)
443+
}
444+
445+
log.info('Invalidating the CloudFront cache...')
446+
// const result = await runCommand('aws cloudfront create-invalidation --distribution-id E1U4Z2E9NJW9J --paths "/*"', {
447+
// ...options,
448+
// cwd: p.projectPath(),
449+
// stdin: 'pipe',
450+
// })
451+
452+
if (options.paths) {
453+
const result = await runCommand(
454+
`aws cloudfront create-invalidation --distribution-id E1U4Z2E9NJW9J --paths ${options.paths}`,
455+
{
456+
...options,
457+
cwd: p.projectPath(), // TODO: this should be the cloud path
458+
stdin: 'pipe',
459+
},
460+
) // TODO: this should be the cloud path
461+
462+
if (result.isErr()) {
463+
await outro(
464+
'While running the cloud command, there was an issue',
465+
{ startTime, useSeconds: true },
466+
result.error,
467+
)
468+
process.exit(ExitCode.FatalError)
469+
}
470+
471+
await outro('Exited', { startTime, useSeconds: true })
472+
process.exit(ExitCode.Success)
473+
}
474+
475+
log.info('Not implemented yet. Read more about `buddy cloud` here: https://stacksjs.org/docs/cloud')
476+
process.exit(ExitCode.Success)
477+
})
478+
376479
buddy.on('cloud:*', () => {
377480
console.error('Invalid command: %s\nSee --help for a list of available commands.', buddy.args.join(' '))
378481
process.exit(1)

storage/framework/core/cloud/src/cloud/cdn.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export class CdnStack {
3939
cdnCachePolicy: cloudfront.CachePolicy
4040
apiCachePolicy: cloudfront.CachePolicy | undefined
4141
vanityUrl: string
42-
realtimeLogConfig: cloudfront.RealtimeLogConfig
42+
realtimeLogConfig!: cloudfront.RealtimeLogConfig
4343
props: CdnStackProps
4444

4545
constructor(scope: Construct, props: CdnStackProps) {
Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,64 @@
11
import { config } from '@stacksjs/config'
2-
import { websiteSourceHash } from '@stacksjs/utils'
2+
import { path as p } from '@stacksjs/path'
3+
import { hasFiles } from '@stacksjs/storage'
4+
import { docsSourceHash, websiteSourceHash } from '@stacksjs/utils'
35
import type { aws_cloudfront as cloudfront, aws_s3 as s3 } from 'aws-cdk-lib'
46
import { AssetHashType, aws_s3_deployment as s3deploy } from 'aws-cdk-lib'
57
import type { Construct } from 'constructs'
68
import type { NestedCloudProps } from '../types'
79

810
export interface DeploymentStackProps extends NestedCloudProps {
9-
publicBucket: s3.Bucket
11+
appBucket: s3.Bucket
12+
docsBucket: s3.Bucket
1013
privateBucket: s3.Bucket
1114
cdn: cloudfront.Distribution
1215
}
1316

1417
export class DeploymentStack {
1518
privateSource: string
1619
docsSource: string
17-
websiteSource: string
20+
appSource: string
1821

1922
constructor(scope: Construct, props: DeploymentStackProps) {
2023
// following paths are relative to where the command is run from
2124
this.privateSource = '../../private'
2225
this.docsSource = '../docs/dist/'
23-
this.websiteSource = config.app.docMode === true ? this.docsSource : '../views/web/dist/'
26+
this.appSource = config.app.docMode === true ? this.docsSource : '../views/web/dist/'
2427

2528
new s3deploy.BucketDeployment(scope, 'Website', {
2629
sources: [
27-
s3deploy.Source.asset(this.websiteSource, {
30+
s3deploy.Source.asset(this.appSource, {
2831
assetHash: websiteSourceHash(),
2932
assetHashType: AssetHashType.CUSTOM,
3033
}),
3134
],
32-
destinationBucket: props.publicBucket,
35+
destinationBucket: props.appBucket,
3336
distribution: props.cdn,
3437
distributionPaths: ['/*'],
3538
})
3639

40+
// if docs should be deployed, add it to the deployment
41+
if (this.shouldDeployDocs()) {
42+
new s3deploy.BucketDeployment(scope, 'Docs', {
43+
sources: [
44+
s3deploy.Source.asset(this.docsSource, {
45+
assetHash: docsSourceHash(),
46+
assetHashType: AssetHashType.CUSTOM,
47+
}),
48+
],
49+
destinationBucket: props.appBucket,
50+
distribution: props.cdn,
51+
distributionPaths: ['/docs/*'],
52+
})
53+
}
54+
3755
new s3deploy.BucketDeployment(scope, 'PrivateFiles', {
3856
sources: [s3deploy.Source.asset(this.privateSource)],
3957
destinationBucket: props.privateBucket,
4058
})
4159
}
60+
61+
shouldDeployDocs() {
62+
return hasFiles(p.projectPath('docs'))
63+
}
4264
}

storage/framework/core/cloud/src/cloud/docs.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ export class DocsStack {
1616

1717
constructor(scope: Construct, props: DocsStackProps) {
1818
// if docsPrefix is not set, then we know we are in docsMode and the documentation lives at the root of the domain
19-
const docsPrefix = config.app.docMode ? '' : config.docs.base
19+
const docsPrefix = 'docs'
20+
// const docsPrefix = config.app.docMode ? '' : config.docs.base
2021

2122
// this edge function ensures pretty docs urls
2223
// soon to be reused for our Meema features

storage/framework/core/cloud/src/helpers.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,13 @@ export async function getOrCreateTimestamp(): Promise<string> {
606606
}
607607
}
608608

609+
610+
// get the CloudFront distribution ID of the current stack
611+
export async function getCloudFrontDistributionId(): Promise<string> {
612+
return await runCommand(`aws cloudfront list-distributions --query "DistributionList.Items[?Origins.Items[0].DomainName=='${config.app.url}'].Id"`, {
613+
}
614+
615+
609616
// function isProductionEnv(env: string) {
610617
// return env === 'production' || env === 'prod'
611618
// }

storage/framework/core/types/src/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,8 @@ export interface CloudCliOptions extends CliOptions {
351351
ssh?: boolean
352352
connect?: boolean
353353
jumpBox?: boolean
354+
invalidateCache?: boolean
355+
paths?: string
354356
}
355357
export interface CommitOptions extends CliOptions {}
356358
export interface KeyOptions extends CliOptions {}

storage/framework/core/utils/src/hash.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,18 @@ export function originRequestFunctionHash() {
1414
}
1515

1616
export function websiteSourceHash() {
17-
const docsSrc = [p.projectPath('docs'), p.projectPath('config/docs.ts')]
18-
1917
const websiteSrc = [
2018
p.projectPath('resources/views'),
21-
// p.projectPath('config/app.ts'),
2219
]
2320

24-
return config.app.docMode === true ? hashPaths(docsSrc) : hashPaths(websiteSrc)
21+
return hashPaths(websiteSrc)
22+
}
23+
24+
export function docsSourceHash() {
25+
const docsSrc = [
26+
p.projectPath('docs'),
27+
p.projectPath('config/docs.ts')
28+
]
29+
30+
return hashPaths(docsSrc)
2531
}

0 commit comments

Comments
 (0)