1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-28 07:13:29 +00:00
Files
browser/apps/cli/src/commands/serve.command.ts
Nick Krantz 727689d827 [PM-24534] Archive via CLI (#16502)
* refactor `canInteract` into a component level usage.

- The default service is going to be used in the CLI which won't make use of the UI-related aspects

* all nested entities to be imported from the vault

* initial add of archive command to the cli

* add archive to oss serve

* check for deleted cipher when attempting to archive

* add searchability/list functionality for archived ciphers

* restore an archived cipher

* unarchive a cipher when a user is editing it and has lost their premium status

* add missing feature flags

* re-export only needed services from the vault

* add needed await

* add prompt when applicable for editing an archived cipher

* move cipher archive service into `common/vault`

* fix testing code
2025-09-30 10:45:04 -04:00

86 lines
3.0 KiB
TypeScript

import http from "node:http";
import net from "node:net";
import * as koaRouter from "@koa/router";
import { OptionValues } from "commander";
import * as koa from "koa";
import * as koaBodyParser from "koa-bodyparser";
import * as koaJson from "koa-json";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { OssServeConfigurator } from "../oss-serve-configurator";
import { ServiceContainer } from "../service-container/service-container";
export class ServeCommand {
constructor(
protected serviceContainer: ServiceContainer,
protected serveConfigurator: OssServeConfigurator,
) {}
async run(options: OptionValues) {
const protectOrigin = !options.disableOriginProtection;
const port = options.port || 8087;
const hostname = options.hostname || "localhost";
this.serviceContainer.logService.info(
`Starting server on ${hostname}:${port} with ${
protectOrigin ? "origin protection" : "no origin protection"
}`,
);
const server = new koa();
const router = new koaRouter();
process.env.BW_SERVE = "true";
process.env.BW_NOINTERACTION = "true";
server
.use(async (ctx, next) => {
if (protectOrigin && ctx.headers.origin != undefined) {
ctx.status = 403;
this.serviceContainer.logService.warning(
`Blocking request from "${
Utils.isNullOrEmpty(ctx.headers.origin)
? "(Origin header value missing)"
: ctx.headers.origin
}"`,
);
return;
}
await next();
})
.use(koaBodyParser())
.use(koaJson({ pretty: false, param: "pretty" }));
await this.serveConfigurator.configureRouter(router);
server.use(router.routes()).use(router.allowedMethods());
if (hostname.startsWith("fd+connected://")) {
const fd = parseInt(hostname.slice("fd+connected://".length));
const httpServer = http.createServer(server.callback());
const socket = new net.Socket({ fd: fd, readable: true, writable: true });
// allow idle sockets, incomplete handshakes and slow requests
httpServer.keepAliveTimeout = 0;
httpServer.headersTimeout = 0;
httpServer.timeout = 0;
socket.pause();
httpServer.emit("connection", socket);
socket.resume(); // Let the HTTP parser start reading
} else if (hostname.startsWith("fd+listening://")) {
const fd = parseInt(hostname.slice("fd+listening://".length));
server.listen({ fd }, () => {
this.serviceContainer.logService.info("Listening on " + hostname);
});
} else if (hostname.startsWith("unix://")) {
const socketPath = hostname.slice("unix://".length);
server.listen(socketPath, () => {
this.serviceContainer.logService.info("Listening on " + hostname);
});
} else {
server.listen(port, hostname === "all" ? null : hostname, () => {
this.serviceContainer.logService.info("Listening on " + hostname + ":" + port);
});
}
}
}