feat: support for subpath based access in SH apps (#3449)

Co-authored-by: Balu Babu <balub997@gmail.com>
This commit is contained in:
James George
2023-11-22 19:35:35 +05:30
committed by GitHub
parent ee1425d0dd
commit 1829c088cc
18 changed files with 262 additions and 48 deletions

View File

@@ -59,3 +59,6 @@ VITE_BACKEND_API_URL=http://localhost:3170/v1
# Terms Of Service And Privacy Policy Links (Optional) # Terms Of Service And Privacy Policy Links (Optional)
VITE_APP_TOS_LINK=https://docs.hoppscotch.io/support/terms VITE_APP_TOS_LINK=https://docs.hoppscotch.io/support/terms
VITE_APP_PRIVACY_POLICY_LINK=https://docs.hoppscotch.io/support/privacy VITE_APP_PRIVACY_POLICY_LINK=https://docs.hoppscotch.io/support/privacy
# Set to `true` for subpath based access
ENABLE_SUBPATH_BASED_ACCESS=false

View File

@@ -0,0 +1,19 @@
:3000 {
try_files {path} /
root * /site/selfhost-web
file_server
}
:3100 {
try_files {path} /
root * /site/sh-admin-multiport-setup
file_server
}
:3170 {
reverse_proxy localhost:8080
}
:80 {
respond 404
}

View File

@@ -0,0 +1,37 @@
:3000 {
respond 404
}
:3100 {
respond 404
}
:3170 {
reverse_proxy localhost:8080
}
:80 {
# Serve the `selfhost-web` SPA by default
root * /site/selfhost-web
file_server
handle_path /admin* {
root * /site/sh-admin-subpath-access
file_server
# Ensures any non-existent file in the server is routed to the SPA
try_files {path} /
}
# Handle requests under `/backend*` path
handle_path /backend* {
reverse_proxy localhost:8080
}
# Catch-all route for unknown paths, serves `selfhost-web` SPA
handle {
root * /site/selfhost-web
file_server
try_files {path} /
}
}

View File

@@ -1,11 +0,0 @@
:3000 {
try_files {path} /
root * /site/selfhost-web
file_server
}
:3100 {
try_files {path} /
root * /site/sh-admin
file_server
}

View File

@@ -49,7 +49,8 @@ execSync(`npx import-meta-env -x build.env -e build.env -p "/site/**/*"`)
fs.rmSync("build.env") fs.rmSync("build.env")
const caddyProcess = runChildProcessWithPrefix("caddy", ["run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"], "App/Admin Dashboard Caddy") const caddyFileName = process.env.ENABLE_SUBPATH_BASED_ACCESS === 'true' ? 'aio-subpath-access.Caddyfile' : 'aio-multiport-setup.Caddyfile'
const caddyProcess = runChildProcessWithPrefix("caddy", ["run", "--config", `/etc/caddy/${caddyFileName}`, "--adapter", "caddyfile"], "App/Admin Dashboard Caddy")
const backendProcess = runChildProcessWithPrefix("pnpm", ["run", "start:prod"], "Backend Server") const backendProcess = runChildProcessWithPrefix("pnpm", ["run", "start:prod"], "Backend Server")
caddyProcess.on("exit", (code) => { caddyProcess.on("exit", (code) => {

View File

@@ -17,7 +17,7 @@ services:
environment: environment:
# Edit the below line to match your PostgresDB URL if you have an outside DB (make sure to update the .env file as well) # Edit the below line to match your PostgresDB URL if you have an outside DB (make sure to update the .env file as well)
- DATABASE_URL=postgresql://postgres:testpass@hoppscotch-db:5432/hoppscotch?connect_timeout=300 - DATABASE_URL=postgresql://postgres:testpass@hoppscotch-db:5432/hoppscotch?connect_timeout=300
- PORT=3170 - PORT=8080
volumes: volumes:
# Uncomment the line below when modifying code. Only applicable when using the "dev" target. # Uncomment the line below when modifying code. Only applicable when using the "dev" target.
# - ./packages/hoppscotch-backend/:/usr/src/app # - ./packages/hoppscotch-backend/:/usr/src/app
@@ -26,6 +26,7 @@ services:
hoppscotch-db: hoppscotch-db:
condition: service_healthy condition: service_healthy
ports: ports:
- "3180:80"
- "3170:3170" - "3170:3170"
# The main hoppscotch app. This will be hosted at port 3000 # The main hoppscotch app. This will be hosted at port 3000
@@ -42,7 +43,8 @@ services:
depends_on: depends_on:
- hoppscotch-backend - hoppscotch-backend
ports: ports:
- "3000:8080" - "3080:80"
- "3000:3000"
# The Self Host dashboard for managing the app. This will be hosted at port 3100 # The Self Host dashboard for managing the app. This will be hosted at port 3100
# NOTE: To do TLS or play around with how the app is hosted, you can look into the Caddyfile for # NOTE: To do TLS or play around with how the app is hosted, you can look into the Caddyfile for
@@ -58,7 +60,8 @@ services:
depends_on: depends_on:
- hoppscotch-backend - hoppscotch-backend
ports: ports:
- "3100:8080" - "3280:80"
- "3100:3100"
# The service that spins up all 3 services at once in one container # The service that spins up all 3 services at once in one container
hoppscotch-aio: hoppscotch-aio:
@@ -76,6 +79,7 @@ services:
- "3000:3000" - "3000:3000"
- "3100:3100" - "3100:3100"
- "3170:3170" - "3170:3170"
- "3080:80"
# The preset DB service, you can delete/comment the below lines if # The preset DB service, you can delete/comment the below lines if
# you are using an external postgres instance # you are using an external postgres instance

View File

@@ -0,0 +1,3 @@
:80 :3170 {
reverse_proxy localhost:8080
}

View File

@@ -0,0 +1,66 @@
#!/usr/local/bin/node
// @ts-check
import { spawn } from 'child_process';
import process from 'process';
function runChildProcessWithPrefix(command, args, prefix) {
const childProcess = spawn(command, args);
childProcess.stdout.on('data', (data) => {
const output = data.toString().trim().split('\n');
output.forEach((line) => {
console.log(`${prefix} | ${line}`);
});
});
childProcess.stderr.on('data', (data) => {
const error = data.toString().trim().split('\n');
error.forEach((line) => {
console.error(`${prefix} | ${line}`);
});
});
childProcess.on('close', (code) => {
console.log(`${prefix} Child process exited with code ${code}`);
});
childProcess.on('error', (stuff) => {
console.error('error');
console.error(stuff);
});
return childProcess;
}
const caddyProcess = runChildProcessWithPrefix(
'caddy',
['run', '--config', '/etc/caddy/backend.Caddyfile', '--adapter', 'caddyfile'],
'App/Admin Dashboard Caddy',
);
const backendProcess = runChildProcessWithPrefix(
'pnpm',
['run', 'start:prod'],
'Backend Server',
);
caddyProcess.on('exit', (code) => {
console.log(`Exiting process because Caddy Server exited with code ${code}`);
process.exit(code);
});
backendProcess.on('exit', (code) => {
console.log(
`Exiting process because Backend Server exited with code ${code}`,
);
process.exit(code);
});
process.on('SIGINT', () => {
console.log('SIGINT received, exiting...');
caddyProcess.kill('SIGINT');
backendProcess.kill('SIGINT');
process.exit(0);
});

View File

@@ -1,5 +1,5 @@
:8080 { :8080 {
try_files {path} / try_files {path} /
root * /site root * /site
file_server file_server
} }

View File

@@ -0,0 +1,5 @@
:80 :3000 {
try_files {path} /
root * /site
file_server
}

View File

@@ -223,6 +223,8 @@ export default defineConfig({
/twitter/, /twitter/,
/github/, /github/,
/announcements/, /announcements/,
/admin/,
/backend/,
], ],
}, },
}), }),

View File

@@ -1,5 +1,5 @@
:8080 { :8080 {
try_files {path} / try_files {path} /
root * /site root * /site
file_server file_server
} }

View File

@@ -1,18 +1,70 @@
#!/usr/local/bin/node #!/usr/local/bin/node
import { execSync } from "child_process" import { execSync, spawn } from 'child_process';
import fs from "fs" import fs from 'fs';
import process from 'process';
function runChildProcessWithPrefix(command, args, prefix) {
const childProcess = spawn(command, args);
childProcess.stdout.on('data', (data) => {
const output = data.toString().trim().split('\n');
output.forEach((line) => {
console.log(`${prefix} | ${line}`);
});
});
childProcess.stderr.on('data', (data) => {
const error = data.toString().trim().split('\n');
error.forEach((line) => {
console.error(`${prefix} | ${line}`);
});
});
childProcess.on('close', (code) => {
console.log(`${prefix} Child process exited with code ${code}`);
});
childProcess.on('error', (stuff) => {
console.log('error');
console.log(stuff);
});
return childProcess;
}
const envFileContent = Object.entries(process.env) const envFileContent = Object.entries(process.env)
.filter(([env]) => env.startsWith("VITE_")) .filter(([env]) => env.startsWith('VITE_'))
.map(([env, val]) => `${env}=${ .map(
(val.startsWith("\"") && val.endsWith("\"")) ([env, val]) =>
? val `${env}=${val.startsWith('"') && val.endsWith('"') ? val : `"${val}"`}`
: `"${val}"` )
}`) .join('\n');
.join("\n")
fs.writeFileSync("build.env", envFileContent) fs.writeFileSync('build.env', envFileContent);
execSync(`npx import-meta-env -x build.env -e build.env -p "/site/**/*"`) execSync(`npx import-meta-env -x build.env -e build.env -p "/site/**/*"`);
fs.rmSync("build.env") fs.rmSync('build.env');
const caddyFileName =
process.env.ENABLE_SUBPATH_BASED_ACCESS === 'true'
? 'sh-admin-subpath-access.Caddyfile'
: 'sh-admin-multiport-setup.Caddyfile';
const caddyProcess = runChildProcessWithPrefix(
'caddy',
['run', '--config', `/etc/caddy/${caddyFileName}`, '--adapter', 'caddyfile'],
'App/Admin Dashboard Caddy'
);
caddyProcess.on('exit', (code) => {
console.log(`Exiting process because Caddy Server exited with code ${code}`);
process.exit(code);
});
process.on('SIGINT', () => {
console.log('SIGINT received, exiting...');
caddyProcess.kill('SIGINT');
process.exit(0);
});

View File

@@ -0,0 +1,5 @@
:80 :3100 {
try_files {path} /
root * /site/sh-admin-multiport-setup
file_server
}

View File

@@ -0,0 +1,11 @@
:80 :3100 {
handle_path /admin* {
root * /site/sh-admin-subpath-access
file_server
try_files {path} /
}
handle {
respond 404
}
}

View File

@@ -42,7 +42,7 @@ export const isLoadingInitialRoute = readonly(_isLoadingInitialRoute);
export default <HoppModule>{ export default <HoppModule>{
onVueAppInit(app) { onVueAppInit(app) {
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHistory(import.meta.env.BASE_URL),
routes, routes,
}); });

View File

@@ -6,7 +6,7 @@
:class="{ 'min-h-screen': statusCode !== 404 }" :class="{ 'min-h-screen': statusCode !== 404 }"
> >
<img <img
:src="`/images/youre_lost.svg`" :src="imgUrl"
loading="lazy" loading="lazy"
class="inline-flex flex-col object-contain object-center mb-2 h-46 w-46" class="inline-flex flex-col object-contain object-center mb-2 h-46 w-46"
:alt="message" :alt="message"
@@ -44,6 +44,8 @@ const props = defineProps({
}, },
}); });
const imgUrl = `${import.meta.env.BASE_URL}images/youre_lost.svg`
const statusCode = computed(() => props.error?.statusCode ?? 404); const statusCode = computed(() => props.error?.statusCode ?? 404);
const message = computed( const message = computed(

View File

@@ -13,16 +13,19 @@ RUN pnpm install -f --offline
FROM base_builder as backend FROM base_builder as backend
RUN apk add caddy
WORKDIR /usr/src/app/packages/hoppscotch-backend WORKDIR /usr/src/app/packages/hoppscotch-backend
RUN pnpm exec prisma generate RUN pnpm exec prisma generate
RUN pnpm run build RUN pnpm run build
COPY --from=base_builder /usr/src/app/packages/hoppscotch-backend/backend.Caddyfile /etc/caddy/backend.Caddyfile
# Remove the env file to avoid backend copying it in and using it # Remove the env file to avoid backend copying it in and using it
RUN rm "../../.env" RUN rm "../../.env"
ENV PRODUCTION="true" ENV PRODUCTION="true"
ENV PORT=3170 ENV PORT=8080
ENV APP_PORT=${PORT} ENV APP_PORT=${PORT}
ENV DB_URL=${DATABASE_URL} ENV DB_URL=${DATABASE_URL}
CMD ["pnpm", "run", "start:prod"] CMD ["node", "/usr/src/app/packages/hoppscotch-backend/prod_run.mjs"]
EXPOSE 80
EXPOSE 3170 EXPOSE 3170
FROM base_builder as fe_builder FROM base_builder as fe_builder
@@ -31,34 +34,42 @@ RUN pnpm run generate
FROM caddy:2-alpine as app FROM caddy:2-alpine as app
WORKDIR /site WORKDIR /site
COPY --from=fe_builder /usr/src/app/packages/hoppscotch-sh-admin/prod_run.mjs /usr COPY --from=fe_builder /usr/src/app/packages/hoppscotch-selfhost-web/prod_run.mjs /usr
COPY --from=fe_builder /usr/src/app/packages/hoppscotch-selfhost-web/Caddyfile /etc/caddy/Caddyfile COPY --from=fe_builder /usr/src/app/packages/hoppscotch-selfhost-web/selfhost-web.Caddyfile /etc/caddy/selfhost-web.Caddyfile
COPY --from=fe_builder /usr/src/app/packages/hoppscotch-selfhost-web/dist/ . COPY --from=fe_builder /usr/src/app/packages/hoppscotch-selfhost-web/dist/ .
RUN apk add nodejs npm RUN apk add nodejs npm
RUN npm install -g @import-meta-env/cli RUN npm install -g @import-meta-env/cli
EXPOSE 8080 EXPOSE 80
CMD ["/bin/sh", "-c", "node /usr/prod_run.mjs && caddy run --config /etc/caddy/Caddyfile --adapter caddyfile"] EXPOSE 3000
CMD ["/bin/sh", "-c", "node /usr/prod_run.mjs && caddy run --config /etc/caddy/selfhost-web.Caddyfile --adapter caddyfile"]
FROM base_builder as sh_admin_builder FROM base_builder as sh_admin_builder
WORKDIR /usr/src/app/packages/hoppscotch-sh-admin WORKDIR /usr/src/app/packages/hoppscotch-sh-admin
RUN pnpm run build # Generate two builds for `sh-admin`, one based on subpath-access and the regular build
RUN pnpm run build --outDir dist-multiport-setup
RUN pnpm run build --outDir dist-subpath-access --base /admin/
FROM caddy:2-alpine as sh_admin FROM caddy:2-alpine as sh_admin
WORKDIR /site WORKDIR /site
COPY --from=sh_admin_builder /usr/src/app/packages/hoppscotch-sh-admin/prod_run.mjs /usr COPY --from=sh_admin_builder /usr/src/app/packages/hoppscotch-sh-admin/prod_run.mjs /usr
COPY --from=sh_admin_builder /usr/src/app/packages/hoppscotch-sh-admin/Caddyfile /etc/caddy/Caddyfile COPY --from=sh_admin_builder /usr/src/app/packages/hoppscotch-sh-admin/sh-admin-multiport-setup.Caddyfile /etc/caddy/sh-admin-multiport-setup.Caddyfile
COPY --from=sh_admin_builder /usr/src/app/packages/hoppscotch-sh-admin/dist/ . COPY --from=sh_admin_builder /usr/src/app/packages/hoppscotch-sh-admin/sh-admin-subpath-access.Caddyfile /etc/caddy/sh-admin-subpath-access.Caddyfile
COPY --from=sh_admin_builder /usr/src/app/packages/hoppscotch-sh-admin/dist-multiport-setup /site/sh-admin-multiport-setup
COPY --from=sh_admin_builder /usr/src/app/packages/hoppscotch-sh-admin/dist-subpath-access /site/sh-admin-subpath-access
RUN apk add nodejs npm RUN apk add nodejs npm
RUN npm install -g @import-meta-env/cli RUN npm install -g @import-meta-env/cli
EXPOSE 8080 EXPOSE 80
CMD ["/bin/sh", "-c", "node /usr/prod_run.mjs && caddy run --config /etc/caddy/Caddyfile --adapter caddyfile"] EXPOSE 3100
CMD ["node","/usr/prod_run.mjs"]
FROM backend as aio FROM backend as aio
RUN apk add caddy tini RUN apk add caddy tini
RUN npm install -g @import-meta-env/cli RUN npm install -g @import-meta-env/cli
COPY --from=fe_builder /usr/src/app/packages/hoppscotch-selfhost-web/dist /site/selfhost-web COPY --from=fe_builder /usr/src/app/packages/hoppscotch-selfhost-web/dist /site/selfhost-web
COPY --from=sh_admin_builder /usr/src/app/packages/hoppscotch-sh-admin/dist /site/sh-admin COPY --from=sh_admin_builder /usr/src/app/packages/hoppscotch-sh-admin/dist-multiport-setup /site/sh-admin-multiport-setup
COPY aio.Caddyfile /etc/caddy/Caddyfile COPY --from=sh_admin_builder /usr/src/app/packages/hoppscotch-sh-admin/dist-subpath-access /site/sh-admin-subpath-access
COPY aio-multiport-setup.Caddyfile /etc/caddy/aio-multiport-setup.Caddyfile
COPY aio-subpath-access.Caddyfile /etc/caddy/aio-subpath-access.Caddyfile
ENTRYPOINT [ "tini", "--" ] ENTRYPOINT [ "tini", "--" ]
RUN apk --no-cache add curl RUN apk --no-cache add curl
COPY --chmod=755 healthcheck.sh . COPY --chmod=755 healthcheck.sh .
@@ -67,3 +78,7 @@ CMD ["node", "/usr/src/app/aio_run.mjs"]
EXPOSE 3170 EXPOSE 3170
EXPOSE 3000 EXPOSE 3000
EXPOSE 3100 EXPOSE 3100
EXPOSE 80