committed by
GitHub
parent
f0b7526847
commit
df05e2dab3
22
self-hosting/.env.template
Normal file
22
self-hosting/.env.template
Normal file
@@ -0,0 +1,22 @@
|
||||
NODE_ENV="production"
|
||||
SELF_HOSTED="true"
|
||||
GEO_IP_HOST="http://op-geo:8080"
|
||||
NEXT_PUBLIC_CLERK_SIGN_IN_URL="/login"
|
||||
NEXT_PUBLIC_CLERK_SIGN_UP_URL="/register"
|
||||
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL="/"
|
||||
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL="/"
|
||||
BATCH_SIZE="5000"
|
||||
BATCH_INTERVAL="10000"
|
||||
# Will be replaced with the setup script
|
||||
REDIS_URL="$REDIS_URL"
|
||||
CLICKHOUSE_URL="$CLICKHOUSE_URL"
|
||||
CLICKHOUSE_DB="$CLICKHOUSE_DB"
|
||||
CLICKHOUSE_USER="$CLICKHOUSE_USER"
|
||||
CLICKHOUSE_PASSWORD="$CLICKHOUSE_PASSWORD"
|
||||
DATABASE_URL="$DATABASE_URL"
|
||||
DATABASE_URL_DIRECT="$DATABASE_URL_DIRECT"
|
||||
NEXT_PUBLIC_DASHBOARD_URL="$NEXT_PUBLIC_DASHBOARD_URL"
|
||||
NEXT_PUBLIC_API_URL="$NEXT_PUBLIC_API_URL"
|
||||
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="$NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY"
|
||||
CLERK_SECRET_KEY="$CLERK_SECRET_KEY"
|
||||
CLERK_SIGNING_SECRET="$CLERK_SIGNING_SECRET"
|
||||
3
self-hosting/.gitignore
vendored
Normal file
3
self-hosting/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.env
|
||||
docker-compose.yml
|
||||
caddy/Caddyfile
|
||||
19
self-hosting/caddy/Caddyfile.template
Normal file
19
self-hosting/caddy/Caddyfile.template
Normal file
@@ -0,0 +1,19 @@
|
||||
$DOMAIN_NAME {$SSL_CONFIG
|
||||
encode gzip
|
||||
|
||||
handle_path /api* {
|
||||
reverse_proxy op-api:3000
|
||||
}
|
||||
|
||||
reverse_proxy /* op-dashboard:3000
|
||||
}
|
||||
|
||||
worker.$DOMAIN_NAME {$SSL_CONFIG
|
||||
encode gzip
|
||||
|
||||
basic_auth {
|
||||
admin $BASIC_AUTH_PASSWORD
|
||||
}
|
||||
|
||||
reverse_proxy op-worker:3000
|
||||
}
|
||||
25
self-hosting/clickhouse/clickhouse-config.xml
Normal file
25
self-hosting/clickhouse/clickhouse-config.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<clickhouse>
|
||||
<logger>
|
||||
<level>warning</level>
|
||||
<console>true</console>
|
||||
</logger>
|
||||
|
||||
<keep_alive_timeout>10</keep_alive_timeout>
|
||||
<!--
|
||||
Avoid the warning: "Listen [::]:9009 failed: Address family for hostname not supported".
|
||||
If Docker has IPv6 disabled, bind ClickHouse to IPv4 to prevent this issue.
|
||||
Add this to the configuration to ensure it listens on all IPv4 interfaces:
|
||||
<listen_host>0.0.0.0</listen_host>
|
||||
-->
|
||||
|
||||
<!-- Stop all the unnecessary logging -->
|
||||
<query_thread_log remove="remove"/>
|
||||
<query_log remove="remove"/>
|
||||
<text_log remove="remove"/>
|
||||
<trace_log remove="remove"/>
|
||||
<metric_log remove="remove"/>
|
||||
<asynchronous_metric_log remove="remove"/>
|
||||
<session_log remove="remove"/>
|
||||
<part_log remove="remove"/>
|
||||
|
||||
</clickhouse>
|
||||
8
self-hosting/clickhouse/clickhouse-user-config.xml
Normal file
8
self-hosting/clickhouse/clickhouse-user-config.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<clickhouse>
|
||||
<profiles>
|
||||
<default>
|
||||
<log_queries>0</log_queries>
|
||||
<log_query_threads>0</log_query_threads>
|
||||
</default>
|
||||
</profiles>
|
||||
</clickhouse>
|
||||
45
self-hosting/danger_wipe_everything
Executable file
45
self-hosting/danger_wipe_everything
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Set the project name if it's not the directory name
|
||||
# COMPOSE_PROJECT_NAME=your_project_name
|
||||
|
||||
# Use the directory name as the project name if not set
|
||||
PROJECT_NAME=${COMPOSE_PROJECT_NAME:-$(basename "$(pwd)")}
|
||||
|
||||
echo "Cleaning up Docker resources for project: $PROJECT_NAME"
|
||||
|
||||
# Stop and remove containers, networks, and volumes
|
||||
echo "Stopping and removing containers, networks, and volumes..."
|
||||
docker-compose down --volumes --remove-orphans
|
||||
|
||||
# Remove any remaining project-specific volumes
|
||||
echo "Removing any remaining project volumes..."
|
||||
project_volumes=$(docker volume ls --filter name="$PROJECT_NAME" -q)
|
||||
if [ -n "$project_volumes" ]; then
|
||||
docker volume rm $project_volumes
|
||||
fi
|
||||
|
||||
# Remove project-specific images
|
||||
echo "Removing project-specific images..."
|
||||
project_images=$(docker-compose config --images)
|
||||
if [ -n "$project_images" ]; then
|
||||
docker rmi $project_images
|
||||
fi
|
||||
|
||||
# Remove any dangling images
|
||||
echo "Removing dangling images..."
|
||||
docker image prune -f
|
||||
|
||||
# Remove any dangling volumes
|
||||
echo "Removing dangling volumes..."
|
||||
docker volume prune -f
|
||||
|
||||
echo "Cleanup complete. All project containers, images, volumes, and related resources have been removed."
|
||||
|
||||
# List remaining containers, images, and volumes
|
||||
echo "Remaining containers:"
|
||||
docker ps -a
|
||||
echo "Remaining images:"
|
||||
docker images
|
||||
echo "Remaining volumes:"
|
||||
docker volume ls
|
||||
150
self-hosting/docker-compose.template.yml
Normal file
150
self-hosting/docker-compose.template.yml
Normal file
@@ -0,0 +1,150 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
op-proxy:
|
||||
image: caddy:2-alpine
|
||||
restart: always
|
||||
ports:
|
||||
- '80:80'
|
||||
- '443:443'
|
||||
volumes:
|
||||
- op-proxy-data:/data
|
||||
- op-proxy-config:/config
|
||||
- ./caddy/Caddyfile:/etc/caddy/Caddyfile
|
||||
depends_on:
|
||||
- op-dashboard
|
||||
- op-api
|
||||
|
||||
op-db:
|
||||
image: postgres:14-alpine
|
||||
restart: always
|
||||
volumes:
|
||||
- op-db-data:/var/lib/postgresql/data
|
||||
environment:
|
||||
- POSTGRES_PASSWORD
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U postgres']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
ports:
|
||||
- 5431:5432
|
||||
|
||||
op-kv:
|
||||
image: redis:7.2.5-alpine
|
||||
restart: always
|
||||
volumes:
|
||||
- op-kv-data:/data
|
||||
command:
|
||||
[
|
||||
'redis-server',
|
||||
'--requirepass',
|
||||
'${REDIS_PASSWORD}',
|
||||
'--maxmemory-policy',
|
||||
'noeviction',
|
||||
]
|
||||
ports:
|
||||
- 6378:6379
|
||||
environment:
|
||||
- REDIS_PASSWORD=${REDIS_PASSWORD}
|
||||
|
||||
op-geo:
|
||||
image: observabilitystack/geoip-api:latest
|
||||
restart: always
|
||||
|
||||
op-ch:
|
||||
image: clickhouse/clickhouse-server:23.3.7.5-alpine
|
||||
restart: always
|
||||
volumes:
|
||||
- op-ch-data:/var/lib/clickhouse
|
||||
- op-ch-logs:/var/log/clickhouse-server
|
||||
- ./clickhouse/clickhouse-config.xml:/etc/clickhouse-server/config.d/op-config.xml:ro
|
||||
- ./clickhouse/clickhouse-user-config.xml:/etc/clickhouse-server/users.d/op-user-config.xml:ro
|
||||
environment:
|
||||
- CLICKHOUSE_DB
|
||||
- CLICKHOUSE_USER
|
||||
- CLICKHOUSE_PASSWORD
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'clickhouse-client --query "SELECT 1"']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 262144
|
||||
hard: 262144
|
||||
ports:
|
||||
- 8999:9000
|
||||
- 8122:8123
|
||||
|
||||
op-ch-migrator:
|
||||
image: clickhouse/clickhouse-server:23.3.7.5-alpine
|
||||
depends_on:
|
||||
- op-ch
|
||||
volumes:
|
||||
- ../packages/db/clickhouse_init.sql:/migrations/clickhouse_init.sql
|
||||
environment:
|
||||
- CLICKHOUSE_DB
|
||||
- CLICKHOUSE_USER
|
||||
- CLICKHOUSE_PASSWORD
|
||||
entrypoint: /bin/sh -c
|
||||
command: >
|
||||
"
|
||||
echo 'Waiting for ClickHouse to start...';
|
||||
while ! clickhouse-client --host op-ch --user=$CLICKHOUSE_USER --password=$CLICKHOUSE_PASSWORD --query 'SELECT 1;' 2>/dev/null; do
|
||||
echo 'ClickHouse is unavailable - sleeping 1s...';
|
||||
sleep 1;
|
||||
done;
|
||||
|
||||
echo 'ClickHouse started. Running migrations...';
|
||||
clickhouse-client --host op-ch --database=$CLICKHOUSE_DB --user=$CLICKHOUSE_USER --password=$CLICKHOUSE_PASSWORD --queries-file /migrations/clickhouse_init.sql;
|
||||
"
|
||||
|
||||
op-api:
|
||||
image: lindesvard/openpanel-api:latest
|
||||
restart: always
|
||||
command: sh -c "sleep 10 && pnpm -r run migrate:deploy && pnpm start"
|
||||
depends_on:
|
||||
- op-db
|
||||
- op-ch
|
||||
- op-kv
|
||||
- op-geo
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
op-dashboard:
|
||||
image: lindesvard/openpanel-dashboard:latest
|
||||
restart: always
|
||||
depends_on:
|
||||
- op-db
|
||||
- op-ch
|
||||
- op-kv
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
op-worker:
|
||||
image: lindesvard/openpanel-worker:latest
|
||||
restart: always
|
||||
depends_on:
|
||||
- op-db
|
||||
- op-ch
|
||||
- op-kv
|
||||
env_file:
|
||||
- .env
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: $OP_WORKER_REPLICAS
|
||||
|
||||
volumes:
|
||||
op-db-data:
|
||||
driver: local
|
||||
op-kv-data:
|
||||
driver: local
|
||||
op-ch-data:
|
||||
driver: local
|
||||
op-ch-logs:
|
||||
driver: local
|
||||
op-proxy-data:
|
||||
driver: local
|
||||
op-proxy-config:
|
||||
driver: local
|
||||
3
self-hosting/logs
Executable file
3
self-hosting/logs
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker compose logs -f
|
||||
22
self-hosting/package.json
Normal file
22
self-hosting/package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "@openpanel/self-hosting",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@types/inquirer": "^9.0.7",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"bcrypt": "^5.1.1",
|
||||
"inquirer": "^9.3.1",
|
||||
"jiti": "^1.21.6",
|
||||
"js-yaml": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^5.0.2"
|
||||
}
|
||||
}
|
||||
716
self-hosting/pnpm-lock.yaml
generated
Normal file
716
self-hosting/pnpm-lock.yaml
generated
Normal file
@@ -0,0 +1,716 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@types/inquirer':
|
||||
specifier: ^9.0.7
|
||||
version: 9.0.7
|
||||
'@types/js-yaml':
|
||||
specifier: ^4.0.9
|
||||
version: 4.0.9
|
||||
bcrypt:
|
||||
specifier: ^5.1.1
|
||||
version: 5.1.1
|
||||
inquirer:
|
||||
specifier: ^9.3.1
|
||||
version: 9.3.1
|
||||
jiti:
|
||||
specifier: ^1.21.6
|
||||
version: 1.21.6
|
||||
js-yaml:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0
|
||||
|
||||
devDependencies:
|
||||
'@types/bcrypt':
|
||||
specifier: ^5.0.2
|
||||
version: 5.0.2
|
||||
|
||||
packages:
|
||||
|
||||
/@inquirer/figures@1.0.3:
|
||||
resolution: {integrity: sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==}
|
||||
engines: {node: '>=18'}
|
||||
dev: false
|
||||
|
||||
/@mapbox/node-pre-gyp@1.0.11:
|
||||
resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
detect-libc: 2.0.3
|
||||
https-proxy-agent: 5.0.1
|
||||
make-dir: 3.1.0
|
||||
node-fetch: 2.7.0
|
||||
nopt: 5.0.0
|
||||
npmlog: 5.0.1
|
||||
rimraf: 3.0.2
|
||||
semver: 7.6.3
|
||||
tar: 6.2.1
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@types/bcrypt@5.0.2:
|
||||
resolution: {integrity: sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==}
|
||||
dependencies:
|
||||
'@types/node': 20.14.9
|
||||
dev: true
|
||||
|
||||
/@types/inquirer@9.0.7:
|
||||
resolution: {integrity: sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==}
|
||||
dependencies:
|
||||
'@types/through': 0.0.33
|
||||
rxjs: 7.8.1
|
||||
dev: false
|
||||
|
||||
/@types/js-yaml@4.0.9:
|
||||
resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
|
||||
dev: false
|
||||
|
||||
/@types/node@20.14.9:
|
||||
resolution: {integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==}
|
||||
dependencies:
|
||||
undici-types: 5.26.5
|
||||
|
||||
/@types/through@0.0.33:
|
||||
resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==}
|
||||
dependencies:
|
||||
'@types/node': 20.14.9
|
||||
dev: false
|
||||
|
||||
/abbrev@1.1.1:
|
||||
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
|
||||
dev: false
|
||||
|
||||
/agent-base@6.0.2:
|
||||
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
|
||||
engines: {node: '>= 6.0.0'}
|
||||
dependencies:
|
||||
debug: 4.3.6
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/ansi-escapes@4.3.2:
|
||||
resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
type-fest: 0.21.3
|
||||
dev: false
|
||||
|
||||
/ansi-regex@5.0.1:
|
||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/ansi-styles@4.3.0:
|
||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
dev: false
|
||||
|
||||
/aproba@2.0.0:
|
||||
resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
|
||||
dev: false
|
||||
|
||||
/are-we-there-yet@2.0.0:
|
||||
resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
|
||||
engines: {node: '>=10'}
|
||||
deprecated: This package is no longer supported.
|
||||
dependencies:
|
||||
delegates: 1.0.0
|
||||
readable-stream: 3.6.2
|
||||
dev: false
|
||||
|
||||
/argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
dev: false
|
||||
|
||||
/balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
dev: false
|
||||
|
||||
/base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
dev: false
|
||||
|
||||
/bcrypt@5.1.1:
|
||||
resolution: {integrity: sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@mapbox/node-pre-gyp': 1.0.11
|
||||
node-addon-api: 5.1.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/bl@4.1.0:
|
||||
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
|
||||
dependencies:
|
||||
buffer: 5.7.1
|
||||
inherits: 2.0.4
|
||||
readable-stream: 3.6.2
|
||||
dev: false
|
||||
|
||||
/brace-expansion@1.1.11:
|
||||
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
concat-map: 0.0.1
|
||||
dev: false
|
||||
|
||||
/buffer@5.7.1:
|
||||
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
ieee754: 1.2.1
|
||||
dev: false
|
||||
|
||||
/chalk@4.1.2:
|
||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
supports-color: 7.2.0
|
||||
dev: false
|
||||
|
||||
/chardet@0.7.0:
|
||||
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
|
||||
dev: false
|
||||
|
||||
/chownr@2.0.0:
|
||||
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/cli-cursor@3.1.0:
|
||||
resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
restore-cursor: 3.1.0
|
||||
dev: false
|
||||
|
||||
/cli-spinners@2.9.2:
|
||||
resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/cli-width@4.1.0:
|
||||
resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==}
|
||||
engines: {node: '>= 12'}
|
||||
dev: false
|
||||
|
||||
/clone@1.0.4:
|
||||
resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
|
||||
engines: {node: '>=0.8'}
|
||||
dev: false
|
||||
|
||||
/color-convert@2.0.1:
|
||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||
engines: {node: '>=7.0.0'}
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
dev: false
|
||||
|
||||
/color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
dev: false
|
||||
|
||||
/color-support@1.1.3:
|
||||
resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
dev: false
|
||||
|
||||
/console-control-strings@1.1.0:
|
||||
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
|
||||
dev: false
|
||||
|
||||
/debug@4.3.6:
|
||||
resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
dependencies:
|
||||
ms: 2.1.2
|
||||
dev: false
|
||||
|
||||
/defaults@1.0.4:
|
||||
resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
|
||||
dependencies:
|
||||
clone: 1.0.4
|
||||
dev: false
|
||||
|
||||
/delegates@1.0.0:
|
||||
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
|
||||
dev: false
|
||||
|
||||
/detect-libc@2.0.3:
|
||||
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
dev: false
|
||||
|
||||
/external-editor@3.1.0:
|
||||
resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
|
||||
engines: {node: '>=4'}
|
||||
dependencies:
|
||||
chardet: 0.7.0
|
||||
iconv-lite: 0.4.24
|
||||
tmp: 0.0.33
|
||||
dev: false
|
||||
|
||||
/fs-minipass@2.1.0:
|
||||
resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
|
||||
engines: {node: '>= 8'}
|
||||
dependencies:
|
||||
minipass: 3.3.6
|
||||
dev: false
|
||||
|
||||
/fs.realpath@1.0.0:
|
||||
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
|
||||
dev: false
|
||||
|
||||
/gauge@3.0.2:
|
||||
resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
|
||||
engines: {node: '>=10'}
|
||||
deprecated: This package is no longer supported.
|
||||
dependencies:
|
||||
aproba: 2.0.0
|
||||
color-support: 1.1.3
|
||||
console-control-strings: 1.1.0
|
||||
has-unicode: 2.0.1
|
||||
object-assign: 4.1.1
|
||||
signal-exit: 3.0.7
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
wide-align: 1.1.5
|
||||
dev: false
|
||||
|
||||
/glob@7.2.3:
|
||||
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
|
||||
deprecated: Glob versions prior to v9 are no longer supported
|
||||
dependencies:
|
||||
fs.realpath: 1.0.0
|
||||
inflight: 1.0.6
|
||||
inherits: 2.0.4
|
||||
minimatch: 3.1.2
|
||||
once: 1.4.0
|
||||
path-is-absolute: 1.0.1
|
||||
dev: false
|
||||
|
||||
/has-flag@4.0.0:
|
||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/has-unicode@2.0.1:
|
||||
resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
|
||||
dev: false
|
||||
|
||||
/https-proxy-agent@5.0.1:
|
||||
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
|
||||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
agent-base: 6.0.2
|
||||
debug: 4.3.6
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/iconv-lite@0.4.24:
|
||||
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
dev: false
|
||||
|
||||
/ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
dev: false
|
||||
|
||||
/inflight@1.0.6:
|
||||
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
|
||||
deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
|
||||
dependencies:
|
||||
once: 1.4.0
|
||||
wrappy: 1.0.2
|
||||
dev: false
|
||||
|
||||
/inherits@2.0.4:
|
||||
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
|
||||
dev: false
|
||||
|
||||
/inquirer@9.3.1:
|
||||
resolution: {integrity: sha512-A5IdVr1I04XqPlwrGgTJMKmzRg5ropqNpSeqo0vj1ZmluSCNSFaPZz4eazdPrhVcZfej7fCEYvD2NYa1KjkTJA==}
|
||||
engines: {node: '>=18'}
|
||||
dependencies:
|
||||
'@inquirer/figures': 1.0.3
|
||||
ansi-escapes: 4.3.2
|
||||
cli-width: 4.1.0
|
||||
external-editor: 3.1.0
|
||||
mute-stream: 1.0.0
|
||||
ora: 5.4.1
|
||||
picocolors: 1.0.1
|
||||
run-async: 3.0.0
|
||||
rxjs: 7.8.1
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
wrap-ansi: 6.2.0
|
||||
dev: false
|
||||
|
||||
/is-fullwidth-code-point@3.0.0:
|
||||
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/is-interactive@1.0.0:
|
||||
resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/is-unicode-supported@0.1.0:
|
||||
resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==}
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/jiti@1.21.6:
|
||||
resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/js-yaml@4.1.0:
|
||||
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
dev: false
|
||||
|
||||
/log-symbols@4.1.0:
|
||||
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
is-unicode-supported: 0.1.0
|
||||
dev: false
|
||||
|
||||
/make-dir@3.1.0:
|
||||
resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
semver: 6.3.1
|
||||
dev: false
|
||||
|
||||
/mimic-fn@2.1.0:
|
||||
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/minimatch@3.1.2:
|
||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
|
||||
dependencies:
|
||||
brace-expansion: 1.1.11
|
||||
dev: false
|
||||
|
||||
/minipass@3.3.6:
|
||||
resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
yallist: 4.0.0
|
||||
dev: false
|
||||
|
||||
/minipass@5.0.0:
|
||||
resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/minizlib@2.1.2:
|
||||
resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
|
||||
engines: {node: '>= 8'}
|
||||
dependencies:
|
||||
minipass: 3.3.6
|
||||
yallist: 4.0.0
|
||||
dev: false
|
||||
|
||||
/mkdirp@1.0.4:
|
||||
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/ms@2.1.2:
|
||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||
dev: false
|
||||
|
||||
/mute-stream@1.0.0:
|
||||
resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==}
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
dev: false
|
||||
|
||||
/node-addon-api@5.1.0:
|
||||
resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==}
|
||||
dev: false
|
||||
|
||||
/node-fetch@2.7.0:
|
||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
peerDependencies:
|
||||
encoding: ^0.1.0
|
||||
peerDependenciesMeta:
|
||||
encoding:
|
||||
optional: true
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
dev: false
|
||||
|
||||
/nopt@5.0.0:
|
||||
resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
|
||||
engines: {node: '>=6'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
abbrev: 1.1.1
|
||||
dev: false
|
||||
|
||||
/npmlog@5.0.1:
|
||||
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
|
||||
deprecated: This package is no longer supported.
|
||||
dependencies:
|
||||
are-we-there-yet: 2.0.0
|
||||
console-control-strings: 1.1.0
|
||||
gauge: 3.0.2
|
||||
set-blocking: 2.0.0
|
||||
dev: false
|
||||
|
||||
/object-assign@4.1.1:
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
dev: false
|
||||
|
||||
/onetime@5.1.2:
|
||||
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
|
||||
engines: {node: '>=6'}
|
||||
dependencies:
|
||||
mimic-fn: 2.1.0
|
||||
dev: false
|
||||
|
||||
/ora@5.4.1:
|
||||
resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
bl: 4.1.0
|
||||
chalk: 4.1.2
|
||||
cli-cursor: 3.1.0
|
||||
cli-spinners: 2.9.2
|
||||
is-interactive: 1.0.0
|
||||
is-unicode-supported: 0.1.0
|
||||
log-symbols: 4.1.0
|
||||
strip-ansi: 6.0.1
|
||||
wcwidth: 1.0.1
|
||||
dev: false
|
||||
|
||||
/os-tmpdir@1.0.2:
|
||||
resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/path-is-absolute@1.0.1:
|
||||
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
/picocolors@1.0.1:
|
||||
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
|
||||
dev: false
|
||||
|
||||
/readable-stream@3.6.2:
|
||||
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
|
||||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
string_decoder: 1.3.0
|
||||
util-deprecate: 1.0.2
|
||||
dev: false
|
||||
|
||||
/restore-cursor@3.1.0:
|
||||
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
onetime: 5.1.2
|
||||
signal-exit: 3.0.7
|
||||
dev: false
|
||||
|
||||
/rimraf@3.0.2:
|
||||
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
|
||||
deprecated: Rimraf versions prior to v4 are no longer supported
|
||||
hasBin: true
|
||||
dependencies:
|
||||
glob: 7.2.3
|
||||
dev: false
|
||||
|
||||
/run-async@3.0.0:
|
||||
resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
dev: false
|
||||
|
||||
/rxjs@7.8.1:
|
||||
resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
|
||||
dependencies:
|
||||
tslib: 2.6.3
|
||||
dev: false
|
||||
|
||||
/safe-buffer@5.2.1:
|
||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||
dev: false
|
||||
|
||||
/safer-buffer@2.1.2:
|
||||
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||
dev: false
|
||||
|
||||
/semver@6.3.1:
|
||||
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/semver@7.6.3:
|
||||
resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/set-blocking@2.0.0:
|
||||
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
|
||||
dev: false
|
||||
|
||||
/signal-exit@3.0.7:
|
||||
resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
|
||||
dev: false
|
||||
|
||||
/string-width@4.2.3:
|
||||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
emoji-regex: 8.0.0
|
||||
is-fullwidth-code-point: 3.0.0
|
||||
strip-ansi: 6.0.1
|
||||
dev: false
|
||||
|
||||
/string_decoder@1.3.0:
|
||||
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
dev: false
|
||||
|
||||
/strip-ansi@6.0.1:
|
||||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
ansi-regex: 5.0.1
|
||||
dev: false
|
||||
|
||||
/supports-color@7.2.0:
|
||||
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
dev: false
|
||||
|
||||
/tar@6.2.1:
|
||||
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
chownr: 2.0.0
|
||||
fs-minipass: 2.1.0
|
||||
minipass: 5.0.0
|
||||
minizlib: 2.1.2
|
||||
mkdirp: 1.0.4
|
||||
yallist: 4.0.0
|
||||
dev: false
|
||||
|
||||
/tmp@0.0.33:
|
||||
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
|
||||
engines: {node: '>=0.6.0'}
|
||||
dependencies:
|
||||
os-tmpdir: 1.0.2
|
||||
dev: false
|
||||
|
||||
/tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
dev: false
|
||||
|
||||
/tslib@2.6.3:
|
||||
resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==}
|
||||
dev: false
|
||||
|
||||
/type-fest@0.21.3:
|
||||
resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/undici-types@5.26.5:
|
||||
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
|
||||
|
||||
/util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
dev: false
|
||||
|
||||
/wcwidth@1.0.1:
|
||||
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
|
||||
dependencies:
|
||||
defaults: 1.0.4
|
||||
dev: false
|
||||
|
||||
/webidl-conversions@3.0.1:
|
||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
dev: false
|
||||
|
||||
/whatwg-url@5.0.0:
|
||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||
dependencies:
|
||||
tr46: 0.0.3
|
||||
webidl-conversions: 3.0.1
|
||||
dev: false
|
||||
|
||||
/wide-align@1.1.5:
|
||||
resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
dev: false
|
||||
|
||||
/wrap-ansi@6.2.0:
|
||||
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
dev: false
|
||||
|
||||
/wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
dev: false
|
||||
|
||||
/yallist@4.0.0:
|
||||
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
|
||||
dev: false
|
||||
476
self-hosting/quiz.ts
Normal file
476
self-hosting/quiz.ts
Normal file
@@ -0,0 +1,476 @@
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import bcrypt from 'bcrypt';
|
||||
import inquirer from 'inquirer';
|
||||
import yaml from 'js-yaml';
|
||||
|
||||
function generatePassword(length: number) {
|
||||
const charset =
|
||||
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
let password = '';
|
||||
for (let i = 0, n = charset.length; i < length; ++i) {
|
||||
password += charset.charAt(Math.floor(Math.random() * n));
|
||||
}
|
||||
return password;
|
||||
}
|
||||
|
||||
function writeCaddyfile(domainName: string, basicAuthPassword: string) {
|
||||
const caddyfileTemplatePath = path.resolve(
|
||||
__dirname,
|
||||
'caddy',
|
||||
'Caddyfile.template'
|
||||
);
|
||||
const caddyfilePath = path.resolve(__dirname, 'caddy', 'Caddyfile');
|
||||
fs.writeFileSync(
|
||||
caddyfilePath,
|
||||
fs
|
||||
.readFileSync(caddyfileTemplatePath, 'utf-8')
|
||||
.replaceAll('$DOMAIN_NAME', domainName.replace(/https?:\/\//, ''))
|
||||
.replaceAll(
|
||||
'$BASIC_AUTH_PASSWORD',
|
||||
bcrypt.hashSync(basicAuthPassword, 10)
|
||||
)
|
||||
.replaceAll(
|
||||
'$SSL_CONFIG',
|
||||
domainName.includes('localhost:443') ? '\n\ttls internal' : ''
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export interface DockerComposeFile {
|
||||
version: string;
|
||||
services: Record<
|
||||
string,
|
||||
{
|
||||
image: string;
|
||||
restart: string;
|
||||
ports: string[];
|
||||
volumes: string[];
|
||||
depends_on: string[];
|
||||
}
|
||||
>;
|
||||
volumes?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
const stripTrailingSlash = (str: string) =>
|
||||
str.endsWith('/') ? str.slice(0, -1) : str;
|
||||
|
||||
function searchAndReplaceDockerCompose(replacements: [string, string][]) {
|
||||
const dockerComposePath = path.resolve(__dirname, 'docker-compose.yml');
|
||||
const dockerComposeContent = fs.readFileSync(dockerComposePath, 'utf-8');
|
||||
const dockerComposeReplaced = replacements.reduce(
|
||||
(acc, [search, replace]) => acc.replaceAll(search, replace),
|
||||
dockerComposeContent
|
||||
);
|
||||
|
||||
fs.writeFileSync(dockerComposePath, dockerComposeReplaced);
|
||||
}
|
||||
|
||||
function removeServiceFromDockerCompose(serviceName: string) {
|
||||
const dockerComposePath = path.resolve(__dirname, 'docker-compose.yml');
|
||||
const dockerComposeContent = fs.readFileSync(dockerComposePath, 'utf-8');
|
||||
|
||||
// Parse the YAML file
|
||||
const dockerCompose = yaml.load(dockerComposeContent) as DockerComposeFile;
|
||||
|
||||
// Remove the service
|
||||
if (dockerCompose.services && dockerCompose.services[serviceName]) {
|
||||
delete dockerCompose.services[serviceName];
|
||||
console.log(`Service '${serviceName}' has been removed.`);
|
||||
} else {
|
||||
console.log(`Service '${serviceName}' not found.`);
|
||||
// return;
|
||||
}
|
||||
|
||||
// filter depends_on
|
||||
Object.keys(dockerCompose.services).forEach((service) => {
|
||||
if (dockerCompose.services[service]?.depends_on) {
|
||||
// @ts-expect-error
|
||||
dockerCompose.services[service].depends_on = dockerCompose.services[
|
||||
service
|
||||
].depends_on.filter((dep) => dep !== serviceName);
|
||||
}
|
||||
});
|
||||
|
||||
// filter volumes
|
||||
Object.keys(dockerCompose.volumes ?? {}).forEach((volume) => {
|
||||
if (dockerCompose.volumes && volume.startsWith(serviceName)) {
|
||||
delete dockerCompose.volumes[volume];
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.keys(dockerCompose.volumes ?? {}).length === 0) {
|
||||
delete dockerCompose.volumes;
|
||||
}
|
||||
|
||||
// Convert the object back to YAML
|
||||
const newYaml = yaml.dump(dockerCompose, {
|
||||
lineWidth: -1,
|
||||
});
|
||||
fs.writeFileSync(dockerComposePath, newYaml);
|
||||
}
|
||||
|
||||
function writeEnvFile(envs: {
|
||||
POSTGRES_PASSWORD: string | undefined;
|
||||
REDIS_PASSWORD: string | undefined;
|
||||
CLICKHOUSE_URL: string;
|
||||
CLICKHOUSE_DB: string;
|
||||
CLICKHOUSE_USER: string;
|
||||
CLICKHOUSE_PASSWORD: string;
|
||||
REDIS_URL: string;
|
||||
DATABASE_URL: string;
|
||||
DOMAIN_NAME: string;
|
||||
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: string;
|
||||
CLERK_SECRET_KEY: string;
|
||||
CLERK_SIGNING_SECRET: string;
|
||||
}) {
|
||||
const envTemplatePath = path.resolve(__dirname, '.env.template');
|
||||
const envPath = path.resolve(__dirname, '.env');
|
||||
const envTemplate = fs.readFileSync(envTemplatePath, 'utf-8');
|
||||
|
||||
let newEnvFile = envTemplate
|
||||
.replace('$CLICKHOUSE_URL', envs.CLICKHOUSE_URL)
|
||||
.replace('$CLICKHOUSE_DB', envs.CLICKHOUSE_DB)
|
||||
.replace('$CLICKHOUSE_USER', envs.CLICKHOUSE_USER)
|
||||
.replace('$CLICKHOUSE_PASSWORD', envs.CLICKHOUSE_PASSWORD)
|
||||
.replace('$REDIS_URL', envs.REDIS_URL)
|
||||
.replace('$DATABASE_URL', envs.DATABASE_URL)
|
||||
.replace('$DATABASE_URL_DIRECT', envs.DATABASE_URL)
|
||||
.replace('$NEXT_PUBLIC_DASHBOARD_URL', stripTrailingSlash(envs.DOMAIN_NAME))
|
||||
.replace(
|
||||
'$NEXT_PUBLIC_API_URL',
|
||||
`${stripTrailingSlash(envs.DOMAIN_NAME)}/api`
|
||||
)
|
||||
.replace(
|
||||
'$NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY',
|
||||
envs.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
|
||||
)
|
||||
.replace('$CLERK_SECRET_KEY', envs.CLERK_SECRET_KEY)
|
||||
.replace('$CLERK_SIGNING_SECRET', envs.CLERK_SIGNING_SECRET);
|
||||
|
||||
if (envs.POSTGRES_PASSWORD) {
|
||||
newEnvFile += `\nPOSTGRES_PASSWORD=${envs.POSTGRES_PASSWORD}`;
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
envPath,
|
||||
newEnvFile
|
||||
.split('\n')
|
||||
.filter((line) => {
|
||||
return !line.includes('=""');
|
||||
})
|
||||
.join('\n')
|
||||
);
|
||||
}
|
||||
|
||||
async function initiateOnboarding() {
|
||||
const T = ' ';
|
||||
const message = [
|
||||
'',
|
||||
'DISCLAIMER: This script is provided as-is and without warranty. Use at your own risk.',
|
||||
'',
|
||||
'',
|
||||
'WORTH MENTIONING: This is an early version of the script and it may not cover all scenarios.',
|
||||
' We recommend using our cloud service for production workloads until we release a stable version of self-hosting.',
|
||||
'',
|
||||
'',
|
||||
"With that said let's get started! 🤠",
|
||||
'',
|
||||
`Hey and welcome to Openpanel's self-hosting setup! 🚀\n`,
|
||||
`Before you continue, please make sure you have the following:`,
|
||||
`${T}1. Docker and Docker Compose installed on your machine.`,
|
||||
`${T}2. A domain name that you can use for this setup and point it to this machine's ip`,
|
||||
`${T}3. A Clerk.com account`,
|
||||
`${T}${T}- If you don't have one, you can create one at https://clerk.dev`,
|
||||
`${T}${T}- We'll need NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, CLERK_SECRET_KEY, CLERK_SIGNING_SECRET`,
|
||||
`${T}${T}- Create a webhook pointing to https://your_domain/api/webhook/clerk\n`,
|
||||
'For more information you can read our article on self-hosting at https://docs.openpanel.dev/docs/self-hosting\n',
|
||||
];
|
||||
|
||||
console.log(
|
||||
'******************************************************************************\n'
|
||||
);
|
||||
console.log(message.join('\n'));
|
||||
console.log(
|
||||
'\n******************************************************************************'
|
||||
);
|
||||
|
||||
// Domain name
|
||||
|
||||
const domainNameResponse = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'domainName',
|
||||
message: "What's the domain name you want to use?",
|
||||
default: process.env.DEBUG ? 'http://localhost' : undefined,
|
||||
prefix: '🌐',
|
||||
validate: (value) => {
|
||||
if (value.startsWith('http://') || value.startsWith('https://')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'Please enter a valid domain name. Should start with "http://" or "https://"';
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
// Dependencies
|
||||
|
||||
const dependenciesResponse = await inquirer.prompt([
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'dependencies',
|
||||
message: 'Which of these dependencies will you need us to install?',
|
||||
choices: ['Clickhouse', 'Redis', 'Postgres'],
|
||||
default: ['Clickhouse', 'Redis', 'Postgres'],
|
||||
prefix: '📦',
|
||||
},
|
||||
]);
|
||||
|
||||
let envs: Record<string, string> = {};
|
||||
if (!dependenciesResponse.dependencies.includes('Clickhouse')) {
|
||||
const clickhouseResponse = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'CLICKHOUSE_URL',
|
||||
message: 'Enter your ClickHouse URL:',
|
||||
default: process.env.DEBUG ? 'http://clickhouse:8123' : undefined,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'CLICKHOUSE_DB',
|
||||
message: 'Enter your ClickHouse DB name:',
|
||||
default: process.env.DEBUG ? 'db_openpanel' : undefined,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'CLICKHOUSE_USER',
|
||||
message: 'Enter your ClickHouse user name:',
|
||||
default: process.env.DEBUG ? 'user_openpanel' : undefined,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'CLICKHOUSE_PASSWORD',
|
||||
message: 'Enter your ClickHouse password:',
|
||||
default: process.env.DEBUG ? 'ch_password' : undefined,
|
||||
},
|
||||
]);
|
||||
|
||||
envs = {
|
||||
...envs,
|
||||
...clickhouseResponse,
|
||||
};
|
||||
}
|
||||
|
||||
if (!dependenciesResponse.dependencies.includes('Redis')) {
|
||||
const redisResponse = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'REDIS_URL',
|
||||
message: 'Enter your Redis URL:',
|
||||
default: process.env.DEBUG ? 'redis://redis:6379' : undefined,
|
||||
},
|
||||
]);
|
||||
envs = {
|
||||
...envs,
|
||||
...redisResponse,
|
||||
};
|
||||
}
|
||||
|
||||
if (!dependenciesResponse.dependencies.includes('Postgres')) {
|
||||
const dbResponse = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'DATABASE_URL',
|
||||
message: 'Enter your Database URL:',
|
||||
default: process.env.DEBUG
|
||||
? 'postgresql://postgres:postgres@postgres:5432/postgres?schema=public'
|
||||
: undefined,
|
||||
},
|
||||
]);
|
||||
envs = {
|
||||
...envs,
|
||||
...dbResponse,
|
||||
};
|
||||
}
|
||||
|
||||
// Proxy
|
||||
|
||||
const proxyResponse = await inquirer.prompt([
|
||||
{
|
||||
type: 'list',
|
||||
name: 'proxy',
|
||||
message:
|
||||
'Do you already have a web service setup or would you like us to install Caddy with SSL?',
|
||||
choices: ['Install Caddy with SSL', 'Bring my own'],
|
||||
},
|
||||
]);
|
||||
|
||||
// Clerk
|
||||
|
||||
const clerkResponse = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY',
|
||||
message: 'Enter your Clerk Publishable Key:',
|
||||
default: process.env.DEBUG ? 'pk_test_1234567890' : undefined,
|
||||
validate: (value) => {
|
||||
if (value.startsWith('pk_live_') || value.startsWith('pk_test_')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'Please enter a valid Clerk Publishable Key. Should start with "pk_live_" or "pk_test_"';
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'CLERK_SECRET_KEY',
|
||||
message: 'Enter your Clerk Secret Key:',
|
||||
default: process.env.DEBUG ? 'sk_test_1234567890' : undefined,
|
||||
validate: (value) => {
|
||||
if (value.startsWith('sk_live_') || value.startsWith('sk_test_')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'Please enter a valid Clerk Secret Key. Should start with "sk_live_" or "sk_test_"';
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'CLERK_SIGNING_SECRET',
|
||||
message: 'Enter your Clerk Signing Secret:',
|
||||
default: process.env.DEBUG ? 'whsec_1234567890' : undefined,
|
||||
validate: (value) => {
|
||||
if (value.startsWith('whsec_')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'Please enter a valid Clerk Signing Secret. Should start with "whsec_"';
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
// OS
|
||||
|
||||
const cpus = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'CPUS',
|
||||
default: os.cpus().length,
|
||||
message: 'How many CPUs do you have?',
|
||||
validate: (value) => {
|
||||
const parsed = parseInt(value, 10);
|
||||
|
||||
if (Number.isNaN(parsed)) {
|
||||
return 'Please enter a valid number';
|
||||
}
|
||||
|
||||
if (parsed < 1) {
|
||||
return 'Please enter a number greater than 0';
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const basicAuth = await inquirer.prompt<{
|
||||
password: string;
|
||||
}>([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'password',
|
||||
default: generatePassword(12),
|
||||
message: 'Give a password for basic auth',
|
||||
validate: (value) => {
|
||||
if (!value) {
|
||||
return 'Please enter a valid password';
|
||||
}
|
||||
|
||||
if (value.length < 5) {
|
||||
return 'Password should be atleast 5 characters';
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
console.log('');
|
||||
console.log('Creating .env file...\n');
|
||||
const POSTGRES_PASSWORD = generatePassword(20);
|
||||
const REDIS_PASSWORD = generatePassword(20);
|
||||
|
||||
writeEnvFile({
|
||||
POSTGRES_PASSWORD: envs.DATABASE_URL ? undefined : POSTGRES_PASSWORD,
|
||||
REDIS_PASSWORD: envs.REDIS_URL ? undefined : REDIS_PASSWORD,
|
||||
CLICKHOUSE_URL: envs.CLICKHOUSE_URL || 'http://op-ch:8123',
|
||||
CLICKHOUSE_DB: envs.CLICKHOUSE_DB || 'openpanel',
|
||||
CLICKHOUSE_USER: envs.CLICKHOUSE_USER || 'openpanel',
|
||||
CLICKHOUSE_PASSWORD: envs.CLICKHOUSE_PASSWORD || generatePassword(20),
|
||||
REDIS_URL: envs.REDIS_URL || 'redis://op-kv:6379',
|
||||
DATABASE_URL:
|
||||
envs.DATABASE_URL ||
|
||||
`postgresql://postgres:${POSTGRES_PASSWORD}@op-db:5432/postgres?schema=public`,
|
||||
DOMAIN_NAME: domainNameResponse.domainName,
|
||||
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:
|
||||
clerkResponse.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY || '',
|
||||
CLERK_SECRET_KEY: clerkResponse.CLERK_SECRET_KEY || '',
|
||||
CLERK_SIGNING_SECRET: clerkResponse.CLERK_SIGNING_SECRET || '',
|
||||
});
|
||||
|
||||
console.log('Updating docker-compose.yml file...\n');
|
||||
fs.copyFileSync(
|
||||
path.resolve(__dirname, 'docker-compose.template.yml'),
|
||||
path.resolve(__dirname, 'docker-compose.yml')
|
||||
);
|
||||
|
||||
if (envs.CLICKHOUSE_URL) {
|
||||
removeServiceFromDockerCompose('op-ch');
|
||||
removeServiceFromDockerCompose('op-ch-migrator');
|
||||
}
|
||||
|
||||
if (envs.REDIS_URL) {
|
||||
removeServiceFromDockerCompose('op-kv');
|
||||
}
|
||||
|
||||
if (envs.DATABASE_URL) {
|
||||
removeServiceFromDockerCompose('op-db');
|
||||
}
|
||||
|
||||
if (proxyResponse.proxy === 'Bring my own') {
|
||||
removeServiceFromDockerCompose('op-proxy');
|
||||
} else {
|
||||
writeCaddyfile(domainNameResponse.domainName, basicAuth.password);
|
||||
}
|
||||
|
||||
searchAndReplaceDockerCompose([['$OP_WORKER_REPLICAS', cpus.CPUS]]);
|
||||
|
||||
console.log(
|
||||
[
|
||||
'======================================================================',
|
||||
'Here are some good things to know before you continue:',
|
||||
'',
|
||||
`1. Make sure that your webhook is pointing at ${domainNameResponse.domainName}/api/webhook/clerk`,
|
||||
'',
|
||||
'2. Commands:',
|
||||
'\t- ./start (example: ./start)',
|
||||
'\t- ./stop (example: ./stop)',
|
||||
'\t- ./logs (example: ./logs)',
|
||||
'\t- ./rebuild (example: ./rebuild op-dashboard)',
|
||||
'',
|
||||
'3. Danger zone!',
|
||||
'\t- ./danger_wipe_everything (example: ./danger_wipe_everything)',
|
||||
'',
|
||||
'4. More about self-hosting: https://docs.openpanel.dev/docs/self-hosting',
|
||||
'======================================================================',
|
||||
'',
|
||||
`Start OpenPanel with "./start" inside the self-hosting directory`,
|
||||
'',
|
||||
'',
|
||||
].join('\n')
|
||||
);
|
||||
}
|
||||
|
||||
initiateOnboarding();
|
||||
6
self-hosting/rebuild
Executable file
6
self-hosting/rebuild
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
NAME=$1
|
||||
|
||||
docker compose build $NAME
|
||||
docker compose up -d --no-deps --force-recreate $NAME
|
||||
96
self-hosting/setup
Executable file
96
self-hosting/setup
Executable file
@@ -0,0 +1,96 @@
|
||||
#!/bin/bash
|
||||
|
||||
NODE_VERSION=20.15.0
|
||||
|
||||
# Function to install Node.js
|
||||
install_nvm_and_node() {
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash
|
||||
source ~/.bashrc
|
||||
export NVM_DIR="$HOME/.nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
|
||||
nvm install $NODE_VERSION
|
||||
nvm use $NODE_VERSION
|
||||
}
|
||||
|
||||
# Function to install pnpm
|
||||
install_pnpm() {
|
||||
echo "Installing pnpm..."
|
||||
npm install -g pnpm
|
||||
}
|
||||
|
||||
# Function to install Docker
|
||||
install_docker() {
|
||||
echo "Installing Docker..."
|
||||
# Add Docker's official GPG key:
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ca-certificates curl gnupg
|
||||
sudo install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
sudo chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
|
||||
# Add the repository to Apt sources:
|
||||
echo \
|
||||
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
|
||||
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
|
||||
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
sudo apt-get update
|
||||
|
||||
# Install Docker packages:
|
||||
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
|
||||
# Add current user to docker group
|
||||
sudo usermod -aG docker $USER
|
||||
|
||||
echo "Docker installed successfully. You may need to log out and back in for group changes to take effect."
|
||||
}
|
||||
|
||||
# Check if Node.js is installed
|
||||
if ! command -v node >/dev/null 2>&1; then
|
||||
echo "********************************************************************************"
|
||||
echo "********************************************************************************"
|
||||
echo "Do you wish to automatically install Node.js version $NODE_VERSION using NVM? (yes/no)"
|
||||
echo "********************************************************************************"
|
||||
echo "********************************************************************************"
|
||||
read user_choice
|
||||
|
||||
case $user_choice in
|
||||
[Yy]* )
|
||||
install_nvm_and_node;;
|
||||
[Nn]* )
|
||||
echo "Please install Node.js version $NODE_VERSION by yourself as per your preference. Exiting script."
|
||||
exit 1;;
|
||||
* )
|
||||
echo "Invalid input. Please answer yes or no."
|
||||
exit 1;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Check if Docker is installed
|
||||
if ! command -v docker >/dev/null 2>&1; then
|
||||
echo "********************************************************************************"
|
||||
echo "********************************************************************************"
|
||||
echo "Docker is not installed. Do you wish to install Docker? (yes/no)"
|
||||
echo "********************************************************************************"
|
||||
echo "********************************************************************************"
|
||||
read docker_choice
|
||||
|
||||
case $docker_choice in
|
||||
[Yy]* )
|
||||
install_docker;;
|
||||
[Nn]* )
|
||||
echo "Skipping Docker installation.";;
|
||||
* )
|
||||
echo "Invalid input. Skipping Docker installation.";;
|
||||
esac
|
||||
else
|
||||
echo "Docker is already installed."
|
||||
fi
|
||||
|
||||
|
||||
# Check if pnpm is installed
|
||||
if ! command -v pnpm >/dev/null 2>&1; then
|
||||
install_pnpm
|
||||
fi
|
||||
|
||||
pnpm --ignore-workspace install
|
||||
./node_modules/.bin/jiti quiz.ts
|
||||
3
self-hosting/start
Executable file
3
self-hosting/start
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker compose up -d
|
||||
3
self-hosting/stop
Executable file
3
self-hosting/stop
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker compose down
|
||||
28
self-hosting/tsconfig.json
Normal file
28
self-hosting/tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
|
||||
},
|
||||
"exclude": ["node_modules", "build", "dist"],
|
||||
"include": ["."]
|
||||
}
|
||||
Reference in New Issue
Block a user