@eudiplo/cli 0.1.0

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/README.md ADDED
@@ -0,0 +1,83 @@
1
+ # EUDIPLO CLI
2
+
3
+ The EUDIPLO CLI provides deployment-neutral commands for inspecting an EUDIPLO
4
+ instance and deployment-driver commands for local Docker Compose demos.
5
+
6
+ ## Quick Start
7
+
8
+ ```bash
9
+ npx @eudiplo/cli demo
10
+ npx @eudiplo/cli instance add production --url https://eudiplo.example.com
11
+ npx @eudiplo/cli doctor --instance production
12
+ ```
13
+
14
+ `demo` creates a local `.eudiplo.env`, copies the bundled EUDIPLO Docker Compose
15
+ template, and starts the `compose` driver. The Compose template is bundled with
16
+ the npm package and checked during build against the canonical file in
17
+ `deployment/docker-compose/docker-compose.yml`.
18
+
19
+ ## Package and Binary Names
20
+
21
+ The npm package is scoped as `@eudiplo/cli`, while the installed binary is
22
+ `eudiplo`. That means one-off usage should use `npx @eudiplo/cli`; after global
23
+ or project installation, run `eudiplo` directly.
24
+
25
+ ```bash
26
+ pnpm add -D @eudiplo/cli
27
+ pnpm eudiplo status
28
+ ```
29
+
30
+ ## Commands
31
+
32
+ Deployment-neutral commands work with any registered EUDIPLO instance and do not
33
+ require Docker:
34
+
35
+ ```bash
36
+ eudiplo instance add production --url https://eudiplo.example.com
37
+ eudiplo doctor --instance production
38
+ eudiplo status --instance production
39
+ eudiplo config validate
40
+ ```
41
+
42
+ Compose driver commands require a `compose` instance:
43
+
44
+ ```bash
45
+ eudiplo init --target compose
46
+ eudiplo up
47
+ eudiplo down
48
+ eudiplo logs
49
+ ```
50
+
51
+ If a driver-specific command is used with an unsupported target, the CLI exits
52
+ with a clear error, for example:
53
+
54
+ ```text
55
+ logs is not available for externally managed deployments
56
+ ```
57
+
58
+ ## Configuration
59
+
60
+ The CLI stores instance metadata outside the source tree. By default it writes to
61
+ `~/.eudiplo/config.json`. Override the location for tests or isolated runs with:
62
+
63
+ ```bash
64
+ EUDIPLO_CLI_HOME=/tmp/eudiplo-cli eudiplo config validate
65
+ EUDIPLO_CLI_CONFIG=/tmp/eudiplo-cli/config.json eudiplo config validate
66
+ ```
67
+
68
+ Do not store secrets in the CLI config. Commands that need authenticated API
69
+ access read credentials from environment variables such as `EUDIPLO_CLIENT_ID`
70
+ and `EUDIPLO_CLIENT_SECRET`.
71
+
72
+ ## Local Development
73
+
74
+ From the repository root:
75
+
76
+ ```bash
77
+ pnpm --filter @eudiplo/cli test
78
+ pnpm --filter @eudiplo/cli build
79
+ pnpm --filter @eudiplo/cli lint
80
+ ```
81
+
82
+ The build runs `assets:check`, which fails if the bundled Compose template has
83
+ drifted from the canonical deployment Compose file.
package/dist/args.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { ParsedArgs } from "./types.js";
2
+ export declare function parseArgs(args: string[]): ParsedArgs;
3
+ export declare function readStringFlag(flags: Record<string, string | boolean>, name: string): string | undefined;
4
+ //# sourceMappingURL=args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAgCpD;AAED,wBAAgB,cAAc,CAC1B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,EACvC,IAAI,EAAE,MAAM,GACb,MAAM,GAAG,SAAS,CAGpB"}
package/dist/args.js ADDED
@@ -0,0 +1,35 @@
1
+ export function parseArgs(args) {
2
+ const flags = {};
3
+ const positionals = [];
4
+ for (let index = 0; index < args.length; index += 1) {
5
+ const token = args[index];
6
+ if (!token.startsWith("--")) {
7
+ positionals.push(token);
8
+ continue;
9
+ }
10
+ const [rawName, rawValue] = token.slice(2).split("=", 2);
11
+ if (rawValue !== undefined) {
12
+ flags[rawName] = rawValue;
13
+ continue;
14
+ }
15
+ const next = args[index + 1];
16
+ if (next && !next.startsWith("--")) {
17
+ flags[rawName] = next;
18
+ index += 1;
19
+ }
20
+ else {
21
+ flags[rawName] = true;
22
+ }
23
+ }
24
+ return {
25
+ command: positionals[0],
26
+ subject: positionals[1],
27
+ positionals: positionals.slice(2),
28
+ flags,
29
+ };
30
+ }
31
+ export function readStringFlag(flags, name) {
32
+ const value = flags[name];
33
+ return typeof value === "string" && value.length > 0 ? value : undefined;
34
+ }
35
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,SAAS,CAAC,IAAc;IACpC,MAAM,KAAK,GAAqC,EAAE,CAAC;IACnD,MAAM,WAAW,GAAa,EAAE,CAAC;IAEjC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAClD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACxB,SAAS;QACb,CAAC;QAED,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACzD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YACzB,KAAK,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAC;YAC1B,SAAS;QACb,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAC7B,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,KAAK,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;YACtB,KAAK,IAAI,CAAC,CAAC;QACf,CAAC;aAAM,CAAC;YACJ,KAAK,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;QAC1B,CAAC;IACL,CAAC;IAED,OAAO;QACH,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;QACvB,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;QACvB,WAAW,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;QACjC,KAAK;KACR,CAAC;AACN,CAAC;AAED,MAAM,UAAU,cAAc,CAC1B,KAAuC,EACvC,IAAY;IAEZ,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7E,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare const defaultComposeFileName = "eudiplo.compose.yaml";
2
+ export declare const defaultEnvFileName = ".eudiplo.env";
3
+ export declare function createDemoEnv(): string;
4
+ export declare function createComposeFile(): Promise<string>;
5
+ //# sourceMappingURL=compose-template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose-template.d.ts","sourceRoot":"","sources":["../src/compose-template.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,sBAAsB,yBAAyB,CAAC;AAC7D,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AAEjD,wBAAgB,aAAa,IAAI,MAAM,CAUtC;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAEzD"}
@@ -0,0 +1,19 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { readFile } from "node:fs/promises";
3
+ export const defaultComposeFileName = "eudiplo.compose.yaml";
4
+ export const defaultEnvFileName = ".eudiplo.env";
5
+ export function createDemoEnv() {
6
+ return [
7
+ "EUDIPLO_ENV_FILE=.eudiplo.env",
8
+ "EUDIPLO_IMAGE=ghcr.io/openwallet-foundation/eudiplo-demo:main",
9
+ "PUBLIC_URL=http://localhost:3000",
10
+ `MASTER_SECRET=${randomBytes(32).toString("base64")}`,
11
+ "AUTH_CLIENT_ID=root",
12
+ "AUTH_CLIENT_SECRET=root",
13
+ "",
14
+ ].join("\n");
15
+ }
16
+ export async function createComposeFile() {
17
+ return readFile(new URL("../templates/docker-compose.yml", import.meta.url), "utf8");
18
+ }
19
+ //# sourceMappingURL=compose-template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose-template.js","sourceRoot":"","sources":["../src/compose-template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,MAAM,CAAC,MAAM,sBAAsB,GAAG,sBAAsB,CAAC;AAC7D,MAAM,CAAC,MAAM,kBAAkB,GAAG,cAAc,CAAC;AAEjD,MAAM,UAAU,aAAa;IACzB,OAAO;QACL,+BAA+B;QAC/B,+DAA+D;QAC/D,kCAAkC;QAChC,iBAAiB,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;QACrD,qBAAqB;QACrB,yBAAyB;QACzB,EAAE;KACL,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACnC,OAAO,QAAQ,CAAC,IAAI,GAAG,CAAC,iCAAiC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;AACzF,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { CliConfig, InstanceConfig } from "./types.js";
2
+ export declare function resolveConfigPath(env: NodeJS.ProcessEnv): string;
3
+ export declare function loadConfig(path: string): Promise<CliConfig>;
4
+ export declare function saveConfig(path: string, config: CliConfig): Promise<void>;
5
+ export declare function validateConfig(value: unknown): CliConfig;
6
+ export declare function upsertInstance(config: CliConfig, name: string, instance: InstanceConfig): CliConfig;
7
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAI5D,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAUhE;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAUjE;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAM/E;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAkBxD;AAED,wBAAgB,cAAc,CAC1B,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,cAAc,GACzB,SAAS,CAQX"}
package/dist/config.js ADDED
@@ -0,0 +1,113 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { homedir } from "node:os";
4
+ const emptyConfig = () => ({ instances: {} });
5
+ export function resolveConfigPath(env) {
6
+ if (env.EUDIPLO_CLI_CONFIG) {
7
+ return resolve(env.EUDIPLO_CLI_CONFIG);
8
+ }
9
+ const baseDir = env.EUDIPLO_CLI_HOME
10
+ ? resolve(env.EUDIPLO_CLI_HOME)
11
+ : join(homedir(), ".eudiplo");
12
+ return join(baseDir, "config.json");
13
+ }
14
+ export async function loadConfig(path) {
15
+ try {
16
+ const contents = await readFile(path, "utf8");
17
+ return validateConfig(JSON.parse(contents));
18
+ }
19
+ catch (error) {
20
+ if (isNodeError(error) && error.code === "ENOENT") {
21
+ return emptyConfig();
22
+ }
23
+ throw error;
24
+ }
25
+ }
26
+ export async function saveConfig(path, config) {
27
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
28
+ await writeFile(path, `${JSON.stringify(config, null, 4)}\n`, {
29
+ encoding: "utf8",
30
+ mode: 0o600,
31
+ });
32
+ }
33
+ export function validateConfig(value) {
34
+ if (!isRecord(value)) {
35
+ throw new Error("Config must be a JSON object.");
36
+ }
37
+ if (!isRecord(value.instances)) {
38
+ throw new Error("Config must define an instances object.");
39
+ }
40
+ const instances = {};
41
+ for (const [name, instance] of Object.entries(value.instances)) {
42
+ instances[name] = validateInstanceConfig(name, instance);
43
+ }
44
+ return {
45
+ defaultInstance: validateDefaultInstance(value.defaultInstance, instances),
46
+ instances,
47
+ };
48
+ }
49
+ export function upsertInstance(config, name, instance) {
50
+ return {
51
+ defaultInstance: config.defaultInstance ?? name,
52
+ instances: {
53
+ ...config.instances,
54
+ [name]: instance,
55
+ },
56
+ };
57
+ }
58
+ function isRecord(value) {
59
+ return typeof value === "object" && value !== null && !Array.isArray(value);
60
+ }
61
+ function isNodeError(error) {
62
+ return error instanceof Error && "code" in error;
63
+ }
64
+ function validateDefaultInstance(value, instances) {
65
+ if (typeof value !== "string") {
66
+ return undefined;
67
+ }
68
+ if (!Object.hasOwn(instances, value)) {
69
+ throw new Error(`Default instance ${value} is not defined in instances.`);
70
+ }
71
+ return value;
72
+ }
73
+ function validateInstanceConfig(name, value) {
74
+ if (!isRecord(value)) {
75
+ throw new Error(`Instance ${name} must be an object.`);
76
+ }
77
+ if (value.target !== "compose" && value.target !== "external") {
78
+ throw new Error(`Instance ${name} has unsupported target ${String(value.target)}.`);
79
+ }
80
+ if (typeof value.url !== "string" || value.url.length === 0) {
81
+ throw new Error(`Instance ${name} must define a url.`);
82
+ }
83
+ validateHttpUrl(value.url, `Instance ${name} url`);
84
+ validateOptionalHttpUrl(value.clientUrl, `Instance ${name} clientUrl`);
85
+ return {
86
+ target: value.target,
87
+ url: value.url,
88
+ clientUrl: optionalString(value.clientUrl),
89
+ composeFile: optionalString(value.composeFile),
90
+ envFile: optionalString(value.envFile),
91
+ projectName: optionalString(value.projectName),
92
+ };
93
+ }
94
+ function optionalString(value) {
95
+ return typeof value === "string" ? value : undefined;
96
+ }
97
+ function validateOptionalHttpUrl(value, label) {
98
+ if (typeof value === "string") {
99
+ validateHttpUrl(value, label);
100
+ }
101
+ }
102
+ function validateHttpUrl(value, label) {
103
+ try {
104
+ const url = new URL(value);
105
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
106
+ throw new Error("Unsupported URL protocol.");
107
+ }
108
+ }
109
+ catch {
110
+ throw new Error(`${label} must be an absolute HTTP(S) URL.`);
111
+ }
112
+ }
113
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { CommandContext, InstanceConfig } from "./types.js";
2
+ export type CheckStatus = "pass" | "warn" | "fail";
3
+ export interface DoctorCheck {
4
+ name: string;
5
+ status: CheckStatus;
6
+ message: string;
7
+ }
8
+ export declare function runDoctor(instance: InstanceConfig, context: CommandContext, driverDiagnostics: string[]): Promise<DoctorCheck[]>;
9
+ export declare function hasFailedChecks(checks: DoctorCheck[]): boolean;
10
+ export declare function formatChecks(checks: DoctorCheck[]): string;
11
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEnD,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,SAAS,CAC3B,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,cAAc,EACvB,iBAAiB,EAAE,MAAM,EAAE,GAC5B,OAAO,CAAC,WAAW,EAAE,CAAC,CAuDxB;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAE9D;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAI1D"}
package/dist/doctor.js ADDED
@@ -0,0 +1,116 @@
1
+ export async function runDoctor(instance, context, driverDiagnostics) {
2
+ const checks = [];
3
+ const baseUrl = parseUrl(instance.url);
4
+ if (!baseUrl) {
5
+ return [
6
+ {
7
+ name: "public URL",
8
+ status: "fail",
9
+ message: `${instance.url} is not a valid absolute URL`,
10
+ },
11
+ ];
12
+ }
13
+ checks.push({
14
+ name: "public URL",
15
+ status: baseUrl.protocol === "https:" ? "pass" : "warn",
16
+ message: baseUrl.protocol === "https:"
17
+ ? `${baseUrl.href} uses HTTPS`
18
+ : `${baseUrl.href} does not use HTTPS`,
19
+ });
20
+ checks.push(await checkEndpoint("API reachability", baseUrl, "/api/docs", context));
21
+ checks.push(await checkEndpoint("health endpoint", baseUrl, "/health", context));
22
+ checks.push(checkAuthentication(context));
23
+ if (instance.clientUrl) {
24
+ const clientUrl = parseUrl(instance.clientUrl);
25
+ checks.push(clientUrl
26
+ ? await checkEndpoint("client connectivity", clientUrl, "/", context)
27
+ : {
28
+ name: "client connectivity",
29
+ status: "fail",
30
+ message: `${instance.clientUrl} is not a valid absolute URL`,
31
+ });
32
+ }
33
+ else {
34
+ checks.push({
35
+ name: "client connectivity",
36
+ status: "warn",
37
+ message: "No client URL configured for this instance.",
38
+ });
39
+ }
40
+ for (const diagnostic of driverDiagnostics) {
41
+ checks.push({
42
+ name: "driver diagnostics",
43
+ status: "warn",
44
+ message: diagnostic,
45
+ });
46
+ }
47
+ return checks;
48
+ }
49
+ export function hasFailedChecks(checks) {
50
+ return checks.some((check) => check.status === "fail");
51
+ }
52
+ export function formatChecks(checks) {
53
+ return checks
54
+ .map((check) => `${formatStatus(check.status)} ${check.name}: ${check.message}`)
55
+ .join("\n");
56
+ }
57
+ async function checkEndpoint(name, baseUrl, path, context) {
58
+ const url = new URL(path, baseUrl);
59
+ try {
60
+ const response = await context.fetch(url, { method: "GET" });
61
+ if (response.ok) {
62
+ return {
63
+ name,
64
+ status: "pass",
65
+ message: `${url.href} returned HTTP ${response.status}`,
66
+ };
67
+ }
68
+ return {
69
+ name,
70
+ status: "fail",
71
+ message: `${url.href} returned HTTP ${response.status}`,
72
+ };
73
+ }
74
+ catch (error) {
75
+ return {
76
+ name,
77
+ status: "fail",
78
+ message: `${url.href} could not be reached: ${String(error)}`,
79
+ };
80
+ }
81
+ }
82
+ function checkAuthentication(context) {
83
+ const hasClientId = Boolean(context.env.EUDIPLO_CLIENT_ID);
84
+ const hasClientSecret = Boolean(context.env.EUDIPLO_CLIENT_SECRET);
85
+ if (hasClientId && hasClientSecret) {
86
+ return {
87
+ name: "authentication configuration",
88
+ status: "pass",
89
+ message: "Client credentials are available in environment variables.",
90
+ };
91
+ }
92
+ return {
93
+ name: "authentication configuration",
94
+ status: "warn",
95
+ message: "Set EUDIPLO_CLIENT_ID and EUDIPLO_CLIENT_SECRET when commands need authenticated API access.",
96
+ };
97
+ }
98
+ function parseUrl(value) {
99
+ try {
100
+ const url = new URL(value);
101
+ return url.protocol === "http:" || url.protocol === "https:" ? url : undefined;
102
+ }
103
+ catch {
104
+ return undefined;
105
+ }
106
+ }
107
+ function formatStatus(status) {
108
+ if (status === "pass") {
109
+ return "PASS";
110
+ }
111
+ if (status === "warn") {
112
+ return "WARN";
113
+ }
114
+ return "FAIL";
115
+ }
116
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { DeploymentDriver, DeploymentTarget, InstanceConfig } from "./types.js";
2
+ export declare const drivers: Record<DeploymentTarget, DeploymentDriver>;
3
+ export declare function ensureComposeProject(cwd: string): Promise<InstanceConfig>;
4
+ export declare function unsupportedCommand(command: string, target: DeploymentTarget): string;
5
+ //# sourceMappingURL=drivers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drivers.d.ts","sourceRoot":"","sources":["../src/drivers.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAER,gBAAgB,EAChB,gBAAgB,EAEhB,cAAc,EACjB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,gBAAgB,EAAE,gBAAgB,CAiC9D,CAAC;AAEF,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAmB/E;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAKpF"}
@@ -0,0 +1,110 @@
1
+ import { access, writeFile } from "node:fs/promises";
2
+ import { join, resolve } from "node:path";
3
+ import { spawn } from "node:child_process";
4
+ import { createComposeFile, createDemoEnv, defaultComposeFileName, defaultEnvFileName, } from "./compose-template.js";
5
+ export const drivers = {
6
+ compose: {
7
+ target: "compose",
8
+ async diagnostics(instance, context) {
9
+ const messages = [];
10
+ if (!(await commandExists("docker", context))) {
11
+ messages.push("Docker is not available on PATH.");
12
+ }
13
+ if (instance.composeFile) {
14
+ try {
15
+ await access(resolve(context.cwd, instance.composeFile));
16
+ }
17
+ catch {
18
+ messages.push(`Compose file not found: ${instance.composeFile}`);
19
+ }
20
+ }
21
+ return messages;
22
+ },
23
+ up(options) {
24
+ return runCompose(["up", "-d", ...options.args], options);
25
+ },
26
+ down(options) {
27
+ return runCompose(["down", ...options.args], options);
28
+ },
29
+ logs(options) {
30
+ return runCompose(["logs", "-f", ...options.args], options);
31
+ },
32
+ },
33
+ external: {
34
+ target: "external",
35
+ async diagnostics() {
36
+ return [];
37
+ },
38
+ },
39
+ };
40
+ export async function ensureComposeProject(cwd) {
41
+ const composePath = join(cwd, defaultComposeFileName);
42
+ const envPath = join(cwd, defaultEnvFileName);
43
+ if (!(await exists(composePath))) {
44
+ await writeFile(composePath, await createComposeFile(), "utf8");
45
+ }
46
+ if (!(await exists(envPath))) {
47
+ await writeFile(envPath, createDemoEnv(), { encoding: "utf8", mode: 0o600 });
48
+ }
49
+ return {
50
+ target: "compose",
51
+ url: "http://localhost:3000",
52
+ clientUrl: "http://localhost:4200",
53
+ composeFile: defaultComposeFileName,
54
+ envFile: defaultEnvFileName,
55
+ projectName: "eudiplo-demo",
56
+ };
57
+ }
58
+ export function unsupportedCommand(command, target) {
59
+ if (target === "external") {
60
+ return `${command} is not available for externally managed deployments`;
61
+ }
62
+ return `${command} is not available for ${target} deployments`;
63
+ }
64
+ async function runCompose(args, { instance, context }) {
65
+ const composeArgs = ["compose"];
66
+ if (instance.envFile) {
67
+ composeArgs.push("--env-file", resolve(context.cwd, instance.envFile));
68
+ }
69
+ if (instance.composeFile) {
70
+ composeArgs.push("-f", resolve(context.cwd, instance.composeFile));
71
+ }
72
+ if (instance.projectName) {
73
+ composeArgs.push("--project-name", instance.projectName);
74
+ }
75
+ composeArgs.push(...args);
76
+ return new Promise((resolveProcess) => {
77
+ const child = spawn("docker", composeArgs, {
78
+ cwd: context.cwd,
79
+ env: context.env,
80
+ stdio: "inherit",
81
+ });
82
+ child.on("error", (error) => {
83
+ context.stderr.write(`${error.message}\n`);
84
+ resolveProcess(1);
85
+ });
86
+ child.on("close", (code) => resolveProcess(code ?? 1));
87
+ });
88
+ }
89
+ async function commandExists(command, context) {
90
+ const path = context.env.PATH ?? "";
91
+ const executableNames = process.platform === "win32" ? [command, `${command}.exe`] : [command];
92
+ for (const directory of path.split(process.platform === "win32" ? ";" : ":")) {
93
+ for (const executableName of executableNames) {
94
+ if (await exists(join(directory, executableName))) {
95
+ return true;
96
+ }
97
+ }
98
+ }
99
+ return false;
100
+ }
101
+ async function exists(path) {
102
+ try {
103
+ await access(path);
104
+ return true;
105
+ }
106
+ catch {
107
+ return false;
108
+ }
109
+ }
110
+ //# sourceMappingURL=drivers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drivers.js","sourceRoot":"","sources":["../src/drivers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from "./runtime.js";
3
+ const exitCode = await runCli(process.argv.slice(2));
4
+ process.exitCode = exitCode;
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACrD,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { CommandContext } from "./types.js";
2
+ export declare function runCli(args: string[], context?: CommandContext): Promise<number>;
3
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,cAAc,EAAgC,MAAM,YAAY,CAAC;AAE1F,wBAAsB,MAAM,CACxB,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,GAAE,cAAiC,GAC3C,OAAO,CAAC,MAAM,CAAC,CAajB"}
@@ -0,0 +1,181 @@
1
+ import { parseArgs, readStringFlag } from "./args.js";
2
+ import { loadConfig, resolveConfigPath, saveConfig, upsertInstance } from "./config.js";
3
+ import { drivers, ensureComposeProject, unsupportedCommand } from "./drivers.js";
4
+ import { formatChecks, hasFailedChecks, runDoctor } from "./doctor.js";
5
+ export async function runCli(args, context = defaultContext()) {
6
+ const parsed = parseArgs(args);
7
+ if (!parsed.command || parsed.flags.help === true || parsed.command === "help") {
8
+ context.stdout.write(`${helpText()}\n`);
9
+ return 0;
10
+ }
11
+ try {
12
+ return await dispatch(parsed, context);
13
+ }
14
+ catch (error) {
15
+ context.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
16
+ return 1;
17
+ }
18
+ }
19
+ function defaultContext() {
20
+ return {
21
+ cwd: process.cwd(),
22
+ env: process.env,
23
+ stdout: process.stdout,
24
+ stderr: process.stderr,
25
+ fetch,
26
+ };
27
+ }
28
+ async function dispatch(parsed, context) {
29
+ const configPath = resolveConfigPath(context.env);
30
+ const config = await loadConfig(configPath);
31
+ switch (parsed.command) {
32
+ case "init":
33
+ return init(configPath, config, parsed, context);
34
+ case "demo":
35
+ return demo(configPath, config, context);
36
+ case "instance":
37
+ return instance(configPath, config, parsed, context);
38
+ case "config":
39
+ return validateConfigCommand(configPath, parsed, context);
40
+ case "doctor":
41
+ return doctor(config, parsed, context);
42
+ case "status":
43
+ return status(config, parsed, context);
44
+ case "up":
45
+ case "down":
46
+ case "logs":
47
+ return driverCommand(config, parsed.command, parsed, context);
48
+ default:
49
+ throw new Error(`Unknown command: ${parsed.command}`);
50
+ }
51
+ }
52
+ async function init(configPath, config, parsed, context) {
53
+ const target = parseTarget(readStringFlag(parsed.flags, "target") ?? "compose");
54
+ const name = readStringFlag(parsed.flags, "instance") ?? "local";
55
+ const url = readStringFlag(parsed.flags, "url");
56
+ if (target === "compose") {
57
+ const instance = await ensureComposeProject(context.cwd);
58
+ const nextConfig = upsertInstance(config, name, {
59
+ ...instance,
60
+ url: url ?? instance.url,
61
+ });
62
+ await saveConfig(configPath, nextConfig);
63
+ context.stdout.write(`Initialized compose instance ${name}. Run npx @eudiplo/cli up to start it.\n`);
64
+ return 0;
65
+ }
66
+ if (!url) {
67
+ throw new Error("External instances require --url.");
68
+ }
69
+ const nextConfig = upsertInstance(config, name, { target, url });
70
+ await saveConfig(configPath, nextConfig);
71
+ context.stdout.write(`Initialized external instance ${name}.\n`);
72
+ return 0;
73
+ }
74
+ async function demo(configPath, config, context) {
75
+ const instance = await ensureComposeProject(context.cwd);
76
+ const nextConfig = upsertInstance(config, "local", instance);
77
+ await saveConfig(configPath, nextConfig);
78
+ context.stdout.write("Starting EUDIPLO demo with Docker Compose...\n");
79
+ return drivers.compose.up?.({
80
+ instanceName: "local",
81
+ instance,
82
+ args: [],
83
+ context,
84
+ }) ?? 1;
85
+ }
86
+ async function instance(configPath, config, parsed, context) {
87
+ if (parsed.subject !== "add") {
88
+ throw new Error("Usage: eudiplo instance add <name> --url <url> [--target external]");
89
+ }
90
+ const name = parsed.positionals[0];
91
+ if (!name) {
92
+ throw new Error("Instance name is required.");
93
+ }
94
+ const url = readStringFlag(parsed.flags, "url");
95
+ if (!url) {
96
+ throw new Error("--url is required.");
97
+ }
98
+ const target = parseTarget(readStringFlag(parsed.flags, "target") ?? "external");
99
+ const clientUrl = readStringFlag(parsed.flags, "client-url");
100
+ const nextConfig = upsertInstance(config, name, { target, url, clientUrl });
101
+ await saveConfig(configPath, nextConfig);
102
+ context.stdout.write(`Added ${target} instance ${name}.\n`);
103
+ return 0;
104
+ }
105
+ async function validateConfigCommand(configPath, parsed, context) {
106
+ if (parsed.subject !== "validate") {
107
+ throw new Error("Usage: eudiplo config validate");
108
+ }
109
+ const config = await loadConfig(configPath);
110
+ const instanceNames = Object.keys(config.instances);
111
+ context.stdout.write(`Config is valid: ${configPath}\n`);
112
+ context.stdout.write(`Instances: ${instanceNames.length}\n`);
113
+ if (config.defaultInstance) {
114
+ context.stdout.write(`Default instance: ${config.defaultInstance}\n`);
115
+ }
116
+ for (const name of instanceNames) {
117
+ const instance = config.instances[name];
118
+ context.stdout.write(`- ${name}: ${instance.target} ${instance.url}\n`);
119
+ }
120
+ return 0;
121
+ }
122
+ async function doctor(config, parsed, context) {
123
+ const [instanceName, instance] = resolveInstance(config, parsed);
124
+ const driver = drivers[instance.target];
125
+ const checks = await runDoctor(instance, context, await driver.diagnostics(instance, context));
126
+ context.stdout.write(`Doctor for ${instanceName} (${instance.target})\n`);
127
+ context.stdout.write(`${formatChecks(checks)}\n`);
128
+ return hasFailedChecks(checks) ? 1 : 0;
129
+ }
130
+ async function status(config, parsed, context) {
131
+ const [instanceName, instance] = resolveInstance(config, parsed);
132
+ const checks = await runDoctor(instance, context, []);
133
+ const health = checks.find((check) => check.name === "health endpoint");
134
+ context.stdout.write(`${instanceName} (${instance.target}) ${instance.url}\n`);
135
+ if (health) {
136
+ context.stdout.write(`${health.status.toUpperCase()} ${health.message}\n`);
137
+ }
138
+ return health?.status === "fail" ? 1 : 0;
139
+ }
140
+ async function driverCommand(config, command, parsed, context) {
141
+ const [instanceName, instance] = resolveInstance(config, parsed);
142
+ const driver = drivers[instance.target];
143
+ const handler = driver[command];
144
+ if (!handler) {
145
+ throw new Error(unsupportedCommand(command, instance.target));
146
+ }
147
+ return handler({ instanceName, instance, args: parsed.positionals, context });
148
+ }
149
+ function resolveInstance(config, parsed) {
150
+ const name = readStringFlag(parsed.flags, "instance") ?? config.defaultInstance;
151
+ if (!name) {
152
+ throw new Error("No instance selected. Use --instance or add an instance first.");
153
+ }
154
+ const instance = config.instances[name];
155
+ if (!instance) {
156
+ throw new Error(`Unknown instance: ${name}`);
157
+ }
158
+ return [name, instance];
159
+ }
160
+ function parseTarget(value) {
161
+ if (value === "compose" || value === "external") {
162
+ return value;
163
+ }
164
+ throw new Error(`Unsupported target: ${value}`);
165
+ }
166
+ function helpText() {
167
+ return `EUDIPLO CLI
168
+
169
+ Usage:
170
+ eudiplo demo
171
+ eudiplo init --target compose [--instance local]
172
+ eudiplo up|down|logs [--instance local]
173
+ eudiplo instance add <name> --url <url> [--target external] [--client-url <url>]
174
+ eudiplo doctor [--instance name]
175
+ eudiplo status [--instance name]
176
+ eudiplo config validate
177
+
178
+ One-off npm usage:
179
+ npx @eudiplo/cli demo`;
180
+ }
181
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ export type DeploymentTarget = "compose" | "external";
2
+ export interface InstanceConfig {
3
+ target: DeploymentTarget;
4
+ url: string;
5
+ clientUrl?: string;
6
+ composeFile?: string;
7
+ envFile?: string;
8
+ projectName?: string;
9
+ }
10
+ export interface CliConfig {
11
+ defaultInstance?: string;
12
+ instances: Record<string, InstanceConfig>;
13
+ }
14
+ export interface CommandContext {
15
+ cwd: string;
16
+ env: NodeJS.ProcessEnv;
17
+ stdout: Pick<NodeJS.WriteStream, "write">;
18
+ stderr: Pick<NodeJS.WriteStream, "write">;
19
+ fetch: typeof fetch;
20
+ }
21
+ export interface DriverCommandOptions {
22
+ instanceName: string;
23
+ instance: InstanceConfig;
24
+ args: string[];
25
+ context: CommandContext;
26
+ }
27
+ export interface DeploymentDriver {
28
+ target: DeploymentTarget;
29
+ diagnostics(instance: InstanceConfig, context: CommandContext): Promise<string[]>;
30
+ up?(options: DriverCommandOptions): Promise<number>;
31
+ down?(options: DriverCommandOptions): Promise<number>;
32
+ logs?(options: DriverCommandOptions): Promise<number>;
33
+ }
34
+ export interface ParsedArgs {
35
+ command?: string;
36
+ subject?: string;
37
+ positionals: string[];
38
+ flags: Record<string, string | boolean>;
39
+ }
40
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,UAAU,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC3B,MAAM,EAAE,gBAAgB,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC1C,KAAK,EAAE,OAAO,KAAK,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,cAAc,CAAC;IACzB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,EAAE,cAAc,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,gBAAgB,CAAC;IACzB,WAAW,CAAC,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAClF,EAAE,CAAC,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,IAAI,CAAC,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtD,IAAI,CAAC,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACzD;AAED,MAAM,WAAW,UAAU;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;CAC3C"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@eudiplo/cli",
3
+ "version": "0.1.0",
4
+ "description": "Deployment-aware command line tools for EUDIPLO",
5
+ "private": false,
6
+ "type": "module",
7
+ "bin": {
8
+ "eudiplo": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "templates",
13
+ "README.md"
14
+ ],
15
+ "scripts": {
16
+ "assets:check": "node ../../scripts/check-cli-assets.js",
17
+ "build": "pnpm assets:check && tsc -p tsconfig.json",
18
+ "dev": "tsx src/index.ts",
19
+ "test": "vitest run",
20
+ "lint": "biome lint .",
21
+ "prepublishOnly": "pnpm build"
22
+ },
23
+ "keywords": [
24
+ "eudiplo",
25
+ "cli",
26
+ "eudi",
27
+ "wallet",
28
+ "oid4vci",
29
+ "oid4vp"
30
+ ],
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/openwallet-foundation/eudiplo.git",
34
+ "directory": "apps/cli"
35
+ },
36
+ "homepage": "https://github.com/openwallet-foundation/eudiplo/tree/main/apps/cli#readme",
37
+ "bugs": {
38
+ "url": "https://github.com/openwallet-foundation/eudiplo/issues"
39
+ },
40
+ "author": "OpenWallet Foundation",
41
+ "license": "Apache-2.0",
42
+ "engines": {
43
+ "node": ">=22.0.0"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "devDependencies": {
49
+ "@biomejs/biome": "2.5.3",
50
+ "@types/node": "^26.0.1",
51
+ "tsx": "^4.22.4",
52
+ "typescript": "^6.0.3",
53
+ "vitest": "4.1.10"
54
+ }
55
+ }
@@ -0,0 +1,171 @@
1
+ # EUDIPLO Docker Compose - Multi-Profile Deployment
2
+ #
3
+ # Usage:
4
+ # docker compose up # Minimal: just EUDIPLO (SQLite, local storage)
5
+ # docker compose --profile standard up # Standard: + Postgres + MinIO
6
+ # docker compose --profile full up # Full: + Postgres + MinIO + Vault
7
+ #
8
+ # For more information, see the documentation:
9
+ # https://openwallet-foundation.github.io/eudiplo/docs/latest/deployment/docker-compose/
10
+
11
+ services:
12
+ # =============================================================================
13
+ # CORE SERVICES (always run)
14
+ # =============================================================================
15
+
16
+ eudiplo:
17
+ image: ${EUDIPLO_IMAGE:-ghcr.io/openwallet-foundation/eudiplo:latest}
18
+ restart: always
19
+ env_file:
20
+ - ${EUDIPLO_ENV_FILE:-.env}
21
+ ports:
22
+ - '3000:3000'
23
+ volumes:
24
+ - ${EUDIPLO_CONFIG_MOUNT:-eudiplo-config:/app/config}
25
+ - eudiplo-uploads:/app/uploads
26
+ depends_on:
27
+ postgres:
28
+ condition: service_healthy
29
+ required: false
30
+ minio:
31
+ condition: service_healthy
32
+ required: false
33
+ healthcheck:
34
+ test: ['CMD', 'curl', '-f', 'http://localhost:3000/health']
35
+ interval: 30s
36
+ timeout: 10s
37
+ retries: 3
38
+ start_period: 40s
39
+ networks:
40
+ - eudiplo-network
41
+
42
+ eudiplo-client:
43
+ image: ghcr.io/openwallet-foundation/eudiplo-client:latest
44
+ restart: always
45
+ ports:
46
+ - '4200:8080'
47
+ depends_on:
48
+ eudiplo:
49
+ condition: service_healthy
50
+ environment:
51
+ - API_BASE_URL=http://eudiplo:3000
52
+ # Uncomment to serve the client from a subpath behind a reverse proxy:
53
+ # - CLIENT_BASE_HREF=/eudiplo-client/
54
+ healthcheck:
55
+ test: ['CMD', 'wget', '-qO', '/dev/null', 'http://localhost:8080']
56
+ interval: 30s
57
+ timeout: 10s
58
+ retries: 3
59
+ start_period: 10s
60
+ networks:
61
+ - eudiplo-network
62
+
63
+ # =============================================================================
64
+ # STANDARD PROFILE: PostgreSQL + MinIO
65
+ # =============================================================================
66
+
67
+ postgres:
68
+ image: postgres:16
69
+ restart: always
70
+ profiles: ['standard', 'full']
71
+ environment:
72
+ POSTGRES_USER: ${DB_USERNAME:-eudiplo}
73
+ POSTGRES_PASSWORD: ${DB_PASSWORD:-changeme}
74
+ POSTGRES_DB: ${DB_DATABASE:-eudiplo}
75
+ volumes:
76
+ - postgres-data:/var/lib/postgresql/data
77
+ healthcheck:
78
+ test:
79
+ [
80
+ 'CMD-SHELL',
81
+ 'pg_isready -U ${DB_USERNAME:-eudiplo} -d ${DB_DATABASE:-eudiplo}',
82
+ ]
83
+ interval: 10s
84
+ timeout: 5s
85
+ retries: 5
86
+ networks:
87
+ - eudiplo-network
88
+
89
+ minio:
90
+ image: quay.io/minio/minio:latest
91
+ restart: always
92
+ profiles: ['standard', 'full']
93
+ ports:
94
+ - '9000:9000'
95
+ - '9001:9001'
96
+ environment:
97
+ MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
98
+ MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
99
+ volumes:
100
+ - minio-data:/data
101
+ command: server /data --console-address ":9001"
102
+ healthcheck:
103
+ test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']
104
+ interval: 30s
105
+ timeout: 10s
106
+ retries: 3
107
+ start_period: 10s
108
+ networks:
109
+ - eudiplo-network
110
+
111
+ # MinIO bucket initialization job
112
+ minio-init:
113
+ image: quay.io/minio/mc:latest
114
+ profiles: ['standard', 'full']
115
+ depends_on:
116
+ minio:
117
+ condition: service_healthy
118
+ environment:
119
+ MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin}
120
+ MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin}
121
+ MINIO_BUCKET: ${S3_BUCKET:-uploads}
122
+ entrypoint: >
123
+ /bin/sh -c "
124
+ mc alias set local http://minio:9000 $$MINIO_ROOT_USER $$MINIO_ROOT_PASSWORD;
125
+ mc mb -p local/$$MINIO_BUCKET || true;
126
+ mc anonymous set download local/$$MINIO_BUCKET || true;
127
+ echo 'Bucket created successfully';
128
+ "
129
+ networks:
130
+ - eudiplo-network
131
+
132
+ # =============================================================================
133
+ # FULL PROFILE: + HashiCorp Vault
134
+ # =============================================================================
135
+
136
+ vault:
137
+ image: hashicorp/vault:latest
138
+ restart: always
139
+ profiles: ['full']
140
+ ports:
141
+ - '8200:8200'
142
+ environment:
143
+ VAULT_DEV_ROOT_TOKEN_ID: ${VAULT_TOKEN:-root}
144
+ VAULT_DEV_LISTEN_ADDRESS: 0.0.0.0:8200
145
+ VAULT_API_ADDR: http://0.0.0.0:8200
146
+ cap_add:
147
+ - IPC_LOCK
148
+ volumes:
149
+ - vault-data:/vault/data
150
+ - vault-logs:/vault/logs
151
+ command: vault server -dev -dev-listen-address=0.0.0.0:8200
152
+ networks:
153
+ - eudiplo-network
154
+
155
+ networks:
156
+ eudiplo-network:
157
+ driver: bridge
158
+
159
+ volumes:
160
+ eudiplo-config:
161
+ driver: local
162
+ eudiplo-uploads:
163
+ driver: local
164
+ postgres-data:
165
+ driver: local
166
+ minio-data:
167
+ driver: local
168
+ vault-data:
169
+ driver: local
170
+ vault-logs:
171
+ driver: local