mirror of
https://github.com/docusealco/docuseal.git
synced 2026-08-07 15:25:16 +00:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ffd198505 | |||
| d595e7d52b | |||
| aabbf2dc7f | |||
| 95e4bf945c | |||
| 15523d8377 | |||
| e2cda2bd5c | |||
| 12b98fdcd4 | |||
| be208a1080 | |||
| 9268c60f9e | |||
| 52b310e1a8 | |||
| 16757acbe0 | |||
| dcbdc9e60c | |||
| 254aa9e950 | |||
| 28290c4635 | |||
| a72882ce0f | |||
| fd0b57c651 | |||
| 780ced3e7f | |||
| f8f11b6fa6 | |||
| bdaaa5276c | |||
| e149e08f33 | |||
| 47bd0571f9 | |||
| 5352a25ede | |||
| ead316df00 | |||
| dac7594aeb | |||
| f4f226ac63 | |||
| 2112b2f3d3 | |||
| b112fc0c81 |
@@ -30,3 +30,5 @@ linters:
|
||||
Enabled: false
|
||||
Style/ZeroLengthPredicate:
|
||||
Enabled: false
|
||||
Style/RedundantConstantBase:
|
||||
Enabled: false
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
*.html linguist-detectable=false
|
||||
@@ -4,28 +4,28 @@ source 'https://rubygems.org'
|
||||
|
||||
ruby '3.2.2'
|
||||
|
||||
gem 'audited'
|
||||
gem 'aws-sdk-s3'
|
||||
gem 'azure-storage-blob'
|
||||
gem 'aws-sdk-s3', require: false
|
||||
gem 'azure-storage-blob', require: false
|
||||
gem 'bootsnap', require: false
|
||||
gem 'devise'
|
||||
gem 'dotenv', require: false
|
||||
gem 'faraday'
|
||||
gem 'google-cloud-storage'
|
||||
gem 'google-cloud-storage', require: false
|
||||
gem 'hexapdf'
|
||||
gem 'image_processing'
|
||||
gem 'lograge'
|
||||
gem 'mysql2'
|
||||
gem 'mysql2', require: false
|
||||
gem 'oj'
|
||||
gem 'pagy'
|
||||
gem 'pg'
|
||||
gem 'pg', require: false
|
||||
gem 'premailer-rails'
|
||||
gem 'puma'
|
||||
gem 'rails'
|
||||
gem 'rails-i18n'
|
||||
gem 'rollbar', require: ENV.key?('ROLLBAR_ACCESS_TOKEN')
|
||||
gem 'ruby-vips'
|
||||
gem 'shakapacker'
|
||||
gem 'sqlite3'
|
||||
gem 'sqlite3', require: false
|
||||
gem 'strip_attributes'
|
||||
gem 'turbo-rails'
|
||||
gem 'tzinfo-data'
|
||||
|
||||
+2
-4
@@ -72,9 +72,6 @@ GEM
|
||||
activerecord (>= 3.2, < 8.0)
|
||||
rake (>= 10.4, < 14.0)
|
||||
ast (2.4.2)
|
||||
audited (5.3.3)
|
||||
activerecord (>= 5.0, < 7.1)
|
||||
request_store (~> 1.2)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.781.0)
|
||||
aws-sdk-core (3.175.0)
|
||||
@@ -379,6 +376,7 @@ GEM
|
||||
railties (>= 5.2)
|
||||
retriable (3.1.2)
|
||||
rexml (3.2.5)
|
||||
rollbar (3.4.0)
|
||||
rspec-core (3.12.2)
|
||||
rspec-support (~> 3.12.0)
|
||||
rspec-expectations (3.12.3)
|
||||
@@ -488,7 +486,6 @@ PLATFORMS
|
||||
|
||||
DEPENDENCIES
|
||||
annotate
|
||||
audited
|
||||
aws-sdk-s3
|
||||
azure-storage-blob
|
||||
better_html
|
||||
@@ -517,6 +514,7 @@ DEPENDENCIES
|
||||
puma
|
||||
rails
|
||||
rails-i18n
|
||||
rollbar
|
||||
rspec-rails
|
||||
rubocop
|
||||
rubocop-performance
|
||||
|
||||
@@ -1,24 +1,74 @@
|
||||
# README
|
||||
<h1 align="center" style="border-bottom: none">
|
||||
<div>
|
||||
<a href="https://www.docuseal.co">
|
||||
<img alt="DocuSeal" src="https://github.com/docusealco/docuseal/assets/5418788/c12cd051-81cd-4402-bc3a-92f2cfdc1b06" width="80" />
|
||||
<br>
|
||||
</a>
|
||||
DocuSeal
|
||||
</div>
|
||||
</h1>
|
||||
<h3 align="center">
|
||||
Open source document filling and signing
|
||||
</h3>
|
||||
<p align="center">
|
||||
<a href="https://hub.docker.com/r/docuseal/docuseal">
|
||||
<img alt="Docker releases" src="https://img.shields.io/docker/v/docuseal/docuseal">
|
||||
</a>
|
||||
<a href="https://discord.gg/qygYCDGck9">
|
||||
<img src="https://img.shields.io/discord/1125112641170448454?logo=discord"/>
|
||||
</a>
|
||||
<a href="https://twitter.com/intent/follow?screen_name=docusealco">
|
||||
<img src="https://img.shields.io/twitter/follow/docusealco?style=social" alt="Follow @docusealco" />
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
DocuSeal is an open source platform that provides secure and efficient digital document signing and processing. Create PDF forms to have them filled and signed online on any device with an easy-to-use, mobile-optimized web tool.
|
||||
</p>
|
||||
<h2 align="center">
|
||||
<a href="https://demo.docuseal.co">✨ Live Demo</a>
|
||||
</h2>
|
||||
|
||||
This README would normally document whatever steps are necessary to get the
|
||||
application up and running.
|
||||
[](https://demo.docuseal.co)
|
||||
|
||||
Things you may want to cover:
|
||||
## Features
|
||||
- [x] PDF form fields builder (WYSIWYG)
|
||||
- [x] 10 field types available (Signature, Date, File, Checkbox etc.)
|
||||
- [x] Multiple submitters per document
|
||||
- [x] Automated emails via SMTP
|
||||
- [x] Files storage on AWS S3, Google Storage, or Azure
|
||||
- [x] Automatic PDF eSignature
|
||||
- [x] PDF signature verification
|
||||
- [x] Users management
|
||||
- [x] Mobile-optimized
|
||||
- [x] Easy to deploy in minutes
|
||||
|
||||
* Ruby version
|
||||
## Deploy
|
||||
|
||||
* System dependencies
|
||||
| Heroku | Railway | DigitalOcean |
|
||||
| ---------------------------------------------------------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
|
||||
| [<img alt="Deploy on Heroku" src="https://www.herokucdn.com/deploy/button.svg" height="40">](https://heroku.com/deploy?template=https://github.com/docusealco/docuseal-heroku) | [<img alt="Deploy on Railway" src="https://railway.app/button.svg" height="40">](https://railway.app/template/IGoDnc?referralCode=ruU7JR) | [<img alt="Deploy on DigitalOcean" src="https://www.deploytodo.com/do-btn-blue.svg" height="40">](https://cloud.digitalocean.com/apps/new?repo=https://github.com/docusealco/docuseal-digitalocean/tree/master&refcode=421d50f53990) |
|
||||
|
||||
* Configuration
|
||||
|
||||
* Database creation
|
||||
#### Docker
|
||||
|
||||
* Database initialization
|
||||
```sh
|
||||
docker run --name docuseal -p 3000:3000 -v.:/data docuseal/docuseal
|
||||
```
|
||||
|
||||
* How to run the test suite
|
||||
By default DocuSeal docker container uses an SQLite database to store data and configurations. Alternatively, it is possible use PostgreSQL or MySQL databases by specifying the `DATABASE_URL` env variable.
|
||||
|
||||
* Services (job queues, cache servers, search engines, etc.)
|
||||
#### Docker Compose
|
||||
|
||||
* Deployment instructions
|
||||
Download docker-compose.yml into your private server:
|
||||
```sh
|
||||
curl https://raw.githubusercontent.com/docusealco/docuseal/master/docker-compose.yml > docker-compose.yml
|
||||
```
|
||||
|
||||
* ...
|
||||
Run the app under a custom domain over https using docker compose (make sure your DNS points to the server to automatically issue ssl certs with Caddy):
|
||||
```sh
|
||||
HOST=your-domain-name.com docker-compose up
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
DocuSeal is released under the GNU Affero General Public License v3.0.
|
||||
|
||||
@@ -23,7 +23,7 @@ class ApplicationController < ActionController::Base
|
||||
private
|
||||
|
||||
def sign_in_for_demo
|
||||
sign_in(User.first) unless signed_in?
|
||||
sign_in(User.order('random()').take) unless signed_in?
|
||||
end
|
||||
|
||||
def current_account
|
||||
|
||||
@@ -10,9 +10,9 @@ class SubmitFormController < ApplicationController
|
||||
Submitter.preload(submission: { template: { documents_attachments: { preview_images_attachments: :blob } } })
|
||||
.find_by!(slug: params[:slug])
|
||||
|
||||
cookies.signed[:submitter_sid] = @submitter.signed_id
|
||||
return redirect_to submit_form_completed_path(@submitter.slug) if @submitter.completed_at?
|
||||
|
||||
redirect_to submit_form_completed_path(@submitter.slug) if @submitter.completed_at?
|
||||
cookies[:submitter_sid] = @submitter.signed_id
|
||||
end
|
||||
|
||||
def update
|
||||
@@ -41,7 +41,7 @@ class SubmitFormController < ApplicationController
|
||||
private
|
||||
|
||||
def normalized_values
|
||||
params[:values].to_unsafe_h.transform_values do |v|
|
||||
params.fetch(:values, {}).to_unsafe_h.transform_values do |v|
|
||||
if params[:cast_boolean] == 'true'
|
||||
v == 'true'
|
||||
else
|
||||
|
||||
@@ -24,24 +24,30 @@ class UsersController < ApplicationController
|
||||
if @user.save
|
||||
UserMailer.invitation_email(@user).deliver_later!
|
||||
|
||||
redirect_to settings_users_path, notice: 'User has been invited.'
|
||||
redirect_to settings_users_path, notice: 'User has been invited'
|
||||
else
|
||||
render turbo_stream: turbo_stream.replace(:modal, template: 'users/new'), status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
return redirect_to settings_users_path, notice: 'Unable to update user.' if Docuseal.demo?
|
||||
|
||||
if @user.update(user_params.compact_blank)
|
||||
redirect_to settings_users_path, notice: 'User has been updated.'
|
||||
redirect_to settings_users_path, notice: 'User has been updated'
|
||||
else
|
||||
render turbo_stream: turbo_stream.replace(:modal, template: 'users/edit'), status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
if Docuseal.demo? || @user.id == current_user.id
|
||||
return redirect_to settings_users_path, notice: 'Unable to remove user'
|
||||
end
|
||||
|
||||
@user.update!(deleted_at: Time.current)
|
||||
|
||||
redirect_to settings_users_path, notice: 'User has been removed.'
|
||||
redirect_to settings_users_path, notice: 'User has been removed'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -14,6 +14,7 @@ window.customElements.define('submission-form', class extends HTMLElement {
|
||||
authenticityToken: this.dataset.authenticityToken,
|
||||
canSendEmail: this.dataset.canSendEmail === 'true',
|
||||
isDirectUpload: this.dataset.isDirectUpload === 'true',
|
||||
isDemo: this.dataset.isDemo === 'true',
|
||||
values: reactive(JSON.parse(this.dataset.values)),
|
||||
attachments: reactive(JSON.parse(this.dataset.attachments)),
|
||||
fields: JSON.parse(this.dataset.fields)
|
||||
|
||||
@@ -40,18 +40,38 @@
|
||||
Download
|
||||
</span>
|
||||
</button>
|
||||
<a
|
||||
v-if="isDemo"
|
||||
target="_blank"
|
||||
href="https://github.com/docusealco/docuseal"
|
||||
class="white-button flex items-center space-x-1 w-full"
|
||||
>
|
||||
<IconBrandGithub />
|
||||
<span>
|
||||
Star on Github
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-center mt-4">
|
||||
Signed with
|
||||
<a
|
||||
href="https://www.docuseal.co"
|
||||
target="_blank"
|
||||
class="underline"
|
||||
>DocuSeal</a> - open source documents software
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { IconCircleCheck, IconMail, IconDownload, IconInnerShadowTop } from '@tabler/icons-vue'
|
||||
import { IconCircleCheck, IconBrandGithub, IconMail, IconDownload, IconInnerShadowTop } from '@tabler/icons-vue'
|
||||
|
||||
export default {
|
||||
name: 'FormCompleted',
|
||||
components: {
|
||||
IconCircleCheck,
|
||||
IconInnerShadowTop,
|
||||
IconBrandGithub,
|
||||
IconMail,
|
||||
IconDownload
|
||||
},
|
||||
@@ -60,6 +80,11 @@ export default {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
isDemo: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
canSendEmail: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
{{ message }}
|
||||
</div>
|
||||
<div class="text-xs">
|
||||
<span class="font-medium">Click to upload</span> or drag and drop
|
||||
<span class="font-medium">Click to upload</span> or drag and drop files
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -21,18 +21,12 @@
|
||||
name="authenticity_token"
|
||||
:value="authenticityToken"
|
||||
>
|
||||
<input
|
||||
v-if="currentStep === stepFields.length - 1"
|
||||
type="hidden"
|
||||
name="completed"
|
||||
value="true"
|
||||
>
|
||||
<input
|
||||
value="put"
|
||||
name="_method"
|
||||
type="hidden"
|
||||
>
|
||||
<div class="mt-4">
|
||||
<div class="md:mt-4">
|
||||
<div v-if="['cells', 'text'].includes(currentField.type)">
|
||||
<label
|
||||
v-if="currentField.name"
|
||||
@@ -226,7 +220,7 @@
|
||||
@attached="[attachments.push($event), $refs.areas.scrollIntoField(currentField)]"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-8">
|
||||
<div class="mt-6 md:mt-8">
|
||||
<button
|
||||
type="submit"
|
||||
class="base-button w-full flex justify-center"
|
||||
@@ -252,6 +246,7 @@
|
||||
</form>
|
||||
<FormCompleted
|
||||
v-else
|
||||
:is-demo="isDemo"
|
||||
:can-send-email="canSendEmail"
|
||||
:submitter-slug="submitterSlug"
|
||||
/>
|
||||
@@ -263,7 +258,7 @@
|
||||
href="#"
|
||||
class="inline border border-base-300 h-3 w-3 rounded-full mx-1"
|
||||
:class="{ 'bg-base-300': index === currentStep, 'bg-base-content': index < currentStep || isCompleted, 'bg-white': index > currentStep }"
|
||||
@click.prevent="isCompleted ? '' : goToStep(step, true)"
|
||||
@click.prevent="isCompleted ? '' : [saveStep(), goToStep(step, true)]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -323,6 +318,11 @@ export default {
|
||||
required: true,
|
||||
default: false
|
||||
},
|
||||
isDemo: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false
|
||||
},
|
||||
values: {
|
||||
type: Object,
|
||||
required: false,
|
||||
@@ -401,13 +401,13 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
saveStep () {
|
||||
saveStep (formData) {
|
||||
if (this.isCompleted) {
|
||||
return Promise.resolve({})
|
||||
} else {
|
||||
return fetch(this.submitPath, {
|
||||
method: 'POST',
|
||||
body: new FormData(this.$refs.form)
|
||||
body: formData || new FormData(this.$refs.form)
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -420,7 +420,13 @@ export default {
|
||||
|
||||
await stepPromise()
|
||||
|
||||
this.saveStep().then(response => {
|
||||
const formData = new FormData(this.$refs.form)
|
||||
|
||||
if (this.currentStep === this.stepFields.length - 1) {
|
||||
formData.append('completed', 'true')
|
||||
}
|
||||
|
||||
this.saveStep(formData).then(response => {
|
||||
const nextStep = this.stepFields[this.currentStep + 1]
|
||||
|
||||
if (nextStep) {
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
{{ message }}
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
<span class="font-medium">Click to upload</span> or drag and drop
|
||||
<span class="font-medium">Click to upload</span> or drag and drop files
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -67,6 +67,25 @@
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-if="fields.length < 4"
|
||||
class="text-xs p-2 border border-base-200 rounded"
|
||||
>
|
||||
<ui>
|
||||
<li>
|
||||
Draw a text field on the page with a mouse
|
||||
</li>
|
||||
<li>
|
||||
Single click of the page to add a checkbox
|
||||
</li>
|
||||
<li>
|
||||
Drag & drop any other field type on the page
|
||||
</li>
|
||||
<li>
|
||||
Click on the field type above to add it to the form without drawing it on the document
|
||||
</li>
|
||||
</ui>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# slug :string not null
|
||||
# ua :string
|
||||
# uuid :string not null
|
||||
# values :string not null
|
||||
# values :text not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# submission_id :bigint not null
|
||||
@@ -32,7 +32,7 @@ class Submitter < ApplicationRecord
|
||||
belongs_to :submission
|
||||
|
||||
attribute :values, :string, default: -> { {} }
|
||||
attribute :slug, :string, default: -> { SecureRandom.base58(8) }
|
||||
attribute :slug, :string, default: -> { SecureRandom.base58(10) }
|
||||
|
||||
serialize :values, JSON
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# deleted_at :datetime
|
||||
# fields :string not null
|
||||
# fields :text not null
|
||||
# name :string not null
|
||||
# schema :string not null
|
||||
# schema :text not null
|
||||
# slug :string not null
|
||||
# submitters :string not null
|
||||
# submitters :text not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# account_id :bigint not null
|
||||
@@ -36,7 +36,7 @@ class Template < ApplicationRecord
|
||||
attribute :fields, :string, default: -> { [] }
|
||||
attribute :schema, :string, default: -> { [] }
|
||||
attribute :submitters, :string, default: -> { [{ name: DEFAULT_SUBMITTER_NAME, uuid: SecureRandom.uuid }] }
|
||||
attribute :slug, :string, default: -> { SecureRandom.base58(8) }
|
||||
attribute :slug, :string, default: -> { SecureRandom.base58(10) }
|
||||
|
||||
serialize :fields, JSON
|
||||
serialize :schema, JSON
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<% if Docuseal.demo? %><%= render 'shared/demo_alert' %><% end %>
|
||||
<% if @templates.any? %>
|
||||
<div class="flex justify-between mb-4 items-center">
|
||||
<h1 class="text-4xl font-bold">Templates</h1>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
Verify Signed PDF
|
||||
</div>
|
||||
<div class="text-xs">
|
||||
<span class="font-medium">Click to upload</span> or drag and drop
|
||||
<span class="font-medium">Click to upload</span> or drag and drop files
|
||||
</div>
|
||||
</div>
|
||||
<input id="file" name="files[]" class="hidden" data-action="change:file-dropzone#onSelectFiles" data-target="file-dropzone.input" type="file" accept="application/pdf" multiple>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="<%= local_assigns[:class] %>" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M3 19c3.333 -2 5 -4 5 -6c0 -3 -1 -3 -2 -3s-2.032 1.085 -2 3c.034 2.048 1.658 2.877 2.5 4c1.5 2 2.5 2.5 3.5 1c.667 -1 1.167 -1.833 1.5 -2.5c1 2.333 2.333 3.5 4 3.5h2.5"></path>
|
||||
<path d="M20 17v-12c0 -1.121 -.879 -2 -2 -2s-2 .879 -2 2v12l2 2l2 -2z"></path>
|
||||
<path d="M16 7h4"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 584 B |
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-theme="docuseal">
|
||||
<html data-theme="docuseal" lang="en">
|
||||
<head>
|
||||
<title>
|
||||
DocuSeal | Open Source Document Filling and Signing
|
||||
@@ -10,6 +10,7 @@
|
||||
<%= csp_meta_tag %>
|
||||
<%= javascript_pack_tag 'application', defer: true %>
|
||||
<%= stylesheet_pack_tag 'application', media: 'all' %>
|
||||
<%= render 'shared/posthog' if ENV['POSTHOG_TOKEN'] %>
|
||||
</head>
|
||||
<body>
|
||||
<turbo-frame id="modal"></turbo-frame>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-theme="docuseal">
|
||||
<html data-theme="docuseal" lang="en">
|
||||
<head>
|
||||
<title>
|
||||
DocuSeal | Open Source Document Filling and Signing
|
||||
</title>
|
||||
<%= render 'shared/meta' %>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="robots" content="noindex">
|
||||
<%= csrf_meta_tags %>
|
||||
<%= csp_meta_tag %>
|
||||
<%= javascript_pack_tag 'form', defer: true %>
|
||||
<%= stylesheet_pack_tag 'form', media: 'all' %>
|
||||
<%= render 'shared/posthog' if ENV['POSTHOG_TOKEN'] %>
|
||||
</head>
|
||||
<body>
|
||||
<%= yield %>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-theme="docuseal">
|
||||
<html data-theme="docuseal" lang="en">
|
||||
<head>
|
||||
<title>
|
||||
DocuSeal | Open Source Document Filling and Signing
|
||||
@@ -10,6 +10,7 @@
|
||||
<%= csp_meta_tag %>
|
||||
<%= javascript_pack_tag 'application', defer: true %>
|
||||
<%= stylesheet_pack_tag 'application', media: 'all' %>
|
||||
<%= render 'shared/posthog' if ENV['POSTHOG_TOKEN'] %>
|
||||
</head>
|
||||
<body>
|
||||
<% if flash.present? %><%= render 'shared/flash' %><% end %>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<div class="mx-auto mt-1.5">
|
||||
<div class="py-3 rounded-2xl flex items-center justify-between mx-4 md:mx-0">
|
||||
<div class="w-full text-center">
|
||||
<span class="font-bold">DocuSeal Demo Environment</span>
|
||||
<br>
|
||||
Feel free to
|
||||
<a href="<%= new_template_path %>" data-turbo-frame="modal" class="inline underline font-medium">create a new template</a> document form or
|
||||
<a href="<%= start_form_url(slug: ::Template.first&.slug) %>" target="_blank" class="inline underline font-medium">submit the existing one</a> 😊
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
<a href="https://github.com/docusealco/docuseal" class="btn btn-neutral btn-sm btn-outline inline-flex items-center justify-center" target="_blank" alt="Star on GitHub" style="height: 37px">
|
||||
<span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
||||
</svg>
|
||||
</span>
|
||||
<span class="flex">
|
||||
<span class="hidden lg:block">Star on </span>GitHub
|
||||
</span>
|
||||
</a>
|
||||
@@ -1,3 +1,6 @@
|
||||
<% if Docuseal.demo? || (request.path != '/' && !devise_controller?) %>
|
||||
<meta name="robots" content="noindex">
|
||||
<% end %>
|
||||
<meta name="description" content="Open source, self-hosted tool to streamline document filling and signing. Create custom PDF forms to complete and sign with an easy to use online tool.">
|
||||
<meta property="og:title" content="DocuSeal | Open Source Document Filling and Signing">
|
||||
<meta property="og:description" content="Open source, self-hosted tool to streamline document filling and signing. Create custom PDF forms to complete and sign with an easy to use online tool.">
|
||||
|
||||
@@ -4,13 +4,19 @@
|
||||
<%= render 'shared/logo' %>
|
||||
<span>DocuSeal</span>
|
||||
</a>
|
||||
<a href="<%= Docuseal::GITHUB_URL %>" target="_blank" class="inline">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/docusealco/docuseal?style=social&label=" style="height: 22px">
|
||||
</a>
|
||||
<% unless Docuseal.demo? %>
|
||||
<a href="<%= Docuseal::GITHUB_URL %>" target="_blank" class="inline">
|
||||
<img alt="GitHub Repo stars" src="https://www.docuseal.co/github-badge.svg" style="height: 22px">
|
||||
</a>
|
||||
<% end %>
|
||||
</div>
|
||||
<% if signed_in? %>
|
||||
<div class="space-x-6">
|
||||
<%= link_to 'Settings', settings_profile_index_path, class: 'hidden md:inline font-medium text-lg' %>
|
||||
<div class="space-x-4 flex items-center">
|
||||
<% if Docuseal.demo? %>
|
||||
<%= render 'shared/github_button' %>
|
||||
<% else %>
|
||||
<%= link_to 'Settings', settings_profile_index_path, class: 'hidden md:inline font-medium text-lg mr-2' %>
|
||||
<% end %>
|
||||
<div class="dropdown dropdown-end">
|
||||
<label tabindex="0" class="cursor-pointer bg-base-content text-purple-300 rounded-full p-2 w-9 justify-center flex">
|
||||
<span class="text-sm align-text-top"><%= current_user.initials %></span>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<script>
|
||||
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
|
||||
posthog.init('<%= ENV.fetch('POSTHOG_TOKEN', nil) %>',{api_host:'https://app.posthog.com',autocapture: false,enable_recording_console_log: true,capture_pageview: false,session_recording:{maskAllInputs: false,maskInputOptions: {password: true}}})
|
||||
</script>
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="block w-full md:w-52">
|
||||
<div class="block w-full md:w-52 flex-none">
|
||||
<menu-active>
|
||||
<ul class="menu px-0">
|
||||
<li class="menu-title py-0"><span>Settings</span></li>
|
||||
@@ -44,5 +44,8 @@
|
||||
<%= svg_icon('send', class: 'h-5 w-5') %>
|
||||
Tell about us
|
||||
</a>
|
||||
<a href="mailto:<%= Docuseal::SUPPORT_EMAIL %>" target="_blank" class="w-full block mt-4 underline text-center">
|
||||
<%= Docuseal::SUPPORT_EMAIL %>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,12 +12,7 @@
|
||||
<div class="flex items-center bg-base-300 rounded-xl p-4 mb-4">
|
||||
<div class="flex items-center">
|
||||
<div class="mr-3">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M3 19c3.333 -2 5 -4 5 -6c0 -3 -1 -3 -2 -3s-2.032 1.085 -2 3c.034 2.048 1.658 2.877 2.5 4c1.5 2 2.5 2.5 3.5 1c.667 -1 1.167 -1.833 1.5 -2.5c1 2.333 2.333 3.5 4 3.5h2.5" />
|
||||
<path d="M20 17v-12c0 -1.121 -.879 -2 -2 -2s-2 .879 -2 2v12l2 2l2 -2z" />
|
||||
<path d="M16 7h4" />
|
||||
</svg>
|
||||
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-lg font-bold mb-1"><%= @submitter.submission.template.name %></p>
|
||||
|
||||
@@ -1,43 +1,36 @@
|
||||
<div class="max-w-xl mx-auto px-2 mt-12 mb-4">
|
||||
<div class="card bg-base-200">
|
||||
<div class="card-body">
|
||||
<div class="space-y-4 w-full md:px-10 mx-auto">
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="flex justify-center">
|
||||
<span class="mr-3">
|
||||
<%= render 'shared/logo', width: '50px', height: '50px' %>
|
||||
</span>
|
||||
<h1 class="text-5xl font-bold text-center">DocuSeal</h1>
|
||||
</a>
|
||||
<p class="text-xl font-semibold text-center">You have been invited to submit the form</p>
|
||||
<div class="flex items-center bg-base-content/10 rounded-xl p-4">
|
||||
<div class="flex items-center">
|
||||
<div class="mr-3">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M3 19c3.333 -2 5 -4 5 -6c0 -3 -1 -3 -2 -3s-2.032 1.085 -2 3c.034 2.048 1.658 2.877 2.5 4c1.5 2 2.5 2.5 3.5 1c.667 -1 1.167 -1.833 1.5 -2.5c1 2.333 2.333 3.5 4 3.5h2.5" />
|
||||
<path d="M20 17v-12c0 -1.121 -.879 -2 -2 -2s-2 .879 -2 2v12l2 2l2 -2z" />
|
||||
<path d="M16 7h4" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<p class="font-bold mb-1"><%= @template.name %></p>
|
||||
<p class="text-sm">Invited by <%= @template.account.name %></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="max-w-md mx-auto px-2 mt-12 mb-4">
|
||||
<div class="space-y-6 mx-auto">
|
||||
<div class="space-y-6">
|
||||
<div class="text-center w-full space-y-6">
|
||||
<a href="/" class="flex justify-center">
|
||||
<span class="mr-3">
|
||||
<%= render 'shared/logo', width: '50px', height: '50px' %>
|
||||
</span>
|
||||
<h1 class="text-5xl font-bold text-center">DocuSeal</h1>
|
||||
</a>
|
||||
<p class="text-xl font-semibold text-center">You have been invited to submit a form</p>
|
||||
</div>
|
||||
<div class="flex items-center bg-base-300 rounded-xl p-4 mb-4">
|
||||
<div class="flex items-center">
|
||||
<div class="mr-3">
|
||||
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-lg font-bold mb-1"><%= @template.name %></p>
|
||||
<p class="text-sm">Invited by <%= @template.account.name %></p>
|
||||
</div>
|
||||
</div>
|
||||
<%= form_for @submitter, url: start_form_path(@template.slug), data: { turbo_frame: :_top }, method: :put, html: { class: 'space-y-4' } do |f| %>
|
||||
<div class="form-control !mt-0">
|
||||
<%= f.label :email, class: 'label' %>
|
||||
<%= f.email_field :email, required: true, class: 'base-input', placeholder: 'Provide your email to start' %>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<%= f.button button_title(title: 'Start', disabled_with: 'Starting'), class: 'base-button' %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<%= form_for @submitter, url: start_form_path(@template.slug), data: { turbo_frame: :_top }, method: :put, html: { class: 'space-y-4' } do |f| %>
|
||||
<div class="form-control !mt-0">
|
||||
<%= f.label :email, class: 'label' %>
|
||||
<%= f.email_field :email, required: true, class: 'base-input', placeholder: 'Provide your email to start' %>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<%= f.button button_title(title: 'Start', disabled_with: 'Starting'), class: 'base-button' %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<%= render 'shared/attribution' %>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div style="max-width: 1600px" class="mx-auto pl-4">
|
||||
<div class="flex justify-between py-1.5 items-center pr-4">
|
||||
<div class="flex justify-between py-1.5 items-center pr-4 sticky top-0 md:relative z-10 bg-base-100">
|
||||
<a href="<%= template_path(@submission.template) %>" class="flex space-x-3 py-1">
|
||||
<span><%= render 'shared/logo', width: 40, height: 40 %></span>
|
||||
<span class="text-3xl font-semibold focus:text-clip"><%= @submission.template.name %></span>
|
||||
@@ -21,7 +21,7 @@
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex" style="max-height: calc(100vh - 60px)">
|
||||
<div class="flex md:max-h-[calc(100vh-60px)]">
|
||||
<div class="overflow-y-auto overflow-x-hidden hidden lg:block w-52 flex-none pr-3 mt-0.5 pt-0.5">
|
||||
<% @submission.template.schema.each do |item| %>
|
||||
<% document = @submission.template.documents.find { |a| item['attachment_uuid'] == a.uuid } %>
|
||||
@@ -82,7 +82,7 @@
|
||||
</div>
|
||||
<% if submitter && !submitter.completed_at? %>
|
||||
<div class="mt-2 mb-1">
|
||||
<a class="btn btn-sm btn-primary w-full" target="_blank" href="<%= submit_form_url(slug: submitter.slug) %>">
|
||||
<a class="btn btn-sm btn-primary w-full" target="_blank" href="<%= submit_form_path(slug: submitter.slug) %>">
|
||||
Submit Form
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -12,12 +12,7 @@
|
||||
<div class="flex items-center bg-base-300 rounded-xl p-4 mb-4">
|
||||
<div class="flex items-center">
|
||||
<div class="mr-3">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-10 h-10" width="44" height="44" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M3 19c3.333 -2 5 -4 5 -6c0 -3 -1 -3 -2 -3s-2.032 1.085 -2 3c.034 2.048 1.658 2.877 2.5 4c1.5 2 2.5 2.5 3.5 1c.667 -1 1.167 -1.833 1.5 -2.5c1 2.333 2.333 3.5 4 3.5h2.5" />
|
||||
<path d="M20 17v-12c0 -1.121 -.879 -2 -2 -2s-2 .879 -2 2v12l2 2l2 -2z" />
|
||||
<path d="M16 7h4" />
|
||||
</svg>
|
||||
<%= svg_icon('writing_sign', class: 'w-10 h-10') %>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-lg font-bold mb-1"><%= @submitter.submission.template.name %></p>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<div class="mx-auto" style="max-width: 1000px">
|
||||
<div class="relative md:mx-32">
|
||||
<div id="form_container" class="shadow-md bg-base-100 absolute bottom-0 md:bottom-4 w-full border border-base-200 border p-4 rounded">
|
||||
<submission-form data-is-direct-upload="<%= Docuseal.active_storage_public? %>" data-submitter-uuid="<%= @submitter.uuid %>" data-submitter-slug="<%= @submitter.slug %>" data-can-send-email="<%= Accounts.can_send_emails?(Struct.new(:id).new(@submitter.submission.template.account_id)) %>" data-attachments="<%= attachments_index.values.select { |e| e.record_id == @submitter.id }.to_json(only: %i[uuid], methods: %i[url filename content_type]) %>" data-fields="<%= @submitter.submission.template.fields.select { |f| f['submitter_uuid'] == @submitter.uuid }.to_json %>" data-values="<%= @submitter.values.to_json %>" data-authenticity-token="<%= form_authenticity_token %>"></submission-form>
|
||||
<submission-form data-is-demo="<%= Docuseal.demo? %>" data-is-direct-upload="<%= Docuseal.active_storage_public? %>" data-submitter-uuid="<%= @submitter.uuid %>" data-submitter-slug="<%= @submitter.slug %>" data-can-send-email="<%= Accounts.can_send_emails?(Struct.new(:id).new(@submitter.submission.template.account_id)) %>" data-attachments="<%= attachments_index.values.select { |e| e.record_id == @submitter.id }.to_json(only: %i[uuid], methods: %i[url filename content_type]) %>" data-fields="<%= @submitter.submission.template.fields.select { |f| f['submitter_uuid'] == @submitter.uuid }.to_json %>" data-values="<%= @submitter.values.to_json %>" data-authenticity-token="<%= form_authenticity_token %>"></submission-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -157,13 +157,19 @@
|
||||
<% else %>
|
||||
<div class="card bg-base-200">
|
||||
<div class="card-body text-center py-16">
|
||||
<div class="max-w-md mx-auto">
|
||||
<div class="max-w-lg mx-auto">
|
||||
<p class="text-3xl font-bold text-base-content mb-4">There are no Submissions</p>
|
||||
<p class="text-gray-600">Send an invitation to fill and submit the documents</p>
|
||||
<%= link_to new_template_submission_path(@template), class: 'base-button mt-6', data: { turbo_frame: 'modal' } do %>
|
||||
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
||||
<span class="mr-1">Add Recipients</span>
|
||||
<% end %>
|
||||
<p class="text-gray-600">Send an invitation to fill and complete the form</p>
|
||||
<div class="space-x-2">
|
||||
<%= link_to new_template_submission_path(@template), class: 'base-button mt-6', data: { turbo_frame: 'modal' } do %>
|
||||
<%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %>
|
||||
<span class="mr-1">Add Recipients</span>
|
||||
<% end %>
|
||||
<%= link_to start_form_url(slug: @template.slug), class: 'white-button mt-6', target: '_blank', rel: 'noopener' do %>
|
||||
<%= svg_icon('writing', class: 'w-6 h-6') %>
|
||||
<span class="mr-1">Submit it Yourself</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0 md:space-x-2">
|
||||
<div class="flex flex-wrap space-y-4 md:flex-nowrap md:space-y-0 md:space-x-10">
|
||||
<%= render 'shared/settings_nav' %>
|
||||
<div class="flex-grow">
|
||||
<div class="flex justify-between mb-4">
|
||||
|
||||
+10
-1
@@ -1,6 +1,7 @@
|
||||
default: &default
|
||||
adapter: postgresql
|
||||
encoding: unicode
|
||||
pool: <%= ENV.fetch('RAILS_MAX_THREADS', 15) %>
|
||||
|
||||
development:
|
||||
<<: *default
|
||||
@@ -10,8 +11,16 @@ test:
|
||||
<<: *default
|
||||
database: docuseal_test
|
||||
|
||||
<% if ENV['DATABASE_URL'].to_s.empty? %>
|
||||
production:
|
||||
<% if ENV['DATABASE_URL'].to_s.empty? %>
|
||||
adapter: sqlite3
|
||||
database: <%= ENV['WORKDIR'] || '.' %>/db.sqlite3
|
||||
<% elsif ENV['DATABASE_URL'].match?(/\Apostgres/) %>
|
||||
<<: *default
|
||||
url: <%= ENV['DATABASE_URL'] %>
|
||||
<% elsif ENV['DATABASE_URL'].match?(/\Amysql/) %>
|
||||
adapter: mysql2
|
||||
encoding: utf8mb4
|
||||
pool: <%= ENV.fetch('RAILS_MAX_THREADS', 15) %>
|
||||
url: <%= ENV['DATABASE_URL'] %>
|
||||
<% end %>
|
||||
|
||||
@@ -110,7 +110,7 @@ Rails.application.configure do
|
||||
config.active_record.encryption = {
|
||||
primary_key: encryption_secret.first(32),
|
||||
deterministic_key: encryption_secret.last(32),
|
||||
key_derivation_salt: encryption_secret
|
||||
key_derivation_salt: Digest::SHA256.hexdigest(encryption_secret)
|
||||
}
|
||||
|
||||
# Do not dump schema after migrations.
|
||||
|
||||
@@ -15,7 +15,7 @@ Rails.configuration.to_prepare do
|
||||
|
||||
ActiveStorage::DirectUploadsController.before_action do
|
||||
next if current_user
|
||||
next if Submitter.find_signed(cookies.signed[:submitter_sid])
|
||||
next if Submitter.find_signed(cookies[:submitter_sid])
|
||||
|
||||
head :forbidden
|
||||
end
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
if defined?(Rollbar)
|
||||
Rollbar.configure do |config|
|
||||
config.access_token = ENV.fetch('ROLLBAR_ACCESS_TOKEN', nil)
|
||||
|
||||
config.enabled = true
|
||||
|
||||
config.exception_level_filters['ActionController::RoutingError'] = 'ignore'
|
||||
|
||||
config.environment = ENV['ROLLBAR_ENV'].presence || Rails.env
|
||||
end
|
||||
end
|
||||
+4
-4
@@ -86,7 +86,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_07_01_075115) do
|
||||
t.string "uuid", null: false
|
||||
t.string "email", null: false
|
||||
t.string "slug", null: false
|
||||
t.string "values", null: false
|
||||
t.text "values", null: false
|
||||
t.string "ua"
|
||||
t.string "ip"
|
||||
t.datetime "sent_at"
|
||||
@@ -102,9 +102,9 @@ ActiveRecord::Schema[7.0].define(version: 2023_07_01_075115) do
|
||||
create_table "templates", force: :cascade do |t|
|
||||
t.string "slug", null: false
|
||||
t.string "name", null: false
|
||||
t.string "schema", null: false
|
||||
t.string "fields", null: false
|
||||
t.string "submitters", null: false
|
||||
t.text "schema", null: false
|
||||
t.text "fields", null: false
|
||||
t.text "submitters", null: false
|
||||
t.bigint "author_id", null: false
|
||||
t.bigint "account_id", null: false
|
||||
t.datetime "deleted_at"
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
app:
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
image: docuseal/docuseal:latest
|
||||
ports:
|
||||
- 3000:3000
|
||||
volumes:
|
||||
- .:/data
|
||||
environment:
|
||||
FORCE_SSL: 'true'
|
||||
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/docuseal
|
||||
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
volumes:
|
||||
- './pg_data:/var/lib/postgresql/data'
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: docuseal
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
caddy:
|
||||
image: caddy:latest
|
||||
command: caddy reverse-proxy --from $HOST --to app:3000
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
- 443:443/udp
|
||||
volumes:
|
||||
- .:/data
|
||||
environment:
|
||||
- HOST=${HOST}
|
||||
@@ -3,6 +3,31 @@
|
||||
module Accounts
|
||||
module_function
|
||||
|
||||
def create_duplicate(account)
|
||||
new_account = account.dup
|
||||
|
||||
new_user = account.users.first.dup
|
||||
|
||||
new_user.account = new_account
|
||||
new_user.encrypted_password = SecureRandom.hex
|
||||
new_user.email = "#{SecureRandom.hex}@docuseal.co"
|
||||
|
||||
account.templates.each do |template|
|
||||
new_template = template.dup
|
||||
|
||||
new_template.account = new_account
|
||||
new_template.slug = SecureRandom.base58(10)
|
||||
|
||||
new_template.save!
|
||||
|
||||
Templates::CloneAttachments.call(template: new_template, original_template: template)
|
||||
end
|
||||
|
||||
new_user.save!(validate: false)
|
||||
|
||||
new_account
|
||||
end
|
||||
|
||||
def load_signing_certs(account)
|
||||
certs =
|
||||
if Docuseal.multitenant?
|
||||
|
||||
+2
-1
@@ -6,9 +6,10 @@ module Docuseal
|
||||
PRODUCT_NAME = 'DocuSeal'
|
||||
DEFAULT_APP_URL = 'http://localhost:3000'
|
||||
GITHUB_URL = 'https://github.com/docusealco/docuseal'
|
||||
DISCORD_URL = 'https://discord.com/invite/B5wg5wKk'
|
||||
DISCORD_URL = 'https://discord.gg/qygYCDGck9'
|
||||
TWITTER_URL = 'https://twitter.com/docusealco'
|
||||
TWITTER_HANDLE = '@docusealco'
|
||||
SUPPORT_EMAIL = 'support@docuseal.co'
|
||||
|
||||
CERTS = JSON.parse(ENV.fetch('CERTS', '{}'))
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ module Submissions
|
||||
FONT_NAME = 'Helvetica'
|
||||
|
||||
INFO_CREATOR = "#{Docuseal::PRODUCT_NAME} (#{Docuseal::PRODUCT_URL})".freeze
|
||||
SIGN_REASON = 'Signed by %<email>s with docuseal.co'
|
||||
SIGN_REASON = 'Signed by %<email>s with DocuSeal.co'
|
||||
|
||||
TEXT_LEFT_MARGIN = 1
|
||||
TEXT_TOP_MARGIN = 1
|
||||
@@ -40,25 +40,29 @@ module Submissions
|
||||
|
||||
value = submitter.values[field['uuid']]
|
||||
|
||||
next if Array.wrap(value).compact_blank.blank?
|
||||
|
||||
canvas = page.canvas(type: :overlay)
|
||||
|
||||
case field['type']
|
||||
when 'image', 'signature'
|
||||
attachment = submitter.attachments.find { |a| a.uuid == value }
|
||||
|
||||
io = StringIO.new(download_supported_image_data(attachment))
|
||||
image = Vips::Image.new_from_buffer(attachment.download, '').autorot
|
||||
|
||||
scale = [(area['w'] * width) / attachment.metadata['width'],
|
||||
(area['h'] * height) / attachment.metadata['height']].min
|
||||
scale = [(area['w'] * width) / image.width,
|
||||
(area['h'] * height) / image.height].min
|
||||
|
||||
io = StringIO.new(image.resize([scale * 4, 1].min).write_to_buffer('.png'))
|
||||
|
||||
canvas.image(
|
||||
io,
|
||||
at: [
|
||||
(area['x'] * width) + (area['w'] * width / 2) - ((attachment.metadata['width'] * scale) / 2),
|
||||
height - (area['y'] * height) - (attachment.metadata['height'] * scale / 2) - (area['h'] * height / 2)
|
||||
(area['x'] * width) + (area['w'] * width / 2) - ((image.width * scale) / 2),
|
||||
height - (area['y'] * height) - (image.height * scale / 2) - (area['h'] * height / 2)
|
||||
],
|
||||
width: attachment.metadata['width'] * scale,
|
||||
height: attachment.metadata['height'] * scale
|
||||
width: image.width * scale,
|
||||
height: image.height * scale
|
||||
)
|
||||
when 'file'
|
||||
page[:Annots] ||= []
|
||||
@@ -244,7 +248,7 @@ module Submissions
|
||||
page.box.height = attachment.metadata['height'] * scale
|
||||
|
||||
page.canvas.image(
|
||||
StringIO.new(download_supported_image_data(attachment)),
|
||||
StringIO.new(attachment.preview_images.first.download),
|
||||
at: [0, 0],
|
||||
width: page.box.width,
|
||||
height: page.box.height
|
||||
@@ -253,15 +257,6 @@ module Submissions
|
||||
pdf
|
||||
end
|
||||
|
||||
def download_supported_image_data(attachment)
|
||||
if SUPPORTED_IMAGE_TYPES.include?(attachment.content_type)
|
||||
attachment.download
|
||||
else
|
||||
Vips::Image.new_from_buffer(attachment.download, '')
|
||||
.write_to_buffer('.png')
|
||||
end
|
||||
end
|
||||
|
||||
def h
|
||||
Rails.application.routes.url_helpers
|
||||
end
|
||||
|
||||
@@ -28,7 +28,7 @@ module Templates
|
||||
ActiveStorage::Attachment.where(name: ATTACHMENT_NAME, record: attachment).destroy_all
|
||||
|
||||
image = Vips::Image.new_from_buffer(binary, '')
|
||||
image = image.resize(MAX_WIDTH / image.width.to_f)
|
||||
image = image.autorot.resize(MAX_WIDTH / image.width.to_f)
|
||||
|
||||
io = StringIO.new(image.write_to_buffer(FORMAT, Q: Q, interlace: true))
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB |
Reference in New Issue
Block a user