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:
parent
bf85d8fa76
commit
f7df6bc0d7
|
|
@ -2,3 +2,38 @@
|
|||
/Dockerfile
|
||||
/data
|
||||
\.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/
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ on:
|
|||
- "client/**"
|
||||
- "docker/**"
|
||||
- "docker-compose*.yml"
|
||||
- ".github/workflows/dockerhub.yml"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
|
|
@ -30,8 +31,8 @@ jobs:
|
|||
echo "UI_TAGS=${{secrets.DOCKER_UI_REPO}}:latest,${{secrets.DOCKER_UI_REPO}}:${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "VERSION=dev" >> $GITHUB_ENV
|
||||
echo "API_TAGS=${{secrets.DOCKER_API_REPO}}:dev" >> $GITHUB_ENV
|
||||
echo "UI_TAGS=${{secrets.DOCKER_UI_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,${{secrets.DOCKER_UI_REPO}}:dev-${GITHUB_SHA::7}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Check out the repo
|
||||
|
|
@ -59,6 +60,8 @@ jobs:
|
|||
build-args: |
|
||||
APP_ENV=${{ env.VERSION == 'dev' && 'local' || 'production' }}
|
||||
tags: ${{ env.API_TAGS }}
|
||||
cache-from: type=registry,ref=${{secrets.DOCKER_API_REPO}}:dev
|
||||
cache-to: type=inline
|
||||
|
||||
- name: Build and push Client image
|
||||
uses: docker/build-push-action@v5
|
||||
|
|
@ -68,3 +71,5 @@ jobs:
|
|||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ env.UI_TAGS }}
|
||||
cache-from: type=registry,ref=${{secrets.DOCKER_UI_REPO}}:dev
|
||||
cache-to: type=inline
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace App\Http;
|
|||
use App\Http\Middleware\AcceptsJsonMiddleware;
|
||||
use App\Http\Middleware\AuthenticateJWT;
|
||||
use App\Http\Middleware\CustomDomainRestriction;
|
||||
use App\Http\Middleware\DevCorsMiddleware;
|
||||
use App\Http\Middleware\ImpersonationMiddleware;
|
||||
use App\Http\Middleware\IsAdmin;
|
||||
use App\Http\Middleware\IsModerator;
|
||||
|
|
@ -25,6 +26,7 @@ class Kernel extends HttpKernel
|
|||
protected $middleware = [
|
||||
// \App\Http\Middleware\TrustHosts::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
DevCorsMiddleware::class,
|
||||
\Illuminate\Http\Middleware\HandleCors::class,
|
||||
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
|
|
@ -66,7 +68,7 @@ class Kernel extends HttpKernel
|
|||
],
|
||||
|
||||
'api-external' => [
|
||||
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
|
||||
\Illuminate\Routing\Middleware\ThrottleRequests::class . ':api',
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -28,6 +28,8 @@ return [
|
|||
|
||||
'env' => env('APP_ENV', 'production'),
|
||||
|
||||
'dev_cors' => env('APP_DEV_CORS', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Debug Mode
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
/Dockerfile
|
||||
/.dockerignore
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
/>
|
||||
|
||||
<!-- Remember Me -->
|
||||
<div class="relative flex items-start mt-5">
|
||||
<div class="relative flex items-center mt-3">
|
||||
<CheckboxInput
|
||||
v-model="remember"
|
||||
class="w-full md:w-1/2"
|
||||
|
|
@ -52,7 +52,7 @@
|
|||
|
||||
<!-- Submit Button -->
|
||||
<v-button
|
||||
class="w-full flex"
|
||||
class="w-full flex mt-2"
|
||||
:loading="form.busy || loading"
|
||||
>
|
||||
Log in to continue
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ import { useFeatureFlagsStore } from '~/stores/featureFlags'
|
|||
export default defineNuxtRouteMiddleware(async () => {
|
||||
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()
|
||||
}
|
||||
})
|
||||
|
|
@ -1,5 +1,11 @@
|
|||
export default defineNuxtRouteMiddleware(() => {
|
||||
export default defineNuxtRouteMiddleware(async () => {
|
||||
const authStore = useAuthStore()
|
||||
const featureFlagsStore = useFeatureFlagsStore()
|
||||
|
||||
// Ensure feature flags are loaded
|
||||
if (!featureFlagsStore.isLoaded) {
|
||||
await featureFlagsStore.fetchFlags()
|
||||
}
|
||||
|
||||
if (useFeatureFlag('self_hosted')) {
|
||||
if (authStore.check && authStore.user?.email === 'admin@opnform.com') {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@codemirror/lang-html": "^6.4.9",
|
||||
"@hcaptcha/vue3-hcaptcha": "^1.3.0",
|
||||
"@iconify-json/material-symbols": "^1.2.4",
|
||||
"@nuxt/ui": "^2.19.2",
|
||||
"@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": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/tailwindcss/-/tailwindcss-0.2.1.tgz",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="bg-white">
|
||||
<div class="bg-white" v-if="workspace">
|
||||
<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="pt-4 pb-0">
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
Your Forms
|
||||
</h2>
|
||||
<v-button
|
||||
v-if="!workspace.is_readonly"
|
||||
v-if="!workspace?.is_readonly"
|
||||
v-track.create_form_click
|
||||
:to="{ name: 'forms-create' }"
|
||||
>
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
again.
|
||||
</div>
|
||||
<v-button
|
||||
v-if="!workspace.is_readonly && forms.length === 0"
|
||||
v-if="!workspace?.is_readonly && forms.length === 0"
|
||||
v-track.create_form_click
|
||||
class="mt-4"
|
||||
:to="{ name: 'forms-create' }"
|
||||
|
|
@ -182,7 +182,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!workspace.is_pro"
|
||||
v-if="!workspace?.is_pro"
|
||||
class="px-4"
|
||||
>
|
||||
<UAlert
|
||||
|
|
|
|||
|
|
@ -1,62 +1,74 @@
|
|||
<template>
|
||||
<modal
|
||||
:show="showModal"
|
||||
max-width="lg"
|
||||
@close="logout"
|
||||
>
|
||||
<div class="">
|
||||
<h2 class="font-medium text-3xl mb-3">
|
||||
<div class=" bg-gray-50 flex flex-col justify-center sm:px-6 lg:px-8 py-10 flex-grow">
|
||||
<div class="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<h2 class="text-center text-3xl font-bold tracking-tight text-gray-900">
|
||||
Welcome to OpnForm!
|
||||
</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.
|
||||
Please enter your email and create a password to continue.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form
|
||||
class="mt-4"
|
||||
@submit.prevent="updateCredentials"
|
||||
@keydown="form.onKeydown($event)"
|
||||
>
|
||||
<!-- Email -->
|
||||
<text-input
|
||||
name="email"
|
||||
:form="form"
|
||||
label="Email"
|
||||
:required="true"
|
||||
placeholder="Your email address"
|
||||
/>
|
||||
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||
<form
|
||||
@submit.prevent="updateCredentials"
|
||||
@keydown="form.onKeydown($event)"
|
||||
>
|
||||
<!-- Email -->
|
||||
<text-input
|
||||
name="email"
|
||||
:form="form"
|
||||
label="Email"
|
||||
:required="true"
|
||||
placeholder="Your email address"
|
||||
/>
|
||||
|
||||
<!-- Password -->
|
||||
<text-input
|
||||
native-type="password"
|
||||
placeholder="Your password"
|
||||
name="password"
|
||||
:form="form"
|
||||
label="Password"
|
||||
:required="true"
|
||||
/>
|
||||
<!-- Password -->
|
||||
<text-input
|
||||
native-type="password"
|
||||
placeholder="Your password"
|
||||
name="password"
|
||||
:form="form"
|
||||
label="Password"
|
||||
:required="true"
|
||||
/>
|
||||
|
||||
<!-- Password Confirmation-->
|
||||
<text-input
|
||||
native-type="password"
|
||||
:form="form"
|
||||
:required="true"
|
||||
placeholder="Enter confirm password"
|
||||
name="password_confirmation"
|
||||
label="Confirm Password"
|
||||
/>
|
||||
<!-- Password Confirmation-->
|
||||
<text-input
|
||||
native-type="password"
|
||||
:form="form"
|
||||
:required="true"
|
||||
placeholder="Enter confirm password"
|
||||
name="password_confirmation"
|
||||
label="Confirm Password"
|
||||
/>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<v-button
|
||||
class="mx-auto"
|
||||
:loading="form.busy || loading"
|
||||
>
|
||||
Update Credentials
|
||||
</v-button>
|
||||
</form>
|
||||
</modal>
|
||||
<!-- Submit Button -->
|
||||
<div class="mt-6">
|
||||
<v-button
|
||||
class="w-full justify-center"
|
||||
:loading="form.busy || loading"
|
||||
>
|
||||
Update Credentials
|
||||
</v-button>
|
||||
</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>
|
||||
|
||||
<script setup>
|
||||
|
|
@ -67,7 +79,7 @@ const workspacesStore = useWorkspacesStore()
|
|||
const formsStore = useFormsStore()
|
||||
const user = computed(() => authStore.user)
|
||||
const router = useRouter()
|
||||
const showModal = ref(true)
|
||||
const loading = ref(false)
|
||||
const form = useForm({
|
||||
name: "",
|
||||
email: "",
|
||||
|
|
@ -82,6 +94,7 @@ onMounted(() => {
|
|||
})
|
||||
|
||||
const updateCredentials = () => {
|
||||
loading.value = true
|
||||
form
|
||||
.post("update-credentials")
|
||||
.then(async (data) => {
|
||||
|
|
@ -95,11 +108,13 @@ const updateCredentials = () => {
|
|||
console.error(error)
|
||||
useAlert().error(error.response._data.message)
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const logout = () => {
|
||||
authStore.logout()
|
||||
showModal.value = false
|
||||
router.push({ name: "login" })
|
||||
}
|
||||
</script>
|
||||
|
|
@ -3,11 +3,15 @@ import { ref } from 'vue'
|
|||
|
||||
export const useFeatureFlagsStore = defineStore('feature_flags', () => {
|
||||
const flags = ref({})
|
||||
const isLoaded = ref(false)
|
||||
|
||||
async function fetchFlags() {
|
||||
if (isLoaded.value) return
|
||||
|
||||
try {
|
||||
const { data } = await useOpnApi('content/feature-flags')
|
||||
flags.value = data.value
|
||||
isLoaded.value = true
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch feature flags:', error)
|
||||
}
|
||||
|
|
@ -20,5 +24,5 @@ export const useFeatureFlagsStore = defineStore('feature_flags', () => {
|
|||
}, flags.value)
|
||||
}
|
||||
|
||||
return { flags, fetchFlags, getFlag }
|
||||
return { flags, isLoaded, fetchFlags, getFlag }
|
||||
})
|
||||
|
|
@ -1,35 +1,70 @@
|
|||
services:
|
||||
api: &api-base
|
||||
image: jhumanj/opnform-api:dev
|
||||
container_name: opnform-api
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile.api
|
||||
args:
|
||||
APP_ENV: local
|
||||
volumes:
|
||||
- APP_ENV=local
|
||||
volumes: &api-base-volumes
|
||||
- ./api:/usr/share/nginx/html:delegated
|
||||
- /usr/share/nginx/html/vendor # Exclude vendor directory from the mount
|
||||
- ./api/storage:/usr/share/nginx/html/storage:delegated # Mount storage directory directly
|
||||
environment:
|
||||
- ./api/storage:/usr/share/nginx/html/storage:delegated
|
||||
environment: &api-base-env
|
||||
APP_ENV: local
|
||||
APP_DEV_CORS: "true"
|
||||
IS_API_WORKER: "false"
|
||||
# Database settings
|
||||
DB_HOST: db
|
||||
REDIS_HOST: redis
|
||||
DB_DATABASE: ${DB_DATABASE:-forge}
|
||||
DB_USERNAME: ${DB_USERNAME:-forge}
|
||||
DB_PASSWORD: ${DB_PASSWORD:-forge}
|
||||
DB_CONNECTION: ${DB_CONNECTION:-pgsql}
|
||||
# Storage settings
|
||||
FILESYSTEM_DISK: local
|
||||
LOCAL_FILESYSTEM_VISIBILITY: public
|
||||
APP_ENV: local
|
||||
PHP_IDE_CONFIG: "serverName=Docker"
|
||||
XDEBUG_MODE: "${XDEBUG_MODE:-off}"
|
||||
XDEBUG_CONFIG: "client_host=host.docker.internal"
|
||||
APP_URL: "http://localhost"
|
||||
# Development settings
|
||||
PHP_IDE_CONFIG: serverName=Docker
|
||||
XDEBUG_MODE: ${XDEBUG_MODE:-off}
|
||||
XDEBUG_CONFIG: client_host=host.docker.internal
|
||||
APP_URL: http://localhost
|
||||
FRONT_URL: http://localhost:3000
|
||||
depends_on:
|
||||
db:
|
||||
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:
|
||||
image: jhumanj/opnform-client:dev
|
||||
container_name: opnform-client
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile.client
|
||||
|
|
@ -57,6 +92,8 @@ services:
|
|||
- "24678:24678" # Vite HMR port
|
||||
|
||||
ingress:
|
||||
image: nginx:1
|
||||
container_name: opnform-ingress
|
||||
volumes:
|
||||
- ./docker/nginx.dev.conf:/etc/nginx/templates/default.conf.template
|
||||
environment:
|
||||
|
|
@ -65,13 +102,7 @@ services:
|
|||
ports:
|
||||
- "80:80"
|
||||
depends_on:
|
||||
- api
|
||||
- ui
|
||||
|
||||
api-worker:
|
||||
<<: *api-base
|
||||
environment:
|
||||
IS_API_WORKER: "true"
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
api:
|
||||
condition: service_started
|
||||
ui:
|
||||
condition: service_started
|
||||
|
|
@ -1,55 +1,81 @@
|
|||
---
|
||||
services:
|
||||
api: &api
|
||||
api: &api-environment
|
||||
image: jhumanj/opnform-api:latest
|
||||
container_name: opnform-api
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile.api
|
||||
args:
|
||||
APP_ENV: production
|
||||
environment: &api-environment # Add this anchor
|
||||
- APP_ENV=production
|
||||
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
|
||||
REDIS_HOST: redis
|
||||
DB_DATABASE: ${DB_DATABASE:-forge}
|
||||
DB_USERNAME: ${DB_USERNAME:-forge}
|
||||
DB_PASSWORD: ${DB_PASSWORD:-forge}
|
||||
DB_CONNECTION: ${DB_CONNECTION:-pgsql}
|
||||
# Storage settings
|
||||
FILESYSTEM_DISK: local
|
||||
LOCAL_FILESYSTEM_VISIBILITY: public
|
||||
env_file:
|
||||
- ./api/.env
|
||||
volumes:
|
||||
- opnform_storage:/usr/share/nginx/html/storage:rw
|
||||
- ./api/.env
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_started
|
||||
|
||||
api-worker:
|
||||
image: jhumanj/opnform-api:latest
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile.api
|
||||
args:
|
||||
APP_ENV: production
|
||||
command: php artisan queue:work
|
||||
<<: *api-environment
|
||||
container_name: opnform-api-worker
|
||||
command: ["php", "artisan", "queue:work"]
|
||||
volumes: *api-environment-volumes
|
||||
environment:
|
||||
<<: *api-environment
|
||||
<<: *api-env
|
||||
APP_ENV: production
|
||||
IS_API_WORKER: "true"
|
||||
env_file:
|
||||
- ./api/.env
|
||||
volumes:
|
||||
- opnform_storage:/usr/share/nginx/html/storage:rw
|
||||
- ./api/.env
|
||||
|
||||
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:
|
||||
image: jhumanj/opnform-client:latest
|
||||
container_name: opnform-client
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile.client
|
||||
env_file:
|
||||
- ./client/.env
|
||||
- ./client/.env
|
||||
|
||||
redis:
|
||||
image: redis:7
|
||||
container_name: opnform-redis
|
||||
|
||||
db:
|
||||
image: postgres:16
|
||||
container_name: opnform-db
|
||||
environment:
|
||||
POSTGRES_DB: ${DB_DATABASE:-forge}
|
||||
POSTGRES_USER: ${DB_USERNAME:-forge}
|
||||
|
|
@ -64,10 +90,16 @@ services:
|
|||
|
||||
ingress:
|
||||
image: nginx:1
|
||||
container_name: opnform-ingress
|
||||
volumes:
|
||||
- ./docker/nginx.conf:/etc/nginx/templates/default.conf.template
|
||||
ports:
|
||||
- 80:80
|
||||
depends_on:
|
||||
api:
|
||||
condition: service_started
|
||||
ui:
|
||||
condition: service_started
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
# Install system dependencies and PHP extensions
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
git \
|
||||
curl \
|
||||
libzip-dev \
|
||||
libpng-dev \
|
||||
libonig-dev \
|
||||
libxml2-dev \
|
||||
zip \
|
||||
unzip \
|
||||
postgresql-client \
|
||||
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 \
|
||||
&& docker-php-ext-enable redis
|
||||
|
||||
# Install xdebug if not in production
|
||||
ARG APP_ENV=production
|
||||
RUN if [ "$APP_ENV" != "production" ]; then \
|
||||
pecl install xdebug && \
|
||||
docker-php-ext-enable xdebug; \
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
|
||||
# Create storage directories
|
||||
RUN mkdir -p storage/framework/sessions \
|
||||
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
|
||||
|
||||
# Configure PHP
|
||||
COPY docker/php/php.ini /usr/local/etc/php/conf.d/app.ini
|
||||
COPY docker/php/php-fpm.conf /usr/local/etc/php-fpm.d/www.conf
|
||||
# Copy the rest of the application
|
||||
COPY api/ .
|
||||
|
||||
WORKDIR /usr/share/nginx/html
|
||||
|
||||
# Copy application files
|
||||
COPY api/artisan artisan
|
||||
COPY api/bootstrap ./bootstrap
|
||||
COPY api/config ./config
|
||||
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
|
||||
# Run composer scripts and clear cache
|
||||
RUN composer dump-autoload -o \
|
||||
&& php artisan package:discover --ansi \
|
||||
&& composer clear-cache \
|
||||
&& chmod -R 775 storage \
|
||||
&& chown -R www-data:www-data storage
|
||||
|
||||
# Setup entrypoint
|
||||
COPY docker/php-fpm-entrypoint /usr/local/bin/opnform-entrypoint
|
||||
RUN chmod a+x /usr/local/bin/*
|
||||
|
||||
|
|
|
|||
|
|
@ -4,18 +4,18 @@ map $original_uri $api_uri {
|
|||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name opnform;
|
||||
root /usr/share/nginx/html/public;
|
||||
listen 80;
|
||||
server_name opnform;
|
||||
root /usr/share/nginx/html/public;
|
||||
|
||||
access_log /dev/stdout;
|
||||
error_log /dev/stderr error;
|
||||
error_log /dev/stderr error;
|
||||
|
||||
index index.html index.htm index.php;
|
||||
|
||||
location / {
|
||||
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-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
|
|
@ -24,16 +24,22 @@ server {
|
|||
}
|
||||
|
||||
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$ {
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass api:9000;
|
||||
fastcgi_pass opnform-api:9000;
|
||||
fastcgi_index index.php;
|
||||
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;
|
||||
}
|
||||
|
||||
# Deny access to . files
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,35 +4,16 @@ map $original_uri $api_uri {
|
|||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name opnform;
|
||||
root /usr/share/nginx/html/public;
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html/public;
|
||||
|
||||
access_log /dev/stdout;
|
||||
error_log /dev/stderr error;
|
||||
error_log /dev/stderr error;
|
||||
|
||||
index index.html index.htm index.php;
|
||||
|
||||
# Development CORS headers
|
||||
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
|
||||
# Frontend proxy
|
||||
location / {
|
||||
proxy_http_version 1.1;
|
||||
proxy_pass http://ui:3000;
|
||||
|
|
@ -43,23 +24,23 @@ server {
|
|||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 86400;
|
||||
proxy_buffering off;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
||||
|
||||
# HMR websocket support
|
||||
location /_nuxt {
|
||||
proxy_pass http://ui:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
|
||||
# API handling
|
||||
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$ {
|
||||
|
|
@ -67,7 +48,13 @@ server {
|
|||
fastcgi_pass api:9000;
|
||||
fastcgi_index index.php;
|
||||
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_read_timeout 300;
|
||||
}
|
||||
|
||||
# Deny access to . files
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ main() {
|
|||
prep_storage
|
||||
wait_for_db
|
||||
apply_db_migrations
|
||||
run_init_project
|
||||
run_init_project
|
||||
run_server "$@"
|
||||
fi
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ wait_for_db() {
|
|||
|
||||
apply_db_migrations() {
|
||||
echo "Running DB Migrations"
|
||||
./artisan migrate
|
||||
./artisan migrate --force
|
||||
}
|
||||
|
||||
run_init_project() {
|
||||
|
|
@ -59,7 +59,7 @@ run_init_project() {
|
|||
|
||||
run_server() {
|
||||
echo "Starting server $@"
|
||||
exec /usr/local/bin/docker-php-entrypoint "$@"
|
||||
exec "$@"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -10,13 +10,15 @@ import CloudVersion from "/snippets/cloud-version.mdx";
|
|||
## Overview
|
||||
|
||||
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
|
||||
- Automatic dependency management
|
||||
- PostgreSQL database and Redis setup
|
||||
- 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
|
||||
|
||||
|
|
@ -32,18 +34,19 @@ This is the recommended way to get started with OpnForm development.
|
|||
cd OpnForm
|
||||
```
|
||||
|
||||
2. Create environment files:
|
||||
2. Run the setup script in development mode:
|
||||
```bash
|
||||
./scripts/setup-env.sh
|
||||
```
|
||||
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
|
||||
chmod +x scripts/docker-setup.sh
|
||||
./scripts/docker-setup.sh --dev
|
||||
```
|
||||
|
||||
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
|
||||
- API: http://localhost/api
|
||||
|
||||
|
|
@ -60,28 +63,54 @@ You will be prompted to change your email and password after your first login.
|
|||
|
||||
## Development Features
|
||||
|
||||
### Hot Reload
|
||||
### Frontend Development
|
||||
|
||||
The development setup includes hot reload capabilities:
|
||||
- Frontend (Nuxt.js): Changes to files in the `client` directory trigger automatic rebuilds
|
||||
- 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`)
|
||||
The development setup includes advanced frontend features:
|
||||
- **Hot Module Replacement (HMR)**: Changes to Vue components and styles are instantly reflected without page reload
|
||||
- **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:
|
||||
- `./api`: Mounted to the API container with vendor directory preserved
|
||||
- `./client`: Mounted to the UI container with node_modules preserved
|
||||
- Database and Redis data are persisted through Docker volumes
|
||||
|
||||
### Container Services
|
||||
|
||||
The development environment includes:
|
||||
- `api`: Laravel API service with hot reload
|
||||
- `ui`: Nuxt.js frontend with HMR
|
||||
- `api-worker`: Laravel queue worker
|
||||
- `db`: PostgreSQL database
|
||||
- `redis`: Redis server
|
||||
- `ingress`: Nginx reverse proxy
|
||||
```
|
||||
OpnForm/
|
||||
├── api/ # Laravel API (mounted to api container)
|
||||
│ ├── vendor/ # Preserved in container
|
||||
│ └── storage/ # Mounted for logs and uploads
|
||||
├── client/ # Nuxt frontend (mounted to ui container)
|
||||
│ └── node_modules/ # Preserved in container
|
||||
└── docker/ # Docker configuration files
|
||||
```
|
||||
|
||||
## Common Tasks
|
||||
|
||||
|
|
@ -95,6 +124,9 @@ docker compose -f docker-compose.yml -f docker-compose.dev.yml exec api php arti
|
|||
|
||||
# NPM commands
|
||||
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
|
||||
|
|
@ -105,35 +137,48 @@ View container logs:
|
|||
# All containers
|
||||
docker compose -f docker-compose.yml -f docker-compose.dev.yml logs -f
|
||||
|
||||
# Specific container
|
||||
docker compose -f docker-compose.yml -f docker-compose.dev.yml logs -f [service]
|
||||
# Specific container (e.g., frontend)
|
||||
docker compose -f docker-compose.yml -f docker-compose.dev.yml logs -f ui
|
||||
```
|
||||
|
||||
### Database Management
|
||||
### Database Access
|
||||
|
||||
The PostgreSQL database is accessible:
|
||||
- From containers: `host=db`
|
||||
- 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
|
||||
|
||||
### Container Issues
|
||||
If containers aren't starting properly:
|
||||
```bash
|
||||
# Remove all containers and volumes
|
||||
docker compose down -v
|
||||
|
||||
# Rebuild and start
|
||||
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build
|
||||
# Clean everything and restart
|
||||
./scripts/docker-setup.sh --dev
|
||||
```
|
||||
|
||||
### Permission Issues
|
||||
If you encounter permission issues with storage or vendor directories:
|
||||
If you encounter permission issues:
|
||||
```bash
|
||||
# Fix storage permissions
|
||||
docker compose -f docker-compose.yml -f docker-compose.dev.yml exec api chmod -R 775 storage
|
||||
|
||||
# Fix vendor permissions
|
||||
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
|
||||
```
|
||||
|
|
@ -8,49 +8,34 @@ import CloudVersion from "/snippets/cloud-version.mdx";
|
|||
<CloudVersion/>
|
||||
|
||||
<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>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker
|
||||
- Docker Compose
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/JhumanJ/OpnForm.git
|
||||
cd OpnForm
|
||||
```
|
||||
|
||||
2. Set up environment files:
|
||||
|
||||
2. Run the setup script:
|
||||
```bash
|
||||
./scripts/setup-env.sh --docker
|
||||
chmod +x scripts/docker-setup.sh
|
||||
./scripts/docker-setup.sh
|
||||
```
|
||||
|
||||
3. (Optional) Customize the environment variables:
|
||||
|
||||
You can modify two environment files to customize your OpnForm installation:
|
||||
- The `.env` file in the `api` directory for backend configuration
|
||||
- The `.env` file in the `client` directory for frontend configuration
|
||||
|
||||
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
|
||||
The script will:
|
||||
- Create necessary environment files
|
||||
- Pull required Docker images
|
||||
- Start all containers in production mode
|
||||
- Display access information
|
||||
|
||||
3. Access your OpnForm instance at `http://localhost`
|
||||
|
||||
### Initial Login
|
||||
|
||||
After installation, use these credentials to access the app:
|
||||
After deployment, use these credentials to access the app:
|
||||
|
||||
- Email: `admin@opnform.com`
|
||||
- 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>
|
||||
|
||||
## 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
|
||||
|
||||
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 Client Image](https://hub.docker.com/r/jhumanj/opnform-client)
|
||||
|
||||
We recommend using these official images for your OpnForm deployment.
|
||||
### Building Custom Images
|
||||
|
||||
|
||||
## 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:
|
||||
While we recommend using the official images, you can build custom images if needed:
|
||||
|
||||
```bash
|
||||
# Build all images
|
||||
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:
|
||||
|
||||
```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`:
|
||||
### Custom Configuration
|
||||
Create a `docker-compose.override.yml` to customize your deployment:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
api:
|
||||
image: opnform-api:local
|
||||
environment:
|
||||
PHP_MEMORY_LIMIT: 1G
|
||||
ui:
|
||||
image: opnform-ui:local
|
||||
api-worker:
|
||||
image: opnform-api:local
|
||||
ingress:
|
||||
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
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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}"
|
||||
Loading…
Reference in New Issue