From 4afa02c21e10f96ae96a55bdd13b1470ad8182ee Mon Sep 17 00:00:00 2001 From: seven Date: Fri, 29 Nov 2024 21:11:10 +0800 Subject: [PATCH 1/2] feat: localstack setup init Signed-off-by: seven --- src/commands/index.ts | 9 +++++++++ src/localStack/index.ts | 0 2 files changed, 9 insertions(+) create mode 100644 src/localStack/index.ts diff --git a/src/commands/index.ts b/src/commands/index.ts index 7d8aa0f..f3fa26b 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -52,4 +52,13 @@ program await deploy(stackName, { location: file, parameters: parameter, stage }); }); +// localStack commands +program + .command('local [command]') + .description('localstack commands') + .action(() => { + logger.debug('log command info'); + console.log('localstack commands'); + }); + program.parse(); diff --git a/src/localStack/index.ts b/src/localStack/index.ts new file mode 100644 index 0000000..e69de29 From 5b840de5777289e218cdf1f4f93e13862cfd4422 Mon Sep 17 00:00:00 2001 From: seven Date: Wed, 12 Nov 2025 17:51:19 +0800 Subject: [PATCH 2/2] feat: local step folder setup --- src/commands/index.ts | 17 +++++++++++++ src/commands/local.ts | 45 +++++++++++++++++++++++++++++++++++ src/localStack/index.ts | 0 src/stack/localStack/event.ts | 38 +++++++++++++++++++++++++++++ src/stack/localStack/index.ts | 6 +++++ 5 files changed, 106 insertions(+) create mode 100644 src/commands/local.ts delete mode 100644 src/localStack/index.ts create mode 100644 src/stack/localStack/event.ts create mode 100644 src/stack/localStack/index.ts diff --git a/src/commands/index.ts b/src/commands/index.ts index cab3ba4..6d4d6eb 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -6,6 +6,7 @@ import { validate } from './validate'; import { deploy } from './deploy'; import { template } from './template'; import { destroyStack } from './destroy'; +import { runLocal } from './local'; const program = new Command(); @@ -101,4 +102,20 @@ program }, ); +program + .command('local ') + .description('run Serverless application locally for debugging') + .option('-s, --stage ', 'specify the stage', 'default') + .option('-p, --port ', 'specify the port', '3000') + .option('-d, --debug', 'enable debug mode') + .option('-w, --watch', 'enable file watch', true) + .action(async (stackName, { stage, port, debug, watch }) => { + await runLocal(stackName, { + stage, + port: Number(port) || 3000, + debug: !!debug, + watch: typeof watch === 'boolean' ? watch : true, + }); + }); + program.parse(); diff --git a/src/commands/local.ts b/src/commands/local.ts new file mode 100644 index 0000000..7d10812 --- /dev/null +++ b/src/commands/local.ts @@ -0,0 +1,45 @@ +import { logger, setContext } from '../common'; +import { startLocalStack } from '../stack/localStack'; + +export interface RunLocalOptions { + stage: string; + port: number; + debug: boolean; + watch: boolean; +} + +export const runLocal = async (stackName: string, opts: RunLocalOptions) => { + const { stage, port, debug, watch } = opts; + + await setContext({ stage }); + + logger.info( + `run-local starting: stack=${stackName} stage=${stage} port=${port} debug=${debug} watch=${watch}`, + ); + + await startLocalStack(); + + // if (watch) { + // const cwd = process.cwd(); + // try { + // fs.watch(cwd, { recursive: true }, (eventType, filename) => { + // if (!filename) return; + // const filePath = path.join(cwd, filename); + // logger.info(`file change detected: ${eventType} ${filePath}`); + // }); + // logger.info(`watching files under ${process.cwd()}`); + // } catch (err) { + // logger.warn(`file watch not available: ${String(err)}`); + // } + // } + // + // const shutdown = () => { + // logger.info('shutting down run-local server'); + // server.close(() => process.exit(0)); + // }; + // process.on('SIGINT', shutdown); + // process.on('SIGTERM', shutdown); + // + // // return server for tests if needed + // return { server, port, stage, debug, watch }; +}; diff --git a/src/localStack/index.ts b/src/localStack/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/stack/localStack/event.ts b/src/stack/localStack/event.ts new file mode 100644 index 0000000..88de28a --- /dev/null +++ b/src/stack/localStack/event.ts @@ -0,0 +1,38 @@ +import http from 'node:http'; +import { logger } from '../../common'; +import { EventDomain, EventTypes } from '../../types'; +import { isEmpty } from 'lodash'; + +const startApiGatewayServer = (event: EventDomain) => { + const server = http.createServer((req, res) => { + const matchedTrigger = event.triggers.find( + (trigger) => trigger.method === req.method && trigger.path === req.url, + ); + if (!matchedTrigger) { + res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' }); + res.end('Not Found\n'); + logger.warn(`API Gateway Event - ${req.method} ${req.url} -> Not Found`); + return; + } + + res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' }); + res.end(`Invoked backend: ${matchedTrigger.backend}\n`); + logger.info(`API Gateway Event - ${req.method} ${req.url} -> ${matchedTrigger.backend}`); + }); + + const port = 3000 + Math.floor(Math.random() * 1000); + server.listen(port, () => { + logger.info(`API Gateway "${event.name}" listening on http://localhost:${port}`); + }); +}; + +export const startEvents = (events: Array | undefined) => { + const apiGateways = events?.filter((event) => event.type === EventTypes.API_GATEWAY); + if (isEmpty(apiGateways)) { + return; + } + + apiGateways!.forEach((gateway) => { + startApiGatewayServer(gateway); + }); +}; diff --git a/src/stack/localStack/index.ts b/src/stack/localStack/index.ts new file mode 100644 index 0000000..5887220 --- /dev/null +++ b/src/stack/localStack/index.ts @@ -0,0 +1,6 @@ +export * from './event'; + +export const startLocalStack = async () => { + // Placeholder for starting local stack logic + console.log('Local stack started'); +};