DodoApp: Support NodeJS backend

Change-Id: Ia8d407d009e4d690febfbf4f1270f3baa2d3e781
diff --git a/apps/app-runner/Dockerfile.nodejs.23.1.0 b/apps/app-runner/Dockerfile.nodejs.23.1.0
new file mode 100644
index 0000000..6bb9cb8
--- /dev/null
+++ b/apps/app-runner/Dockerfile.nodejs.23.1.0
@@ -0,0 +1,12 @@
+FROM library/node:23.1.0-alpine3.19
+
+ARG TARGETARCH
+
+# COPY --from=denoland/deno:alpine-2.0.0 /bin/deno /usr/bin/deno
+
+# RUN apk add --update npm
+
+# RUN apt-get update -y
+# RUN apt-get install -y npm
+
+COPY app-runner_${TARGETARCH} /usr/bin/app-runner
diff --git a/apps/app-runner/Makefile b/apps/app-runner/Makefile
index 2bf49c2..9c76326 100644
--- a/apps/app-runner/Makefile
+++ b/apps/app-runner/Makefile
@@ -6,6 +6,7 @@
 manifest_dest_hugo_latest=docker://docker.io/$(repo_name)/app-runner:hugo-latest
 manifest_dest_php_8_2_apache=docker://docker.io/$(repo_name)/app-runner:php-8.2-apache
 manifest_dest_nextjs_deno_2_0_0=docker://docker.io/$(repo_name)/app-runner:nextjs-deno-2.0.0
+manifest_dest_nodejs_23_1_0=docker://docker.io/$(repo_name)/app-runner:nodejs-23.1.0
 endif
 
 clean:
@@ -100,5 +101,20 @@
 	$(podman) manifest push $(repo_name)/app-runner:nextjs-deno-2.0.0 $(manifest_dest_nextjs_deno_2_0_0)
 	$(podman) manifest rm $(repo_name)/app-runner:nextjs-deno-2.0.0
 
+# Nextjs
+
+push_nodejs_23_1_0_arm64: clean build_arm64
+	$(podman) build --platform linux/arm64 --tag=$(repo_name)/app-runner:nodejs-23.1.0-arm64 -f Dockerfile.nodejs.23.1.0 .
+	$(podman) push $(repo_name)/app-runner:nodejs-23.1.0-arm64
+
+push_nodejs_23_1_0_amd64: clean build_amd64
+	$(podman) build --platform linux/amd64 --tag=$(repo_name)/app-runner:nodejs-23.1.0-amd64 -f Dockerfile.nodejs.23.1.0 .
+	$(podman) push $(repo_name)/app-runner:nodejs-23.1.0-amd64
+
+push_nodejs_23_1_0: push_nodejs_23_1_0_arm64 push_nodejs_23_1_0_amd64
+	$(podman) manifest create $(repo_name)/app-runner:nodejs-23.1.0 $(repo_name)/app-runner:nodejs-23.1.0-arm64 $(repo_name)/app-runner:nodejs-23.1.0-amd64
+	$(podman) manifest push $(repo_name)/app-runner:nodejs-23.1.0 $(manifest_dest_nodejs_23_1_0)
+	$(podman) manifest rm $(repo_name)/app-runner:nodejs-23.1.0
+
 # all
-push: push_golang_1_22_0 push_golang_1_20_0 push_hugo push_php_8_2_apache push_nextjs_deno_2_0_0
+push: push_golang_1_22_0 push_golang_1_20_0 push_hugo push_php_8_2_apache push_nextjs_deno_2_0_0 push_nodejs_23_1_0
diff --git a/core/installer/app_configs/dodo_app.cue b/core/installer/app_configs/dodo_app.cue
index 98adcd0..76b0220 100644
--- a/core/installer/app_configs/dodo_app.cue
+++ b/core/installer/app_configs/dodo_app.cue
@@ -277,7 +277,55 @@
 
 #NextjsApp: #NextjsDeno2AppTmpl
 
-#App: #GoApp | #HugoApp | #PHPApp | #NextjsApp
+// NodeJS
+
+#NodeJSAppTmpl: #AppTmpl & {
+	buildPath: string | *"dist"
+	apiPort: 2000
+	ports: [{
+		name: "web"
+		value: 3000
+	}]
+	rootDir: _appDir
+	preBuildCommands: [...#Command] | *[]
+
+	lastCmdEnv: [...string]
+
+	runConfiguration: [{
+		bin: "/usr/local/bin/npm",
+		args: ["ci"]
+	},
+	for c in preBuildCommands {
+		{
+			bin: c.bin
+			args: c.args
+			env: list.Concat([lastCmdEnv, c.env])
+		}
+	}
+	{
+		bin: "/usr/local/bin/npm",
+		args: ["run", "build"]
+		env: lastCmdEnv
+	}, {
+		bin: "/usr/local/bin/node",
+		args: ["./\(buildPath)/index.js"]
+		env: lastCmdEnv
+	}]
+
+	vm: {
+		cloudInit: runCmd: [
+			["sh", "-c", "apt-get install -y npm nodejs"],
+        ]
+	}
+}
+
+#NodeJS2310: #NodeJSAppTmpl & {
+	type: "nodejs-23.1.0"
+}
+
+#NodeJSApp: #NodeJS2310
+
+#App: #GoApp | #HugoApp | #PHPApp | #NextjsApp | #NodeJSApp
 
 app: #App
 _app: app
diff --git a/core/installer/server/dodo-app/schemas/app.schema.json b/core/installer/server/dodo-app/schemas/app.schema.json
index f400c17..55b9941 100644
--- a/core/installer/server/dodo-app/schemas/app.schema.json
+++ b/core/installer/server/dodo-app/schemas/app.schema.json
@@ -17,6 +17,9 @@
         },
         {
           "$ref": "#/definitions/nextjs-deno2"
+        },
+        {
+          "$ref": "#/definitions/nodejs"
         }
       ]
     }
@@ -150,6 +153,41 @@
         "type"
       ]
     },
+    "nodejs": {
+      "type": "object",
+      "properties": {
+        "type": {
+          "type": "string",
+          "oneOf": [
+            {
+              "const": "nodejs-23.1.0"
+            }
+          ]
+        },
+        "ingress": {
+          "$ref": "#/definitions/ingress"
+        },
+        "volumes": {
+          "$ref": "#/definitions/volumes"
+        },
+        "postgresql": {
+          "$ref": "#/definitions/postgresql"
+        },
+        "mongodb": {
+          "$ref": "#/definitions/mongodb"
+        },
+        "dev": {
+          "$ref": "#/definitions/dev"
+        },
+        "preBuildCommands": {
+          "$ref": "#/definitions/commands"
+        }
+      },
+      "additionalProperties": false,
+      "required": [
+        "type"
+      ]
+    },
     "dev": {
       "type": "object",
       "properties": {