Updated docs

This commit is contained in:
Julien Nahum 2024-09-02 16:00:48 +02:00
commit 01c0f5b6ce
35 changed files with 1249 additions and 242 deletions

247
README.md
View File

@ -20,244 +20,37 @@
<a href="https://console.algora.io/org/OpnForm/bounties?status=completed"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2FOpnForm%2Fbounties%3Fstatus%3Dcompleted" alt="Rewarded Bounties"></a>
</p>
> An open-source form builder. It's an alternative to products like Typeform, JotForm, Tally etc.
OpnForm is an open-source form builder.
## Features
## Get Started
- No-code form builder, with infinite number of fields & submissions
- Text inputs, Date inputs, URL inputs, Phone inputs, Email inputs, Checkboxes, Select and Multi-Select inputs, Number Inputs, Star-ratings, File uploads & more
- Embed anywhere (on your website, in your Notion page, etc)
- Email notifications (for both form owner & form respondents)
- Hidden fields
- Form passwords
- URL form pre-fill
- Slack integration
- Webhooks
- Form logic
- Customize colors, add images or even some custom code
- Captcha form protection
- Form closing date
- Limit the number of submissions allowed
The easiest way to get started with OpnForm is to sign up for our [managed service in the Cloud](https://opnform.com/). You get support, backups, upgrades, and more. Your data is safe and secure, and you don't need to worry about maintenance or infrastructure. Check out our quick overview of [cloud vs self-hosting](https://docs.opnform.com/deployment/cloud-vs-self-hosting).
And much more!
## Key Features
## Bounties
- 🚀 No-code builder with unlimited forms & submissions
- 📝 Various input types: Text, Date, URL, File uploads & much more
- 🌐 Embed anywhere
- 📧 Email notifications
- 💬 Integrations (Slack, Webhooks, Discord)
- 🧠 Form logic & customization
- 🛡️ Captcha protection
- 📊 Form analytics
Get paid for contributing to OpnForm! Here are our open bounties:
For a complete list of features and detailed documentation, visit our [Technical Documentation](https://docs.opnform.com).
<a href="https://console.algora.io/org/OpnForm/bounties?status=open">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://console.algora.io/api/og/OpnForm/bounties.png?p=0&status=open&theme=dark">
<img alt="Bounties of OpnForm" src="https://console.algora.io/api/og/OpnForm/bounties.png?p=0&status=open&theme=light">
</picture>
</a>
## Quick Start
## Getting started with OpnForm
The easiest way to get started with OpnForm is through our [official managed service in the Cloud](https://opnform.com/).
The easiest way to get started with OpnForm is with the [official managed service in the Cloud](https://opnform.com/).
For self-hosted installations, please refer to our [Deployment Guides](https://docs.opnform.com/deployment). For detailed instructions on setting up a local environment, check out our [Local Deployment Documentation](https://docs.opnform.com/deployment/local-deployment).
It takes 1 minute to try out the builder for free. You'll have high availability, backups, security, and maintenance all managed for you.
## Support & Community
### Requirements
If you need help or have questions, please join our [Discord community](https://discord.gg/YTSjU2a9TS). For more information and assistance, check out the following resources:
- PHP >= 8.0
- MySQL/MariaDB or PostgreSQL
- Node.js and NPM/Yarn/... to compile assets
## Installation
### Environment Setup
Before you can run the application, you need to set up the environment variables. We have provided a script that will automate the process of creating your `.env` files from the provided examples.
Follow these steps to set up your environment:
1. Make sure you have `openssl` installed, as it is required by the setup script to generate secure keys.
2. Run the setup script from the root of the project:
```bash
chmod +x ./scripts/setup-env.sh
./scripts/setup-env.sh
```
**If you are using Docker** and want to prepare a Docker-specific environment, run the script with the `--docker` flag:
```bash
./scripts/setup-env.sh --docker
```
3. After running the script, review the `.env` and `client/.env` files to ensure all settings are correct for your environment.
Remember to never commit your `.env` files to version control. They should be kept private as they contain sensitive information.
### Docker Installation 🐳
OpnForm can be easily set up using Docker. Pre-built images are available on Docker Hub, which is the recommended method for most users.
#### Prerequisites
- Docker
- Docker Compose
#### Quick Start
1. Clone the repository:
```
git clone https://github.com/JhumanJ/OpnForm.git
cd OpnForm
```
2. Set up environment files by running the provided setup script. For detailed instructions, refer to the [Environment Setup](#environment-setup) section above:
```bash
./scripts/setup-env.sh --docker
```
3. Start the application:
```
docker-compose up -d
```
4. Access OpnForm at http://localhost
> 🌐 **Server Deployment Note**: When deploying to a server, configure the app URLs in both `.env` and `client/.env` files. Set `APP_URL` in `.env`, and both `NUXT_PUBLIC_APP_URL` & `NUXT_PUBLIC_API_BASE` in `client/.env`.
#### Customization
- **Environment Variables**: Modify `.env` and `client/.env` files to customize your setup. For example, to enable email features, configure a [supported mail driver](https://laravel.com/docs/11.x/mail) in the `.env` file.
#### Upgrading
1. Check the upgrade instructions for your target version in the documentation.
2. Update your `docker-compose.yml` file if necessary.
3. Apply changes:
```
docker-compose up -d
```
#### Initial Login
After installation, use these credentials to access the admin panel:
- Email: `admin@opnform.com`
- Password: `password`
⚠️ Change these credentials immediately after your first login.
Note: Public registration is disabled in the self-hosted version. Use the admin account to invite additional users.
#### Building from Source
For development or customization, you can build the Docker images locally:
1. Build the images:
```
docker build -t opnform-ui:local -f docker/Dockerfile.client .
docker build -t opnform-api:local -f docker/Dockerfile.api .
```
2. Create a docker-compose override file:
```
touch docker-compose.override.yml
```
Edit the `docker-compose.override.yml` file to use your locally built images:
```yaml
services:
api:
image: opnform-api:local
ui:
image: opnform-ui:local
api-worker:
image: opnform-api:local
```
3. Start the application:
```
docker-compose up -d
```
This method allows you to make changes to the source code and rebuild the images as needed.
#### Clearing all resources
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:
```bash
docker-compose down -v --rmi all
```
### Using Laravel Valet
This section explains how to get started locally with the project. It's most likely relevant if you're trying to work on the project.
First, let's work with the codebase and its dependencies.
```bash
# Get the code!
git clone git@github.com:JhumanJ/OpnForm.git && cd OpnForm
# Install PHP dependencies
composer install
# Install JS dependencies
cd client && npm install
# Compile assets (see the scripts section in package.json)
npm run dev # or build
```
Now, we can configure Laravel. We just need to prepare some vars in our `.env` file, just create it with `cp .env.example .env` then open it!
Configure the desired database in the `DATABASE_` section. You can fine tune your installation on the [laravel documentation](https://laravel.com/docs/9.x).
Run these artisan commands:
```bash
# Generate needed secrets 🙈
php artisan key:generate
php artisan jwt:secret # and select yes!
# Creates DB schemas
php artisan migrate
```
Now, create an S3 bucket (or equivalent). Create an IAM user with access to this bucket, fill the environment variables: `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION`, `AWS_BUCKET`. In your AWS bucket permissions, add the following under "Cross-origin resource sharing (CORS)":
```json
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["PUT", "POST", "GET", "DELETE"],
"AllowedOrigins": ["*"],
"ExposeHeaders": []
}
]
```
🎉 Done! Enjoy your personal OpnForm instance at: [http://opnform.test](http://opnform.test).
## One-Click Deployment
[![Deploy to RepoCloud](https://d16t0pc4846x52.cloudfront.net/deploylobe.svg)](https://repocloud.io/details/?app_id=294)
## Tech Stack
OpnForm is a standard web application built with:
- [Laravel](https://laravel.com/) PHP framework
- [NuxtJs](https://nuxt.com/) Front-end SSR framework
- [Vue.js 3](https://vuejs.org/) Front-end framework
- [TailwindCSS](https://tailwindcss.com/)
## Contribute
You're more than welcome to contribute to this project. We don't have guidelines on this yet, but we will soon. In the meantime, feel free to ask [any question here](https://github.com/JhumanJ/OpnForm/discussions).
- [Product Helpdesk](https://help.opnform.com)
- [Technical Documentation](https://docs.opnform.com)
## License

View File

@ -295,6 +295,7 @@
Advanced options for your select/multiselect fields.
</p>
<text-area-input
v-model="optionsText"
:name="field.id + '_options_text'"
class="mt-3"
label="Set selection options"
@ -658,6 +659,9 @@ export default {
mbLimit() {
return (this.form?.workspace && this.form?.workspace.max_file_size) ? this.form?.workspace?.max_file_size : 10
},
optionsText() {
return this.field[this.field.type].options.map(option => option.name).join('\n')
},
prefillSelectsOptions() {
if (!['select', 'multi_select'].includes(this.field.type)) return {}

View File

@ -21,21 +21,6 @@
</div>
<div class="flex justify-center mt-5 md:mt-0">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-x-4 gap-y-2">
<template v-if="!useFeatureFlag('self_hosted')">
<router-link
:to="{ name: 'privacy-policy' }"
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
>
Privacy Policy
</router-link>
<router-link
:to="{ name: 'terms-conditions' }"
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
>
Terms & Conditions
</router-link>
</template>
<a
:href="opnformConfig.links.feature_requests"
target="_blank"
@ -57,6 +42,28 @@
>
Discord
</a>
<a
:href="opnformConfig.links.tech_docs"
target="_blank"
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
>
Technical Docs
</a>
<template v-if="!useFeatureFlag('self_hosted')">
<router-link
:to="{ name: 'privacy-policy' }"
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
>
Privacy Policy
</router-link>
<router-link
:to="{ name: 'terms-conditions' }"
class="text-gray-600 dark:text-gray-400 transition-colors duration-300 hover:text-nt-blue"
>
Terms & Conditions
</router-link>
</template>
</div>
</div>
</div>

View File

@ -212,6 +212,7 @@ export default {
"Larger file uploads (50mb)",
"Remove OpnForm branding",
"Priority support",
"Form Analytics"
],
}),

View File

@ -16,5 +16,6 @@ export default {
feature_requests: "https://feedback.opnform.com/",
changelog_url: "https://feedback.opnform.com/changelog",
roadmap: "https://feedback.opnform.com/roadmap",
tech_docs: "https://docs.opnform.com",
},
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 300 KiB

9
docs/README.MD Normal file
View File

@ -0,0 +1,9 @@
# Development
Install the Mintlify CLI to preview the documentation changes locally. To install, use the following command
`npm i -g mintlify`
Run the following command at the root of your documentation (where mint.json is)
`mintlify dev`

View File

@ -0,0 +1,4 @@
---
title: "List Forms"
openapi: "GET /external/zapier/forms"
---

View File

@ -0,0 +1,4 @@
---
title: "New Submission Trigger"
openapi: "POST /external/zapier/webhook"
---

View File

@ -0,0 +1,4 @@
---
title: "Sample Submission Polling"
openapi: "GET /external/zapier/submissions/recent"
---

View File

@ -0,0 +1,4 @@
---
title: "Unsubscribe Webhook"
openapi: "DELETE /external/zapier/webhook"
---

View File

@ -0,0 +1,4 @@
---
title: "Validate API Key"
openapi: "GET /external/zapier/validate"
---

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

View File

@ -0,0 +1,37 @@
---
title: "Introduction"
description: "API Documentation for OpnForm"
---
Welcome to the OpnForm API documentation. This guide provides information on how to interact with the OpnForm API to automate your workflows and integrate OpnForm with your applications.
## Authentication
To authenticate with the OpnForm API, you need an API key. Here's how to create and use an API key.
### Creating an API Key
<Frame>
<img
src="/api-reference/images/create-token.png"
alt="Creating an API Key in OpnForm"
/>
</Frame>
1. Log in to your OpnForm account.
2. Navigate to the Access Tokens page at https://opnform.com/settings/access-tokens.
3. Click on the "Create new token" button.
4. Give your token a name (e.g., "My API Integration").
5. Click "Create" to generate your API key. 6. Copy the generated API key immediately, as it won't be displayed again for security reasons.
### Using the API Key
To use the API key, you need to include it in the header of every request you make to the API as a Bearer token. Here's how to do it:
1. Take your API key that you generated earlier.
2. Add it to the `Authorization` header of your HTTP request.
3. Prefix the key with the word "Bearer" followed by a space.
```
Authorization: Bearer YOUR_API_KEY
```

View File

@ -0,0 +1,200 @@
{
"openapi": "3.0.1",
"info": {
"title": "OpnForm API",
"description": "API for interacting with OpnForm, primarily used for Zapier integration",
"version": "1.0.0"
},
"servers": [
{
"url": "https://api.opnform.com"
}
],
"security": [
{
"bearerAuth": []
}
],
"paths": {
"/external/zapier/validate": {
"get": {
"summary": "Validate API Key",
"description": "This endpoint is used by Zapier to test the validity of the API key.",
"responses": {
"200": {
"description": "API key is valid"
},
"401": {
"description": "Invalid API key"
}
}
}
},
"/external/zapier/forms": {
"get": {
"summary": "List Forms",
"description": "Retrieve a list of forms available in a specific workspace.",
"parameters": [
{
"name": "workspace_id",
"in": "query",
"description": "The ID of the workspace for which to list forms",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Successful response",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Form"
}
}
}
}
}
}
}
},
"/external/zapier/webhook": {
"post": {
"summary": "New Submission Trigger",
"description": "This endpoint is used to set up a webhook for new form submissions.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"hookUrl": {
"type": "string",
"description": "The URL provided by Zapier to send the submission data"
},
"form_id": {
"type": "string",
"description": "The ID of the form for which to trigger the webhook"
}
},
"required": ["hookUrl", "form_id"]
}
}
}
},
"responses": {
"200": {
"description": "Webhook successfully set up"
}
}
},
"delete": {
"summary": "Unsubscribe Webhook",
"description": "This endpoint is used to unsubscribe from the webhook.",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"hookUrl": {
"type": "string",
"description": "The URL provided by Zapier to stop sending the submission data"
},
"form_id": {
"type": "string",
"description": "The ID of the form for which to unsubscribe the webhook"
}
},
"required": ["hookUrl", "form_id"]
}
}
}
},
"responses": {
"200": {
"description": "Webhook successfully unsubscribed"
}
}
}
},
"/external/zapier/submissions/recent": {
"get": {
"summary": "Sample Submission Polling",
"description": "Retrieves the most recent submissions for a specified form.",
"parameters": [
{
"name": "form_id",
"in": "query",
"description": "The ID of the form to retrieve submissions for",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Successful response",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Submission"
}
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Form": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"Submission": {
"type": "object",
"properties": {
"submission_id": {
"type": "string"
},
"form_id": {
"type": "string"
},
"submitted_at": {
"type": "string",
"format": "date-time"
},
"data": {
"type": "object",
"additionalProperties": true
}
}
}
},
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer"
}
}
}
}

View File

@ -0,0 +1,30 @@
---
title: AWS S3 Configuration
description: OpnForm & File uploads
---
OpnForm uses [Laravel's filesystem](https://laravel.com/docs/master/filesystem), which provides a powerful abstraction layer for various file storage systems. While AWS S3 is a popular choice, OpnForm supports both local storage and S3-compatible services.
1. Create an S3 bucket in your AWS account (or use an equivalent service).
2. Create an IAM user with access to this bucket. Ensure the user has the necessary permissions to read from and write to the bucket.
3. Configure CORS for the S3 bucket permissions, by adding the following under "Cross-origin resource sharing (CORS)":
```json
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["PUT", "POST", "GET", "DELETE"],
"AllowedOrigins": ["*"],
"ExposeHeaders": []
}
]
```
4. Set the following environment variables in your OpnForm installation (`api/.env`):
- `AWS_ACCESS_KEY_ID`: Your IAM user's access key ID
- `AWS_SECRET_ACCESS_KEY`: Your IAM user's secret access key
- `AWS_DEFAULT_REGION`: The AWS region where your S3 bucket is located
- `AWS_BUCKET`: The name of your S3 bucket
These settings will enable OpnForm to use your S3 bucket for file storage.

View File

@ -0,0 +1,20 @@
---
title: Using your own domain
description: Setting up a custom domain for OpnForm
---
This page explains how to configure a custom domain for your OpnForm instance.
## Custom Domain Setup
To use a custom domain (or subdomain)with OpnForm:
1. Purchase a domain (if needed)
2. Configure DNS records to point to your OpnForm instance
3. Update OpnForm configuration:
- Set `APP_URL` in `.env`
- Set `NUXT_PUBLIC_APP_URL` and `NUXT_PUBLIC_API_BASE` in `client/.env`
4. Secure with SSL certificate
import CloudVersion from "/snippets/cloud-version.mdx";
<CloudVersion/>

View File

@ -0,0 +1,33 @@
---
title: Email Setup
description: OpnForm & Emails
---
This page explains how to configure email notifications in OpnForm.
By default, with a new Docker deployment, the email driver used is `log`, which simply logs the emails instead of sending them. To enable actual email sending, you need to set up a proper email configuration.
OpnForm uses Laravel's mail system. For more detailed information, you can refer to the [Laravel Mail documentation](https://laravel.com/docs/master/mail).
## Supported Mail Drivers
Laravel supports various mail drivers, including SMTP, Mailgun, Postmark, Amazon SES, and more. In this guide, we'll focus on setting up SMTP, which is widely used.
## Configuring SMTP Settings
To configure SMTP, you need to set the following environment variables in your `.env` file:
| Variable | Description |
|----------|-------------|
| `MAIL_MAILER` | Set this to `smtp` to use the SMTP driver. |
| `MAIL_HOST` | The address of your SMTP server. |
| `MAIL_PORT` | The port used by your SMTP server (common ports are 25, 465, and 587). |
| `MAIL_USERNAME` | The username for your SMTP account. |
| `MAIL_PASSWORD` | The password for your SMTP account. |
| `MAIL_ENCRYPTION` | The encryption method used by your SMTP server (typically `tls` or `ssl`). |
| `MAIL_FROM_ADDRESS` | The email address that emails should be sent from. |
| `MAIL_FROM_NAME` | The name that should appear as the sender of the emails. |
import CloudVersion from "/snippets/cloud-version.mdx";
<CloudVersion/>

View File

@ -0,0 +1,64 @@
---
title: Environment Variables Configuration
description: Detailed guide on configuring environment variables for OpnForm
---
OpnForm uses two `.env` files for configuration: one for the Laravel backend located in the `api` directory, and one for the Nuxt front-end located in the `client` directory.
## Backend Environment Variables
The following environment variables are used to [configure the Laravel](https://laravel.com/docs/11.x/configuration) application (OpnForm's API).
### Dedicated guides
There are dedicated configuration pages available for more detailed setup instructions on specific topics:
- [File Storage (S3)](../configuration/aws-s3)
- [Email Configuration (SMTP)](../configuration/email-setup)
- [Custom Domain](../configuration/custom-domain)
### Other Environment Variables
### Configuration Environment Variables
| Variable Name | Description |
|------------------------------|--------------------------------------------------|
| `JWT_TTL` | Time to live for JSON Web Tokens (JWT). |
| `JWT_SECRET` | Secret key used to sign JWTs. |
| `H_CAPTCHA_SITE_KEY` | Site key for hCaptcha integration. |
| `H_CAPTCHA_SECRET_KEY` | Secret key for hCaptcha integration. |
| `OPEN_AI_API_KEY` | API key for accessing OpenAI services. |
| `UNSPLASH_ACCESS_KEY` | Access key for Unsplash API. |
| `UNSPLASH_SECRET_KEY` | Secret key for Unsplash API. |
| `GOOGLE_CLIENT_ID` | Client ID for Google OAuth. |
| `GOOGLE_CLIENT_SECRET` | Client secret for Google OAuth. |
| `GOOGLE_REDIRECT_URL` | Redirect URL for Google OAuth. |
| `GOOGLE_AUTH_REDIRECT_URL` | Authentication redirect URL for Google OAuth. |
| `GOOGLE_FONTS_API_KEY` | API key for accessing Google Fonts. |
| `FRONT_URL` | Public facing URL of the front-end. |
| `FRONT_API_SECRET` | Shared secret with the front-end. |
### User Options Environment Variables
| Variable Name | Description |
|------------------------------|--------------------------------------------------|
| `ADMIN_EMAILS` | Comma-separated list of admin email addresses. |
| `TEMPLATE_EDITOR_EMAILS` | Comma-separated list of template editor emails. |
| `EXTRA_PRO_USERS_EMAILS` | Comma-separated list of extra pro user emails. |
| `MODERATOR_EMAILS` | Comma-separated list of moderator email addresses. |
## Front-end Environment Variables
### Front-end Environment Variables
| Variable Name | Description |
|------------------------------|--------------------------------------------------|
| `NUXT_PUBLIC_APP_URL` | Public facing URL of the Nuxt application. |
| `NUXT_PUBLIC_API_BASE` | Base URL for the Laravel API. |
| `NUXT_PUBLIC_H_CAPTCHA_SITE_KEY` | Site key for hCaptcha integration on the front-end. |
| `NUXT_API_SECRET` | Shared secret key between Nuxt and Laravel backend. |
import CloudVersion from "/snippets/cloud-version.mdx";
<CloudVersion/>

View File

@ -0,0 +1,10 @@
# Bounties
This page will explain the bounty system for OpnForm contributions.
Topics include:
- How the bounty system works
- Current open bounties
- How to claim a bounty
- Payout process

View File

@ -0,0 +1,20 @@
---
title: "Getting Started with Contributing"
description: "Learn how to contribute to OpnForm"
---
Welcome to the OpnForm contributing guide! Here are some helpful links to get you started:
<CardGroup cols={2}>
<Card title="Setting up the development environment" icon="gear" href="../configuration/environment-variables">
Learn how to configure the environment variables for OpnForm.
</Card>
<Card title="Adding a new form block" icon="puzzle-piece" href="new-form-block">
Follow the steps to add a new form block to your project.
</Card>
<Card title="Integrating new services" icon="plug" href="new-integration">
Discover how to integrate new services with OpnForm.
</Card>
</CardGroup>
We sometimes offer bounties for certain issues. Check out our [bounties page](https://console.algora.io/org/OpnForm) for more information.

View File

@ -0,0 +1,6 @@
---
title: "Creating a New Form Block"
description: "Learn how to create a new form block for OpnForm"
---
<Warning>Whoops not ready yet! This page is still to be written.</Warning>

View File

@ -0,0 +1,6 @@
---
title: "Adding a New Integration"
description: "Learn how to add a new integration to OpnForm"
---
<Warning>Whoops not ready yet! This page is still to be written.</Warning>

View File

@ -0,0 +1,34 @@
---
title: "Cloud vs Self-Hosting"
description: "Understand the differences between cloud and self-hosting options for OpnForm"
---
When deciding between using OpnForm's cloud service or self-hosting, it's important to understand the key differences and benefits of each option. Below is a comparison to help you make an informed decision:
| | Self-Hosted | OpnForm Cloud |
| ------------------------------ | ----------------------------------- | ---------------------------------- |
| High availability servers | \~$50+/month | ✅ |
| Load balancer | \~$15+/month | ✅ |
| Managed database | \~$45-70+/month | ✅ |
| SMTP server | \~$15+/month | ✅ |
| Support | Discord | Live Chat |
| Setup time | Manual | ✅ Up and running in minutes |
| Upgrades | Manual | ✅ Automatic |
| Multi-zone availability | Manual | ✅ |
| Backups | Manual | ✅ Automatic |
| Monitoring | Manual | ✅ |
| Custom code and forks | ✅ | ❌ |
| SSL certificate | Manual | ✅ |
| Where your money goes | 3rd-party services | Improving OpnForm |
### When Should You Self-Host?
There are specific scenarios where self-hosting OpnForm might be the better choice:
- **Regulatory Compliance**: If you need an air-gapped environment for regulatory reasons (e.g., HIPAA compliance).
- **Custom Builds**: If you want to run a custom build or fork of OpnForm with your own customizations.
- **Custom Integrations**: If you need to use community or custom integrations that are not supported in the cloud version.
For most users, the managed OpnForm Cloud service will be the better option due to its ease of use, automatic updates, and comprehensive support.
*This comparison is inspired by the [Metabase Cloud vs Self-Hosting page](https://www.metabase.com/docs/latest/cloud/cloud-vs-self-hosting).*

View File

@ -0,0 +1,94 @@
---
title: "Docker"
description: "OpnForm can be easily set up using Docker. We provide pre-built images on Docker Hub, which is the recommended method for most users."
---
import CloudVersion from "/snippets/cloud-version.mdx";
<CloudVersion/>
## 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:
```bash
./scripts/setup-env.sh --docker
```
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
## Docker Images
OpnForm provides pre-built Docker images for easy deployment. You can find our official Docker images on Docker Hub:
- [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 Your Own Docker Images
If you prefer to build your own Docker images, you can do so using the provided Dockerfiles in the repository:
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 .
```
### 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
services:
api:
image: opnform-api:local
ui:
image: opnform-ui:local
api-worker:
image: opnform-api:local
```
### Clearing all resources
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:
```
docker-compose down -v --rmi all
```

View File

@ -0,0 +1,64 @@
---
title: "Local Deployment"
description: "Set up OpnForm locally for development"
---
import CloudVersion from "/snippets/cloud-version.mdx";
<CloudVersion/>
## Requirements
Before proceeding with the local deployment, ensure you have the following prerequisites installed on your system:
- **PHP**: Version 8.0 or higher
- **Composer**: The PHP dependency manager
- **Node.js**: Version 14 or higher
- **NPM** or **Yarn**: Package managers for Node.js
- **MySQL**, or **PostgreSQL**: A supported database system
Make sure these components are properly installed and configured on your local machine before proceeding with the deployment steps.
## Local setup
1. Install Laravel Herd -Download and install Laravel Herd from the official website: https://herd.laravel.com/
2. Clone the repository and install dependencies:
```bash
git clone git@github.com:JhumanJ/OpnForm.git && cd OpnForm
cd api && composer install
cd ../client && npm install
```
3. Compile assets and run dev server:
```bash
cd client && npm run dev # or build
```
4. Set up environment files:
```bash
./scripts/setup-env.sh
```
This script will create the necessary `.env` files for both the API and client.
5. Run the migrations:
```bash
cd api
php artisan migrate
```
6. Set up Herd:
For detailed instructions on setting up Herd, refer to the [Herd documentation](https://herd.laravel.com/docs).
- Open the Herd application
- Add your OpnForm's `api` directory to Herd
- Herd will automatically configure a local domain for your project
This will start the Nuxt.js development server, typically on `http://localhost:3000`.
8. Access your local OpnForm installation:
- The API will be available at the domain provided by Herd (e.g., `http://opnform.test`)
- The frontend will be accessible at `http://localhost:3000`

BIN
docs/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

49
docs/favicon.svg Normal file
View File

@ -0,0 +1,49 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.95343 21.1394C4.89586 21.1304 2.25471 19.458 0.987296 16.2895C-0.280118 13.121 0.108924 9.16314 1.74363 5.61505C4.8012 5.62409 7.44235 7.29648 8.70976 10.465C9.97718 13.6335 9.58814 17.5914 7.95343 21.1394Z" fill="white"/>
<path d="M7.95343 21.1394C4.89586 21.1304 2.25471 19.458 0.987296 16.2895C-0.280118 13.121 0.108924 9.16314 1.74363 5.61505C4.8012 5.62409 7.44235 7.29648 8.70976 10.465C9.97718 13.6335 9.58814 17.5914 7.95343 21.1394Z" fill="url(#paint0_radial_101_2703)"/>
<path d="M7.95343 21.1394C4.89586 21.1304 2.25471 19.458 0.987296 16.2895C-0.280118 13.121 0.108924 9.16314 1.74363 5.61505C4.8012 5.62409 7.44235 7.29648 8.70976 10.465C9.97718 13.6335 9.58814 17.5914 7.95343 21.1394Z" fill="black" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
<path d="M7.95343 21.1394C4.89586 21.1304 2.25471 19.458 0.987296 16.2895C-0.280118 13.121 0.108924 9.16314 1.74363 5.61505C4.8012 5.62409 7.44235 7.29648 8.70976 10.465C9.97718 13.6335 9.58814 17.5914 7.95343 21.1394Z" fill="url(#paint1_linear_101_2703)" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
<path d="M8.68359 10.4755C9.94543 13.63 9.56145 17.5723 7.9354 21.1112C4.89702 21.0957 2.27411 19.4306 1.01347 16.279C-0.248375 13.1245 0.135612 9.18218 1.76165 5.64328C4.80004 5.65883 7.42295 7.32386 8.68359 10.4755Z" stroke="url(#paint2_linear_101_2703)" stroke-opacity="0.05" stroke-width="0.056338"/>
<path d="M7.31038 21.2574C11.3543 20.2215 14.8836 17.3754 16.6285 13.2361C18.3735 9.09671 17.9448 4.58749 15.8598 0.976291C11.8159 2.01214 8.2866 4.85826 6.54167 8.99762C4.79674 13.137 5.2254 17.6462 7.31038 21.2574Z" fill="white"/>
<path d="M7.31038 21.2574C11.3543 20.2215 14.8836 17.3754 16.6285 13.2361C18.3735 9.09671 17.9448 4.58749 15.8598 0.976291C11.8159 2.01214 8.2866 4.85826 6.54167 8.99762C4.79674 13.137 5.2254 17.6462 7.31038 21.2574Z" fill="url(#paint3_radial_101_2703)"/>
<path d="M16.6026 13.2251C14.8642 17.349 11.3512 20.1866 7.32411 21.2248C5.25257 17.624 4.82926 13.1324 6.56764 9.00855C8.30603 4.88472 11.819 2.04706 15.8461 1.00889C17.9176 4.60967 18.3409 9.10131 16.6026 13.2251Z" stroke="url(#paint4_linear_101_2703)" stroke-opacity="0.05" stroke-width="0.056338"/>
<path d="M7.23368 21.2069C9.78906 23.2373 13.2102 23.9506 16.5772 22.8141C19.9441 21.6775 22.5058 18.9445 23.7304 15.6382C21.175 13.6078 17.7538 12.8944 14.3869 14.031C11.0199 15.1676 8.45822 17.9006 7.23368 21.2069Z" fill="white"/>
<path d="M7.23368 21.2069C9.78906 23.2373 13.2102 23.9506 16.5772 22.8141C19.9441 21.6775 22.5058 18.9445 23.7304 15.6382C21.175 13.6078 17.7538 12.8944 14.3869 14.031C11.0199 15.1676 8.45822 17.9006 7.23368 21.2069Z" fill="url(#paint5_radial_101_2703)"/>
<path d="M7.23368 21.2069C9.78906 23.2373 13.2102 23.9506 16.5772 22.8141C19.9441 21.6775 22.5058 18.9445 23.7304 15.6382C21.175 13.6078 17.7538 12.8944 14.3869 14.031C11.0199 15.1676 8.45822 17.9006 7.23368 21.2069Z" fill="black" fill-opacity="0.2" style="mix-blend-mode:hard-light"/>
<path d="M7.23368 21.2069C9.78906 23.2373 13.2102 23.9506 16.5772 22.8141C19.9441 21.6775 22.5058 18.9445 23.7304 15.6382C21.175 13.6078 17.7538 12.8944 14.3869 14.031C11.0199 15.1676 8.45822 17.9006 7.23368 21.2069Z" fill="url(#paint6_linear_101_2703)" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
<path d="M16.5682 22.7874C13.2176 23.9184 9.81361 23.2124 7.2672 21.1975C8.49194 17.9068 11.0444 15.189 14.3959 14.0577C17.7465 12.9266 21.1504 13.6326 23.6968 15.6476C22.4721 18.9383 19.9196 21.656 16.5682 22.7874Z" stroke="url(#paint7_linear_101_2703)" stroke-opacity="0.05" stroke-width="0.056338"/>
<defs>
<radialGradient id="paint0_radial_101_2703" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(-3.00503 15.023) rotate(-10.029) scale(17.9572 17.784)">
<stop stop-color="#00B0BB"/>
<stop offset="1" stop-color="#00DB65"/>
</radialGradient>
<linearGradient id="paint1_linear_101_2703" x1="7.39036" y1="4.81308" x2="1.62975" y2="18.6894" gradientUnits="userSpaceOnUse">
<stop stop-color="#18E299"/>
<stop offset="1"/>
</linearGradient>
<linearGradient id="paint2_linear_101_2703" x1="7.94816" y1="8.01563" x2="1.7612" y2="18.746" gradientUnits="userSpaceOnUse">
<stop/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
<radialGradient id="paint3_radial_101_2703" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(8.11404 20.8822) rotate(-75.7542) scale(21.6246 23.7772)">
<stop stop-color="#00BBBB"/>
<stop offset="0.712616" stop-color="#00DB65"/>
</radialGradient>
<linearGradient id="paint4_linear_101_2703" x1="7.60205" y1="5.8709" x2="15.5561" y2="16.3719" gradientUnits="userSpaceOnUse">
<stop/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
<radialGradient id="paint5_radial_101_2703" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(7.84537 21.5181) rotate(-20.3525) scale(18.5603 17.32)">
<stop stop-color="#00B0BB"/>
<stop offset="1" stop-color="#00DB65"/>
</radialGradient>
<linearGradient id="paint6_linear_101_2703" x1="16.8078" y1="13.0071" x2="10.0409" y2="22.9937" gradientUnits="userSpaceOnUse">
<stop stop-color="#00B1BC"/>
<stop offset="1"/>
</linearGradient>
<linearGradient id="paint7_linear_101_2703" x1="16.8078" y1="13.0071" x2="14.1687" y2="23.841" gradientUnits="userSpaceOnUse">
<stop/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

161
docs/images/hero-dark.svg Normal file
View File

@ -0,0 +1,161 @@
<svg width="700" height="320" viewBox="0 0 700 320" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2862_30)">
<rect width="700" height="320" rx="16" fill="url(#paint0_linear_2862_30)"/>
<path d="M311.889 247.3C283.097 247.215 258.226 231.466 246.292 201.629C234.357 171.793 238.02 134.523 253.414 101.112C282.206 101.197 307.077 116.945 319.011 146.782C330.946 176.619 327.283 213.888 311.889 247.3Z" fill="white"/>
<path d="M311.889 247.3C283.097 247.215 258.226 231.466 246.292 201.629C234.357 171.793 238.02 134.523 253.414 101.112C282.206 101.197 307.077 116.945 319.011 146.782C330.946 176.619 327.283 213.888 311.889 247.3Z" fill="url(#paint1_radial_2862_30)"/>
<path d="M311.889 247.3C283.097 247.215 258.226 231.466 246.292 201.629C234.357 171.793 238.02 134.523 253.414 101.112C282.206 101.197 307.077 116.945 319.011 146.782C330.946 176.619 327.283 213.888 311.889 247.3Z" fill="black" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
<path d="M311.889 247.3C283.097 247.215 258.226 231.466 246.292 201.629C234.357 171.793 238.02 134.523 253.414 101.112C282.206 101.197 307.077 116.945 319.011 146.782C330.946 176.619 327.283 213.888 311.889 247.3Z" fill="url(#paint2_linear_2862_30)" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
<path d="M311.72 247.034C283.108 246.887 258.409 231.208 246.538 201.531C234.656 171.825 238.271 134.702 253.583 101.377C282.195 101.524 306.894 117.203 318.765 146.88C330.647 176.586 327.031 213.709 311.72 247.034Z" stroke="url(#paint3_linear_2862_30)" stroke-opacity="0.05" stroke-width="0.530516"/>
<path d="M305.839 247.174C343.92 237.419 377.154 210.619 393.585 171.64C410.017 132.661 405.98 90.1988 386.347 56.1934C348.266 65.9477 315.032 92.7486 298.601 131.728C282.169 170.706 286.206 213.168 305.839 247.174Z" fill="white"/>
<path d="M305.839 247.174C343.92 237.419 377.154 210.619 393.585 171.64C410.017 132.661 405.98 90.1988 386.347 56.1934C348.266 65.9477 315.032 92.7486 298.601 131.728C282.169 170.706 286.206 213.168 305.839 247.174Z" fill="url(#paint4_radial_2862_30)"/>
<path d="M393.341 171.537C376.971 210.369 343.89 237.091 305.969 246.867C286.462 212.959 282.476 170.663 298.845 131.831C315.215 92.9978 348.295 66.2765 386.217 56.5004C405.724 90.4077 409.71 132.704 393.341 171.537Z" stroke="url(#paint5_linear_2862_30)" stroke-opacity="0.05" stroke-width="0.530516"/>
<path d="M305.686 246.995C329.749 266.114 361.965 272.832 393.67 262.129C425.376 251.426 449.499 225.691 461.03 194.556C436.967 175.437 404.751 168.719 373.045 179.422C341.34 190.125 317.217 215.86 305.686 246.995Z" fill="white"/>
<path d="M305.686 246.995C329.749 266.114 361.965 272.832 393.67 262.129C425.376 251.426 449.499 225.691 461.03 194.556C436.967 175.437 404.751 168.719 373.045 179.422C341.34 190.125 317.217 215.86 305.686 246.995Z" fill="url(#paint6_radial_2862_30)"/>
<path d="M305.686 246.995C329.749 266.114 361.965 272.832 393.67 262.129C425.376 251.426 449.499 225.691 461.03 194.556C436.967 175.437 404.751 168.719 373.045 179.422C341.34 190.125 317.217 215.86 305.686 246.995Z" fill="black" fill-opacity="0.2" style="mix-blend-mode:hard-light"/>
<path d="M305.686 246.995C329.749 266.114 361.965 272.832 393.67 262.129C425.376 251.426 449.499 225.691 461.03 194.556C436.967 175.437 404.751 168.719 373.045 179.422C341.34 190.125 317.217 215.86 305.686 246.995Z" fill="url(#paint7_linear_2862_30)" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
<path d="M393.586 261.878C362.034 272.529 329.98 265.88 306.002 246.907C317.534 215.919 341.57 190.327 373.13 179.673C404.681 169.023 436.735 175.671 460.714 194.644C449.181 225.632 425.145 251.224 393.586 261.878Z" stroke="url(#paint8_linear_2862_30)" stroke-opacity="0.05" stroke-width="0.530516"/>
<g opacity="0.8" filter="url(#filter0_f_2862_30)">
<circle cx="660" cy="-60" r="160" fill="#18E244" fill-opacity="0.4"/>
</g>
<g opacity="0.8" filter="url(#filter1_f_2862_30)">
<circle cx="20" cy="213" r="160" fill="#18CAE2" fill-opacity="0.33"/>
</g>
<g opacity="0.8" filter="url(#filter2_f_2862_30)">
<circle cx="660" cy="480" r="160" fill="#18E2B2" fill-opacity="0.52"/>
</g>
<g opacity="0.8" filter="url(#filter3_f_2862_30)">
<circle cx="20" cy="413" r="160" fill="#4018E2" fill-opacity="0.22"/>
</g>
<path opacity="0.2" d="M0 50H700" stroke="url(#paint9_radial_2862_30)" stroke-dasharray="4 4"/>
<path opacity="0.1" d="M0 82H700" stroke="url(#paint10_radial_2862_30)" stroke-dasharray="4 4"/>
<path opacity="0.2" d="M239 0L239 320" stroke="url(#paint11_radial_2862_30)" stroke-dasharray="4 4"/>
<path opacity="0.1" d="M271 0L271 320" stroke="url(#paint12_radial_2862_30)" stroke-dasharray="4 4"/>
<path opacity="0.2" d="M461 0L461 320" stroke="url(#paint13_radial_2862_30)" stroke-dasharray="4 4"/>
<path opacity="0.1" d="M429 0L429 320" stroke="url(#paint14_radial_2862_30)" stroke-dasharray="4 4"/>
<path opacity="0.2" d="M0 271H700" stroke="url(#paint15_radial_2862_30)" stroke-dasharray="4 4"/>
<path opacity="0.1" d="M0 239H700" stroke="url(#paint16_radial_2862_30)" stroke-dasharray="4 4"/>
<g style="mix-blend-mode:overlay" opacity="0.1">
<path d="M0 160H700" stroke="url(#paint17_linear_2862_30)"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.2">
<path d="M511 -1L189 321" stroke="url(#paint18_linear_2862_30)"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.2">
<path d="M511 321L189 -1" stroke="url(#paint19_linear_2862_30)"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.1">
<circle cx="350" cy="160" r="111" stroke="white"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.1">
<circle cx="350" cy="160" r="79" stroke="white"/>
</g>
</g>
<defs>
<filter id="filter0_f_2862_30" x="260" y="-460" width="800" height="800" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="120" result="effect1_foregroundBlur_2862_30"/>
</filter>
<filter id="filter1_f_2862_30" x="-380" y="-187" width="800" height="800" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="120" result="effect1_foregroundBlur_2862_30"/>
</filter>
<filter id="filter2_f_2862_30" x="260" y="80" width="800" height="800" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="120" result="effect1_foregroundBlur_2862_30"/>
</filter>
<filter id="filter3_f_2862_30" x="-380" y="13" width="800" height="800" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="120" result="effect1_foregroundBlur_2862_30"/>
</filter>
<linearGradient id="paint0_linear_2862_30" x1="1.04308e-05" y1="320" x2="710.784" y2="26.0793" gradientUnits="userSpaceOnUse">
<stop stop-color="#18E299" stop-opacity="0.09"/>
<stop offset="0.729167" stop-color="#0D9373" stop-opacity="0.08"/>
</linearGradient>
<radialGradient id="paint1_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(208.697 189.703) rotate(-10.029) scale(169.097 167.466)">
<stop stop-color="#00B0BB"/>
<stop offset="1" stop-color="#00DB65"/>
</radialGradient>
<linearGradient id="paint2_linear_2862_30" x1="306.587" y1="93.5598" x2="252.341" y2="224.228" gradientUnits="userSpaceOnUse">
<stop stop-color="#18E299"/>
<stop offset="1"/>
</linearGradient>
<linearGradient id="paint3_linear_2862_30" x1="311.84" y1="123.717" x2="253.579" y2="224.761" gradientUnits="userSpaceOnUse">
<stop/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
<radialGradient id="paint4_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(313.407 243.64) rotate(-75.7542) scale(203.632 223.902)">
<stop stop-color="#00BBBB"/>
<stop offset="0.712616" stop-color="#00DB65"/>
</radialGradient>
<linearGradient id="paint5_linear_2862_30" x1="308.586" y1="102.284" x2="383.487" y2="201.169" gradientUnits="userSpaceOnUse">
<stop/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
<radialGradient id="paint6_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(311.446 249.925) rotate(-20.3524) scale(174.776 163.096)">
<stop stop-color="#00B0BB"/>
<stop offset="1" stop-color="#00DB65"/>
</radialGradient>
<linearGradient id="paint7_linear_2862_30" x1="395.842" y1="169.781" x2="332.121" y2="263.82" gradientUnits="userSpaceOnUse">
<stop stop-color="#00B1BC"/>
<stop offset="1"/>
</linearGradient>
<linearGradient id="paint8_linear_2862_30" x1="395.842" y1="169.781" x2="370.99" y2="271.799" gradientUnits="userSpaceOnUse">
<stop/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
<radialGradient id="paint9_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(350 50) scale(398.125 182)">
<stop offset="0.348958" stop-color="#84FFD3"/>
<stop offset="0.880208" stop-color="#18E299" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint10_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(350 82) scale(398.125 182)">
<stop offset="0.348958" stop-color="#84FFD3"/>
<stop offset="0.880208" stop-color="#18E299" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint11_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(239 160) rotate(90) scale(182 182)">
<stop offset="0.348958" stop-color="#84FFD3"/>
<stop offset="0.880208" stop-color="#18E299" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint12_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(271 160) rotate(90) scale(182 182)">
<stop offset="0.348958" stop-color="#84FFD3"/>
<stop offset="0.880208" stop-color="#18E299" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint13_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(461 160) rotate(90) scale(182 182)">
<stop offset="0.348958" stop-color="#84FFD3"/>
<stop offset="0.880208" stop-color="#18E299" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint14_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(429 160) rotate(90) scale(182 182)">
<stop offset="0.348958" stop-color="#84FFD3"/>
<stop offset="0.880208" stop-color="#18E299" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint15_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(350 271) scale(398.125 182)">
<stop offset="0.348958" stop-color="#84FFD3"/>
<stop offset="0.880208" stop-color="#18E299" stop-opacity="0"/>
</radialGradient>
<radialGradient id="paint16_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(350 239) scale(398.125 182)">
<stop offset="0.348958" stop-color="#84FFD3"/>
<stop offset="0.880208" stop-color="#18E299" stop-opacity="0"/>
</radialGradient>
<linearGradient id="paint17_linear_2862_30" x1="0" y1="160" x2="700" y2="160" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.1"/>
<stop offset="0.5" stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.1"/>
</linearGradient>
<linearGradient id="paint18_linear_2862_30" x1="511" y1="-1" x2="189" y2="321" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.1"/>
<stop offset="0.5" stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.1"/>
</linearGradient>
<linearGradient id="paint19_linear_2862_30" x1="511" y1="321" x2="189" y2="-0.999997" gradientUnits="userSpaceOnUse">
<stop stop-color="white" stop-opacity="0.1"/>
<stop offset="0.5" stop-color="white"/>
<stop offset="1" stop-color="white" stop-opacity="0.1"/>
</linearGradient>
<clipPath id="clip0_2862_30">
<rect width="700" height="320" rx="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

155
docs/images/hero-light.svg Normal file
View File

@ -0,0 +1,155 @@
<svg width="700" height="320" viewBox="0 0 700 320" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2862_278)">
<rect width="700" height="320" rx="16" fill="url(#paint0_linear_2862_278)"/>
<path d="M311.889 247.3C283.097 247.215 258.226 231.466 246.292 201.629C234.357 171.793 238.02 134.523 253.414 101.112C282.206 101.197 307.077 116.945 319.011 146.782C330.946 176.619 327.283 213.888 311.889 247.3Z" fill="white"/>
<path d="M311.889 247.3C283.097 247.215 258.226 231.466 246.292 201.629C234.357 171.793 238.02 134.523 253.414 101.112C282.206 101.197 307.077 116.945 319.011 146.782C330.946 176.619 327.283 213.888 311.889 247.3Z" fill="url(#paint1_radial_2862_278)"/>
<path d="M311.889 247.3C283.097 247.215 258.226 231.466 246.292 201.629C234.357 171.793 238.02 134.523 253.414 101.112C282.206 101.197 307.077 116.945 319.011 146.782C330.946 176.619 327.283 213.888 311.889 247.3Z" fill="black" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
<path d="M311.889 247.3C283.097 247.215 258.226 231.466 246.292 201.629C234.357 171.793 238.02 134.523 253.414 101.112C282.206 101.197 307.077 116.945 319.011 146.782C330.946 176.619 327.283 213.888 311.889 247.3Z" fill="url(#paint2_linear_2862_278)" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
<path d="M311.72 247.034C283.108 246.887 258.409 231.208 246.538 201.531C234.656 171.825 238.271 134.702 253.583 101.377C282.195 101.524 306.894 117.203 318.765 146.88C330.647 176.586 327.031 213.709 311.72 247.034Z" stroke="url(#paint3_linear_2862_278)" stroke-opacity="0.05" stroke-width="0.530516"/>
<path d="M305.839 247.174C343.92 237.419 377.154 210.619 393.585 171.64C410.017 132.661 405.98 90.1988 386.347 56.1934C348.266 65.9477 315.032 92.7486 298.601 131.728C282.169 170.706 286.206 213.168 305.839 247.174Z" fill="white"/>
<path d="M305.839 247.174C343.92 237.419 377.154 210.619 393.585 171.64C410.017 132.661 405.98 90.1988 386.347 56.1934C348.266 65.9477 315.032 92.7486 298.601 131.728C282.169 170.706 286.206 213.168 305.839 247.174Z" fill="url(#paint4_radial_2862_278)"/>
<path d="M393.341 171.537C376.971 210.369 343.89 237.091 305.969 246.867C286.462 212.959 282.476 170.663 298.845 131.831C315.215 92.9978 348.295 66.2765 386.217 56.5004C405.724 90.4077 409.71 132.704 393.341 171.537Z" stroke="url(#paint5_linear_2862_278)" stroke-opacity="0.05" stroke-width="0.530516"/>
<path d="M305.686 246.995C329.75 266.114 361.965 272.832 393.671 262.129C425.376 251.426 449.499 225.691 461.03 194.556C436.967 175.437 404.751 168.719 373.046 179.422C341.34 190.125 317.217 215.86 305.686 246.995Z" fill="white"/>
<path d="M305.686 246.995C329.75 266.114 361.965 272.832 393.671 262.129C425.376 251.426 449.499 225.691 461.03 194.556C436.967 175.437 404.751 168.719 373.046 179.422C341.34 190.125 317.217 215.86 305.686 246.995Z" fill="url(#paint6_radial_2862_278)"/>
<path d="M305.686 246.995C329.75 266.114 361.965 272.832 393.671 262.129C425.376 251.426 449.499 225.691 461.03 194.556C436.967 175.437 404.751 168.719 373.046 179.422C341.34 190.125 317.217 215.86 305.686 246.995Z" fill="black" fill-opacity="0.2" style="mix-blend-mode:hard-light"/>
<path d="M305.686 246.995C329.75 266.114 361.965 272.832 393.671 262.129C425.376 251.426 449.499 225.691 461.03 194.556C436.967 175.437 404.751 168.719 373.046 179.422C341.34 190.125 317.217 215.86 305.686 246.995Z" fill="url(#paint7_linear_2862_278)" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
<path d="M393.586 261.878C362.035 272.529 329.981 265.88 306.002 246.907C317.535 215.919 341.571 190.327 373.13 179.673C404.682 169.023 436.736 175.671 460.715 194.644C449.182 225.632 425.146 251.224 393.586 261.878Z" stroke="url(#paint8_linear_2862_278)" stroke-opacity="0.05" stroke-width="0.530516"/>
<g opacity="0.8" filter="url(#filter0_f_2862_278)">
<circle cx="660" cy="-60" r="160" fill="#18E299" fill-opacity="0.4"/>
</g>
<g opacity="0.8" filter="url(#filter1_f_2862_278)">
<circle cx="20" cy="213" r="160" fill="#18E299" fill-opacity="0.33"/>
</g>
<g opacity="0.8" filter="url(#filter2_f_2862_278)">
<circle cx="660" cy="480" r="160" fill="#18E299" fill-opacity="0.52"/>
</g>
<g opacity="0.8" filter="url(#filter3_f_2862_278)">
<circle cx="20" cy="413" r="160" fill="#18E299" fill-opacity="0.22"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.1">
<path d="M0 50H700" stroke="black" stroke-dasharray="4 4"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.1">
<path d="M0 82H700" stroke="black" stroke-dasharray="4 4"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.1">
<path d="M239 0L239 320" stroke="black" stroke-dasharray="4 4"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.1">
<path d="M271 0L271 320" stroke="black" stroke-dasharray="4 4"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.1">
<path d="M461 0L461 320" stroke="black" stroke-dasharray="4 4"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.1">
<path d="M350 0L350 320" stroke="url(#paint9_linear_2862_278)"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.1">
<path d="M429 0L429 320" stroke="black" stroke-dasharray="4 4"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.1">
<path d="M0 271H700" stroke="black" stroke-dasharray="4 4"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.1">
<path d="M0 239H700" stroke="black" stroke-dasharray="4 4"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.1">
<path d="M0 160H700" stroke="url(#paint10_linear_2862_278)"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.1">
<path d="M511 -1L189 321" stroke="url(#paint11_linear_2862_278)"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.1">
<path d="M511 321L189 -1" stroke="url(#paint12_linear_2862_278)"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.05">
<circle cx="350" cy="160" r="111" stroke="black"/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.05">
<circle cx="350" cy="160" r="79" stroke="black"/>
</g>
</g>
<defs>
<filter id="filter0_f_2862_278" x="260" y="-460" width="800" height="800" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="120" result="effect1_foregroundBlur_2862_278"/>
</filter>
<filter id="filter1_f_2862_278" x="-380" y="-187" width="800" height="800" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="120" result="effect1_foregroundBlur_2862_278"/>
</filter>
<filter id="filter2_f_2862_278" x="260" y="80" width="800" height="800" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="120" result="effect1_foregroundBlur_2862_278"/>
</filter>
<filter id="filter3_f_2862_278" x="-380" y="13" width="800" height="800" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="120" result="effect1_foregroundBlur_2862_278"/>
</filter>
<linearGradient id="paint0_linear_2862_278" x1="1.04308e-05" y1="320" x2="710.784" y2="26.0793" gradientUnits="userSpaceOnUse">
<stop stop-color="#18E299" stop-opacity="0.09"/>
<stop offset="0.729167" stop-color="#0D9373" stop-opacity="0.08"/>
</linearGradient>
<radialGradient id="paint1_radial_2862_278" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(208.697 189.703) rotate(-10.029) scale(169.097 167.466)">
<stop stop-color="#00B0BB"/>
<stop offset="1" stop-color="#00DB65"/>
</radialGradient>
<linearGradient id="paint2_linear_2862_278" x1="306.587" y1="93.5598" x2="252.341" y2="224.228" gradientUnits="userSpaceOnUse">
<stop stop-color="#18E299"/>
<stop offset="1"/>
</linearGradient>
<linearGradient id="paint3_linear_2862_278" x1="311.84" y1="123.717" x2="253.579" y2="224.761" gradientUnits="userSpaceOnUse">
<stop/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
<radialGradient id="paint4_radial_2862_278" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(313.407 243.64) rotate(-75.7542) scale(203.632 223.902)">
<stop stop-color="#00BBBB"/>
<stop offset="0.712616" stop-color="#00DB65"/>
</radialGradient>
<linearGradient id="paint5_linear_2862_278" x1="308.586" y1="102.284" x2="383.487" y2="201.169" gradientUnits="userSpaceOnUse">
<stop/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
<radialGradient id="paint6_radial_2862_278" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(311.447 249.925) rotate(-20.3524) scale(174.776 163.096)">
<stop stop-color="#00B0BB"/>
<stop offset="1" stop-color="#00DB65"/>
</radialGradient>
<linearGradient id="paint7_linear_2862_278" x1="395.843" y1="169.781" x2="332.121" y2="263.82" gradientUnits="userSpaceOnUse">
<stop stop-color="#00B1BC"/>
<stop offset="1"/>
</linearGradient>
<linearGradient id="paint8_linear_2862_278" x1="395.843" y1="169.781" x2="370.991" y2="271.799" gradientUnits="userSpaceOnUse">
<stop/>
<stop offset="1" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint9_linear_2862_278" x1="350" y1="0" x2="350" y2="320" gradientUnits="userSpaceOnUse">
<stop stop-opacity="0"/>
<stop offset="0.0001" stop-opacity="0.3"/>
<stop offset="0.333333"/>
<stop offset="0.666667"/>
<stop offset="1" stop-opacity="0.3"/>
</linearGradient>
<linearGradient id="paint10_linear_2862_278" x1="0" y1="160" x2="700" y2="160" gradientUnits="userSpaceOnUse">
<stop stop-opacity="0.1"/>
<stop offset="0.5"/>
<stop offset="1" stop-opacity="0.1"/>
</linearGradient>
<linearGradient id="paint11_linear_2862_278" x1="511" y1="-1" x2="189" y2="321" gradientUnits="userSpaceOnUse">
<stop stop-opacity="0.1"/>
<stop offset="0.5"/>
<stop offset="1" stop-opacity="0.1"/>
</linearGradient>
<linearGradient id="paint12_linear_2862_278" x1="511" y1="321" x2="189" y2="-0.999997" gradientUnits="userSpaceOnUse">
<stop stop-opacity="0.1"/>
<stop offset="0.5"/>
<stop offset="1" stop-opacity="0.1"/>
</linearGradient>
<clipPath id="clip0_2862_278">
<rect width="700" height="320" rx="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

1
docs/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg fill="none" height="120" viewBox="0 0 120 120" width="120" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="60" x2="60" y1="2" y2="118.352"><stop offset="0" stop-color="#2563eb"/><stop offset="1" stop-color="#60a5fa"/></linearGradient><path clip-rule="evenodd" d="m80.6502 118.352c22.9638-8.418 39.3498-30.4712 39.3498-56.352 0-33.1371-26.8629-60-60-60s-60 26.8629-60 60c0 25.8808 16.3862 47.934 39.3498 56.352l16.631-38.9411c-7.4746-1.9924-12.9808-8.8086-12.9808-16.9109 0-9.665 7.835-17.5 17.5-17.5s17.5 7.835 17.5 17.5c0 8.4269-5.9563 15.4627-13.8885 17.1269z" fill="url(#a)" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 698 B

122
docs/mint.json Normal file
View File

@ -0,0 +1,122 @@
{
"$schema": "https://mintlify.com/schema.json",
"name": "OpnForm Technical Docs",
"logo": {
"light": "/logo.svg",
"dark": "/logo.svg"
},
"favicon": "/logo.svg",
"colors": {
"primary": "#3b82f6",
"light": "#60a5fa",
"dark": "#2563eb",
"anchors": {
"from": "#2563eb",
"to": "#60a5fa"
}
},
"topbarLinks": [
{
"name": "OpnForm",
"url": "https://opnform.com?utm_source=docs&utm_medium=topbar&utm_campaign=technical_docs"
},
{
"name": "GitHub",
"url": "https://github.com/JhumanJ/OpnForm"
}
],
"topbarCtaButton": {
"name": "Dashboard",
"url": "https://opnform.com"
},
"tabs": [
{
"name": "API Reference",
"url": "api-reference"
},
{
"name": "Contributing",
"url": "contributing"
}
],
"anchors": [
{
"name": "Product Documentation",
"icon": "book-open-cover",
"url": "https://help.opnform.com"
},
{
"name": "Community",
"icon": "discord",
"url": "https://discord.gg/YTSjU2a9TS"
},
{
"name": "Changelog",
"icon": "sparkles",
"url": "https://feedback.opnform.com/changelog"
},
{
"name": "Roadmap",
"icon": "road",
"url": "https://feedback.opnform.com/roadmap"
}
],
"navigation": [
{
"group": "Get Started",
"pages": [
"introduction",
"tech-stack"
]
},
{
"group": "Deployment",
"pages": [
"deployment/docker",
"deployment/local-deployment",
"deployment/cloud-vs-self-hosting"
]
},
{
"group": "Configuration",
"pages": [
"configuration/environment-variables",
"configuration/aws-s3",
"configuration/email-setup",
"configuration/custom-domain"
]
},
{
"group": "API Reference",
"pages": [
"api-reference/introduction"
]
},
{
"group": "Zapier Endpoints",
"pages": [
"api-reference/endpoint/validate-api-key",
"api-reference/endpoint/list-forms",
"api-reference/endpoint/new-submission-trigger",
"api-reference/endpoint/unsubscribe-webhook",
"api-reference/endpoint/sample-submission-polling"
]
},
{
"group": "Contributing",
"pages": [
"contributing/getting-started",
"contributing/new-form-block",
"contributing/new-integration"
]
}
],
"footerSocials": {
"twitter": "https://twitter.com/opnform",
"github": "https://github.com/JhumanJ/OpnForm"
},
"openapi": "/api-reference/openapi.json",
"feedback": {
"suggestEdit": true
}
}

View File

@ -0,0 +1,3 @@
<Note>
The easiest way to get started with OpnForm is through our [official managed service in the Cloud](https://opnform.com/?utm_source=docs&utm_medium=introduction&utm_campaign=cloud_version). It takes just 1 minute to try out the builder for free, with high availability, backups, security, and maintenance all managed for you.
</Note>

63
docs/tech-stack.mdx Normal file
View File

@ -0,0 +1,63 @@
---
title: "Tech Stack"
description: "Overview of OpnForm's technology stack"
---
OpnForm is built using a modern and robust technology stack, ensuring high performance, scalability, and maintainability. Here's an overview of the main technologies used in the project:
## Backend
<Card title="PHP" icon="php" href="https://www.php.net/" horizontal>
Version 8.0 or higher, powering the core backend functionality.
</Card>
<Card title="Laravel" icon="laravel" href="https://laravel.com/" horizontal>
The PHP framework that powers the backend of OpnForm.
</Card>
## Database
<Card title="SQL Database" icon="database" href="https://en.wikipedia.org/wiki/SQL" horizontal>
Supports relational database management systems like MySQL/MariaDB and PostgreSQL.
</Card>
## Frontend
<Card title="Vue.js 3" icon="vuejs" href="https://v3.vuejs.org/" horizontal>
The progressive JavaScript framework used for building the user interface.
</Card>
<Card title="Nuxt.js" icon="mountains" href="https://nuxt.com/" horizontal>
The Vue.js framework that enables server-side rendering and other powerful features.
</Card>
<Card title="TailwindCSS" icon="wind" href="https://tailwindcss.com/" horizontal>
A utility-first CSS framework for rapidly building custom user interfaces.
</Card>
## Asset Compilation
<Card title="Node.js" icon="node-js" href="https://nodejs.org/" horizontal>
Used for running JavaScript tools in the development environment.
</Card>
<Card title="NPM" icon="npm" href="https://www.npmjs.com/" horizontal>
Package manager for installing and managing frontend dependencies.
</Card>
## Additional Technologies
<Card title="Docker" icon="docker" href="https://www.docker.com/" horizontal>
Containerization platform used for easy deployment and development setup.
</Card>
<Card title="Redis" icon="layer-group" href="https://redis.io/" horizontal>
In-memory data structure store, used for caching and as a message broker.
</Card>
<Card title="AWS S3" icon="bucket" href="https://aws.amazon.com/s3/" horizontal>
Used for file storage (or compatible alternatives).
</Card>
This tech stack allows OpnForm to deliver a fast, responsive, and feature-rich form building experience. For detailed installation instructions and system requirements, please refer to our [Docker deployment guide](deployment/docker) or [Local deployment guide](deployment/local-deployment).