Refactor Docker configuration and development setup

- Update .dockerignore with comprehensive ignore patterns for API and client
- Modify docker-compose files to improve service configurations
- Enhance Nginx configuration for development and production environments
- Refactor Dockerfile.api with improved build process
- Add docker-setup.sh script for simplified Docker deployment
- Update update-credentials.vue page with improved UI
- Remove hCaptcha dependency from package-lock.json
- Update PHP configuration and entrypoint scripts
This commit is contained in:
Julien Nahum 2025-01-29 16:00:01 +01:00
parent bf85d8fa76
commit f7df6bc0d7
26 changed files with 1978 additions and 1749 deletions

View File

@ -2,3 +2,38 @@
/Dockerfile /Dockerfile
/data /data
\.env \.env
# API ignores
api/vendor/
api/storage/framework/
api/storage/logs/
api/storage/app/
api/.env
api/.env.*
api/.git/
api/.idea/
api/.vscode/
api/node_modules/
api/*.log
# Client ignores
client/node_modules/
client/.nuxt/
client/dist/
client/.output/
client/coverage/
client/.env
client/.env.*
client/.git/
client/.idea/
client/.vscode/
client/npm-debug.log*
client/yarn-debug.log*
client/yarn-error.log*
# Global ignores
**/.DS_Store
**/*.log
**/.git/
**/.idea/
**/.vscode/

View File

@ -11,6 +11,7 @@ on:
- "client/**" - "client/**"
- "docker/**" - "docker/**"
- "docker-compose*.yml" - "docker-compose*.yml"
- ".github/workflows/dockerhub.yml"
workflow_dispatch: workflow_dispatch:
permissions: permissions:
@ -30,8 +31,8 @@ jobs:
echo "UI_TAGS=${{secrets.DOCKER_UI_REPO}}:latest,${{secrets.DOCKER_UI_REPO}}:${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV echo "UI_TAGS=${{secrets.DOCKER_UI_REPO}}:latest,${{secrets.DOCKER_UI_REPO}}:${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV
else else
echo "VERSION=dev" >> $GITHUB_ENV echo "VERSION=dev" >> $GITHUB_ENV
echo "API_TAGS=${{secrets.DOCKER_API_REPO}}:dev" >> $GITHUB_ENV echo "API_TAGS=${{secrets.DOCKER_API_REPO}}:dev,${{secrets.DOCKER_API_REPO}}:dev-${GITHUB_SHA::7}" >> $GITHUB_ENV
echo "UI_TAGS=${{secrets.DOCKER_UI_REPO}}:dev" >> $GITHUB_ENV echo "UI_TAGS=${{secrets.DOCKER_UI_REPO}}:dev,${{secrets.DOCKER_UI_REPO}}:dev-${GITHUB_SHA::7}" >> $GITHUB_ENV
fi fi
- name: Check out the repo - name: Check out the repo
@ -59,6 +60,8 @@ jobs:
build-args: | build-args: |
APP_ENV=${{ env.VERSION == 'dev' && 'local' || 'production' }} APP_ENV=${{ env.VERSION == 'dev' && 'local' || 'production' }}
tags: ${{ env.API_TAGS }} tags: ${{ env.API_TAGS }}
cache-from: type=registry,ref=${{secrets.DOCKER_API_REPO}}:dev
cache-to: type=inline
- name: Build and push Client image - name: Build and push Client image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@ -68,3 +71,5 @@ jobs:
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
push: true push: true
tags: ${{ env.UI_TAGS }} tags: ${{ env.UI_TAGS }}
cache-from: type=registry,ref=${{secrets.DOCKER_UI_REPO}}:dev
cache-to: type=inline

View File

@ -5,6 +5,7 @@ namespace App\Http;
use App\Http\Middleware\AcceptsJsonMiddleware; use App\Http\Middleware\AcceptsJsonMiddleware;
use App\Http\Middleware\AuthenticateJWT; use App\Http\Middleware\AuthenticateJWT;
use App\Http\Middleware\CustomDomainRestriction; use App\Http\Middleware\CustomDomainRestriction;
use App\Http\Middleware\DevCorsMiddleware;
use App\Http\Middleware\ImpersonationMiddleware; use App\Http\Middleware\ImpersonationMiddleware;
use App\Http\Middleware\IsAdmin; use App\Http\Middleware\IsAdmin;
use App\Http\Middleware\IsModerator; use App\Http\Middleware\IsModerator;
@ -25,6 +26,7 @@ class Kernel extends HttpKernel
protected $middleware = [ protected $middleware = [
// \App\Http\Middleware\TrustHosts::class, // \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class, \App\Http\Middleware\TrustProxies::class,
DevCorsMiddleware::class,
\Illuminate\Http\Middleware\HandleCors::class, \Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class, \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
@ -66,7 +68,7 @@ class Kernel extends HttpKernel
], ],
'api-external' => [ 'api-external' => [
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api', \Illuminate\Routing\Middleware\ThrottleRequests::class . ':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Routing\Middleware\SubstituteBindings::class,
], ],
]; ];

View File

@ -0,0 +1,33 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class DevCorsMiddleware
{
public function handle(Request $request, Closure $next)
{
// Only apply in development mode
if (!config('app.dev_cors')) {
return $next($request);
}
$response = $next($request);
// Handle preflight OPTIONS request
if ($request->isMethod('OPTIONS')) {
$response = response('', 200);
}
// Add CORS headers
$response->headers->set('Access-Control-Allow-Origin', 'http://localhost:3000', true);
$response->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, PATCH', true);
$response->headers->set('Access-Control-Allow-Headers', 'DNT, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Range, Authorization, X-XSRF-TOKEN, Accept', true);
$response->headers->set('Access-Control-Allow-Credentials', 'true', true);
$response->headers->set('Access-Control-Expose-Headers', 'Content-Length, Content-Range', true);
return $response;
}
}

0
api/bootstrap/cache/.gitignore vendored Normal file → Executable file
View File

2723
api/composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,8 @@ return [
'env' => env('APP_ENV', 'production'), 'env' => env('APP_ENV', 'production'),
'dev_cors' => env('APP_DEV_CORS', false),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Application Debug Mode | Application Debug Mode

View File

@ -1,2 +0,0 @@
/Dockerfile
/.dockerignore

View File

@ -30,7 +30,7 @@
/> />
<!-- Remember Me --> <!-- Remember Me -->
<div class="relative flex items-start mt-5"> <div class="relative flex items-center mt-3">
<CheckboxInput <CheckboxInput
v-model="remember" v-model="remember"
class="w-full md:w-1/2" class="w-full md:w-1/2"
@ -52,7 +52,7 @@
<!-- Submit Button --> <!-- Submit Button -->
<v-button <v-button
class="w-full flex" class="w-full flex mt-2"
:loading="form.busy || loading" :loading="form.busy || loading"
> >
Log in to continue Log in to continue

View File

@ -3,7 +3,8 @@ import { useFeatureFlagsStore } from '~/stores/featureFlags'
export default defineNuxtRouteMiddleware(async () => { export default defineNuxtRouteMiddleware(async () => {
const featureFlagsStore = useFeatureFlagsStore() const featureFlagsStore = useFeatureFlagsStore()
if (import.meta.server && Object.keys(featureFlagsStore.flags).length === 0) { // Load flags if they haven't been loaded yet
if (!featureFlagsStore.isLoaded) {
await featureFlagsStore.fetchFlags() await featureFlagsStore.fetchFlags()
} }
}) })

View File

@ -1,5 +1,11 @@
export default defineNuxtRouteMiddleware(() => { export default defineNuxtRouteMiddleware(async () => {
const authStore = useAuthStore() const authStore = useAuthStore()
const featureFlagsStore = useFeatureFlagsStore()
// Ensure feature flags are loaded
if (!featureFlagsStore.isLoaded) {
await featureFlagsStore.fetchFlags()
}
if (useFeatureFlag('self_hosted')) { if (useFeatureFlag('self_hosted')) {
if (authStore.check && authStore.user?.email === 'admin@opnform.com') { if (authStore.check && authStore.user?.email === 'admin@opnform.com') {

View File

@ -8,7 +8,6 @@
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@codemirror/lang-html": "^6.4.9", "@codemirror/lang-html": "^6.4.9",
"@hcaptcha/vue3-hcaptcha": "^1.3.0",
"@iconify-json/material-symbols": "^1.2.4", "@iconify-json/material-symbols": "^1.2.4",
"@nuxt/ui": "^2.19.2", "@nuxt/ui": "^2.19.2",
"@pinia/nuxt": "^0.5.5", "@pinia/nuxt": "^0.5.5",
@ -1338,18 +1337,6 @@
} }
} }
}, },
"node_modules/@hcaptcha/vue3-hcaptcha": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@hcaptcha/vue3-hcaptcha/-/vue3-hcaptcha-1.3.0.tgz",
"integrity": "sha512-IEonS6JiYdU7uy6aeib8cYtMO4nj8utwStbA9bWHyYbOvOvhpkV+AW8vfSKh6SntYxqle/TRwhv+kU9p92CfsA==",
"license": "MIT",
"dependencies": {
"vue": "^3.2.19"
},
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/@headlessui/tailwindcss": { "node_modules/@headlessui/tailwindcss": {
"version": "0.2.1", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/@headlessui/tailwindcss/-/tailwindcss-0.2.1.tgz", "resolved": "https://registry.npmjs.org/@headlessui/tailwindcss/-/tailwindcss-0.2.1.tgz",

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="bg-white"> <div class="bg-white" v-if="workspace">
<div class="flex bg-gray-50 pb-5 border-b"> <div class="flex bg-gray-50 pb-5 border-b">
<div class="w-full md:w-4/5 lg:w-3/5 md:mx-auto md:max-w-4xl p-4"> <div class="w-full md:w-4/5 lg:w-3/5 md:mx-auto md:max-w-4xl p-4">
<div class="pt-4 pb-0"> <div class="pt-4 pb-0">
@ -8,7 +8,7 @@
Your Forms Your Forms
</h2> </h2>
<v-button <v-button
v-if="!workspace.is_readonly" v-if="!workspace?.is_readonly"
v-track.create_form_click v-track.create_form_click
:to="{ name: 'forms-create' }" :to="{ name: 'forms-create' }"
> >
@ -87,7 +87,7 @@
again. again.
</div> </div>
<v-button <v-button
v-if="!workspace.is_readonly && forms.length === 0" v-if="!workspace?.is_readonly && forms.length === 0"
v-track.create_form_click v-track.create_form_click
class="mt-4" class="mt-4"
:to="{ name: 'forms-create' }" :to="{ name: 'forms-create' }"
@ -182,7 +182,7 @@
</div> </div>
</div> </div>
<div <div
v-if="!workspace.is_pro" v-if="!workspace?.is_pro"
class="px-4" class="px-4"
> >
<UAlert <UAlert

View File

@ -1,62 +1,74 @@
<template> <template>
<modal <div class=" bg-gray-50 flex flex-col justify-center sm:px-6 lg:px-8 py-10 flex-grow">
:show="showModal" <div class="sm:mx-auto sm:w-full sm:max-w-md">
max-width="lg" <h2 class="text-center text-3xl font-bold tracking-tight text-gray-900">
@close="logout"
>
<div class="">
<h2 class="font-medium text-3xl mb-3">
Welcome to OpnForm! Welcome to OpnForm!
</h2> </h2>
<p class="text-sm text-gray-600"> <p class="mt-2 text-center text-sm text-gray-600">
You're using the self-hosted version of OpnForm and need to set up your account. You're using the self-hosted version of OpnForm and need to set up your account.
Please enter your email and create a password to continue. Please enter your email and create a password to continue.
</p> </p>
</div> </div>
<form <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
class="mt-4" <div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
@submit.prevent="updateCredentials" <form
@keydown="form.onKeydown($event)" @submit.prevent="updateCredentials"
> @keydown="form.onKeydown($event)"
<!-- Email --> >
<text-input <!-- Email -->
name="email" <text-input
:form="form" name="email"
label="Email" :form="form"
:required="true" label="Email"
placeholder="Your email address" :required="true"
/> placeholder="Your email address"
/>
<!-- Password --> <!-- Password -->
<text-input <text-input
native-type="password" native-type="password"
placeholder="Your password" placeholder="Your password"
name="password" name="password"
:form="form" :form="form"
label="Password" label="Password"
:required="true" :required="true"
/> />
<!-- Password Confirmation--> <!-- Password Confirmation-->
<text-input <text-input
native-type="password" native-type="password"
:form="form" :form="form"
:required="true" :required="true"
placeholder="Enter confirm password" placeholder="Enter confirm password"
name="password_confirmation" name="password_confirmation"
label="Confirm Password" label="Confirm Password"
/> />
<!-- Submit Button --> <!-- Submit Button -->
<v-button <div class="mt-6">
class="mx-auto" <v-button
:loading="form.busy || loading" class="w-full justify-center"
> :loading="form.busy || loading"
Update Credentials >
</v-button> Update Credentials
</form> </v-button>
</modal> </div>
<!-- Cancel Link -->
<div class="mt-4 text-center">
<button
type="button"
class="text-sm text-gray-600 hover:text-gray-900"
@click="logout"
>
Cancel and return to login
</button>
</div>
</form>
</div>
</div>
</div>
</template> </template>
<script setup> <script setup>
@ -67,7 +79,7 @@ const workspacesStore = useWorkspacesStore()
const formsStore = useFormsStore() const formsStore = useFormsStore()
const user = computed(() => authStore.user) const user = computed(() => authStore.user)
const router = useRouter() const router = useRouter()
const showModal = ref(true) const loading = ref(false)
const form = useForm({ const form = useForm({
name: "", name: "",
email: "", email: "",
@ -82,6 +94,7 @@ onMounted(() => {
}) })
const updateCredentials = () => { const updateCredentials = () => {
loading.value = true
form form
.post("update-credentials") .post("update-credentials")
.then(async (data) => { .then(async (data) => {
@ -95,11 +108,13 @@ const updateCredentials = () => {
console.error(error) console.error(error)
useAlert().error(error.response._data.message) useAlert().error(error.response._data.message)
}) })
.finally(() => {
loading.value = false
})
} }
const logout = () => { const logout = () => {
authStore.logout() authStore.logout()
showModal.value = false
router.push({ name: "login" }) router.push({ name: "login" })
} }
</script> </script>

View File

@ -3,11 +3,15 @@ import { ref } from 'vue'
export const useFeatureFlagsStore = defineStore('feature_flags', () => { export const useFeatureFlagsStore = defineStore('feature_flags', () => {
const flags = ref({}) const flags = ref({})
const isLoaded = ref(false)
async function fetchFlags() { async function fetchFlags() {
if (isLoaded.value) return
try { try {
const { data } = await useOpnApi('content/feature-flags') const { data } = await useOpnApi('content/feature-flags')
flags.value = data.value flags.value = data.value
isLoaded.value = true
} catch (error) { } catch (error) {
console.error('Failed to fetch feature flags:', error) console.error('Failed to fetch feature flags:', error)
} }
@ -20,5 +24,5 @@ export const useFeatureFlagsStore = defineStore('feature_flags', () => {
}, flags.value) }, flags.value)
} }
return { flags, fetchFlags, getFlag } return { flags, isLoaded, fetchFlags, getFlag }
}) })

View File

@ -1,35 +1,70 @@
services: services:
api: &api-base api: &api-base
image: jhumanj/opnform-api:dev image: jhumanj/opnform-api:dev
container_name: opnform-api
build: build:
context: . context: .
dockerfile: docker/Dockerfile.api dockerfile: docker/Dockerfile.api
args: args:
APP_ENV: local - APP_ENV=local
volumes: volumes: &api-base-volumes
- ./api:/usr/share/nginx/html:delegated - ./api:/usr/share/nginx/html:delegated
- /usr/share/nginx/html/vendor # Exclude vendor directory from the mount - /usr/share/nginx/html/vendor # Exclude vendor directory from the mount
- ./api/storage:/usr/share/nginx/html/storage:delegated # Mount storage directory directly - ./api/storage:/usr/share/nginx/html/storage:delegated
environment: environment: &api-base-env
APP_ENV: local
APP_DEV_CORS: "true"
IS_API_WORKER: "false"
# Database settings
DB_HOST: db DB_HOST: db
REDIS_HOST: redis REDIS_HOST: redis
DB_DATABASE: ${DB_DATABASE:-forge} DB_DATABASE: ${DB_DATABASE:-forge}
DB_USERNAME: ${DB_USERNAME:-forge} DB_USERNAME: ${DB_USERNAME:-forge}
DB_PASSWORD: ${DB_PASSWORD:-forge} DB_PASSWORD: ${DB_PASSWORD:-forge}
DB_CONNECTION: ${DB_CONNECTION:-pgsql} DB_CONNECTION: ${DB_CONNECTION:-pgsql}
# Storage settings
FILESYSTEM_DISK: local FILESYSTEM_DISK: local
LOCAL_FILESYSTEM_VISIBILITY: public LOCAL_FILESYSTEM_VISIBILITY: public
APP_ENV: local # Development settings
PHP_IDE_CONFIG: "serverName=Docker" PHP_IDE_CONFIG: serverName=Docker
XDEBUG_MODE: "${XDEBUG_MODE:-off}" XDEBUG_MODE: ${XDEBUG_MODE:-off}
XDEBUG_CONFIG: "client_host=host.docker.internal" XDEBUG_CONFIG: client_host=host.docker.internal
APP_URL: "http://localhost" APP_URL: http://localhost
FRONT_URL: http://localhost:3000
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy
redis:
condition: service_started
api-worker:
<<: *api-base
container_name: opnform-api-worker
command: ["php", "artisan", "queue:work"]
volumes: *api-base-volumes
environment:
<<: *api-base-env
APP_ENV: local
IS_API_WORKER: "true"
api-scheduler:
<<: *api-base
container_name: opnform-api-scheduler
command: ["php", "artisan", "schedule:work"]
volumes: *api-base-volumes
environment:
<<: *api-base-env
APP_ENV: local
IS_API_WORKER: "true"
AUTORUN_ENABLED: "false"
# Scheduler settings
CONTAINER_ROLE: scheduler
PHP_MEMORY_LIMIT: 512M
PHP_MAX_EXECUTION_TIME: 60
ui: ui:
image: jhumanj/opnform-client:dev image: jhumanj/opnform-client:dev
container_name: opnform-client
build: build:
context: . context: .
dockerfile: docker/Dockerfile.client dockerfile: docker/Dockerfile.client
@ -57,6 +92,8 @@ services:
- "24678:24678" # Vite HMR port - "24678:24678" # Vite HMR port
ingress: ingress:
image: nginx:1
container_name: opnform-ingress
volumes: volumes:
- ./docker/nginx.dev.conf:/etc/nginx/templates/default.conf.template - ./docker/nginx.dev.conf:/etc/nginx/templates/default.conf.template
environment: environment:
@ -65,13 +102,7 @@ services:
ports: ports:
- "80:80" - "80:80"
depends_on: depends_on:
- api api:
- ui condition: service_started
ui:
api-worker: condition: service_started
<<: *api-base
environment:
IS_API_WORKER: "true"
depends_on:
db:
condition: service_healthy

View File

@ -1,55 +1,81 @@
--- ---
services: services:
api: &api api: &api-environment
image: jhumanj/opnform-api:latest image: jhumanj/opnform-api:latest
container_name: opnform-api
build: build:
context: . context: .
dockerfile: docker/Dockerfile.api dockerfile: docker/Dockerfile.api
args: args:
APP_ENV: production - APP_ENV=production
environment: &api-environment # Add this anchor volumes: &api-environment-volumes
- ./api/storage:/usr/share/nginx/html/storage:rw
- ./api/bootstrap/cache:/usr/share/nginx/html/bootstrap/cache:rw
environment: &api-env
APP_ENV: production
IS_API_WORKER: "false"
# Database settings
DB_HOST: db DB_HOST: db
REDIS_HOST: redis REDIS_HOST: redis
DB_DATABASE: ${DB_DATABASE:-forge} DB_DATABASE: ${DB_DATABASE:-forge}
DB_USERNAME: ${DB_USERNAME:-forge} DB_USERNAME: ${DB_USERNAME:-forge}
DB_PASSWORD: ${DB_PASSWORD:-forge} DB_PASSWORD: ${DB_PASSWORD:-forge}
DB_CONNECTION: ${DB_CONNECTION:-pgsql} DB_CONNECTION: ${DB_CONNECTION:-pgsql}
# Storage settings
FILESYSTEM_DISK: local FILESYSTEM_DISK: local
LOCAL_FILESYSTEM_VISIBILITY: public LOCAL_FILESYSTEM_VISIBILITY: public
env_file: env_file:
- ./api/.env - ./api/.env
volumes: depends_on:
- opnform_storage:/usr/share/nginx/html/storage:rw db:
condition: service_healthy
redis:
condition: service_started
api-worker: api-worker:
image: jhumanj/opnform-api:latest <<: *api-environment
build: container_name: opnform-api-worker
context: . command: ["php", "artisan", "queue:work"]
dockerfile: docker/Dockerfile.api volumes: *api-environment-volumes
args:
APP_ENV: production
command: php artisan queue:work
environment: environment:
<<: *api-environment <<: *api-env
APP_ENV: production
IS_API_WORKER: "true" IS_API_WORKER: "true"
env_file: env_file:
- ./api/.env - ./api/.env
volumes:
- opnform_storage:/usr/share/nginx/html/storage:rw api-scheduler:
<<: *api-environment
container_name: opnform-api-scheduler
command: ["php", "artisan", "schedule:work"]
volumes: *api-environment-volumes
environment:
<<: *api-env
APP_ENV: production
IS_API_WORKER: "true"
# Scheduler settings
CONTAINER_ROLE: scheduler
PHP_MEMORY_LIMIT: 512M
PHP_MAX_EXECUTION_TIME: 60
env_file:
- ./api/.env
ui: ui:
image: jhumanj/opnform-client:latest image: jhumanj/opnform-client:latest
container_name: opnform-client
build: build:
context: . context: .
dockerfile: docker/Dockerfile.client dockerfile: docker/Dockerfile.client
env_file: env_file:
- ./client/.env - ./client/.env
redis: redis:
image: redis:7 image: redis:7
container_name: opnform-redis
db: db:
image: postgres:16 image: postgres:16
container_name: opnform-db
environment: environment:
POSTGRES_DB: ${DB_DATABASE:-forge} POSTGRES_DB: ${DB_DATABASE:-forge}
POSTGRES_USER: ${DB_USERNAME:-forge} POSTGRES_USER: ${DB_USERNAME:-forge}
@ -64,10 +90,16 @@ services:
ingress: ingress:
image: nginx:1 image: nginx:1
container_name: opnform-ingress
volumes: volumes:
- ./docker/nginx.conf:/etc/nginx/templates/default.conf.template - ./docker/nginx.conf:/etc/nginx/templates/default.conf.template
ports: ports:
- 80:80 - 80:80
depends_on:
api:
condition: service_started
ui:
condition: service_started
volumes: volumes:
postgres-data: postgres-data:

View File

@ -1,65 +1,58 @@
# Stage 1: Composer dependencies
FROM composer:latest as composer
WORKDIR /app
COPY api/composer.* ./
ARG APP_ENV=production
RUN if [ "$APP_ENV" = "production" ]; then \
composer install --ignore-platform-req=php --no-dev --optimize-autoloader; \
else \
composer install --ignore-platform-req=php --optimize-autoloader; \
fi
# Stage 2: Final image
FROM php:8.3-fpm FROM php:8.3-fpm
# Install system dependencies and PHP extensions # Install system dependencies
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y \
git \ libzip-dev \
curl \
libpng-dev \ libpng-dev \
libonig-dev \ postgresql-client \
libxml2-dev \
zip \
unzip \
libpq-dev \ libpq-dev \
&& docker-php-ext-install pdo_pgsql mbstring exif pcntl bcmath gd \ unzip \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install composer
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
ENV COMPOSER_ALLOW_SUPERUSER=1
# Install PHP extensions
RUN docker-php-ext-install pdo pgsql pdo_pgsql gd bcmath zip \
&& pecl install redis \ && pecl install redis \
&& docker-php-ext-enable redis && docker-php-ext-enable redis
# Install xdebug if not in production WORKDIR /usr/share/nginx/html/
ARG APP_ENV=production
RUN if [ "$APP_ENV" != "production" ]; then \ # Create storage directories
pecl install xdebug && \ RUN mkdir -p storage/framework/sessions \
docker-php-ext-enable xdebug; \ storage/framework/views \
storage/framework/cache \
storage/logs \
storage/app/public \
bootstrap/cache \
&& chown -R www-data:www-data storage bootstrap/cache \
&& chmod -R 775 storage bootstrap/cache
# Copy composer files and helpers.php first
COPY api/composer.json api/composer.lock ./
COPY api/app/helpers.php ./app/helpers.php
# Install dependencies without running scripts
RUN if [ "$APP_ENV" = "production" ] ; then \
composer install --no-dev --no-scripts --ignore-platform-req=php --optimize-autoloader ; \
else \
composer install --no-scripts --ignore-platform-req=php --optimize-autoloader ; \
fi fi
# Configure PHP # Copy the rest of the application
COPY docker/php/php.ini /usr/local/etc/php/conf.d/app.ini COPY api/ .
COPY docker/php/php-fpm.conf /usr/local/etc/php-fpm.d/www.conf
WORKDIR /usr/share/nginx/html # Run composer scripts and clear cache
RUN composer dump-autoload -o \
# Copy application files && php artisan package:discover --ansi \
COPY api/artisan artisan && composer clear-cache \
COPY api/bootstrap ./bootstrap && chmod -R 775 storage \
COPY api/config ./config && chown -R www-data:www-data storage
COPY api/app ./app
COPY api/database ./database
COPY api/public ./public
COPY api/routes ./routes
COPY api/tests ./tests
COPY api/resources ./resources
COPY api/storage ./storage
# Copy vendor directory from composer stage
COPY --from=composer /app/vendor ./vendor
# Set permissions
RUN chmod -R 775 storage \
&& chmod -R 775 bootstrap/cache \
&& chown -R www-data:www-data /usr/share/nginx/html
# Setup entrypoint
COPY docker/php-fpm-entrypoint /usr/local/bin/opnform-entrypoint COPY docker/php-fpm-entrypoint /usr/local/bin/opnform-entrypoint
RUN chmod a+x /usr/local/bin/* RUN chmod a+x /usr/local/bin/*

View File

@ -4,18 +4,18 @@ map $original_uri $api_uri {
} }
server { server {
listen 80; listen 80;
server_name opnform; server_name opnform;
root /usr/share/nginx/html/public; root /usr/share/nginx/html/public;
access_log /dev/stdout; access_log /dev/stdout;
error_log /dev/stderr error; error_log /dev/stderr error;
index index.html index.htm index.php; index index.html index.htm index.php;
location / { location / {
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_pass http://ui:3000; proxy_pass http://opnform-client:3000;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port; proxy_set_header X-Forwarded-Port $server_port;
@ -24,16 +24,22 @@ server {
} }
location ~/(api|open|local\/temp|forms\/assets)/ { location ~/(api|open|local\/temp|forms\/assets)/ {
try_files $uri $uri/ /index.php?$query_string; set $original_uri $uri;
try_files $uri $uri/ /index.php$is_args$args;
} }
location ~ \.php$ { location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass api:9000; fastcgi_pass opnform-api:9000;
fastcgi_index index.php; fastcgi_index index.php;
include fastcgi_params; include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html/public/index.php; fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_param REQUEST_URI $api_uri; fastcgi_param REQUEST_URI $api_uri;
} }
# Deny access to . files
location ~ /\. {
deny all;
}
} }

View File

@ -4,35 +4,16 @@ map $original_uri $api_uri {
} }
server { server {
listen 80; listen 80;
server_name opnform; server_name localhost;
root /usr/share/nginx/html/public; root /usr/share/nginx/html/public;
access_log /dev/stdout; access_log /dev/stdout;
error_log /dev/stderr error; error_log /dev/stderr error;
index index.html index.htm index.php; index index.html index.htm index.php;
# Development CORS headers # Frontend proxy
add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS, PATCH' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,X-XSRF-TOKEN' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always;
# Handle preflight requests
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS, PATCH' always;
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,X-XSRF-TOKEN' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
# Development proxy settings
location / { location / {
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_pass http://ui:3000; proxy_pass http://ui:3000;
@ -43,23 +24,23 @@ server {
proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port; proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; proxy_set_header Connection "Upgrade";
proxy_read_timeout 86400;
proxy_buffering off;
} }
# HMR websocket support # HMR websocket support
location /_nuxt { location /_nuxt {
proxy_pass http://ui:3000; proxy_pass http://ui:3000;
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade; proxy_cache_bypass $http_upgrade;
} }
# API handling
location ~/(api|open|local\/temp|forms\/assets)/ { location ~/(api|open|local\/temp|forms\/assets)/ {
try_files $uri $uri/ /index.php?$query_string; set $original_uri $uri;
try_files $uri $uri/ /index.php$is_args$args;
} }
location ~ \.php$ { location ~ \.php$ {
@ -67,7 +48,13 @@ server {
fastcgi_pass api:9000; fastcgi_pass api:9000;
fastcgi_index index.php; fastcgi_index index.php;
include fastcgi_params; include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html/public/index.php; fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_param REQUEST_URI $api_uri; fastcgi_param REQUEST_URI $api_uri;
fastcgi_read_timeout 300;
}
# Deny access to . files
location ~ /\. {
deny all;
} }
} }

View File

@ -10,7 +10,7 @@ main() {
prep_storage prep_storage
wait_for_db wait_for_db
apply_db_migrations apply_db_migrations
run_init_project run_init_project
run_server "$@" run_server "$@"
fi fi
} }
@ -49,7 +49,7 @@ wait_for_db() {
apply_db_migrations() { apply_db_migrations() {
echo "Running DB Migrations" echo "Running DB Migrations"
./artisan migrate ./artisan migrate --force
} }
run_init_project() { run_init_project() {
@ -59,7 +59,7 @@ run_init_project() {
run_server() { run_server() {
echo "Starting server $@" echo "Starting server $@"
exec /usr/local/bin/docker-php-entrypoint "$@" exec "$@"
} }
main "$@" main "$@"

15
docker/php/php-fpm.conf Normal file
View File

@ -0,0 +1,15 @@
[www]
user = www-data
group = www-data
listen = 9000
pm = dynamic
pm.max_children = 20
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 1000
clear_env = no
catch_workers_output = yes
decorate_workers_output = no

27
docker/php/php.ini Normal file
View File

@ -0,0 +1,27 @@
; PHP Configuration
memory_limit = 512M
max_execution_time = 60
upload_max_filesize = 64M
post_max_size = 64M
max_input_vars = 3000
; Error reporting
error_reporting = E_ALL
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /dev/stderr
; Date
date.timezone = UTC
; Session
session.save_handler = redis
session.save_path = "tcp://redis:6379"
; OpCache
opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=0

View File

@ -10,13 +10,15 @@ import CloudVersion from "/snippets/cloud-version.mdx";
## Overview ## Overview
OpnForm provides a Docker-based development environment that offers: OpnForm provides a Docker-based development environment that offers:
- Hot-reload for both frontend and backend - Hot Module Replacement (HMR) for real-time frontend updates
- Vue DevTools integration for debugging
- PHP hot reload for backend changes
- Xdebug support for PHP debugging - Xdebug support for PHP debugging
- Automatic dependency management - Automatic dependency management
- PostgreSQL database and Redis setup - PostgreSQL database and Redis setup
- Nginx reverse proxy configuration - Nginx reverse proxy configuration
This is the recommended way to get started with OpnForm development. For details about the Docker architecture and components, see our [Docker Deployment](/deployment/docker) guide.
## Prerequisites ## Prerequisites
@ -32,18 +34,19 @@ This is the recommended way to get started with OpnForm development.
cd OpnForm cd OpnForm
``` ```
2. Create environment files: 2. Run the setup script in development mode:
```bash ```bash
./scripts/setup-env.sh chmod +x scripts/docker-setup.sh
``` ./scripts/docker-setup.sh --dev
This will create the necessary `.env` files for both the API and client. See our [Environment Variables](/configuration/environment-variables) guide for configuration details.
3. Start the development environment:
```bash
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
``` ```
4. Access your development environment: This script will:
- Create necessary environment files
- Pull or build required Docker images
- Start all containers in development mode
- Display access information
3. Access your development environment:
- Frontend: http://localhost:3000 - Frontend: http://localhost:3000
- API: http://localhost/api - API: http://localhost/api
@ -60,28 +63,54 @@ You will be prompted to change your email and password after your first login.
## Development Features ## Development Features
### Hot Reload ### Frontend Development
The development setup includes hot reload capabilities: The development setup includes advanced frontend features:
- Frontend (Nuxt.js): Changes to files in the `client` directory trigger automatic rebuilds - **Hot Module Replacement (HMR)**: Changes to Vue components and styles are instantly reflected without page reload
- Backend (Laravel): Changes to files in the `api` directory are immediately reflected, except for queued jobs which require restarting the api-worker container (`docker compose -f docker-compose.yml -f docker-compose.dev.yml restart api-worker`) - **Vue DevTools**: Full integration for component inspection and state management debugging ([learn more](https://devtools.vuejs.org/))
- **Source Maps**: Enabled for easier debugging
- **Fast Refresh**: Preserves component state during updates
- **Error Overlay**: Displays errors directly in the browser
### File Structure ### Backend Development
The Laravel API service provides:
- **PHP Hot Reload**: Changes to PHP files are immediately available
- **Xdebug Integration**: Ready for step-by-step debugging
- **Laravel Telescope**: Available for request and queue monitoring
- **Artisan Commands**: Direct access to Laravel's CLI tools
<Note>
Queue workers require restart after code changes:
```bash
docker compose -f docker-compose.yml -f docker-compose.dev.yml restart api-worker
```
</Note>
### Development URLs
- **Frontend**: http://localhost:3000
- Direct access to Nuxt dev server
- Includes HMR websocket connection
- Vue DevTools available
- **API**: http://localhost/api
- Handled by Nginx reverse proxy
- Automatic routing to PHP-FPM
- Supports file uploads and long requests
## File Structure
The development setup mounts your local directories into the containers: The development setup mounts your local directories into the containers:
- `./api`: Mounted to the API container with vendor directory preserved ```
- `./client`: Mounted to the UI container with node_modules preserved OpnForm/
- Database and Redis data are persisted through Docker volumes ├── api/ # Laravel API (mounted to api container)
│ ├── vendor/ # Preserved in container
### Container Services │ └── storage/ # Mounted for logs and uploads
├── client/ # Nuxt frontend (mounted to ui container)
The development environment includes: │ └── node_modules/ # Preserved in container
- `api`: Laravel API service with hot reload └── docker/ # Docker configuration files
- `ui`: Nuxt.js frontend with HMR ```
- `api-worker`: Laravel queue worker
- `db`: PostgreSQL database
- `redis`: Redis server
- `ingress`: Nginx reverse proxy
## Common Tasks ## Common Tasks
@ -95,6 +124,9 @@ docker compose -f docker-compose.yml -f docker-compose.dev.yml exec api php arti
# NPM commands # NPM commands
docker compose -f docker-compose.yml -f docker-compose.dev.yml exec ui npm [command] docker compose -f docker-compose.yml -f docker-compose.dev.yml exec ui npm [command]
# Database commands
docker compose -f docker-compose.yml -f docker-compose.dev.yml exec db psql -U forge
``` ```
### Accessing Logs ### Accessing Logs
@ -105,35 +137,48 @@ View container logs:
# All containers # All containers
docker compose -f docker-compose.yml -f docker-compose.dev.yml logs -f docker compose -f docker-compose.yml -f docker-compose.dev.yml logs -f
# Specific container # Specific container (e.g., frontend)
docker compose -f docker-compose.yml -f docker-compose.dev.yml logs -f [service] docker compose -f docker-compose.yml -f docker-compose.dev.yml logs -f ui
``` ```
### Database Management ### Database Access
The PostgreSQL database is accessible: The PostgreSQL database is accessible:
- From containers: `host=db` - From containers: `host=db`
- From your machine: `localhost:5432` - From your machine: `localhost:5432`
- Default credentials: username=forge, password=forge, database=forge - Default credentials:
```
Host: localhost
Port: 5432
Database: forge
Username: forge
Password: forge
```
## Troubleshooting ## Troubleshooting
### Container Issues ### Container Issues
If containers aren't starting properly: If containers aren't starting properly:
```bash ```bash
# Remove all containers and volumes # Clean everything and restart
docker compose down -v ./scripts/docker-setup.sh --dev
# Rebuild and start
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build
``` ```
### Permission Issues ### Permission Issues
If you encounter permission issues with storage or vendor directories: If you encounter permission issues:
```bash ```bash
# Fix storage permissions # Fix storage permissions
docker compose -f docker-compose.yml -f docker-compose.dev.yml exec api chmod -R 775 storage docker compose -f docker-compose.yml -f docker-compose.dev.yml exec api chmod -R 775 storage
# Fix vendor permissions # Fix vendor permissions
docker compose -f docker-compose.yml -f docker-compose.dev.yml exec api chmod -R 775 vendor docker compose -f docker-compose.yml -f docker-compose.dev.yml exec api chmod -R 775 vendor
``` ```
### HMR Issues
If hot reload isn't working:
1. Check browser console for WebSocket errors
2. Ensure ports 3000 and 24678 are available
3. Try restarting the UI container:
```bash
docker compose -f docker-compose.yml -f docker-compose.dev.yml restart ui
```

View File

@ -8,49 +8,34 @@ import CloudVersion from "/snippets/cloud-version.mdx";
<CloudVersion/> <CloudVersion/>
<Tip> <Tip>
This guide is for deploying OpnForm on a production server. If you're looking to **develop OpnForm locally**, check out our [Docker Development Setup](/deployment/docker-development) guide which provides **hot-reload and other development features**. Looking to develop OpnForm locally? Check out our [Docker Development Setup](/deployment/docker-development) guide which provides hot-reload and other development features.
</Tip> </Tip>
## Prerequisites
- Docker
- Docker Compose
## Quick Start ## Quick Start
1. Clone the repository: 1. Clone the repository:
```bash ```bash
git clone https://github.com/JhumanJ/OpnForm.git git clone https://github.com/JhumanJ/OpnForm.git
cd OpnForm cd OpnForm
``` ```
2. Set up environment files: 2. Run the setup script:
```bash ```bash
./scripts/setup-env.sh --docker chmod +x scripts/docker-setup.sh
./scripts/docker-setup.sh
``` ```
3. (Optional) Customize the environment variables: The script will:
- Create necessary environment files
You can modify two environment files to customize your OpnForm installation: - Pull required Docker images
- The `.env` file in the `api` directory for backend configuration - Start all containers in production mode
- The `.env` file in the `client` directory for frontend configuration - Display access information
For more information on available environment variables, please refer to our [Environment Variables](/configuration/environment-variables) page.
4. Start the application:
```bash
docker compose up -d
```
5. Access OpnForm at http://localhost
3. Access your OpnForm instance at `http://localhost`
### Initial Login ### Initial Login
After installation, use these credentials to access the app: After deployment, use these credentials to access the app:
- Email: `admin@opnform.com` - Email: `admin@opnform.com`
- Password: `password` - Password: `password`
@ -59,71 +44,171 @@ You will be prompted to change your email and password after your first login.
<Note>Public registration is disabled in the self-hosted version. Use the admin account to invite additional users.</Note> <Note>Public registration is disabled in the self-hosted version. Use the admin account to invite additional users.</Note>
## Architecture
```mermaid
graph TD
A[Nginx Proxy] --> B[Frontend - Nuxt SSR]
A --> C[Backend - Laravel API]
C --> D[PostgreSQL]
C --> E[Redis]
C --> F[Queue Worker]
C --> G[Scheduler]
```
### Components
<Tabs>
<Tab title="Frontend">
The Nuxt frontend service:
- Server-Side Rendered application
- Built with Vue 3 and Tailwind CSS
- Handles dynamic rendering and client-side interactivity
- Optimized for production performance
</Tab>
<Tab title="Backend">
The Laravel API service:
- Handles business logic and data persistence
- Provides REST API endpoints
- Manages file uploads and processing
- Includes required PHP extensions (pgsql, redis, etc.)
- Configured for PostgreSQL and Redis connections
</Tab>
<Tab title="Workers">
Background processing services:
- **API Worker**: Processes queued jobs (emails, exports, etc.)
- **API Scheduler**: Handles scheduled tasks and periodic cleanups
- Both share the same codebase as the main API
</Tab>
<Tab title="Databases">
Data storage services:
- **PostgreSQL**: Primary database for all application data
- **Redis**: Used for:
- Session storage
- Cache
- Queue management
- Real-time features
</Tab>
<Tab title="Proxy">
The Nginx proxy service:
- Routes requests between frontend and backend
- Handles SSL termination
- Manages file upload limits
- Serves static assets
- Configured for optimal performance
</Tab>
</Tabs>
## Docker Images ## Docker Images
OpnForm provides pre-built Docker images for easy deployment. You can find our official Docker images on Docker Hub: OpnForm provides pre-built Docker images for easy deployment:
- [OpnForm API Image](https://hub.docker.com/r/jhumanj/opnform-api) - [OpnForm API Image](https://hub.docker.com/r/jhumanj/opnform-api)
- [OpnForm Client Image](https://hub.docker.com/r/jhumanj/opnform-client) - [OpnForm Client Image](https://hub.docker.com/r/jhumanj/opnform-client)
We recommend using these official images for your OpnForm deployment. ### Building Custom Images
While we recommend using the official images, you can build custom images if needed:
## Building Your Own Docker Images
By default, OpnForm uses pre-built images from Docker Hub. However, if you want to build the images yourself (for development or customization), you have two options:
### Option 1: Using Docker Compose (Recommended)
The simplest way to build the images is using Docker Compose:
```bash ```bash
# Build all images
docker compose build docker compose build
# Or build specific images
docker build -t opnform-api:local -f docker/Dockerfile.api .
docker build -t opnform-ui:local -f docker/Dockerfile.client .
``` ```
This will build all the necessary images. You can then start the application as usual: ### Custom Configuration
Create a `docker-compose.override.yml` to customize your deployment:
```bash
docker compose up -d
```
### Option 2: Building Images Manually
You can also build the images manually using the provided Dockerfiles:
1. Build the API image:
```bash
docker build -t opnform-api:local -f docker/Dockerfile.api .
```
2. Build the UI image:
```bash
docker build -t opnform-ui:local -f docker/Dockerfile.client .
```
If you build the images manually, make sure to update your docker-compose.override.yml to use these local images (see below).
### Overriding Docker Compose Configuration
You can override the default Docker Compose configuration by creating a `docker-compose.override.yml` file. This allows you to customize various aspects of the deployment without modifying the main `docker-compose.yml` file.
Example `docker-compose.override.yml`:
```yaml ```yaml
services: services:
api: api:
image: opnform-api:local image: opnform-api:local
environment:
PHP_MEMORY_LIMIT: 1G
ui: ui:
image: opnform-ui:local image: opnform-ui:local
api-worker: ingress:
image: opnform-api:local volumes:
- ./custom-nginx.conf:/etc/nginx/conf.d/default.conf
``` ```
## Maintenance
### Clearing all resources ### Updates
To completely remove all Docker containers, networks, and volumes created by Docker Compose and also remove all images used by these services, you can use the following command: 1. Pull latest changes:
```bash
git pull origin main
```
2. Update containers:
```bash
docker compose pull
docker compose up -d
```
### Monitoring
View container logs:
```bash
# All containers
docker compose logs -f
# Specific container
docker compose logs -f api
``` ```
docker compose down -v --rmi all
Monitor container health:
```bash
docker compose ps
```
## Troubleshooting
### Container Issues
If containers aren't starting:
```bash
# View detailed logs
docker compose logs -f
# Recreate containers
docker compose down
docker compose up -d
```
### Database Issues
If database connections fail:
```bash
# Check database status
docker compose exec db pg_isready
# View database logs
docker compose logs db
```
### Cache Issues
Clear various caches:
```bash
# Clear application cache
docker compose exec api php artisan cache:clear
# Clear config cache
docker compose exec api php artisan config:clear
# Clear route cache
docker compose exec api php artisan route:clear
```
### Permission Issues
Fix storage permissions:
```bash
docker compose exec api chown -R www-data:www-data storage
docker compose exec api chmod -R 775 storage
``` ```

69
scripts/docker-setup.sh Executable file
View File

@ -0,0 +1,69 @@
#!/bin/bash
set -e
# Colors for output
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
NC='\033[0m'
# ASCII Art
echo -e "${BLUE}"
cat << "EOF"
____ ______
/ __ \____ ____ / ____/___ _________ ___
/ / / / __ \/ __ \/ /_ / __ \/ ___/ __ `__ \
/ /_/ / /_/ / / / / __/ / /_/ / / / / / / / /
\____/ .___/_/ /_/_/ \____/_/ /_/ /_/ /_/
/_/
EOF
echo -e "${NC}"
# Default values
DEV_MODE=false
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
# Parse command line arguments
while [[ "$#" -gt 0 ]]; do
case $1 in
--dev) DEV_MODE=true ;;
*) echo "Unknown parameter: $1"; exit 1 ;;
esac
shift
done
cd "$PROJECT_ROOT"
echo -e "${BLUE}Starting OpnForm Docker setup...${NC}"
# Run the environment setup script with --docker flag
echo -e "${GREEN}Setting up environment files...${NC}"
bash "$SCRIPT_DIR/setup-env.sh" --docker
# Determine which compose files to use
COMPOSE_FILES="-f docker-compose.yml"
if [ "$DEV_MODE" = true ]; then
echo -e "${YELLOW}Development mode enabled - using docker-compose.dev.yml${NC}"
COMPOSE_FILES="$COMPOSE_FILES -f docker-compose.dev.yml"
fi
# Start Docker containers
echo -e "${GREEN}Starting Docker containers...${NC}"
docker compose $COMPOSE_FILES up -d
# Display access instructions
if [ "$DEV_MODE" = true ]; then
echo -e "${BLUE}Development environment setup complete!${NC}"
echo -e "${YELLOW}Please wait for the frontend to finish building (this may take a few minutes)${NC}"
echo -e "${GREEN}Then visit: http://localhost:3000${NC}"
else
echo -e "${BLUE}Production environment setup complete!${NC}"
echo -e "${YELLOW}Please wait a moment for all services to start${NC}"
echo -e "${GREEN}Then visit: http://localhost${NC}"
fi
echo -e "${BLUE}Default admin credentials:${NC}"
echo -e "${GREEN}Email: admin@opnform.com${NC}"
echo -e "${GREEN}Password: password${NC}"