feat: self host packaging (HBE-166) (#41)

Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
This commit is contained in:
Ankit Sridhar
2023-04-04 03:17:18 +05:30
committed by GitHub
parent 71e1ada641
commit 8bdb9a657f
22 changed files with 5150 additions and 5152 deletions

View File

@@ -1,13 +1,17 @@
FROM node:lts
FROM node:18.8.0 AS builder
WORKDIR /usr/src/app
# # Install pnpm
RUN npm i -g pnpm
COPY .env .
COPY pnpm-lock.yaml .
RUN pnpm fetch
ENV APP_PORT=${PORT}
ENV DB_URL=${DATABASE_URL}
# # PNPM package install
COPY ./packages/hoppscotch-backend .
RUN pnpm i --filter hoppscotch-backend
@@ -15,12 +19,20 @@ RUN pnpm i --filter hoppscotch-backend
# Prisma bits
RUN pnpm exec prisma generate
FROM builder AS dev
EXPOSE 3170
EXPOSE 9229
ENV APP_PORT=${PORT}
ENV DB_URL=${DATABASE_URL}
ENV PRODUCTION=true
ENV PRODUCTION = false
CMD ["pnpm", "run", "start"]
EXPOSE 3170
FROM builder AS prod
ENV PRODUCTION = true
CMD ["pnpm", "run", "start:prod"]
EXPOSE 3170

View File

@@ -1,30 +0,0 @@
version: '3.0'
services:
local:
build:
dockerfile: packages/hoppscotch-backend/Dockerfile
context: ../../
env_file:
- .env
command: [ "pnpm", "run", "start:dev" ]
environment:
- PRODUCTION=false
- DATABASE_URL=postgresql://postgres:testpass@dev-db:5432/hoppscotch?connect_timeout=300
- PORT=3000
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules/
depends_on:
- dev-db
ports:
- "3170:3000"
- "9229:9229"
dev-db:
image: postgres
ports:
- "5432:5432"
environment:
POSTGRES_PASSWORD: testpass
POSTGRES_DB: hoppscotch

View File

@@ -1,6 +1,6 @@
{
"name": "hoppscotch-backend",
"version": "0.0.1",
"version": "2023.4.0",
"description": "",
"author": "",
"private": true,

View File

@@ -76,7 +76,7 @@ export class AdminService {
template: 'code-your-own',
variables: {
inviteeEmail: inviteeEmail,
magicLink: `${process.env.APP_DOMAIN}`,
magicLink: `${process.env.VITE_BASE_URL}`,
},
});
} catch (e) {

View File

@@ -94,9 +94,9 @@ export class AuthService {
*/
private async generateRefreshToken(userUid: string) {
const refreshTokenPayload: RefreshTokenPayload = {
iss: process.env.APP_DOMAIN,
iss: process.env.VITE_BASE_URL,
sub: userUid,
aud: [process.env.APP_DOMAIN],
aud: [process.env.VITE_BASE_URL],
};
const refreshToken = await this.jwtService.sign(refreshTokenPayload, {
@@ -126,9 +126,9 @@ export class AuthService {
*/
async generateAuthTokens(userUid: string) {
const accessTokenPayload: AccessTokenPayload = {
iss: process.env.APP_DOMAIN,
iss: process.env.VITE_BASE_URL,
sub: userUid,
aud: [process.env.APP_DOMAIN],
aud: [process.env.VITE_BASE_URL],
};
const refreshToken = await this.generateRefreshToken(userUid);
@@ -217,7 +217,7 @@ export class AuthService {
template: 'code-your-own',
variables: {
inviteeEmail: email,
magicLink: `${process.env.APP_DOMAIN}/magic-link?token=${generatedTokens.token}`,
magicLink: `${process.env.VITE_BASE_URL}/magic-link?token=${generatedTokens.token}`,
},
});

View File

@@ -21,6 +21,12 @@ import { UserSettingsResolver } from './user-settings/user-settings.resolver';
import { UserResolver } from './user/user.resolver';
import { Logger } from '@nestjs/common';
import { AdminResolver } from './admin/admin.resolver';
import { TeamEnvsTeamResolver } from './team-environments/team.resolver';
import { TeamTeamInviteExtResolver } from './team-invitation/team-teaminvite-ext.resolver';
import { UserRequestUserCollectionResolver } from './user-request/resolvers/user-collection.resolver';
import { UserEnvsUserResolver } from './user-environment/user.resolver';
import { UserHistoryUserResolver } from './user-history/user.resolver';
import { UserSettingsUserResolver } from './user-settings/user.resolver';
/**
* All the resolvers present in the application.
@@ -31,18 +37,25 @@ const RESOLVERS = [
AdminResolver,
ShortcodeResolver,
TeamResolver,
TeamEnvsTeamResolver,
TeamMemberResolver,
TeamCollectionResolver,
TeamTeamInviteExtResolver,
TeamEnvironmentsResolver,
TeamEnvsTeamResolver,
TeamInvitationResolver,
TeamRequestResolver,
UserResolver,
UserCollectionResolver,
UserEnvironmentsResolver,
UserEnvsUserResolver,
UserHistoryUserResolver,
UserHistoryResolver,
UserCollectionResolver,
UserRequestResolver,
UserRequestUserCollectionResolver,
UserSettingsResolver,
UserSettingsUserResolver,
];
/**

View File

@@ -27,6 +27,8 @@ declare module '@vue/runtime-core' {
AppShortcutsPrompt: typeof import('./components/app/ShortcutsPrompt.vue')['default']
AppSidenav: typeof import('./components/app/Sidenav.vue')['default']
AppSupport: typeof import('./components/app/Support.vue')['default']
ButtonPrimary: typeof import('./../../hoppscotch-ui/src/components/button/Primary.vue')['default']
ButtonSecondary: typeof import('./../../hoppscotch-ui/src/components/button/Secondary.vue')['default']
Collections: typeof import('./components/collections/index.vue')['default']
CollectionsAdd: typeof import('./components/collections/Add.vue')['default']
CollectionsAddFolder: typeof import('./components/collections/AddFolder.vue')['default']
@@ -72,25 +74,6 @@ declare module '@vue/runtime-core' {
History: typeof import('./components/history/index.vue')['default']
HistoryGraphqlCard: typeof import('./components/history/graphql/Card.vue')['default']
HistoryRestCard: typeof import('./components/history/rest/Card.vue')['default']
HoppButtonPrimary: typeof import('@hoppscotch/ui')['HoppButtonPrimary']
HoppButtonSecondary: typeof import('@hoppscotch/ui')['HoppButtonSecondary']
HoppSmartAnchor: typeof import('@hoppscotch/ui')['HoppSmartAnchor']
HoppSmartAutoComplete: typeof import('@hoppscotch/ui')['HoppSmartAutoComplete']
HoppSmartCheckbox: typeof import('@hoppscotch/ui')['HoppSmartCheckbox']
HoppSmartConfirmModal: typeof import('@hoppscotch/ui')['HoppSmartConfirmModal']
HoppSmartExpand: typeof import('@hoppscotch/ui')['HoppSmartExpand']
HoppSmartFileChip: typeof import('@hoppscotch/ui')['HoppSmartFileChip']
HoppSmartItem: typeof import('@hoppscotch/ui')['HoppSmartItem']
HoppSmartLink: typeof import('@hoppscotch/ui')['HoppSmartLink']
HoppSmartModal: typeof import('@hoppscotch/ui')['HoppSmartModal']
HoppSmartProgressRing: typeof import('@hoppscotch/ui')['HoppSmartProgressRing']
HoppSmartRadioGroup: typeof import('@hoppscotch/ui')['HoppSmartRadioGroup']
HoppSmartSlideOver: typeof import('@hoppscotch/ui')['HoppSmartSlideOver']
HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner']
HoppSmartTab: typeof import('@hoppscotch/ui')['HoppSmartTab']
HoppSmartTabs: typeof import('@hoppscotch/ui')['HoppSmartTabs']
HoppSmartWindow: typeof import('@hoppscotch/ui')['HoppSmartWindow']
HoppSmartWindows: typeof import('@hoppscotch/ui')['HoppSmartWindows']
HttpAuthorization: typeof import('./components/http/Authorization.vue')['default']
HttpAuthorizationApiKey: typeof import('./components/http/authorization/ApiKey.vue')['default']
HttpAuthorizationBasic: typeof import('./components/http/authorization/Basic.vue')['default']
@@ -116,18 +99,6 @@ declare module '@vue/runtime-core' {
HttpTestResultReport: typeof import('./components/http/TestResultReport.vue')['default']
HttpTests: typeof import('./components/http/Tests.vue')['default']
HttpURLEncodedParams: typeof import('./components/http/URLEncodedParams.vue')['default']
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
IconLucideCheckCircle: typeof import('~icons/lucide/check-circle')['default']
IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
IconLucideGlobe: typeof import('~icons/lucide/globe')['default']
IconLucideHelpCircle: typeof import('~icons/lucide/help-circle')['default']
IconLucideInbox: typeof import('~icons/lucide/inbox')['default']
IconLucideInfo: typeof import('~icons/lucide/info')['default']
IconLucideLayers: typeof import('~icons/lucide/layers')['default']
IconLucideMinus: typeof import('~icons/lucide/minus')['default']
IconLucideSearch: typeof import('~icons/lucide/search')['default']
IconLucideUser: typeof import('~icons/lucide/user')['default']
IconLucideUsers: typeof import('~icons/lucide/users')['default']
LensesHeadersRenderer: typeof import('./components/lenses/HeadersRenderer.vue')['default']
LensesHeadersRendererEntry: typeof import('./components/lenses/HeadersRendererEntry.vue')['default']
LensesRenderersHTMLLensRenderer: typeof import('./components/lenses/renderers/HTMLLensRenderer.vue')['default']
@@ -147,12 +118,32 @@ declare module '@vue/runtime-core' {
RealtimeLogEntry: typeof import('./components/realtime/LogEntry.vue')['default']
RealtimeSubscription: typeof import('./components/realtime/Subscription.vue')['default']
SmartAccentModePicker: typeof import('./components/smart/AccentModePicker.vue')['default']
SmartAnchor: typeof import('./../../hoppscotch-ui/src/components/smart/Anchor.vue')['default']
SmartAutoComplete: typeof import('./../../hoppscotch-ui/src/components/smart/AutoComplete.vue')['default']
SmartChangeLanguage: typeof import('./components/smart/ChangeLanguage.vue')['default']
SmartCheckbox: typeof import('./../../hoppscotch-ui/src/components/smart/Checkbox.vue')['default']
SmartColorModePicker: typeof import('./components/smart/ColorModePicker.vue')['default']
SmartConfirmModal: typeof import('./../../hoppscotch-ui/src/components/smart/ConfirmModal.vue')['default']
SmartEnvInput: typeof import('./components/smart/EnvInput.vue')['default']
SmartExpand: typeof import('./../../hoppscotch-ui/src/components/smart/Expand.vue')['default']
SmartFileChip: typeof import('./../../hoppscotch-ui/src/components/smart/FileChip.vue')['default']
SmartFontSizePicker: typeof import('./components/smart/FontSizePicker.vue')['default']
SmartIntersection: typeof import('./../../hoppscotch-ui/src/components/smart/Intersection.vue')['default']
SmartItem: typeof import('./../../hoppscotch-ui/src/components/smart/Item.vue')['default']
SmartLink: typeof import('./../../hoppscotch-ui/src/components/smart/Link.vue')['default']
SmartModal: typeof import('./../../hoppscotch-ui/src/components/smart/Modal.vue')['default']
SmartProgressRing: typeof import('./../../hoppscotch-ui/src/components/smart/ProgressRing.vue')['default']
SmartRadio: typeof import('./../../hoppscotch-ui/src/components/smart/Radio.vue')['default']
SmartRadioGroup: typeof import('./../../hoppscotch-ui/src/components/smart/RadioGroup.vue')['default']
SmartSlideOver: typeof import('./../../hoppscotch-ui/src/components/smart/SlideOver.vue')['default']
SmartSpinner: typeof import('./../../hoppscotch-ui/src/components/smart/Spinner.vue')['default']
SmartTab: typeof import('./../../hoppscotch-ui/src/components/smart/Tab.vue')['default']
SmartTabs: typeof import('./../../hoppscotch-ui/src/components/smart/Tabs.vue')['default']
SmartToggle: typeof import('./../../hoppscotch-ui/src/components/smart/Toggle.vue')['default']
SmartTree: typeof import('./components/smart/Tree.vue')['default']
SmartTreeBranch: typeof import('./components/smart/TreeBranch.vue')['default']
SmartWindow: typeof import('./../../hoppscotch-ui/src/components/smart/Window.vue')['default']
SmartWindows: typeof import('./../../hoppscotch-ui/src/components/smart/Windows.vue')['default']
TabPrimary: typeof import('./components/tab/Primary.vue')['default']
TabSecondary: typeof import('./components/tab/Secondary.vue')['default']
Teams: typeof import('./components/teams/index.vue')['default']

View File

@@ -1,4 +1,4 @@
query GetCollectionChildren($collectionID: ID!, $cursor: String) {
query GetCollectionChildren($collectionID: ID!, $cursor: ID) {
collection(collectionID: $collectionID) {
children(cursor: $cursor) {
id

View File

@@ -1,4 +1,4 @@
query GetCollectionChildrenIDs($collectionID: ID!, $cursor: String) {
query GetCollectionChildrenIDs($collectionID: ID!, $cursor: ID) {
collection(collectionID: $collectionID) {
children(cursor: $cursor) {
id

View File

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

View File

@@ -0,0 +1,21 @@
# Initial stage, just build the app
FROM node:lts as builder
WORKDIR /usr/src/app
RUN npm i -g pnpm
COPY . .
RUN pnpm install
WORKDIR /usr/src/app/packages/hoppscotch-selfhost-web/
RUN pnpm run build
# Final stage, take the build artifacts and package it into a static Caddy server
FROM caddy:2-alpine
WORKDIR /site
COPY packages/hoppscotch-selfhost-web/Caddyfile /etc/caddy/Caddyfile
COPY --from=builder /usr/src/app/packages/hoppscotch-selfhost-web/dist/ .
EXPOSE 8080

View File

@@ -37,6 +37,9 @@ export default defineConfig({
build: {
sourcemap: true,
emptyOutDir: true,
rollupOptions: {
maxParallelFileOps: 2,
},
},
resolve: {
alias: {

View File

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

View File

@@ -0,0 +1,21 @@
# Initial stage, just build the app
FROM node:lts as builder
WORKDIR /usr/src/app
RUN npm i -g pnpm
COPY . .
RUN pnpm install
WORKDIR /usr/src/app/packages/hoppscotch-sh-admin/
RUN pnpm run build
# Final stage, take the build artifacts and package it into a static Caddy server
FROM caddy:2-alpine
WORKDIR /site
COPY packages/hoppscotch-sh-admin/Caddyfile /etc/caddy/Caddyfile
COPY --from=builder /usr/src/app/packages/hoppscotch-sh-admin/dist/ .
EXPOSE 8080

View File

@@ -6,8 +6,10 @@
"scripts": {
"dev": "pnpm exec npm-run-all -p -l dev:*",
"dev:vite": "vite",
"dev:gql-codegen": "graphql-codegen --require dotenv/config --config gql-codegen.yml --watch dotenv_config_path=\".env\"",
"build": "vue-tsc && vite build",
"dev:gql-codegen": "graphql-codegen --require dotenv/config --config gql-codegen.yml --watch dotenv_config_path=\"../../.env\"",
"gql-codegen": "graphql-codegen --require dotenv/config --config gql-codegen.yml dotenv_config_path=\"../../.env\"",
"postinstall": "pnpm run gql-codegen",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {

View File

@@ -22,6 +22,24 @@ declare module '@vue/runtime-core' {
HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner']
IconLucideInbox: typeof import('~icons/lucide/inbox')['default']
IconLucideUser: typeof import('~icons/lucide/user')['default']
HoppSmartModal: typeof import('@hoppscotch/ui')['HoppSmartModal']
HoppSmartSpinner: typeof import('@hoppscotch/ui')['HoppSmartSpinner']
IconLucideArrowLeft: typeof import('~icons/lucide/arrow-left')['default']
IconLucideChevronDown: typeof import('~icons/lucide/chevron-down')['default']
IconLucideChevronLeft: typeof import('~icons/lucide/chevron-left')['default']
IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
IconLucideEdit: typeof import('~icons/lucide/edit')['default']
IconLucideFolderTree: typeof import('~icons/lucide/folder-tree')['default']
IconLucideInbox: typeof import('~icons/lucide/inbox')['default']
IconLucideLayoutDashboard: typeof import('~icons/lucide/layout-dashboard')['default']
IconLucideLineChart: typeof import('~icons/lucide/line-chart')['default']
IconLucideMenu: typeof import('~icons/lucide/menu')['default']
IconLucideMoreHorizontal: typeof import('~icons/lucide/more-horizontal')['default']
IconLucideSidebarClose: typeof import('~icons/lucide/sidebar-close')['default']
IconLucideSidebarOpen: typeof import('~icons/lucide/sidebar-open')['default']
IconLucideUser: typeof import('~icons/lucide/user')['default']
IconLucideUserCog: typeof import('~icons/lucide/user-cog')['default']
IconLucideUsers: typeof import('~icons/lucide/users')['default']
ProfilePicture: typeof import('./components/profile/Picture.vue')['default']
TeamsAddMembers: typeof import('./components/teams/AddMembers.vue')['default']
TeamsDetails: typeof import('./components/teams/Details.vue')['default']

View File

@@ -12,22 +12,25 @@ import '../assets/scss/styles.scss';
import { HOPP_MODULES } from './modules';
import { auth } from './helpers/auth';
const app = createApp(App).use(
urql,
createClient({
url: import.meta.env.VITE_BACKEND_GQL_URL,
fetchOptions: () => {
return {
credentials: 'include',
};
},
})
);
// Top-level await is not available in our targets
(async () => {
const app = createApp(App).use(
urql,
createClient({
url: import.meta.env.VITE_BACKEND_GQL_URL,
fetchOptions: () => {
return {
credentials: 'include',
};
},
})
);
// Initialize auth
await auth.performAuthInit();
// Initialize auth
await auth.performAuthInit();
// Initialize modules
HOPP_MODULES.forEach((mod) => mod.onVueAppInit?.(app));
// Initialize modules
HOPP_MODULES.forEach((mod) => mod.onVueAppInit?.(app));
app.mount('#app');
app.mount('#app');
})()

View File

@@ -11,6 +11,7 @@ import path from 'path';
// https://vitejs.dev/config/
export default defineConfig({
envDir: path.resolve(__dirname, "../../"),
server: {
port: 3000,
},