---
title: "ការដំឡើង Quire ជាមួយ Docker Compose"
description: "ដំឡើង Quire លើហេដ្ឋារចនាសម្ព័ន្ធផ្ទាល់របស់អ្នកជាមួយ Docker Compose។"
image: "https://docs.quirelms.com/og.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.quirelms.com/km/llms.txt
> Use this file to discover all available pages before exploring further.

# ការដំឡើង Quire ជាមួយ Docker Compose

<span id="installing-quire-with-docker-compose"></span>

នេះជាផលិតផលពេញលេញនៅលើម៉ាស៊ីនមួយ៖ LMS ការងារក្រោយកម្មវិធីរបស់វា សេវាកំណត់ពេលផ្ទាល់ និងកែសំរួលសហការ និងសេវាជម្រើសនីមួយៗក្រោម profile។ រចនាសម្ព័ន្ធស្ថិតនៅ `docs/architecture/23-ops.md` ផ្នែក 2។

គោលដៅផ្សេងៗ៖ [Vercel](/km/ops/vercel/) និង [Cloudflare Workers](/km/ops/cloudflare/) ដំណើរការតែស្រទាប់ web ប៉ុណ្ណោះ។ ការធ្វើឱ្យប្រសើរស្ថិតក្នុង [upgrade.md](/km/ops/upgrade/) ហើយការបម្រុងទុក និងលំហាត់ស្តារឡើងវិញស្ថិតក្នុង [backup-restore.md](/km/ops/backup-restore/)។

## អ្វីដែលអ្នកត្រូវការ <!--quire:what-you-need-->

- Docker Engine 27 ឬក្រោយមក ជាមួយកម្មវិធីជំនួយ Compose 2.30 ឬក្រោយមក។
- 4 core CPU និង 8 GB សមត្ថភាពសម្រាប់ស្តាខកាន់តែបណ្តោះអាសន្ន។ 8 core និង 16 GB ជាមួយ `--profile full` (ClamAV តែម្នាក់ឯងកាន់ប្រហែល 1.5 GB នៃសញ្ញា)។
- ឈ្មោះ DNS សម្រាប់ស្រទាប់ web និងមួយទៀតសម្រាប់មាតិកាដែលមិនទុកចិត្ត។ ពួកគេត្រូវតែជាម៉ាស៊ីនខុសៗគ្នា៖ កញ្ចប់ SCORM និង HTML ដែលបានបញ្ចូលដំណើរការនៅ content origin ដូច្នេះពួកវាមិនអាចអានគូគីរបស់ LMS បានឡើយ។
- សម្រាប់ការសាកល្បងក្នុងស្រុក `lvh.me` និង `*.localhost` ដោះស្រាយទៅ 127.0.0.1 ដែលជាអ្វីដែល `docker/.env.example` ប្រើ។ សេវា `proxy` ផ្ទាល់នៃស្តាខបម្រើទាំងពីរតាម https ជាមួយសាលបន្ទះការទំនាក់ទំនងសាធារណៈក្នុងស្រុក ដូច្នេះគ្មានអ្វីផ្សេងត្រូវបានដំឡើងទេ (សូមមើល "TLS")។
- ច្រក 80 និង 443 ទំនេរលើម៉ាស៊ីន (`QUIRE_PROXY_HTTP_PORT` និង `QUIRE_PROXY_HTTPS_PORT` ផ្លាស់ទីពួកវា)។

## ការដំណើរការដំបូង <!--quire:first-run-->

```sh
QUIRE_APP_ORIGIN=https://learn.example.org \
QUIRE_CONTENT_ORIGIN=https://content.example-content.org \
QUIRE_SETUP_ADMIN_EMAIL=you@example.org \
  docker/scripts/init-env.sh
docker compose -f docker/compose.yaml up -d --build
docker compose -f docker/compose.yaml logs init
```

`docker/scripts/init-env.sh` សរសេរ `docker/.env` ពី `docker/.env.example` ជាមួយសំណង់ទាំងអស់ដែលបានបង្កើត (ពាក្យសម្ងាត់មូលដ្ឋានទិន្នន័យ សោហត្ថលេខា និងសោ master គូសោចាប់ផ្តើមមាតិកា) ហើយសោហត្ថលេខាចំណុចត្រួតពិនិត្យសវនកម្មក្នុង `docker/secrets/audit-signing-key.pem` ដែល Compose តោងចូលក្នុង worker ជាសំណង់។ វាត្រូវការតែ `sh`, `awk` និង `openssl` ប៉ុណ្ណោះ ហើយបដិសេធការសរសេរបំពាន `docker/.env` ដែលមានរួចហើយ។ ចម្លងឯកសារទាំងពីរចេញពីម៉ាស៊ីន៖ គ្មាន `QUIRE_MASTER_KEY` មូលដ្ឋានទិន្នន័យដែលបានស្តារឡើងវិញមិនអាចឌិគ្រីបលិខិតសម្គាល់ដែលបានផ្ទុករបស់វាបានទេ។ ដើម្បីបំពេញឯកសារដោយដៃជំនួស `cp docker/.env.example docker/.env`។ ឯកសារប្រាប់របៀបបង្កើតសំណង់នីមួយៗ។

origin ទាំងពីរត្រូវតែជា `https`៖ សេវាមាតិកាបដិសេធអ៊ីនធឺណិត http សុទ្ធក្នុងការផលិត ហើយពួកវាមិនអាចចែករំលែក domain ដែលអាចចុះឈ្មោះបានទេ។ សេវា `proxy` បញ្ចប់ TLS សម្រាប់ទាំងពីរ (សូមមើល "TLS")។ `init-env.sh` បដិសេធ origin ប្រភេទ `http://`។

ស្តាខចាប់ផ្តើមតាមលំដាប់ជាក់លាក់ ហើយជំហាននីមួយៗរង់ចាំជំហានមុន៖

1. `postgres` ក្លាយសុខភាពល្អ។ នៅការចាប់ផ្តើមដំបូងបំផុតស្គ្រីប init របស់វា (`docker/postgres/init/90-passwords.sh`) កំណត់ពាក្យសម្ងាត់តួនាទីបួន។
2. `migrate` អនុវត្តការផ្លាស់ប្តូរទាំងអស់ ហើយបង្កើតជញ្ជាំងការងារក្នុងមូលដ្ឋានទិន្នន័យត្រួតពិនិត្យ និងក្នុងមូលដ្ឋានទិន្នន័យ tenant នីមួយៗ ពិនិត្យថាពួកវាទាំងអស់យល់ស្របគ្នា រួចចេញ (docs/ops/upgrade.md)។ ការផ្លាស់ប្តូរដំណើរការរាល់ពេលចាប់ផ្តើម ហើយស្ថិតនិរន្តរ៍ ដូច្នេះការធ្វើឱ្យប្រសើរជាកំណែថ្មីមួយ និងការចាប់ផ្តើមឡើងវិញ។
3. `init` (`apps/web/src/first-run.ts`) កត់ត្រាមូលដ្ឋានទិន្នន័យកម្មវិធីក្រោម `QUIRE_DATABASE_ID` ហើយនៅពេល `QUIRE_SETUP_ADMIN_EMAIL` ត្រូវបានកំណត់ បង្កើតអង្គការដំបូង និងអ្នកគ្រប់គ្រងរបស់វា។ អាសយដ្ឋានចូល និងពាក្យសម្ងាត់ដែលបានបង្កើតត្រូវបានបោះពុម្ពមួយដង ក្នុង `docker compose logs init`។
4. `web`, `content`, `worker`, `scheduler`, `collab` និង `centrifugo` ចាប់ផ្តើម។
5. `proxy` ចាប់ផ្តើមនៅពេល `web` និង `content` សុខភាពល្អ។

បើក `https://demo.` បន្ទាប់ដោយ domain កម្មវិធីរបស់អ្នក (កំណត់ត្រា `init` បោះពុម្ពអាសយដ្ឋានចូលពិត) រួចចូល។ លើការដំឡើងក្នុងស្រុក ទុកចិត្តសាលបន្ទះការទំនាក់ទំនងសាធារណៈរបស់ proxy ជាមុនសិន (សូមមើល "TLS")។ ផ្លាស់ប្តូរពាក្យសម្ងាត់ដែលបានបង្កើតនៅ `/account/security`។

ដំណើរការដែលបានចាប់ផ្តើមដោយគ្មានសំណង់ដែលតម្រូវបដិសេធការចាប់ផ្តើម ហើយដាក់ឈ្មោះការកំណត់ដែលខ្វះក្នុងកំណត់ត្រារបស់វា។ គ្មានអ្វីចាប់ផ្តើមកណ្តាលរៀបចំទេ។

## សេវា និង profiles <!--quire:services-and-profiles-->

| សេវា | Profile | អ្វីដែលវាធ្វើ |
| --- | --- | --- |
| postgres | ជានិច្ច | មូលដ្ឋានទិន្នន័យ (PostgreSQL 18 ជាមួយ pgvector សង់ពី `docker/postgres.Dockerfile`) ជាមួយ WAL ទុកក្នុងបណ្ណសារចាប់តាំងពីការចាប់ផ្តើមដំបូង |
| migrate, init | ជានិច្ច | មួយដង៖ ការផ្លាស់ប្តូរ រួចការដំណើរការដំបូង |
| web | ជានិច្ច | LMS នៅ `QUIRE_HTTP_PORT` (8080) |
| content | ជានិច្ច | origin នៃមាតិកាដែលមិនទុកចិត្ត នៅ `QUIRE_CONTENT_PORT` (8081) |
| worker | ជានិច្ច | ការងារក្រោយកម្មវិធី៖ អ៊ីមែល របាយការណ៍ ការដំណើរការឯកសារ webhooks |
| scheduler | ជានិច្ច | ការងារកើតឡើងដដែល៖ ចុះឈ្មោះកាលវិភាគរត់ 64 ហើយប្រគល់ឱ្យ worker។ មេម្នាក់ក្នុងពេលតែមួយ |
| collab | ជានិច្ច | websocket កែសំរួលសហការ នៅ `QUIRE_COLLAB_HTTP_PORT` (1234) |
| centrifugo | ជានិច្ច | ការបែងចែកផ្ទាល់ នៅ `QUIRE_REALTIME_PORT` (8000) |
| proxy | ជានិច្ច | Caddy ទ្វារមុខ TLS លើច្រក 80 និង 443 (សូមមើល "TLS") |
| valkey | `cache` | Cache និងកំណត់កម្រិតអត្រា |
| clamav | `scan` | ការស្វែងរក malware សម្រាប់ការបញ្ចូល |
| gotenberg | `preview` | Office ទៅ PDF ជាមុន ការរចនាវិញ្ញាបនបត្រ |
| imgproxy | `images` | រូបភាពដែលបានផ្លាស់ទំហំ និងបំប្លែង |
| transcoder | `video` | រូបភាព worker ជាមួយ ffmpeg តែ LGPL សម្រាប់ video renditions |
| seaweedfs | `storage` | ការផ្ទុកវត្ថុឆែកឆេងជាមួយ S3 លើម៉ាស៊ីននេះ |
| otelcol | `observability` | អ្នកប្រមូល OpenTelemetry |
| mailpit | `devmail` | ចាប់អ៊ីមែលចេញទាំងអស់ សម្រាប់សាកល្បង Quire |
| backup | `backup` | ការបម្រុងទុកគោលមួយដង។ សូមមើល backup-restore.md |
| backup-scheduler, backup-offsite | `backup` | ការបម្រុងទុកគោលរៀងរាល់ `QUIRE_BACKUP_INTERVAL_HOURS` និងច្បាប់ចម្លង off-host ដែលបានឌិគ្រីបជាមួយលំហាត់ផ្ទៀងផ្ទាត់ប្រចាំសប្តាហ៍ |
| h5p | `h5p` | រូបភាពឧបករណ៍ H5P LTI 1.3 ដែលអ្នកផ្តល់ក្នុង `QUIRE_H5P_IMAGE` នៅ `QUIRE_H5P_PORT` (8090)។ សូមមើល "ការតភ្ជាប់អ្នកផ្តល់ H5P" |

`--profile full` ចាប់ផ្តើមសេវាជម្រើសទាំងអស់លើកលែង `backup` និង `h5p`។ ចាប់ផ្តើមមួយជាមួយ `docker compose -f docker/compose.yaml --profile scan up -d`។ គ្មានសេវាជម្រើស Quire នៅតែដំណើរការ ហើយប្រាប់ថាអ្វីខ្វះ៖ គ្មានអ្នកស្វែងរកមានន័យថាការបញ្ចូលត្រូវបានរក្សាទុកដោយមិនបានស្វែងរក ហើយអ្នកគ្រប់គ្រងត្រូវបានប្រាប់។ គ្មាន Gotenberg មានន័យថាឯកសារផ្តល់ការទាញយកជំនួសការមើលជាមុន។ គ្មាន transcoder មានន័យថាវីដេអូលេងដូចឯកសារដើម។

រូបភាពភាគីទីបីទាំងអស់ និងការទទួលខុសត្រូវអាជ្ញាប័ណ្ណរបស់វាត្រូវបានរាយក្នុង `docker/third-party-containers.yaml`។

### ការតភ្ជាប់អ្នកផ្តល់ H5P <!--quire:connecting-an-h5p-provider-->

Quire មិនបង្កើត ឬផ្តល់កម្មវិធីរត់ H5P ឬ sidecar ទេ (ADR 0019)។ បើអ្នកប្រើ H5P ផ្តល់ការចុះឈ្មោះ hosted ផ្ទាល់របស់អ្នក ឬដំណើរការ instance H5P self-hosted ផ្ទាល់របស់អ្នកដាច់ដោយឡែកពី Quire។ ចុះឈ្មោះអ្នកផ្តល់នោះជាឧបករណ៍ខាងក្រៅ LTI 1.3 ហើយបន្ថែមមាតិការបស់វាទៅវគ្គសិក្សាជាសកម្មភាពឧបករណ៍។ Quire ប្តូរពិន្ទុ និងវឌ្ឍនភាពសកម្មភាព/ការដាក់ពិន្ទុតាម LTI Assignment and Grade Services (AGS)។ បើអ្នកផ្តល់ក៏ផ្ញើសេចក្តីថ្លែង xAPI ផង រៀបចំរឿងនោះដាច់ដោយឡែកសម្រាប់ឃ្លាំងសេចក្តីថ្លែង xAPI របស់ Quire។ ការប្តូរពិន្ទុ/វឌ្ឍនភាព AGS មិនផ្ញើសេចក្តីថ្លែង xAPI ទេ។ ការនាំចូល Moodle រាយសកម្មភាព H5P ថាត្រូវការការតភ្ជាប់ឧបករណ៍ LTI។ អ្នកផ្តល់នៅតែទទួលខុសត្រូវចំពោះកម្មវិធីរត់ H5P ការនិពន្ធ ធនាគារមាតិកា និងប្រវត្តិការសាកល្បងរបស់វា។

ដើម្បីដំណើរការ instance self-hosted ផ្ទាល់របស់អ្នកលើម៉ាស៊ីននេះ កំណត់ `QUIRE_H5P_IMAGE` ទៅរូបភាពរបស់វា ហើយចាប់ផ្តើម profile `h5p`។ Compose ផ្សាយវានៅ `QUIRE_H5P_PORT` (8090) ហើយរក្សាទិន្នន័យរបស់វាក្នុង volume `h5p-data`។ រូបភាព និងការទទួលខុសត្រូវដែលមកជាមួយវា នៅជារបស់អ្នក។

## ការកំណត់ <!--quire:settings-->

ដំណើរការនីមួយៗអាន `docker/.env`។ ទម្រង់ `docker/.env.example` រាយការកំណត់នីមួយៗជាមួយតម្លៃលំនាំដើមរបស់វា។ ក្រុម៖

### អាសយដ្ឋាន <!--quire:addresses-->

| ការកំណត់ | អត្ថន័យ |
| --- | --- |
| `QUIRE_APP_ORIGIN` | អាសយដ្ឋានសាធារណៈរបស់ LMS ដូចជា `https://learn.example.com` |
| `QUIRE_CONTENT_ORIGIN` | content origin ម៉ាស៊ីនខុសគ្នា |
| `QUIRE_PLATFORM_DOMAINS` | domain ដែលអង្គការស្ថិតក្រោម បំបែកដោយក្បៀស |
| `QUIRE_MARKETING_ORIGIN` | Optional. The marketing site, default `https://quirelms.com`. The only origin the waitlist form (`POST /api/waitlist`, `POST /waitlist`) accepts and redirects to. Comma separated; a `www.` variant is allowed only if listed |
| `QUIRE_DEPLOY_TARGET` | `compose` នៅទីនេះ។ សូមមើលមគ្គុទ្ទេសផ្សេងៗសម្រាប់ `vercel` និង `cloudflare` |
| `QUIRE_TRUSTED_PROXY_CIDRS` | proxy ដែល `X-Forwarded-For` របស់ពួកគេត្រូវបានជឿ |

### សំណង់ <!--quire:secrets-->

| ការកំណត់ | អត្ថន័យ |
| --- | --- |
| `QUIRE_SECRET_KEY` | ហត្ថលេខាសំណើ និងថូខេន។ 64 អក្សរ hex |
| `QUIRE_MASTER_KEY` | រុំលិខិតសម្គាល់ដែលបានរក្សាទុកដូចជា SSO និងសំណង់ webhook។ 32 បៃ base64។ ស្រទាប់ web និង worker ត្រូវការតម្លៃដដែល។ ការបង្វិល៖ [key-rotation.md](/km/ops/key-rotation/) |
| `QUIRE_MASTER_KEY_VERSION` | ស្លាកកំណែនៃសោ master `v1` នៅពេលមិនបានកំណត់។ បង្កើនវានៅពេលអ្នកបង្វិល |
| `QUIRE_MASTER_KEY_RETIRED` | សោ master មុនៗដែលនៅតែត្រូវការដើម្បីអានអ្វីដែលពួកគេបានបិទភ្ជិត ជា `v1=<base64>`។ យកចេញបន្ទាប់ពីការបង្វិលបញ្ចប់ដោយគ្មានអ្វីដែលមិនដោះស្រាយ |
| `QUIRE_COLLAB_SIGNING_KEY` | ចែករំលែកដោយ web និង collab ដើម្បីហត្ថលេខាថូខេនកែសំរួល |
| `QUIRE_BACKUP_SIGNING_KEY` | ហត្ថលេខាការបម្រុងទុកវគ្គសិក្សា (ជម្រើស) |

រក្សាច្បាប់ចម្លងមួយនៃ `QUIRE_MASTER_KEY` នៅកន្លែងផ្សេងក្រៅពីម៉ាស៊ីននេះ។ មូលដ្ឋានទិន្នន័យដែលបានស្តារឡើងវិញដោយគ្មានវាមិនអាចឌិគ្រីបលិខិតសម្គាល់ដែលវាកាន់បានទេ។

### មូលដ្ឋានទិន្នន័យ <!--quire:database-->

| ការកំណត់ | អត្ថន័យ |
| --- | --- |
| `POSTGRES_PASSWORD` | superuser ដែល container និងការបម្រុងទុកប្រើ |
| `QUIRE_DB_APP_PASSWORD`, `QUIRE_DB_MIGRATOR_PASSWORD`, `QUIRE_DB_REPORT_PASSWORD`, `QUIRE_DB_AUDIT_PASSWORD` | ពាក្យសម្ងាត់តួនាទី កំណត់នៅការចាប់ផ្តើមដំបូង |
| `DATABASE_URL` | តួនាទីកម្មវិធី។ សុវត្ថិភាពជួរដេកអនុវត្តលើសំណើទាំងអស់ដែលវាធ្វើ |
| `DATABASE_MIGRATOR_URL`, `QUIRE_MIGRATION_URL` | តួនាទី migrator សម្រាប់ `migrate` និង `init` |
| `QUIRE_SUPERUSER_URL` | ប្រើតែដោយការដំណើរការដំបូង |
| `QUIRE_REPORT_DATABASE_URL` | តួនាទីរបាយការណ៍តែអាន សម្រាប់របាយការណ៍ និងកម្មវិធីសង់របាយការណ៍ |
| `QUIRE_AUDIT_DATABASE_URL` | តួនាទីសវនកម្ម សម្រាប់កុងសូលសវនកម្ម និងការនាំចេញ SIEM |
| `QUIRE_DATABASE_ID` | UUID ណាមួយ ជាក់លាន់សម្រាប់អាយុជីវិតនៃការដំឡើង |

ពាក្យសម្ងាត់តួនាទីត្រូវបានអនុវត្តតែនៅពេល volume នៃមូលដ្ឋានទិន្នន័យត្រូវបានបង្កើតដំបូងបំផុត។ ដើម្បីផ្លាស់ប្តូរមួយក្រោយមក ប្រើ `ALTER ROLE` រួចធ្វើបច្ចុប្បន្នភាព URL ដែលផ្គូផ្គង។

`QUIRE_REPORT_DATABASE_URL` ត្រូវបានប្រើសម្រាប់មូលដ្ឋានទិន្នន័យរូបវ័ណ្ឌដែលបានរៀបចំដោយ `DATABASE_URL`។ សម្រាប់មូលដ្ឋានទិន្នន័យរូបវ័ណ្ឌដែលបានចុះឈ្មោះផ្សេងណាមួយ កំណត់ URL តភ្ជាប់ `quire_report` ផ្ទាល់របស់វាក្នុងបរិស្ថាន web និង worker រួចដាក់ឈ្មោះអថេរក្នុងវាល **Reporting environment variable** នៃមូលដ្ឋានទិន្នន័យនោះជា `env:NAME`។ យោងត្រូវតែចង្អុលទៅមូលដ្ឋានទិន្នន័យតែមួយជាមួយការតភ្ជាប់ app របស់វា ល្អបំផុតគឺ replica អានរបស់វា។ ផ្ទៃរបាយការណ៍នីមួយៗធ្វើតាម tenant ទៅកាន់ការតភ្ជាប់របាយការណ៍នៃមូលដ្ឋានទិន្នន័យផ្ទាល់របស់ខ្លួន៖ កម្មវិធីសង់របាយការណ៍ និងរបាយការណ៍ដែលបានរក្សាទុក ការផ្តល់ដែលបានកំណត់កាលវិភាគ ការនាំចេញរបាយការណ៍ វិភាគទាន កំណត់ត្រាសវនកម្ម ធនធានសវនកម្ម REST និងការស្វែងរកសវនកម្មរបស់ជំនួយការ។ គ្មានណាមួយក្នុងចំណោមពួកវាធ្វើយោង URL របាយការណ៍នៃមូលដ្ឋានទិន្នន័យផ្សេងឡើយ។ នៅពេលមូលដ្ឋានទិន្នន័យគ្មានការតភ្ជាប់របាយការណ៍ របាយការណ៍ធម្មតាដំណើរការលើការតភ្ជាប់កម្មវិធីផ្ទាល់នៃមូលដ្ឋានទិន្នន័យនោះ ខណៈវិភាគទាន និងការអានសវនកម្មទាំងអស់បដិសេធ ហើយប្រាប់ដូច្នេះ ព្រោះតួនាទីកម្មវិធីមិនអាចអានផ្លូវសវនកម្មបានទេ។

### អ្នកបញ្ជូន <!--quire:drivers-->

| ការកំណត់ | កំណែនេះ | ចំណាំ |
| --- | --- | --- |
| `QUIRE_STORAGE_DRIVER` | `local` (លំនាំដើម), `s3` ឬ `azure` | `local` រក្សាឯកសារក្នុង volume `files`។ `s3` គ្របដណ្តប់ AWS S3, R2, GCS interoperability និងការផ្ទុកឆែកឆេង S3 ផ្សេងៗ ជាមួយការបញ្ចូល multipart ដែលអាចបន្តបាន |
| `QUIRE_REALTIME_DRIVER` | `inprocess` (លំនាំដើម), `sse`, `centrifugo` ឬ `durable_objects` | `inprocess` សមរម្យសម្រាប់ container web មួយ។ ប្រើ `centrifugo` ឬ `sse` នៅពេលមានច្រើន |
| `QUIRE_CACHE_DRIVER` | `memory` (លំនាំដើម), `postgres` ឬ `valkey` | `memory` ក្នុងមួយដំណើរការ។ ប្រើ `valkey` ឬ `postgres` ដូច្នេះកំណត់កម្រិតអត្រានៅតែគង់វង់ឆ្លងកាត់ container |
| `QUIRE_VIDEO_DRIVER` | `ffmpeg` (លំនាំដើម) ឬ `progressive_mp4` | ឬអ្នកផ្តល់ hosted៖ Cloudflare Stream, Mux ឬ Bunny តាមសោរបស់ពួកគេ |
| `QUIRE_IMAGE_DRIVER` | `noop` (លំនាំដើម), `imgproxy` ឬ `cloudflare` | `noop` បម្រើរូបភាពនីមួយៗតាមទំហំដើមរបស់វា។ `imgproxy` ត្រូវការ profile `images` និងការកំណត់ខាងក្រោម។ `cloudflare` ប្រើ Cloudflare Images |
| `QUIRE_MEETING_PROVIDER` | `bbb`, `zoom`, `teams`, `meet`, `jitsi` ឬ `in_process` | លំនាំដើមនៃប្រព័ន្ធសម្រាប់វគ្គផ្ទាល់។ មិនបានកំណត់ វគ្គផ្ទាល់ប្រាប់ថាមិនបានរៀបចំ រហូតដល់អង្គការមួយភ្ជាប់គណនីផ្ទាល់របស់វាក្រោម ការរួមបញ្ចូល អ្នកផ្តល់វគ្គផ្ទាល់។ គណនីផ្ទាល់របស់អង្គការមួយតែងឈ្នះលើតម្លៃនេះ។ ការកំណត់ផ្ទាល់របស់អ្នកផ្តល់នីមួយៗ (`BBB_URL` និង `BBB_SECRET` អថេរ `ZOOM_*`, `TEAMS_*`, `GOOGLE_MEET_*` និង `JITSI_*`) ត្រូវបានអានតែសម្រាប់អ្នកផ្តល់ដែលឈ្មោះនៅទីនេះប៉ុណ្ណោះ |
| `QUIRE_MEETING_REGIONS` | បញ្ជីក្បៀសនៃ `eu`, `uk`, `us` | កន្លែងដែលអ្នកផ្តល់លំនាំដើមនៃប្រព័ន្ធដំណើរការកិច្ចប្រជុំ។ មិនបានកំណត់ វាមិនត្រូវបានពិនិត្យប្រឆាំងនឹងអង្គការដែលខ្ទាស់ទៅតំបន់មួយ ដូចមុន។ គណនីផ្ទាល់របស់អង្គការមួយបញ្ជាក់តំបន់របស់វាលើទំព័ររបស់វា |

តម្លៃអ្នកបញ្ជូនដែលកំណែនេះមិនរួមបញ្ចូលត្រូវបានបដិសេធ នៅពេលស្រទាប់ web ចាប់ផ្តើម ជាមួយឈ្មោះការកំណត់ ជំនួសការជំនួសដោយស្ងាត់ដោយតម្លៃលំនាំដើម។

### រូបភាព <!--quire:images-->

ទំព័រសុំរូបភាពក្នុងទំហំកំណត់ជាក់លាក់បួនតាម `/api/files/{id}/image/{size}` ដែលពិនិត្យការចូលដូចគ្នាជាមួយឯកសារផ្ទាល់ រួចបញ្ចូលទៅសេវារូបភាព។ អង្គការនីមួយៗអាចសុំ `QUIRE_IMAGE_SPECS_PER_HOUR` (លំនាំដើម 2000) គូរូបភាព និងទំហំថ្មីមួយម៉ោង។ ទំហំដែលបានបង្កើតរួចហើយក្នុងម៉ោងនោះមិនរាប់ទេ។ ប្រើ `valkey` ឬ `postgres` សម្រាប់ `QUIRE_CACHE_DRIVER` ជាមួយ container web ច្រើនជាងមួយ ដូច្នេះកម្រិតនៅតែគង់វង់ឆ្លងកាត់ពួកវា។

| ការកំណត់ | អ្នកបញ្ជូន | ចំណាំ |
| --- | --- | --- |
| `IMGPROXY_URL` | `imgproxy` | អាសយដ្ឋានដែលកម្មវិធីរុករកឈានដល់ imgproxy ឧទាហរណ៍ `https://images.example.org`។ profile `images` ផ្សាយវានៅ `QUIRE_IMAGES_PORT` (8082) |
| `IMGPROXY_KEY`, `IMGPROXY_SALT` | `imgproxy` | ខ្សែអក្សរ hex តម្លៃដដែលដែល imgproxy ត្រូវបានចាប់ផ្តើម។ បង្កើតនីមួយៗដោយ `openssl rand -hex 32`។ Quire ហត្ថលេខាអាសយដ្ឋានរូបភាពនីមួយៗជាមួយពួកវា ដូច្នេះ imgproxy រចនាគ្មានអ្វីដែល Quire មិនបានសុំទេ |
| `QUIRE_IMAGE_SOURCE_ORIGIN` | `imgproxy` ជាមួយការផ្ទុកក្នុងស្រុក | កន្លែងដែល imgproxy ទាញយករូបភារដើម។ Compose កំណត់ `http://web:3000`។ ជាមួយការផ្ទុក `s3` ឬ `azure` imgproxy ទាញយកពី bucket ហើយនេះមិនត្រូវបានប្រើទេ |
| `CLOUDFLARE_ACCOUNT_ID`, `CLOUDFLARE_IMAGES_TOKEN`, `CLOUDFLARE_IMAGES_ACCOUNT_HASH` | `cloudflare` | ថូខេន API ជាមួយសិទ្ធិកែ Images និង account hash ពី Images, Developer resources។ បើក flexible variants សម្រាប់គណនី |
| `CLOUDFLARE_IMAGES_SIGNING_KEY` | `cloudflare` | ជម្រើស។ នៅពេលបានកំណត់ រូបភាពជាឯកជន ហើយអាសយដ្ឋាននីមួយៗត្រូវបានហត្ថលេខា ហើយផុតកំណត់។ គ្មានវា រូបភាពជាសាធារណៈនៅអាសយដ្ឋានដែលបានបង្កើតពី `QUIRE_SECRET_KEY` ដែលគ្មានអ្នកណាអាចទាយបាន |

Cloudflare Images រក្សាច្បាប់ចម្លងផ្ទាល់របស់វានៃរូបភារដើមនីមួយៗដែលវាបម្រើ។ នៅពេលឯកសារត្រូវបានលុប worker លុបច្បាប់ចម្លងនោះមុនរូបភារដើម។

### ជញ្ជាំងការងារ <!--quire:queue-->

ការងារក្រោយកម្មវិធីប្រើ pg-boss ក្នុងមូលដ្ឋានទិន្នន័យ Postgres តែមួយ ដូច្នេះគ្មានសេវាជញ្ជាំងត្រូវដំណើរការ ហើយគ្មានអ្វីត្រូវរៀបចំទេ។ ការងារត្រូវបានដាក់ក្នុងបញ្ជីក្នុង transaction ដដែលជាមួយការផ្លាស់ប្តូរដែលបណ្តាលឱ្យកើត ដូច្នេះការដួលមិនអាចបាត់មួយ ឬផ្ញើមួយពីរដងបានទេ។ `QUIRE_QUEUE_DRIVER` ជា `pgboss` នៅទីនេះ តម្លៃលំនាំដើមរបស់វា។ `vercel` និង `cloudflare` ផ្លាស់តែការជូនដំណឹងស្រាល និង webhooks ទៅជញ្ជាំងផ្ទាល់នៃប្រព័ន្ធប៉ុណ្ណោះ ហើយមគ្គុទ្ទេស Vercel និង Cloudflare ពិពណ៌នាពីពួកវា និងរបៀបដែលស្រទាប់ web របស់ពួកគេដាក់ក្នុងបញ្ជី។

### អ៊ីមែល <!--quire:email-->

កំណត់មួយក្នុងចំណោមទាំងនេះ៖

- `QUIRE_EMAIL_PROVIDER_CONFIG`៖ object JSON ដែលដាក់ឈ្មោះអ្នកផ្តល់ HTTP និងលិខិតសម្គាល់របស់វា ដូចជា `{"provider":"postmark","token":"..."}`។ Postmark, Amazon SES, Mailgun, SendGrid និង Resend ត្រូវបានគាំទ្រ។
- `QUIRE_SMTP_URL`៖ `smtp://user:password@host:587`។ គោលដៅនេះតែប៉ុណ្ណោះ។ គោលដៅ serverless រារាំង SMTP។

`QUIRE_MAIL_FROM` ជាអ្នកផ្ញើ។ សម្រាប់សាកល្បង Quire ចាប់ផ្តើម profile `devmail` កំណត់ `QUIRE_SMTP_URL=smtp://mailpit:1025` ហើយអានសំបុត្រនៅ `http://localhost:8025`។

### សេវាជម្រើស <!--quire:optional-services-->

| ការកំណត់ | ជាមួយ profile |
| --- | --- |
| `CLAMAV_URL=tcp://clamav:3310` | `scan` |
| `GOTENBERG_URL=http://gotenberg:3000` | `preview` |
| `IMGPROXY_KEY`, `IMGPROXY_SALT` | `images` |
| `VALKEY_URL=redis://valkey:6379` | `cache` |
| `QUIRE_OPENSEARCH_URL` ឬ `QUIRE_MEILISEARCH_URL` | ការស្វែងរកខាងក្រៅ។ Postgres ពេញអត្ថបទជំនួស |
| `QUIRE_BREACH_CHECK_PROVIDER=off`, `QUIRE_BREACH_CHECK_URL` | ការពិនិត្យការលេចធ្លាយពាក្យសម្ងាត់។ បើកតាមលំនាំដើមប្រឆាំងនឹង `api.pwnedpasswords.com` (តែប៉ុន្មានអក្សរប្រាំនៃ hash prefix ត្រូវបានផ្ញើ)។ `off` បិទវា ហើយ URL ចង្អុលទៅ range API ដែលអ្នកដំណើរការ |

### ការអាចមើលឃើញ <!--quire:observability-->

`OTEL_EXPORTER_OTLP_ENDPOINT` ដាក់ឈ្មោះអ្នកប្រមូលដែលដំណើរការទាំងអស់ផ្ញើ trace និង metric ទៅ។ ជាមួយ profile `observability` វាជា `http://otelcol:4318` ហើយ `docker/otel-collector.yaml` ជាកន្លែងដែលអ្នកបន្ថែម exporter សម្រាប់ backend របស់អ្នក។ ស្រទាប់ web worker scheduler content និងដំណើរការ collab នាំចេញ span តាម OTLP/HTTP (សំណើ web ប្រតិបត្តិកម្មមូលដ្ឋានទិន្នន័យ tenant ការងារ worker និងការហៅចេញ) នៅពេលវាត្រូវបានកំណត់ ហើយ metric ទៅ endpoint ដដែលរៀងរាល់នាទី (`OTEL_METRICS_EXPORTER=none` បិតពួកវា)។ `OTEL_TRACES_SAMPLER_ARG` កំណត់ចំនួនភាគរយនៃ trace ដែលត្រូវបានរក្សា។ កំណត់ត្រាចូល standard output ក្នុង `LOG_LEVEL` ហើយ Compose បង្វិលពួកវា។ trace មិនដែលផ្ទុកទិន្នន័យផ្ទាល់ខ្លួនទេ។

### ការចេញក្រៅតាមតំបន់ (ការស្នាក់នៅទិន្នន័យ EU) <!--quire:regional-egress-eu-data-residency-->

`QUIRE_REGION=eu` ប្រាប់ថាស្តាខបម្រើអង្គការសហភាពអឺរ៉ុប។ worker បន្ទាប់មកកាន់សំណើចេញក្រៅនីមួយៗដែលបានធ្វើសម្រាប់អង្គការដែលខ្ទាស់ទៅ EU ទៅ allowlist (21-compliance.md ផ្នែក 8.1)។ allowlist ជាម៉ាស៊ីនដែលសេវាដែលបានរៀបចំប្រកាសសម្រាប់តំបន់ (endpoint ការផ្ទុក អ្នកផ្តល់អ៊ីមែល អ្នកផ្តល់វីដេអូ hosted គោលដៅការផ្ទុកផ្ទាល់របស់អង្គការ អ្នកផ្តល់ AI និងគណនីអ៊ីមែល) ម៉ាស៊ីននៃសេវាណាមួយក្រោមការលើកលែងសកម្ម និងម៉ាស៊ីនដែលអ្នករាយក្នុង `QUIRE_EGRESS_ALLOW_HOSTS`។ សំណើទៅម៉ាស៊ីនសាធារណៈផ្សេងណាមួយត្រូវបានបដិសេធមុននឹងវាត្រូវបានផ្ញើ ការបដិសេធត្រូវបានសរសេរក្នុងផ្លូវសវនកម្មនៃអង្គការជា `privacy/egress_refused` ហើយវាត្រូវបានរាយក្រោម ការអនុវត្តតាម ការស្នាក់នៅទិន្នន័យ។

| ការកំណត់ | តម្លៃ | ប៉ះពាល់ |
| --- | --- | --- |
| `QUIRE_EGRESS_ALLOW_HOSTS` | បញ្ជីក្បៀសនៃឈ្មោះម៉ាស៊ីន ឬ `*.example.org` សម្រាប់ subdomain ទាំងអស់ | ម៉ាស៊ីនបន្ថែមដែលអង្គការ EU អាចឈានដល់។ endpoint webhook, xAPI និង SIEM  feed ប្លុក និងម៉ាស៊ីន Amazon SES ស្ថិតនៅទីនេះ ព្រោះពួកវាជាជម្រើសផ្ទាល់របស់អង្គការមួយ ហើយគ្មានសេវាប្រកាសពួកវា។ loopback អាសយដ្ឋានឯកជន និងឈ្មោះ label តែមួយដូចជា `web` ឬ `clamav` ជាបណ្តាញផ្ទាល់របស់អ្នក ហើយមិនដែលត្រូវបានពិនិត្យទេ |

អង្គការ UK និង US មិនត្រូវបានកាន់ទៅបញ្ជីម៉ាស៊ីនទេ។ ពួកគេរក្សាការពិនិត្យតំបន់សេវា។ កំណត់បញ្ជីនៅលើ worker។ ទំព័រអ្នកគ្រប់គ្រងអានវានៅស្រទាប់ web ដើម្បីបង្ហាញ allowlist ដូច្នេះដាក់វាក្នុង `docker/.env` ដែលសេវានីមួយៗអាន។

ការពិនិត្យកម្មវិធីផ្តល់កំហុសច្បាស់លាស់ និងធាតុសវនកម្ម ហើយវាមិនមែនជាការធានាទេ៖ កូឌូអាចខុស។ ការធានាគឺបណ្តាញ។ Compose មិនអនុវត្តវាសម្រាប់អ្នកទេ។ សម្រាប់ស្តាខតាមតំបន់ ដាក់សេវា `worker` និង `web` លើបណ្តាញ `internal: true` ដែលផ្លូវចេញតែមួយគត់គឺ proxy egress (ឧទាហរណ៍ Squid ឬ container tinyproxy) ដែលអនុញ្ញាតម៉ាស៊ីនដដែលជាមួយ `QUIRE_EGRESS_ALLOW_HOSTS` បន្ថែមម៉ាស៊ីននៃសេវាដែលបានរៀបចំរបស់អ្នក ហើយកំណត់ `HTTPS_PROXY` សម្រាប់សេវាទាំងនោះ។ ទំព័រស្នាក់នៅរាយម៉ាស៊ីនពិតដែលកម្មវិធីអនុញ្ញាត ដូច្នេះបញ្ជីទាំងពីរអាចត្រូវបានប្រៀបធៀប។

## សុខភាព <!--quire:health-->

| Endpoint | អត្ថន័យ |
| --- | --- |
| `/healthz` | ការរស់៖ ដំណើរការឆ្លើយ។ ការពិនិត្យសុខភាព Compose ប្រើនេះ |
| `/readyz` | ភាពរួចរាល់៖ ការពឹងផ្អែកឈានដល់បាន ហើយសេវាជម្រើសនីមួយៗបានរាយការណ៍ថាបានរៀបចំឬអត់។ ចង្អុល load balancer របស់អ្នកនៅទីនេះ |

`docker compose -f docker/compose.yaml ps` បង្ហាញសុខភាពនៃសេវានីមួយៗ។

## TLS <!--quire:tls-->

សេវា `proxy` (Caddy, Apache-2.0, `docker/caddy/Caddyfile`) ជាផ្នែកនៃស្តាខលំនាំដើម។ វាឆ្លើយលើច្រក 80 និង 443 ហើយចែកផ្លូវ៖

| ម៉ាស៊ីន ឬផ្លូវ | ទៅកាន់ |
| --- | --- |
| `QUIRE_PROXY_CONTENT_HOST` | `content` |
| `QUIRE_PROXY_APP_HOST` subdomain នីមួយៗនៃ tenant និង domain ផ្ទាល់ | `web` |
| `/_collab/` លើម៉ាស៊ីនទាំងនោះ | `collab` (websocket, `QUIRE_COLLAB_URL`) |
| `/_realtime/connection/` លើម៉ាស៊ីនទាំងនោះ | client websocket របស់ `centrifugo`។ server API របស់វាមិនដែលត្រូវបានបង្ហាញទេ |
| `/_images/` លើម៉ាស៊ីនទាំងនោះ | `imgproxy` ជាមួយ profile `images` (`IMGPROXY_URL`) |

`init-env.sh` បញ្ខប់ `QUIRE_PROXY_APP_HOST`, `QUIRE_PROXY_CONTENT_HOST`, `QUIRE_PROXY_HTTPS_PORT`, `QUIRE_COLLAB_URL` និង `IMGPROXY_URL` ពី origin ទាំងពីរ ដូច្នេះពួកវាមិនអាចឆ្ងាយពីគ្នាបានទេ។ កែពួកវាជាមួយគ្នាបើអ្នកផ្លាស់ប្តូរ origin ដោយដៃ។

វិញ្ញាបនបត្រធ្វើតាម `QUIRE_PROXY_TLS`៖

- `internal` (លំនាំដើម)៖ សាលបន្ទះការទំនាក់ទំនងសាធារណៈផ្ទាល់របស់ Caddy សម្រាប់ `localhost`, `*.localhost` និង `lvh.me`។ ទុកចិត្ត root របស់វាមួយដង រួចរុករក៖

  ```sh
  docker compose -f docker/compose.yaml cp \
    proxy:/data/caddy/pki/authorities/local/root.crt ./quire-local-ca.crt
  ```

  បន្ថែម `quire-local-ca.crt` ទៅបញ្ជីទុកចិត្តប្រព័ន្ធ ឬកម្មវិធីរុករក។ `curl` ទទួលវាជាមួយ `--cacert`។
- អាសយដ្ឋានអ៊ីមែល៖ វិញ្ញាបនបត្រ ACME ស្វ័យប្រវត្តិ (Let's Encrypt រួច ZeroSSL) សម្រាប់ឈ្មោះម៉ាស៊ីនពិត។ DNS សម្រាប់ origin ទាំងពីរ និងម៉ាស៊ីន tenant ទាំងអស់ត្រូវតែចង្អុលនៅទីនេះ ហើយច្រក 80 និង 443 ត្រូវតែឈានដល់បានពីអ៊ីនធឺណិត។

ម៉ាស៊ីន tenant ត្រូវបានចេញតាមសំណើ នៅការទស្សនាដំបូង ហើយតែនៅពេល web បញ្ជាក់ថាឈ្មោះជារបស់ការដំឡើងនេះ (`/tls-allowed` សួរលើបណ្តាញ Compose)។ គ្មាវិញ្ញាបនបត្រ wildcard ឬកម្មវិធីជំនួយអ្នកផ្តល់ DNS ត្រូវការទេ ហើយភាគីអតីតកាលដែលចង្អុលឈ្មោះមួយទៅម៉ាស៊ីនមិនអាចធ្វើឱ្យវាសុំវិញ្ញាបនបត្របានទេ។ វិញ្ញាបនបត្រ និងសាលបន្ទះក្នុងស្រុកស្ថិតក្នុង volume `caddy-data`។ បម្រុងទុកវាជាមួយផ្សេងៗបើអ្នកប្រើ `internal`។

Web ជឿ `X-Forwarded-For` ពី proxy តែម្នាក់ឯង៖ proxy មានអាសយដ្ឋានជាក់លាក់ (`QUIRE_PROXY_ADDRESS` លំនាំដើម `172.29.64.10`) លើ subnet ជាក់លាក់ (`QUIRE_COMPOSE_SUBNET`) ហើយ `QUIRE_TRUSTED_PROXY_CIDRS` ដាក់ឈ្មោះអាសយដ្ឋាននោះ។ បើ subnet ប៉ះទង្គិចជាមួយបណ្តាញលើម៉ាស៊ីន ផ្លាស់ប្តូរទាំងពីរ ហើយដំណើរការ `docker compose down` មុន `up`។

## ក្រោម proxy បញ្ចូលផ្ទាល់របស់អ្នក <!--quire:behind-your-own-reverse-proxy-->

ដើម្បីប្រើ load balancer ឬ proxy ដែលអ្នកដំណើរការរួចហើយជំនួស ទុក `proxy` ចេញ (`docker compose up -d --scale proxy=0`) ហើយបញ្ចប់ TLS នៅមុខ `web` (8080), `content` (8081), `collab` (1234, websocket) និង `centrifugo` (8000, websocket)។ កំណត់អាសយដ្ឋានសាធារណៈក្នុង `QUIRE_APP_ORIGIN`, `QUIRE_CONTENT_ORIGIN` និង `QUIRE_COLLAB_URL` (`wss://`) ហើយចន្លោះអាសយដ្ឋានរបស់ proxy ក្នុង `QUIRE_TRUSTED_PROXY_CIDRS`។

## ការដោះស្រាយបញ្ហា <!--quire:troubleshooting-->

- `init` ចេញជាមួយ "QUIRE_DATABASE_ID is not a UUID"។ កំណត់វាដោយ `uuidgen`។
- `web` ចាប់ផ្តើមឡើងវិញជាមួយ "did not start on compose"។ កំណត់ត្រារាយការកំណត់នីមួយៗដែលវាមិនអាចគោរព ហើយអ្វីដែលត្រូវប្រើជំនួស។
- ការផ្លាស់ប្តូរពាក្យសម្ងាត់តួនាទីក្នុង `.env` បន្ទាប់ពីការចាប់ផ្តើមដំបូងមិនធ្វើអ្វីទេ៖ ស្គ្រីប init ដំណើរការមួយដង។ ប្រើ `ALTER ROLE`។
- ការបញ្ចូលបរាជ័យជាមួយកំហុសស្វែងរកខណៈ `CLAMAV_URL` ត្រូវបានកំណត់៖ ClamAV ទាញយកសញ្ញារបស់វានៅការចាប់ផ្តើមដំបូង ដែលចំណាយពេលប៉ុន្មាននាទី។

Source: https://docs.quirelms.com/km/ops/install/index.mdx
