Browse Source

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

Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
Ankit Sridhar 1 year ago
parent
commit
8bdb9a657f

+ 0 - 3
.dockerignore

@@ -64,9 +64,6 @@ typings/
 # Yarn Integrity file
 .yarn-integrity
 
-# dotenv environment variables file
-.env
-
 # parcel-bundler cache (https://parceljs.org/)
 .cache
 

+ 52 - 27
.env.example

@@ -1,32 +1,57 @@
-# Google Analytics ID
-VITE_GA_ID=UA-61422507-4
-
-# Google Tag Manager ID
-VITE_GTM_ID=GTM-NMKVBMV
-
-# Firebase config
-VITE_API_KEY=AIzaSyCMsFreESs58-hRxTtiqQrIcimh4i1wbsM
-VITE_AUTH_DOMAIN=postwoman-api.firebaseapp.com
-VITE_DATABASE_URL=https://postwoman-api.firebaseio.com
-VITE_PROJECT_ID=postwoman-api
-VITE_STORAGE_BUCKET=postwoman-api.appspot.com
-VITE_MESSAGING_SENDER_ID=421993993223
-VITE_APP_ID=1:421993993223:web:ec0baa8ee8c02ffa1fc6a2
-VITE_MEASUREMENT_ID=G-BBJ3R80PJT
+#-----------------------Backend Config------------------------------#
+# Prisma Config
+DATABASE_URL=postgresql://postgres:testpass@hoppscotch-db:5432/hoppscotch
+
+# Auth Tokens Config
+SIGNED_COOKIE_SECRET = "supersecretcode"
+JWT_SECRET="secret1233"
+TOKEN_SALT_COMPLEXITY=10
+MAGIC_LINK_TOKEN_VALIDITY= 3
+REFRESH_TOKEN_VALIDITY="604800000" # Default validity is 7 days (604800000 ms) in ms
+ACCESS_TOKEN_VALIDITY="86400000" # Default validity is 1 day (86400000 ms) in ms
+SESSION_SECRET='add some secret here'
+
+# Hoppscotch App Domain Config
+APP_DOMAIN="http://localhost:3170"
+REDIRECT_URL="http://localhost:3170/graphql"
+WHITELISTED_ORIGINS = "http://localhost:3170,http://localhost:3000"
+
+# Google Auth Config
+GOOGLE_CLIENT_ID="************************************************"
+GOOGLE_CLIENT_SECRET="************************************************"
+GOOGLE_CALLBACK_URL="************************************************"
+GOOGLE_SCOPE="['email', 'profile'],"
+
+# Github Auth Config
+GITHUB_CLIENT_ID="************************************************"
+GITHUB_CLIENT_SECRET="************************************************"
+GITHUB_CALLBACK_URL="************************************************"
+GITHUB_SCOPE="user:email"
+
+# Microsoft Auth Config
+MICROSOFT_CLIENT_ID="************************************************"
+MICROSOFT_CLIENT_SECRET="************************************************"
+MICROSOFT_CALLBACK_URL="************************************************"
+MICROSOFT_SCOPE="user.read"
+
+# Mailer config
+MAILER_SMTP_URL="smtps://user@domain.com:pass@smtp.domain.com"
+MAILER_ADDRESS_FROM='"From Name Here" <from@example.com>'
+
+# Rate Limit Config
+RATE_LIMIT_TTL=60 # In seconds
+RATE_LIMIT_MAX=100 # Max requests per IP
+
+
+#-----------------------Frontend Config------------------------------#
+
 
 # Base URLs
-VITE_BASE_URL=https://hoppscotch.io
-VITE_SHORTCODE_BASE_URL=https://hopp.sh
+VITE_BASE_URL=http://localhost:3000
+VITE_SHORTCODE_BASE_URL=http://localhost:3000
 
 # Backend URLs
-VITE_BACKEND_GQL_URL=https://api.hoppscotch.io/graphql
-VITE_BACKEND_WS_URL=wss://api.hoppscotch.io/graphql
-VITE_BACKEND_API_URL=https://api.hoppscotch.io/v1
-
-# Sentry (Optional)
-# VITE_SENTRY_DSN: <Sentry DSN here>
-# VITE_SENTRY_ENVIRONMENT: <Sentry environment value here>
-# VITE_SENTRY_RELEASE_TAG: <Sentry release tag here (for release monitoring)>
+VITE_BACKEND_GQL_URL=http://localhost:3170/graphql
+VITE_BACKEND_WS_URL=wss://localhost:3170/graphql
+VITE_BACKEND_API_URL=http://localhost:3170/v1
 
-# Proxyscotch Access Token (Optional)
-# VITE_PROXYSCOTCH_ACCESS_TOKEN: <Token Set In Proxyscotch Server>

+ 63 - 15
docker-compose.yml

@@ -1,23 +1,71 @@
+# To make it easier to self-host, we have a preset docker compose config that also
+# has a container with a Postgres instance running.
+# You can tweak around this file to match your instances
 version: "3.7"
 
 services:
-  web:
+  # This service runs the backend app in the port 3170
+  hoppscotch-backend:
+    container_name: hoppscotch-backend
     build:
+      dockerfile: packages/hoppscotch-backend/Dockerfile
       context: .
+      target: prod
+    env_file:
+      - ./.env
+    restart: always
+    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)
+      - DATABASE_URL=postgresql://postgres:testpass@hoppscotch-db:5432/hoppscotch?connect_timeout=300
+      - PORT=3000
     volumes:
-      - "./.hoppscotch:/app/.hoppscotch"
-      - "./assets:/app/assets"
-      - "./directives:/app/directives"
-      - "./layouts:/app/layouts"
-      - "./middleware:/app/middleware"
-      - "./pages:/app/pages"
-      - "./plugins:/app/plugins"
-      - "./static:/app/static"
-      - "./store:/app/store"
-      - "./components:/app/components"
-      - "./helpers:/app/helpers"
+      - ./packages/hoppscotch-backend/:/usr/src/app
+      - /usr/src/app/node_modules/
+    depends_on:
+      - hoppscotch-db
     ports:
-      - "3000:3000"
+      - "3170:3000"
+
+  # The main hoppscotch app. This will be hosted at port 3000
+  # NOTE: To do TLS or play around with how the app is hosted, you can look into the Caddyfile for
+  #       the SH admin dashboard server at packages/hoppscotch-selfhost-web/Caddyfile
+  hoppscotch-app:
+    container_name: hoppscotch-app
+    build:
+      dockerfile: packages/hoppscotch-selfhost-web/Dockerfile
+      context: .
+    env_file:
+      - ./.env
+    depends_on:
+      - hoppscotch-backend
+    ports:
+      - "3000:8080"
+
+  # 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
+  #       the SH admin dashboard server at packages/hoppscotch-sh-admin/Caddyfile
+  hoppscotch-sh-admin:
+    container_name: hoppscotch-sh-admin
+    build:
+      dockerfile: packages/hoppscotch-sh-admin/Dockerfile
+      context: .
+    env_file:
+      - ./.env
+    depends_on:
+      - hoppscotch-backend
+    ports:
+      - "3100:8080"
+
+  # The preset DB service, you can delete/comment the below lines if
+  # you are using an external postgres instance
+  # This will be exposed at port 5432
+  hoppscotch-db:
+    image: postgres
+    ports:
+      - "5432:5432"
     environment:
-      HOST: 0.0.0.0
-    command: "pnpm run dev"
+      # NOTE: Please UPDATE THIS PASSWORD!
+      POSTGRES_PASSWORD: testpass
+      POSTGRES_DB: hoppscotch
+
+

+ 18 - 6
packages/hoppscotch-backend/Dockerfile

@@ -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
+
+ENV PRODUCTION = false
+
+CMD ["pnpm", "run", "start"]
 
 EXPOSE 3170
-EXPOSE 9229
 
-ENV APP_PORT=${PORT}
-ENV DB_URL=${DATABASE_URL}
-ENV PRODUCTION=true
 
-CMD ["pnpm", "run", "start"]
+FROM builder AS prod
+
+ENV PRODUCTION = true
+
+CMD ["pnpm", "run", "start:prod"]
+
+EXPOSE 3170
+

+ 0 - 30
packages/hoppscotch-backend/docker-compose.yml

@@ -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

+ 1 - 1
packages/hoppscotch-backend/package.json

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

+ 1 - 1
packages/hoppscotch-backend/src/admin/admin.service.ts

@@ -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) {

+ 5 - 5
packages/hoppscotch-backend/src/auth/auth.service.ts

@@ -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}`,
       },
     });
 

+ 13 - 0
packages/hoppscotch-backend/src/gql-schema.ts

@@ -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,
 ];
 
 /**

+ 22 - 31
packages/hoppscotch-common/src/components.d.ts

@@ -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']

Some files were not shown because too many files changed in this diff