├── .all-contributorsrc ├── .cz.toml ├── .env.example ├── .env.local_dev ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ └── feature-request.yml ├── dependabot.yml └── workflows │ └── Docker.yml ├── .gitignore ├── .husky └── pre-commit ├── .lintstagedrc.json ├── .prettierignore ├── .prettierrc.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Caddyfile ├── INSTALLATION.md ├── LICENSE ├── Makefile ├── README.md ├── USAGE.md ├── apps ├── OpenSign │ ├── .babelrc │ ├── .cz.toml │ ├── .dockerignore │ ├── .eslintrc.json │ ├── .gitignore │ ├── .husky │ │ └── pre-commit │ ├── .prettierignore │ ├── .prettierrc.json │ ├── Dockerhubfile │ ├── README.md │ ├── babel.config.js │ ├── entrypoint.sh │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ ├── favicon.ico │ │ ├── locales │ │ │ ├── de │ │ │ │ └── translation.json │ │ │ ├── en │ │ │ │ └── translation.json │ │ │ ├── es │ │ │ │ └── translation.json │ │ │ ├── fr │ │ │ │ └── translation.json │ │ │ ├── hi │ │ │ │ └── translation.json │ │ │ └── it │ │ │ │ └── translation.json │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ ├── robots.txt │ │ ├── static │ │ │ └── js │ │ │ │ └── assets │ │ │ │ └── images │ │ │ │ └── logo.2a7bff0c1189183fafe71d7d5b94e0cd.png │ │ └── version.txt │ ├── setuptest.js │ ├── src │ │ ├── App.jsx │ │ ├── assets │ │ │ └── images │ │ │ │ ├── dp.png │ │ │ │ ├── folder.png │ │ │ │ ├── login_img.svg │ │ │ │ ├── logo.png │ │ │ │ ├── pad.svg │ │ │ │ ├── pdf3.png │ │ │ │ └── recreatedoc.png │ │ ├── components │ │ │ ├── AddSigner.jsx │ │ │ ├── AddUser.jsx │ │ │ ├── BulkSendUi.jsx │ │ │ ├── EditContactForm.jsx │ │ │ ├── Footer.jsx │ │ │ ├── FullScreenButton.jsx │ │ │ ├── Header.jsx │ │ │ ├── RenderDebugPdf.jsx │ │ │ ├── RotateAlert.jsx │ │ │ ├── SocialMedia.jsx │ │ │ ├── Title.jsx │ │ │ ├── dashboard │ │ │ │ ├── DashboardButton.jsx │ │ │ │ ├── DashboardCard.jsx │ │ │ │ ├── DashboardReport.jsx │ │ │ │ └── GetDashboard.jsx │ │ │ ├── opensigndrive │ │ │ │ └── DriveBody.jsx │ │ │ ├── pdf │ │ │ │ ├── AddRoleModal.jsx │ │ │ │ ├── AgreementContent.jsx │ │ │ │ ├── AgreementSign.jsx │ │ │ │ ├── BorderResize.jsx │ │ │ │ ├── DefaultSignature.jsx │ │ │ │ ├── DraftDocument.jsx │ │ │ │ ├── DragElement.jsx │ │ │ │ ├── DropdownWidgetOption.jsx │ │ │ │ ├── EditTemplate.jsx │ │ │ │ ├── EditorToolbar.jsx │ │ │ │ ├── EmailBody.jsx │ │ │ │ ├── EmailComponent.jsx │ │ │ │ ├── PdfHeader.jsx │ │ │ │ ├── PdfZoom.jsx │ │ │ │ ├── Placeholder.jsx │ │ │ │ ├── PlaceholderCopy.jsx │ │ │ │ ├── PlaceholderType.jsx │ │ │ │ ├── PrevNext.jsx │ │ │ │ ├── RecipientList.jsx │ │ │ │ ├── RenderAllPdfPage.jsx │ │ │ │ ├── RenderPdf.jsx │ │ │ │ ├── SelectLanguage.jsx │ │ │ │ ├── Signedby.jsx │ │ │ │ ├── SignerListComponent.jsx │ │ │ │ ├── SignerListPlace.jsx │ │ │ │ ├── TextFontSetting.jsx │ │ │ │ ├── VerifyEmail.jsx │ │ │ │ ├── WidgetComponent.jsx │ │ │ │ ├── WidgetList.jsx │ │ │ │ ├── WidgetNameModal.jsx │ │ │ │ └── WidgetsValueModal.jsx │ │ │ ├── shared │ │ │ │ └── fields │ │ │ │ │ ├── CreateFolder.jsx │ │ │ │ │ ├── DateFormatSelector.jsx │ │ │ │ │ ├── FolderModal.jsx │ │ │ │ │ ├── SelectFolder.jsx │ │ │ │ │ ├── SelectSigners.jsx │ │ │ │ │ ├── SignersInput.jsx │ │ │ │ │ ├── SuggestionInput.jsx │ │ │ │ │ └── TimezoneSelector.jsx │ │ │ └── sidebar │ │ │ │ ├── Menu.jsx │ │ │ │ ├── Sidebar.jsx │ │ │ │ └── SubMenu.jsx │ │ ├── constant │ │ │ ├── Utils.jsx │ │ │ ├── appinfo.js │ │ │ ├── const.js │ │ │ ├── getReplacedHashQuery.js │ │ │ └── saveFileSize.js │ │ ├── hook │ │ │ ├── useScript.js │ │ │ └── useWindowSize.js │ │ ├── i18n.js │ │ ├── index.css │ │ ├── index.jsx │ │ ├── json │ │ │ ├── FormJson.js │ │ │ ├── ReportJson.js │ │ │ ├── dashboardJson.js │ │ │ └── menuJson.js │ │ ├── layout │ │ │ └── HomeLayout.jsx │ │ ├── pages │ │ │ ├── AddAdmin.jsx │ │ │ ├── ChangePassword.jsx │ │ │ ├── Dashboard.jsx │ │ │ ├── DebugPdf.jsx │ │ │ ├── DocSuccessPage.jsx │ │ │ ├── ForgetPassword.jsx │ │ │ ├── Form.jsx │ │ │ ├── GuestLogin.jsx │ │ │ ├── Login.jsx │ │ │ ├── Managesign.jsx │ │ │ ├── Opensigndrive.jsx │ │ │ ├── PageNotFound.jsx │ │ │ ├── PdfRequestFiles.jsx │ │ │ ├── PlaceHolderSign.jsx │ │ │ ├── Preferences.jsx │ │ │ ├── Report.jsx │ │ │ ├── SignyourselfPdf.jsx │ │ │ ├── TemplatePlaceholder.jsx │ │ │ ├── UpdateExistUserAdmin.jsx │ │ │ ├── UserList.jsx │ │ │ ├── UserProfile.jsx │ │ │ └── VerifyDocument.jsx │ │ ├── polyfills.js │ │ ├── primitives │ │ │ ├── AddContact.jsx │ │ │ ├── Alert.jsx │ │ │ ├── CheckCircle.jsx │ │ │ ├── DotLottieReact.jsx │ │ │ ├── DownloadPdfZip.jsx │ │ │ ├── GetReportDisplay.jsx │ │ │ ├── HandleError.jsx │ │ │ ├── LazyPage.jsx │ │ │ ├── LinkUserModal.jsx │ │ │ ├── Loader.jsx │ │ │ ├── LoaderWithMsg.jsx │ │ │ ├── Modal.jsx │ │ │ ├── ModalUi.jsx │ │ │ ├── PdfDeclineModal.jsx │ │ │ ├── ShareButton.jsx │ │ │ ├── Tooltip.jsx │ │ │ ├── Tour.jsx │ │ │ ├── TourContentWithBtn.jsx │ │ │ ├── Validate.jsx │ │ │ ├── ValidateRoute.jsx │ │ │ ├── ValidateSession.jsx │ │ │ └── sanitizeFileName.js │ │ ├── redux │ │ │ ├── reducers │ │ │ │ ├── ShowTenant.js │ │ │ │ ├── TourStepsReducer.js │ │ │ │ ├── infoReducer.js │ │ │ │ ├── showHeader.js │ │ │ │ └── widgetSlice.js │ │ │ └── store.js │ │ └── styles │ │ │ ├── AddUser.css │ │ │ ├── managesign.css │ │ │ ├── opensigndrive.css │ │ │ ├── quill.css │ │ │ └── signature.css │ ├── tailwind.config.js │ └── vite.config.js ├── OpenSignServer │ ├── .dockerignore │ ├── .ebextensions │ │ └── app.config │ ├── .eslintrc.json │ ├── .github │ │ └── workflows │ │ │ └── ci.yml │ ├── .gitignore │ ├── .nycrc │ ├── .prettierrc │ ├── CODE_OF_CONDUCT.md │ ├── Dockerhubfile │ ├── Utils.js │ ├── app.json │ ├── app.yaml │ ├── auth │ │ └── authadapter.js │ ├── cloud │ │ ├── customRoute │ │ │ ├── customApp.js │ │ │ └── uploadFile.js │ │ ├── main.js │ │ └── parsefunction │ │ │ ├── AddAdmin.js │ │ │ ├── AuthLoginAsMail.js │ │ │ ├── CheckAdminExist.js │ │ │ ├── ContactBookAftersave.js │ │ │ ├── DocumentAfterFind.js │ │ │ ├── DocumentAftersave.js │ │ │ ├── DocumentBeforesave.js │ │ │ ├── ForwardDoc.js │ │ │ ├── GetLogoByDomain.js │ │ │ ├── GetTemplate.js │ │ │ ├── Newsletter.js │ │ │ ├── SendMailOTPv1.js │ │ │ ├── SignatureAfterFind.js │ │ │ ├── TeamsAftersave.js │ │ │ ├── TemplateAfterFind.js │ │ │ ├── TemplateAfterSave.js │ │ │ ├── TemplateBeforesave.js │ │ │ ├── TenantAfterFind.js │ │ │ ├── UpdateExistUserAsAdmin.js │ │ │ ├── UserAfterFInd.js │ │ │ ├── VerifyEmail.js │ │ │ ├── addUser.js │ │ │ ├── createBatchContact.js │ │ │ ├── createBatchDocs.js │ │ │ ├── createDuplicate.js │ │ │ ├── declinedocument.js │ │ │ ├── editContact.js │ │ │ ├── fileUpload.js │ │ │ ├── filterDocs.js │ │ │ ├── generateCertificatebydocId.js │ │ │ ├── getContact.js │ │ │ ├── getDocument.js │ │ │ ├── getDrive.js │ │ │ ├── getReport.js │ │ │ ├── getSignedUrl.js │ │ │ ├── getSigners.js │ │ │ ├── getTeams.js │ │ │ ├── getTenant.js │ │ │ ├── getUserDetails.js │ │ │ ├── getUserId.js │ │ │ ├── getUserListByOrg.js │ │ │ ├── isUserInContactBook.js │ │ │ ├── isextenduser.js │ │ │ ├── linkContactToDoc.js │ │ │ ├── loginUser.js │ │ │ ├── pdf │ │ │ ├── GenerateCertificate.js │ │ │ ├── PDF.js │ │ │ ├── PDFArrayCustom.js │ │ │ └── Placeholder.js │ │ │ ├── recreateDocument.js │ │ │ ├── reportsJson.js │ │ │ ├── saveAsTemplate.js │ │ │ ├── saveFile.js │ │ │ ├── savecontact.js │ │ │ ├── sendMailGmailProvider.js │ │ │ ├── sendMailv3.js │ │ │ ├── updateContactTour.js │ │ │ ├── updatePreferences.js │ │ │ ├── updateTenant.js │ │ │ ├── updateTourStatus.js │ │ │ ├── updatesignaturetype.js │ │ │ └── usersignup.js │ ├── databases │ │ ├── migrations │ │ │ ├── 20231110174122-update_setclp.cjs │ │ │ ├── 20231220171155-create_template_cls.cjs │ │ │ ├── 20240110100110-update_document_cls_cjs.cjs │ │ │ ├── 20240306123606-add_fields_contracts_users_cjs.cjs │ │ │ ├── 20240408133151-update_contracts_document_cls.cjs │ │ │ ├── 20240521184801-update_contracts_document_cls_cjs.cjs │ │ │ ├── 20240527194216-update_contracts_template_cls_cjs.cjs │ │ │ ├── 20240701151539-update_contracts_users_cls_cjs.cjs │ │ │ ├── 20240708200454-create_multiuser_classes.cjs │ │ │ ├── 20240710163936-addfield_declinereason_doc_cls.cjs │ │ │ ├── 20240723162947-update_teams_org_clp.cjs │ │ │ ├── 20240726114557-update_contracts_template_cjs.cjs │ │ │ ├── 20240806141611-update_contracts_users_cjs.cjs │ │ │ ├── 20240822201043-addfield_doccls_cjs.cjs │ │ │ ├── 20240903170628-update_contracts_document_cls_cjs.cjs │ │ │ ├── 20240913121404-add_isenableotp_doc.cjs │ │ │ ├── 20240926163209-add_istourenabled_doc_cjs.cjs │ │ │ ├── 20241004104551-fileadapterId_doccls.cjs │ │ │ ├── 20241106180102-add_signaturetype_field.cjs │ │ │ ├── 20241114175425-add_notifyonsignatures_field.cjs │ │ │ ├── 20241205115937-add_bcc_field_cjs.cjs │ │ │ ├── 20241205163817-add_redirecturl_field_cjs.cjs │ │ │ ├── 20241227112339-update_contracts_document_cls_cjs.cjs │ │ │ ├── 20241227125916-update_contracts_template_cls_cjs.cjs │ │ │ ├── 20250411095519-add_templateid_field.cjs │ │ │ ├── 20250424104819-change_permission.cjs │ │ │ └── 20250427105912-add_name_field_user_cls.cjs │ │ └── seeders │ │ │ └── .gitkeep │ ├── exports │ │ └── .gitkeep │ ├── files │ │ ├── custom_email.html │ │ ├── custom_email.txt │ │ ├── custom_email_subject.txt │ │ ├── password_reset_email.html │ │ ├── password_reset_email.txt │ │ ├── password_reset_email_subject.txt │ │ ├── verification_email.html │ │ ├── verification_email.txt │ │ └── verification_email_subject.txt │ ├── font │ │ └── times.ttf │ ├── index.js │ ├── jsconfig.json │ ├── logo.png │ ├── migrationdb │ │ └── createContactIndex.js │ ├── openshift.json │ ├── package-lock.json │ ├── package.json │ ├── pdfFile │ │ ├── emudhra-test-class2.pfx │ │ └── sample.pdf │ ├── public │ │ ├── assets │ │ │ ├── css │ │ │ │ └── style.css │ │ │ ├── images │ │ │ │ └── parse-logo.png │ │ │ └── js │ │ │ │ └── script.js │ │ ├── openapi.json │ │ └── test.html │ ├── scalingo.json │ └── spec │ │ ├── .eslintrc.json │ │ ├── Tests.spec.js │ │ ├── helper.js │ │ ├── support │ │ └── jasmine.json │ │ └── utils │ │ └── test-runner.js ├── localstack │ └── localstack-script.sh └── mongo │ └── Dockerfile ├── cert ├── local-dev_base64_pfx ├── local_dev.crt ├── local_dev.key └── local_dev.pfx ├── docker-compose.yml ├── package-lock.json └── package.json /.cz.toml: -------------------------------------------------------------------------------- 1 | [tool.commitizen] 2 | version = "0.1.0" # This should be your current semver version 3 | version_files = [ 4 | "package.json:\"version\":", 5 | "package-lock.json:\"version\":", 6 | ] -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:react/recommended", 9 | "plugin:react-hooks/recommended" 10 | ], 11 | "parserOptions": { 12 | "ecmaFeatures": { 13 | "jsx": true 14 | }, 15 | "ecmaVersion": "latest", 16 | "sourceType": "module" 17 | }, 18 | "plugins": ["react"], 19 | "rules": { 20 | "react/prop-types": 0, 21 | "no-unused-vars": 0, 22 | "no-empty": 0, 23 | "no-case-declarations": 0, 24 | "react/no-deprecated": 0, 25 | "no-undef": 0 26 | }, 27 | "ignorePatterns": ["**/build/**", "**/*.config.js"], 28 | "settings": { 29 | "react": { 30 | "version": "detect" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: "Bug Report" 2 | labels: ["bug","up-for-grabs"] 3 | title: "[Bug]: " 4 | assignees: 5 | - andrew-opensignlabs 6 | description: Create a bug report to help us make OpenSign™ the world's best document e-signature platform 7 | body: 8 | - type: textarea 9 | attributes: 10 | label: Issue Description 11 | description: Please provide a clear & concise description of the problem. 12 | - type: textarea 13 | attributes: 14 | label: Expected Behavior 15 | description: Describe what you expected to happen. 16 | - type: textarea 17 | attributes: 18 | label: Current Behavior 19 | description: Describe what is happening instead. 20 | - type: textarea 21 | attributes: 22 | label: Steps to reproduce 23 | description: Please provide step-by-step instructions to reproduce the issue. Include code snippets, URLs, error messages and any other relevant information. 24 | - type: textarea 25 | attributes: 26 | label: Screenshots of the issue(optional) 27 | description: Add screenshots to better explain the issue. 28 | - type: input 29 | attributes: 30 | label: Operating System [e.g. MacOS Sonoma 14.1, Windows 11] 31 | - type: dropdown 32 | id: browsers 33 | attributes: 34 | label: What browsers are you seeing the problem on? 35 | multiple: true 36 | options: 37 | - Chrome 38 | - Firefox 39 | - Safari 40 | - Microsoft Edge 41 | default: 0 42 | - type: input 43 | attributes: 44 | label: What version of OpenSign™ are you seeing this issue on? [e.g. 1.0.6] 45 | validations: 46 | required: true 47 | - type: dropdown 48 | id: environment 49 | attributes: 50 | label: What environment are you seeing the problem on? 51 | multiple: true 52 | options: 53 | - Production (app.opensignlabs.com) 54 | - Staging (staging-app.opensignlabs.com) 55 | - Hosted (app.yourdomain.com) 56 | - Dev (localhost or vercel) 57 | - type: checkboxes 58 | attributes: 59 | label: Please check the boxes that apply to this issue report. 60 | options: 61 | - label: I have searched the existing issues & discussions to make sure that this is not a duplicate. 62 | validations: 63 | required: true 64 | - type: checkboxes 65 | id: terms 66 | attributes: 67 | label: Code of Conduct 68 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://www.opensignlabs.com) 69 | options: 70 | - label: I agree to follow this project's Code of Conduct 71 | required: true 72 | - label: I have searched the existing issues & discussions to make sure that this is not a duplicate. 73 | required: true 74 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest an idea for OpenSign™ 3 | title: "Feature Request: [Short Description]" 4 | labels: [feature request, needs triage] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | ## Thanks for taking the time to fill out this feature request! 10 | 11 | - type: input 12 | id: title 13 | attributes: 14 | label: Feature Title 15 | description: What would you call this feature? 16 | placeholder: e.g., Advanced Signature Verification 17 | validations: 18 | required: true 19 | 20 | - type: textarea 21 | id: feature-description 22 | attributes: 23 | label: Feature Description 24 | description: | 25 | Please provide a detailed description of the feature you're proposing. 26 | placeholder: | 27 | e.g., I'm proposing an advanced signature verification feature that... 28 | validations: 29 | required: true 30 | - type: dropdown 31 | id: featuretype 32 | attributes: 33 | label: What type of feature are you requesting? 34 | multiple: true 35 | options: 36 | - UI/UX Improvement 37 | - Security / Compliance 38 | - Performance / Optimization 39 | - 3rd party integration 40 | - Other 41 | - type: dropdown 42 | id: importance 43 | attributes: 44 | label: Importance 45 | description: How important is this feature to you? 46 | options: 47 | - Critical 48 | - High 49 | - Medium 50 | - Low 51 | validations: 52 | required: true 53 | 54 | - type: textarea 55 | id: additional-context 56 | attributes: 57 | label: Additional Context 58 | description: Any other context or screenshots about the feature request here. 59 | placeholder: Additional details, examples, or screenshots... 60 | 61 | - type: checkboxes 62 | id: terms 63 | attributes: 64 | label: Code of Conduct 65 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://www.opensignlabs.com) 66 | options: 67 | - label: I agree to follow this project's Code of Conduct 68 | required: true 69 | - label: I have searched the existing issues & discussions to make sure that this is not a duplicate. 70 | required: true 71 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | # maintain dependencies for frontend 9 | - package-ecosystem: "npm" # See documentation for possible values 10 | directory: "/apps/OpenSign" # Location of package manifests 11 | schedule: 12 | interval: "weekly" 13 | # maintain dependencies for server 14 | - package-ecosystem: "npm" # See documentation for possible values 15 | directory: "/apps/OpenSignServer" # Location of package manifests 16 | schedule: 17 | interval: "weekly" 18 | # maintain dependencies for microfrontends 19 | - package-ecosystem: "npm" # See documentation for possible values 20 | directory: "microfrontends/SignDocuments" # Location of package manifests 21 | schedule: 22 | interval: "weekly" 23 | -------------------------------------------------------------------------------- /.github/workflows/Docker.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | # 👇 add this block 4 | permissions: 5 | contents: read # allow checkout & metadata-action to read repo 6 | id-token: write # needed by docker/metadata-action v4 7 | 8 | on: 9 | push: 10 | branches: 11 | - 'main' 12 | - 'staging' 13 | 14 | jobs: 15 | docker: 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | include: 20 | - image: opensign/opensign 21 | dockerfile: apps/OpenSign/Dockerhubfile 22 | - image: opensign/opensignserver 23 | dockerfile: apps/OpenSignServer/Dockerhubfile 24 | steps: 25 | - 26 | name: Checkout 27 | uses: actions/checkout@v3 28 | - 29 | name: Login to Docker Hub 30 | uses: docker/login-action@v2 31 | with: 32 | username: ${{ secrets.DOCKERHUB_USERNAME }} 33 | password: ${{ secrets.DOCKERHUB_TOKEN }} 34 | - 35 | name: Extract metadata (tags, labels) for Docker 36 | id: meta 37 | uses: docker/metadata-action@v4 38 | with: 39 | images: ${{ matrix.image }} 40 | - 41 | name: Debug - List files opensign 42 | run: ls -R apps/OpenSign/ # Adjust the path as needed 43 | - 44 | name: Debug - List files opensignserver 45 | run: ls -R apps/OpenSignServer/ # Adjust the path as needed 46 | - 47 | name: Build and push 48 | uses: docker/build-push-action@v4 49 | with: 50 | context: . 51 | file: ${{ matrix.dockerfile }} 52 | push: true 53 | tags: ${{ steps.meta.outputs.tags }} 54 | labels: ${{ steps.meta.outputs.labels }} 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .env 16 | .DS_Store 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | apps/OpenSign/public/mfbuild/* 26 | microfrontends/SignDocuments/build/* 27 | apps/OpenSignServer/files/files/* -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm run lint-staged-changes 5 | -------------------------------------------------------------------------------- /.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.js": "prettier --write" 3 | } 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | public/ 3 | build/ 4 | package.json 5 | package-lock.json 6 | dist/ 7 | docs/ -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": true, 3 | "semi": true, 4 | "trailingComma": "none", 5 | "printWidth": 80, 6 | "tabWidth": 2 7 | } 8 | -------------------------------------------------------------------------------- /Caddyfile: -------------------------------------------------------------------------------- 1 | {$HOST_URL} { 2 | reverse_proxy client:3000 3 | handle_path /api/* { 4 | reverse_proxy server:8080 5 | rewrite * {uri} 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /INSTALLATION.md: -------------------------------------------------------------------------------- 1 | 2 | # INSTALLATON INSTRUCTIONS HAS A NEW HOME 3 | 4 | We've updated our installation instructions and moved them to our dedicated documentation portal to provide you with a better experience and updated guidance. Whether you're looking to self-host OpenSign™ or contribute to its development, you can find detailed instructions tailored to your needs at [docs.opensignlabs.com](https://docs.opensignlabs.com). 5 | 6 | ## Self Hosting 7 | 8 | If you're interested in self-hosting OpenSign™, our new documentation portal provides comprehensive, step-by-step instructions to help you set up OpenSign™ in your own environment. This section is designed for system administrators and those looking to deploy OpenSign™ within their organization or for personal use. 9 | 10 | - **Access Self Hosting Instructions:** [Self Hosting Guide](https://docs.opensignlabs.com/docs/category/docker) 11 | 12 | ## Contributors 13 | 14 | For developers and contributors who are looking to build upon or contribute to OpenSign™, we've prepared a separate section that covers the setup, development environment configuration, and guidelines for contributing to the OpenSign™ project. 15 | 16 | - **Access Contributor Instructions:** [Contributors Guide](https://docs.opensignlabs.com/docs/contribute/INSTALLATION) 17 | 18 | ## Additional Resources 19 | 20 | Should you need further assistance or have any questions, please feel free to reach out to us through our support channels: 21 | 22 | - **Twitter:** [OpenSignHQ on Twitter](https://twitter.com/OpenSignHQ) 23 | - **Facebook:** [OpenSign on Facebook](https://www.facebook.com/profile.php?id=61551030403669) 24 | - **LinkedIn:** [OpenSign™ on LinkedIn](https://www.linkedin.com/company/opensign%E2%84%A2/) 25 | - **Discord:** [Join our Discord Community](https://discord.com/invite/opensign) 26 | - **YouTube:** [OpenSignHQ on YouTube](https://www.youtube.com/@opensignhq) 27 | 28 | We are committed to providing you with the support you need to successfully install and use OpenSign™. Visit our website at [www.opensignlabs.com](https://www.opensignlabs.com) for more information about our project and its features. 29 | 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | @echo "Building with HOST_URL=${HOST_URL}" 3 | cp .env.local_dev .env 4 | cd apps/OpenSign && cp ../../.env.local_dev .env && npm install && npm run build 5 | HOST_URL=${HOST_URL} docker compose up --build --force-recreate 6 | 7 | run: 8 | @echo "Building with HOST_URL=${HOST_URL}" 9 | cp .env.local_dev .env 10 | docker compose up -d 11 | -------------------------------------------------------------------------------- /USAGE.md: -------------------------------------------------------------------------------- 1 | Please visit the official documentation [here](https://docs.opensignlabs.com). -------------------------------------------------------------------------------- /apps/OpenSign/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env", "@babel/preset-react"] 3 | } -------------------------------------------------------------------------------- /apps/OpenSign/.cz.toml: -------------------------------------------------------------------------------- 1 | [tool.commitizen] 2 | version = "0.1.0" # This should be your current semver version 3 | version_files = [ 4 | "package.json:\"version\":", 5 | "package-lock.json:\"version\":", 6 | ] -------------------------------------------------------------------------------- /apps/OpenSign/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /apps/OpenSign/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:react/recommended", 9 | "plugin:react-hooks/recommended" 10 | ], 11 | "parserOptions": { 12 | "ecmaFeatures": { 13 | "jsx": true 14 | }, 15 | "ecmaVersion": "latest", 16 | "sourceType": "module" 17 | }, 18 | "plugins": ["react"], 19 | "rules": { 20 | "react/prop-types": 0, 21 | "no-unused-vars": 1 22 | }, 23 | "ignorePatterns": ["**/build/**", "**/*.config.js"], 24 | "settings": { 25 | "react": { 26 | "version": "detect" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /apps/OpenSign/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | /mfbuild 14 | 15 | # misc 16 | .env 17 | .DS_Store 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | -------------------------------------------------------------------------------- /apps/OpenSign/.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx eslint '**/*.{js,jsx}' 5 | npx pretty-quick --staged '**/*.{js,jsx}' -------------------------------------------------------------------------------- /apps/OpenSign/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | public/ 3 | build/ 4 | package.json 5 | package-lock.json 6 | dist/ 7 | docs/ -------------------------------------------------------------------------------- /apps/OpenSign/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": true, 3 | "semi": true, 4 | "trailingComma": "none", 5 | "printWidth": 80, 6 | "tabWidth": 2 7 | } 8 | -------------------------------------------------------------------------------- /apps/OpenSign/Dockerhubfile: -------------------------------------------------------------------------------- 1 | # Use an official Node runtime as the base image 2 | FROM node:22.14.0 3 | 4 | # Set the working directory inside the container 5 | WORKDIR /usr/src/app 6 | 7 | # Copy package.json and package-lock.json first to leverage Docker cache 8 | COPY apps/OpenSign/package*.json ./ 9 | 10 | # Install application dependencies 11 | RUN npm install 12 | 13 | # Copy the current directory contents into the container 14 | COPY apps/OpenSign/ . 15 | COPY apps/OpenSign/.husky . 16 | COPY apps/OpenSign/entrypoint.sh . 17 | 18 | # make the entrypoint.sh file executable 19 | RUN chmod +x entrypoint.sh 20 | 21 | # Define environment variables if needed 22 | ENV NODE_ENV=production 23 | ENV GENERATE_SOURCEMAP=false 24 | # build 25 | RUN npm run build 26 | 27 | # Inject env.js loader into index.html 28 | RUN sed -i '//a\' build/index.html 29 | 30 | # Make port 3000 available to the world outside this container 31 | EXPOSE 3000 32 | 33 | ENTRYPOINT ["./entrypoint.sh"] 34 | 35 | # Run the application 36 | CMD ["npm", "start"] 37 | -------------------------------------------------------------------------------- /apps/OpenSign/README.md: -------------------------------------------------------------------------------- 1 | # Open Sign 2 | 3 | Open source is true platform to sign pdf with digital signature 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 13 | 14 | The page will reload when you make changes.\ 15 | You may also see any lint errors in the console. 16 | 17 | ### `npm run build` 18 | 19 | Builds the app for production to the `build` folder.\ 20 | It correctly bundles React in production mode and optimizes the build for the best performance. 21 | 22 | The build is minified and the filenames include the hashes.\ 23 | Your app is ready to be deployed! 24 | 25 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 26 | -------------------------------------------------------------------------------- /apps/OpenSign/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@babel/preset-env", "@babel/preset-react"] 3 | }; 4 | -------------------------------------------------------------------------------- /apps/OpenSign/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ENV_FILE=./build/env.js 4 | DOTENV_FILE=./.env.prod # ✅ use .env.prod 5 | 6 | echo "Generating runtime env file at $ENV_FILE..." 7 | 8 | echo "window.RUNTIME_ENV = {" > $ENV_FILE 9 | 10 | # List of keys to include 11 | RUNTIME_KEYS="REACT_APP_SERVERURL" 12 | 13 | for key in $RUNTIME_KEYS; do 14 | # First check docker env (-e), fallback to .env file 15 | value=$(printenv "$key") 16 | 17 | if [ -z "$value" ] && [ -f "$DOTENV_FILE" ]; then 18 | # fallback: read from .env 19 | value=$(grep "^$key=" "$DOTENV_FILE" | cut -d '=' -f2- | tr -d '\r\n' | sed 's/"/\\"/g') 20 | else 21 | value=$(echo "$value" | sed 's/"/\\"/g') 22 | fi 23 | 24 | echo " $key: \"$value\"," >> $ENV_FILE 25 | done 26 | 27 | echo "};" >> $ENV_FILE 28 | 29 | exec "$@" 30 | -------------------------------------------------------------------------------- /apps/OpenSign/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | ...Loading 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /apps/OpenSign/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {} 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /apps/OpenSign/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSign/public/favicon.ico -------------------------------------------------------------------------------- /apps/OpenSign/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSign/public/logo192.png -------------------------------------------------------------------------------- /apps/OpenSign/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSign/public/logo512.png -------------------------------------------------------------------------------- /apps/OpenSign/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Open sign", 3 | "name": "Open sign", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /apps/OpenSign/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /apps/OpenSign/public/static/js/assets/images/logo.2a7bff0c1189183fafe71d7d5b94e0cd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSign/public/static/js/assets/images/logo.2a7bff0c1189183fafe71d7d5b94e0cd.png -------------------------------------------------------------------------------- /apps/OpenSign/public/version.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSign/public/version.txt -------------------------------------------------------------------------------- /apps/OpenSign/setuptest.js: -------------------------------------------------------------------------------- 1 | import "@testing-library/jest-dom"; 2 | -------------------------------------------------------------------------------- /apps/OpenSign/src/assets/images/dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSign/src/assets/images/dp.png -------------------------------------------------------------------------------- /apps/OpenSign/src/assets/images/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSign/src/assets/images/folder.png -------------------------------------------------------------------------------- /apps/OpenSign/src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSign/src/assets/images/logo.png -------------------------------------------------------------------------------- /apps/OpenSign/src/assets/images/pdf3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSign/src/assets/images/pdf3.png -------------------------------------------------------------------------------- /apps/OpenSign/src/assets/images/recreatedoc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSign/src/assets/images/recreatedoc.png -------------------------------------------------------------------------------- /apps/OpenSign/src/components/Footer.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import Package from "../../package.json"; 3 | import axios from "axios"; 4 | import { openInNewTab } from "../constant/Utils"; 5 | import { useTranslation } from "react-i18next"; 6 | const Footer = () => { 7 | const appName = "OpenSign™"; 8 | const { t } = useTranslation(); 9 | const [showButton, setShowButton] = useState(false); 10 | const [version, setVersion] = useState(""); 11 | useEffect(() => { 12 | axios 13 | .get("/version.txt") 14 | .then((response) => { 15 | setVersion(response.data); // Set the retrieved data to the state variable 16 | }) 17 | .catch((error) => { 18 | console.error("Error reading the file:", error); 19 | }); 20 | }, []); 21 | 22 | const handleScroll = () => { 23 | if (window.pageYOffset >= 50) { 24 | setShowButton(true); 25 | } else { 26 | setShowButton(false); 27 | } 28 | }; 29 | 30 | const scrollToTop = () => { 31 | window.scrollTo(0, 0); 32 | setShowButton(false); 33 | }; 34 | 35 | useEffect(() => { 36 | window.addEventListener("scroll", handleScroll); 37 | 38 | return () => { 39 | window.removeEventListener("scroll", handleScroll); 40 | }; 41 | }, []); 42 | 43 | const openUrl = () => { 44 | openInNewTab( 45 | "https://github.com/OpenSignLabs/OpenSign/releases/tag/" + version 46 | ); 47 | }; 48 | return ( 49 | <> 50 | 61 | 69 | 70 | ); 71 | }; 72 | 73 | export default Footer; 74 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/FullScreenButton.jsx: -------------------------------------------------------------------------------- 1 | // FullScreenButton.js 2 | import React, { useState, useEffect } from "react"; 3 | 4 | const FullScreenButton = () => { 5 | const [isFullScreen, setIsFullScreen] = useState(false); 6 | 7 | useEffect(() => { 8 | document.addEventListener("fullscreenchange", handleFullScreenChange); 9 | document.addEventListener("webkitfullscreenchange", handleFullScreenChange); 10 | document.addEventListener("mozfullscreenchange", handleFullScreenChange); 11 | document.addEventListener("MSFullscreenChange", handleFullScreenChange); 12 | 13 | return () => { 14 | document.removeEventListener("fullscreenchange", handleFullScreenChange); 15 | document.removeEventListener( 16 | "webkitfullscreenchange", 17 | handleFullScreenChange 18 | ); 19 | document.removeEventListener( 20 | "mozfullscreenchange", 21 | handleFullScreenChange 22 | ); 23 | document.removeEventListener( 24 | "MSFullscreenChange", 25 | handleFullScreenChange 26 | ); 27 | }; 28 | }, []); 29 | 30 | const handleFullScreenChange = () => { 31 | setIsFullScreen(!!document.fullscreenElement); 32 | }; 33 | 34 | const toggleFullScreen = () => { 35 | if (isFullScreen) { 36 | exitFullScreen(); 37 | } else { 38 | requestFullScreen(); 39 | } 40 | }; 41 | 42 | const requestFullScreen = () => { 43 | if (document.documentElement.requestFullscreen) { 44 | document.documentElement.requestFullscreen(); 45 | } else if (document.documentElement.mozRequestFullScreen) { 46 | document.documentElement.mozRequestFullScreen(); 47 | } else if (document.documentElement.webkitRequestFullscreen) { 48 | document.documentElement.webkitRequestFullscreen(); 49 | } else if (document.documentElement.msRequestFullscreen) { 50 | document.documentElement.msRequestFullscreen(); 51 | } 52 | }; 53 | 54 | const exitFullScreen = () => { 55 | if (document.exitFullscreen) { 56 | document.exitFullscreen(); 57 | } else if (document.mozCancelFullScreen) { 58 | document.mozCancelFullScreen(); 59 | } else if (document.webkitExitFullscreen) { 60 | document.webkitExitFullscreen(); 61 | } else if (document.msExitFullscreen) { 62 | document.msExitFullscreen(); 63 | } 64 | }; 65 | 66 | return ( 67 |
68 | 78 |
79 | ); 80 | }; 81 | 82 | export default FullScreenButton; 83 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/RotateAlert.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ModalUi from "../primitives/ModalUi"; 3 | import { useTranslation } from "react-i18next"; 4 | 5 | function RotateAlert(props) { 6 | const { t } = useTranslation(); 7 | return ( 8 | props.setShowRotateAlert({ status: false, degree: 0 })} 12 | > 13 |
14 |

{t("rotate-alert-mssg")}

15 |
16 |
17 | 24 | 33 |
34 |
35 |
36 | ); 37 | } 38 | 39 | export default RotateAlert; 40 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/SocialMedia.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useTranslation } from "react-i18next"; 3 | import { NavLink } from "react-router"; 4 | 5 | const SocialMedia = () => { 6 | const { t } = useTranslation(); 7 | 8 | return ( 9 | 10 | 15 | 16 | 17 | OpenSign's {t("social-media.github")} 18 | 19 | 20 | 25 | 26 | 27 | OpenSign's {t("social-media.linked-in")} 28 | 29 | 30 | 35 | 36 | 37 | OpenSign's {t("social-media.twitter")} 38 | 39 | 40 | 45 | 46 | 47 | OpenSign's {t("social-media.discord")} 48 | 49 | 50 | 51 | ); 52 | }; 53 | 54 | export default SocialMedia; 55 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/Title.jsx: -------------------------------------------------------------------------------- 1 | import { Helmet } from "react-helmet"; 2 | 3 | function Title({ title, drive }) { 4 | const appName = 5 | "OpenSign™"; 6 | return ( 7 | 8 | {drive ? title : `${title} - ${appName}`} 9 | 10 | 16 | 17 | ); 18 | } 19 | 20 | export default Title; 21 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/dashboard/DashboardButton.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useNavigate } from "react-router"; 3 | import { openInNewTab } from "../../constant/Utils"; 4 | import { useTranslation } from "react-i18next"; 5 | 6 | const DashboardButton = (props) => { 7 | const navigate = useNavigate(); 8 | const { t } = useTranslation(); 9 | 10 | function openReport() { 11 | if (props.Data && props.Data.Redirect_type) { 12 | const Redirect_type = props.Data.Redirect_type; 13 | const id = props.Data.Redirect_id; 14 | if (Redirect_type === "Form") { 15 | navigate(`/form/${id}`); 16 | } else if (Redirect_type === "Report") { 17 | navigate(`/report/${id}`); 18 | } else if (Redirect_type === "Url") { 19 | openInNewTab(id); 20 | } 21 | } 22 | } 23 | return ( 24 |
openReport()} 26 | className={`${ 27 | props.Data && props.Data.Redirect_type 28 | ? "cursor-pointer" 29 | : "cursor-default" 30 | } w-full shadow-md px-3 py-2 op-card bg-base-100`} 31 | > 32 |
33 |
34 | 35 | 40 | 41 |
42 |
43 | {t(`sidebar.${props.Label}`)} 44 | {props.Label === "Sign yourself" && ( 45 |
46 | {t("signyour-self-button")} 47 |
48 | )} 49 | {props.Label === "Request signatures" && ( 50 |
51 | {t("requestsign-button")} 52 |
53 | )} 54 |
55 |
56 |
57 | ); 58 | }; 59 | 60 | export default DashboardButton; 61 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/pdf/AddRoleModal.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ModalUi from "../../primitives/ModalUi"; 3 | import { useTranslation } from "react-i18next"; 4 | 5 | const AddRoleModal = (props) => { 6 | const { t } = useTranslation(); 7 | return ( 8 | 13 |
14 |
15 | props.setRoleName(e.target.value)} 18 | placeholder={ 19 | props.signersdata.length > 0 20 | ? "Role " + (props.signersdata.length + 1) 21 | : "Role 1" 22 | } 23 | className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs mt-1" 24 | /> 25 |

26 | {t("role-ex")}.. 27 |

28 |
29 |
30 | 33 | 40 |
41 |
42 |
43 |
44 | ); 45 | }; 46 | 47 | export default AddRoleModal; 48 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/pdf/BorderResize.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function BorderResize(props) { 4 | const getHeight = () => { 5 | const height = props.posHeight(props.pos, props.isSignYourself); 6 | if (height > 14) { 7 | return "14px"; 8 | } else { 9 | return `${height}px`; 10 | } 11 | }; 12 | 13 | return ( 14 |
23 | ); 24 | } 25 | 26 | export default BorderResize; 27 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/pdf/DragElement.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { getWidgetType, widgets } from "../../constant/Utils"; 3 | 4 | function DragElement(item) { 5 | const getWidgets = widgets; 6 | const filterWidgetPreview = getWidgets.filter( 7 | (data) => data.type === item?.text 8 | ); 9 | 10 | return
{getWidgetType(filterWidgetPreview[0])}
; 11 | } 12 | 13 | export default DragElement; 14 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/pdf/EmailBody.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Tooltip from "../../primitives/Tooltip"; 3 | import ReactQuill from "react-quill-new"; 4 | import "../../styles/quill.css"; 5 | import EditorToolbar, { module1, formats } from "./EditorToolbar"; 6 | import { useTranslation } from "react-i18next"; 7 | 8 | export function EmailBody(props) { 9 | const { t } = useTranslation(); 10 | return ( 11 |
12 |
13 | 16 | e.target.setCustomValidity(t("input-required"))} 19 | onInput={(e) => e.target.setCustomValidity("")} 20 | value={props.requestSubject} 21 | onChange={(e) => props.setRequestSubject(e.target.value)} 22 | placeholder='${senderName} has requested you to sign "${documentName}"' 23 | className="op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full text-xs" 24 | /> 25 | 28 |
29 | 30 | 39 |
40 |
41 |
42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/pdf/PrevNext.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useTranslation } from "react-i18next"; 3 | 4 | function PrevNext({ pageNumber, allPages, changePage }) { 5 | const { t } = useTranslation(); 6 | //for go to previous page 7 | function previousPage() { 8 | changePage(-1); 9 | } 10 | //for go to next page 11 | function nextPage() { 12 | changePage(1); 13 | } 14 | 15 | return ( 16 |
17 | 26 | 27 | {pageNumber || (allPages ? 1 : "--")} {t("of")} {allPages || "--"} 28 | 29 | 38 |
39 | ); 40 | } 41 | 42 | export default PrevNext; 43 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/pdf/SelectLanguage.jsx: -------------------------------------------------------------------------------- 1 | import i18next from "i18next"; 2 | import React, { useState } from "react"; 3 | import { useTranslation } from "react-i18next"; 4 | 5 | function SelectLanguage(props) { 6 | const { i18n } = useTranslation(); 7 | const languages = [ 8 | { value: "en", text: "English" }, //english 9 | { value: "es", text: "Española" }, //spanish 10 | { value: "fr", text: "Français" }, //french 11 | { value: "it", text: "Italiano" }, //italian 12 | { value: "de", text: "Deutsch" }, //german 13 | { value: "hi", text: "हिन्दी" } //hindi 14 | ]; 15 | const defaultLanguage = i18next.language || "en"; 16 | const [lang, setLang] = useState(defaultLanguage); 17 | // This function put query that helps to change the language 18 | const handleChangeLang = (e) => { 19 | setLang(e.target.value); 20 | i18n.changeLanguage(e.target.value); 21 | props?.updateExtUser && props.updateExtUser({ language: e.target.value }); 22 | }; 23 | return ( 24 |
29 | 45 |
46 | ); 47 | } 48 | 49 | export default SelectLanguage; 50 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/pdf/Signedby.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "../../styles/signature.css"; 3 | import { useTranslation } from "react-i18next"; 4 | function Signedby(props) { 5 | const { t } = useTranslation(); 6 | const getFirstLetter = (pdfData) => { 7 | const name = props.isSelfSign 8 | ? (pdfData?.Signers && pdfData?.Signers[0]?.Name) || "User" 9 | : pdfData.ExtUserPtr?.Name; 10 | const firstLetter = name.charAt(0); 11 | return firstLetter; 12 | }; 13 | return ( 14 |
15 |
16 | {props.isSelfSign ? t("user") : t("signed-by")} 17 |
18 |
19 |
20 |
21 | 22 | {getFirstLetter(props.pdfDetails)} 23 | 24 |
25 |
26 | 27 | {props.isSelfSign 28 | ? (props.pdfDetails?.Signers && 29 | props.pdfDetails?.Signers[0]?.Name) || 30 | "User" 31 | : props.pdfDetails.ExtUserPtr.Name || "User"} 32 | 33 | 34 | {props.isSelfSign 35 | ? (props.pdfDetails?.Signers && 36 | props.pdfDetails?.Signers[0]?.Email) || 37 | "" 38 | : props.pdfDetails.ExtUserPtr.Email || ""} 39 | 40 |
41 |
42 |
43 |
44 | ); 45 | } 46 | 47 | export default Signedby; 48 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/pdf/SignerListComponent.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { darkenColor, getFirstLetter } from "../../constant/Utils"; 3 | 4 | function SignerListComponent(props) { 5 | const checkSignerBackColor = (obj) => { 6 | if (obj) { 7 | let data = ""; 8 | if (obj?.Id) { 9 | data = props.signerPos.filter((data) => data.Id === obj.Id); 10 | } else { 11 | data = props.signerPos.filter( 12 | (data) => data.signerObjId === obj.objectId 13 | ); 14 | } 15 | return data && data.length > 0 && data[0].blockColor; 16 | } 17 | }; 18 | const checkUserNameColor = (obj) => { 19 | const getBackColor = checkSignerBackColor(obj); 20 | if (getBackColor) { 21 | const color = darkenColor(getBackColor, 0.4); 22 | return color; 23 | } else { 24 | return "#abd1d0"; 25 | } 26 | }; 27 | 28 | return ( 29 |
33 |
37 | 38 | {getFirstLetter( 39 | props.obj?.Name || props.obj?.email || props.obj?.Role 40 | )} 41 | 42 |
43 |
44 | 45 | {props.obj?.Name || props?.obj?.Role} 46 | 47 | 48 | {props.obj?.Email || props.obj?.email} 49 | 50 |
51 |
52 | ); 53 | } 54 | 55 | export default SignerListComponent; 56 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/pdf/SignerListPlace.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import RecipientList from "./RecipientList"; 3 | import { Tooltip } from "react-tooltip"; 4 | import { useTranslation } from "react-i18next"; 5 | 6 | function SignerListPlace(props) { 7 | const { t } = useTranslation(); 8 | return ( 9 |
10 |
11 | 12 | {props.title ? props.title : "Recipients"} 13 | 14 | {props?.title === "Roles" && ( 15 | <> 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |

{t("role-help.p1")}

24 |

{t("role-help.p2")}

25 |

{t("role-help.p3")}

26 |

{t("role-help.p4")}

27 |

{t("role-help.p5")}

28 |

{t("role-help.p6")}

29 |
30 |
31 | 32 | )} 33 |
34 |
35 |
36 |
37 | 38 |
39 |
40 | {props.handleAddSigner ? ( 41 |
props.handleAddSigner()} 47 | > 48 | {t("add-role")} 49 |
50 | ) : ( 51 |
props.setIsAddSigner(true)} 57 | > 58 | {t("add-recipients")} 59 |
60 | )} 61 |
62 |
63 | ); 64 | } 65 | 66 | export default SignerListPlace; 67 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/pdf/VerifyEmail.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Loader from "../../primitives/Loader"; 3 | import { useTranslation } from "react-i18next"; 4 | 5 | function VerifyEmail(props) { 6 | const { t } = useTranslation(); 7 | return ( 8 | 9 |
10 |

11 | {t("otp-verification")} 12 |

13 | {props.isVerifyModal ? ( 14 |
{ 16 | props.setIsVerifyModal(false); 17 | props.handleVerifyEmail(e); 18 | }} 19 | > 20 |
21 | 22 | 24 | e.target.setCustomValidity(t("input-required")) 25 | } 26 | onInput={(e) => e.target.setCustomValidity("")} 27 | required 28 | type="tel" 29 | pattern="[0-9]{4}" 30 | className="w-full op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content text-xs" 31 | placeholder={t("otp-placeholder")} 32 | value={props.otp} 33 | onChange={(e) => props.setOtp(e.target.value)} 34 | /> 35 |
36 |
37 | 40 | 46 |
47 |
48 | ) : props.otpLoader ? ( 49 |
50 | 51 |
52 | ) : ( 53 |
54 |

{t("verify-email")}

55 |
56 | 63 |
64 |
65 | )} 66 |
67 |
68 | ); 69 | } 70 | 71 | export default VerifyEmail; 72 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/pdf/WidgetList.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { getWidgetType, isMobile } from "../../constant/Utils"; 3 | import { useTranslation } from "react-i18next"; 4 | 5 | function WidgetList(props) { 6 | const { t } = useTranslation(); 7 | return props.updateWidgets.map((item, ind) => { 8 | return ( 9 |
10 |
{ 14 | props.addPositionOfSignature && 15 | props.addPositionOfSignature("onclick", item); 16 | }} 17 | ref={(element) => !isMobile && item.ref(element)} 18 | onMouseMove={(e) => !isMobile && props?.handleDivClick(e)} 19 | onMouseDown={() => !isMobile && props?.handleMouseLeave()} 20 | onTouchStart={(e) => !isMobile && props?.handleDivClick(e)} 21 | > 22 | {item.ref && getWidgetType(item, t(`widgets-name.${item.type}`))} 23 |
24 |
25 | ); 26 | }); 27 | } 28 | 29 | export default WidgetList; 30 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/shared/fields/DateFormatSelector.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { formatDateTime } from "../../../constant/Utils"; 3 | import { useTranslation } from "react-i18next"; 4 | 5 | const DateFormatSelector = (props) => { 6 | const { t } = useTranslation(); 7 | const date = new Date(); 8 | const [selectedFormat, setSelectedFormat] = useState(props.dateFormat); 9 | const [is12Hour, setIs12Hour] = useState(props?.is12HourTime); 10 | 11 | const dateFormats = [ 12 | "MM/DD/YYYY", 13 | "MMMM DD, YYYY", 14 | "DD MMMM, YYYY", 15 | "DD-MM-YYYY", 16 | "DD MMM, YYYY", 17 | "YYYY-MM-DD", 18 | "MM-DD-YYYY", 19 | "MM.DD.YYYY", 20 | "MMM DD, YYYY" 21 | ]; 22 | 23 | // Handle format change 24 | const handleFormatChange = (event) => { 25 | setSelectedFormat(event.target.value); 26 | props.setDateFormat && props.setDateFormat(event.target.value); 27 | }; 28 | const handleHrInput = () => { 29 | setIs12Hour(!is12Hour); 30 | props.setIs12HourTime && props.setIs12HourTime(!is12Hour); 31 | }; 32 | return ( 33 |
34 | 37 | 48 |
49 |
50 | 57 |
12 hr
58 |
59 |
60 | 67 |
24 hr
68 |
69 |
70 |

71 | 72 | {formatDateTime(date, selectedFormat, props?.timezone, is12Hour)} 73 | 74 |

75 |
76 | ); 77 | }; 78 | 79 | export default DateFormatSelector; 80 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/shared/fields/TimezoneSelector.jsx: -------------------------------------------------------------------------------- 1 | import { t } from "i18next"; 2 | import React, { useState } from "react"; 3 | import TimezoneSelect from "react-timezone-select"; 4 | 5 | const TimezoneSelector = (props) => { 6 | const [selectedTimezone, setSelectedTimezone] = useState(props?.timezone); 7 | // Intl.DateTimeFormat().resolvedOptions().timeZone // Default to the user's local timezone 8 | 9 | const onChangeTimezone = (timezone) => { 10 | setSelectedTimezone(timezone?.value); 11 | props.setTimezone && props.setTimezone(timezone?.value); 12 | }; 13 | 14 | return ( 15 | <> 16 |
17 |

18 | {t("select-timezone")} 19 |

20 | onChangeTimezone(timezone)} 23 | unstyled 24 | classNames={{ 25 | control: () => 26 | "op-input op-input-bordered op-input-sm focus:outline-none hover:border-base-content w-full h-full text-[11px]", 27 | valueContainer: () => 28 | "flex flex-row gap-x-[2px] gap-y-[2px] md:gap-y-0 w-full my-[2px]", 29 | multiValue: () => "op-badge op-badge-primary h-full text-[11px]", 30 | multiValueLabel: () => "mb-[2px]", 31 | menu: () => 32 | "mt-1 shadow-md rounded-lg bg-base-200 text-base-content", 33 | menuList: () => "shadow-md rounded-lg overflow-hidden", 34 | option: () => 35 | "bg-base-200 text-base-content rounded-lg m-1 hover:bg-base-300 p-2", 36 | noOptionsMessage: () => "p-2 bg-base-200 rounded-lg m-1 p-2" 37 | }} 38 | /> 39 |
40 | 41 | ); 42 | }; 43 | 44 | export default TimezoneSelector; 45 | -------------------------------------------------------------------------------- /apps/OpenSign/src/components/sidebar/Menu.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useTranslation } from "react-i18next"; 3 | import { NavLink } from "react-router"; 4 | 5 | const Menu = ({ item, isOpen, closeSidebar }) => { 6 | const appName = 7 | "OpenSign™"; 8 | const drivename = appName === "OpenSign™" ? "OpenSign™" : ""; 9 | const { t } = useTranslation(); 10 | return ( 11 |
  • 12 | 19 | `${ 20 | isActive ? " bg-base-300 text-base-content" : "" 21 | } flex items-center justify-start text-left p-3 lg:p-4 text-base-content hover:text-base-content focus:bg-base-300 hover:bg-base-300 hover:no-underline focus:outline-none` 22 | } 23 | onClick={closeSidebar} 24 | tabIndex={isOpen ? 0 : -1} 25 | role="menuitem" 26 | > 27 | 28 | 29 | 30 | 31 | {t(`sidebar.${item.title}`, { appName: drivename })} 32 | 33 | 34 |
  • 35 | ); 36 | }; 37 | 38 | export default Menu; 39 | -------------------------------------------------------------------------------- /apps/OpenSign/src/constant/appinfo.js: -------------------------------------------------------------------------------- 1 | import logo from "../assets/images/logo.png"; 2 | import { getEnv } from "./Utils"; 3 | 4 | export function serverUrl_fn() { 5 | const env = getEnv(); 6 | const serverurl = env?.REACT_APP_SERVERURL 7 | ? env.REACT_APP_SERVERURL // env.REACT_APP_SERVERURL is used for prod 8 | : process.env.REACT_APP_SERVERURL; // process.env.REACT_APP_SERVERURL is used for dev (locally) 9 | let baseUrl = serverurl ? serverurl : window.location.origin + "/api/app"; 10 | return baseUrl; 11 | } 12 | export const appInfo = { 13 | applogo: logo, 14 | appId: process.env.REACT_APP_APPID ? process.env.REACT_APP_APPID : "opensign", 15 | baseUrl: serverUrl_fn(), 16 | defaultRole: "contracts_User", 17 | fev_Icon: 18 | "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAALlJREFUaEPtmN0NwjAMBpNxYDKYiM1Yp90g93CKStH1NbIdfz921Dker2Pc+Js1cDF7MXAxASMGYkAigBI6vh9ZwoXP53uZoAYcvhwdA3mAVbI2aSb+9zFKU4IURB6j/HoPUIEa2G3iGIAhQQDlAUIoE2diabIklISS0NoFPebo77RF6OenEF3QntOm128he0GKrwHyACFoz2MgBqSGkpAEcHs47oHtN5AFakACqMNjQEMoE8SABFCHn4HE2zGHSLeEAAAAAElFTkSuQmCC", 19 | googleClietId: process.env.REACT_APP_GOOGLECLIENTID 20 | ? `${process.env.REACT_APP_GOOGLECLIENTID}` 21 | : "", 22 | metaDescription: 23 | "The fastest way to sign PDFs & request signatures from others.", 24 | settings: [ 25 | { 26 | role: "contracts_Admin", 27 | menuId: "VPh91h0ZHk", 28 | pageType: "dashboard", 29 | pageId: "35KBoSgoAK", 30 | extended_class: "contracts_Users" 31 | }, 32 | { 33 | role: "contracts_OrgAdmin", 34 | menuId: "VPh91h0ZHk", 35 | pageType: "dashboard", 36 | pageId: "35KBoSgoAK", 37 | extended_class: "contracts_Users" 38 | }, 39 | { 40 | role: "contracts_Editor", 41 | menuId: "H9vRfEYKhT", 42 | pageType: "dashboard", 43 | pageId: "35KBoSgoAK", 44 | extended_class: "contracts_Users" 45 | }, 46 | { 47 | role: "contracts_User", 48 | menuId: "H9vRfEYKhT", 49 | pageType: "dashboard", 50 | pageId: "35KBoSgoAK", 51 | extended_class: "contracts_Users" 52 | } 53 | ] 54 | }; 55 | -------------------------------------------------------------------------------- /apps/OpenSign/src/constant/const.js: -------------------------------------------------------------------------------- 1 | export const contactCls = "contracts_Contactbook"; 2 | export const templateCls = "contracts_Template"; 3 | export const documentCls = "contracts_Document"; 4 | export const themeColor = "#47a3ad"; 5 | export const iconColor = "#686968"; 6 | export const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; 7 | export const maxFileSize = 10; // 10MB 8 | export const maxTitleLength = 250; // 250 characters 9 | export const maxNoteLength = 200; // 200 characters 10 | export const maxDescriptionLength = 500; // 500 characters 11 | -------------------------------------------------------------------------------- /apps/OpenSign/src/constant/getReplacedHashQuery.js: -------------------------------------------------------------------------------- 1 | import Parse from "parse"; 2 | /** 3 | * Function returning the queryString in which multiple hash replaced with actual values 4 | * @param str query string 5 | * @param json localstroage data in json 6 | */ 7 | 8 | export default function getReplacedHashQuery(str, json) { 9 | // eslint-disable-next-line 10 | let reg = /(\#.*?\#)/gi; 11 | const currentUser = Parse.User.current(); 12 | const multiHash = str.match(reg); 13 | 14 | let values = {}; 15 | let key = ""; 16 | multiHash.forEach((x) => { 17 | key = x; 18 | key = key.substring(1, key.length - 1); 19 | key = key.split("."); 20 | if (key.length > 1) { 21 | key = x.replace(reg, json[key[0]][key[1]]); 22 | } else if (json[key[0]]) { 23 | key = x.replace(reg, json[key[0]]); 24 | } else if (key[0] === "Date") { 25 | key = x.replace(reg, "Date"); 26 | } else if (key[0] === "today") { 27 | key = x.replace(reg, new Date().toISOString()); 28 | } else { 29 | key = x.replace(reg, currentUser.id); 30 | } 31 | values = { ...values, [x]: key }; 32 | }); 33 | const querySplit = str.split(reg); 34 | const replacedHashWithValuesArr = querySplit.map((hash) => 35 | values[hash] ? values[hash] : hash 36 | ); 37 | const HashFreeQuery = replacedHashWithValuesArr.join(""); 38 | // console.log("HashFreeQuery ", HashFreeQuery); 39 | return HashFreeQuery; 40 | } 41 | -------------------------------------------------------------------------------- /apps/OpenSign/src/constant/saveFileSize.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { serverUrl_fn } from "./appinfo"; 3 | const parseAppId = process.env.REACT_APP_APPID 4 | ? process.env.REACT_APP_APPID 5 | : "opensign"; 6 | const serverUrl = serverUrl_fn(); 7 | export const SaveFileSize = async (size, imageUrl, tenantId) => { 8 | //checking server url and save file's size 9 | const tenantPtr = { 10 | __type: "Pointer", 11 | className: "partners_Tenant", 12 | objectId: tenantId 13 | }; 14 | const _tenantPtr = JSON.stringify(tenantPtr); 15 | try { 16 | const res = await axios.get( 17 | `${serverUrl}/classes/partners_TenantCredits?where={"PartnersTenant":${_tenantPtr}}`, 18 | { 19 | headers: { 20 | "Content-Type": "application/json", 21 | "X-Parse-Application-Id": parseAppId 22 | } 23 | } 24 | ); 25 | const response = res.data.results; 26 | let data; 27 | // console.log("response", response); 28 | if (response && response.length > 0) { 29 | data = { 30 | usedStorage: response[0].usedStorage 31 | ? response[0].usedStorage + size 32 | : size 33 | }; 34 | await axios.put( 35 | `${serverUrl}/classes/partners_TenantCredits/${response[0].objectId}`, 36 | data, 37 | { 38 | headers: { 39 | "Content-Type": "application/json", 40 | "X-Parse-Application-Id": parseAppId 41 | } 42 | } 43 | ); 44 | } else { 45 | data = { usedStorage: size, PartnersTenant: tenantPtr }; 46 | await axios.post(`${serverUrl}/classes/partners_TenantCredits`, data, { 47 | headers: { 48 | "Content-Type": "application/json", 49 | "X-Parse-Application-Id": parseAppId 50 | } 51 | }); 52 | } 53 | } catch (err) { 54 | console.log("err in save usage", err); 55 | } 56 | saveDataFile(size, imageUrl, tenantPtr); 57 | }; 58 | 59 | //function for save fileUrl and file size in particular client db class partners_DataFiles 60 | const saveDataFile = async (size, imageUrl, tenantPtr) => { 61 | const data = { 62 | FileUrl: imageUrl, 63 | FileSize: size, 64 | TenantPtr: tenantPtr 65 | }; 66 | 67 | // console.log("data save",file, data) 68 | try { 69 | await axios.post(`${serverUrl}/classes/partners_DataFiles`, data, { 70 | headers: { 71 | "Content-Type": "application/json", 72 | "X-Parse-Application-Id": parseAppId 73 | } 74 | }); 75 | } catch (err) { 76 | console.log("err in save usage ", err); 77 | } 78 | }; 79 | -------------------------------------------------------------------------------- /apps/OpenSign/src/hook/useScript.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | 3 | /**`useScript` hook is generated scripte for google sign in button */ 4 | export const useScript = (url, onload) => { 5 | useEffect(() => { 6 | const script = document.createElement("script"); 7 | //add url parameter to the script src, for load and it will remove after load in return 8 | script.src = url; 9 | script.async = true; 10 | script.defer = true; 11 | script.onload = onload; 12 | document.head.appendChild(script); 13 | return () => { 14 | document.head.removeChild(script); 15 | }; 16 | }, [url, onload]); 17 | }; 18 | 19 | -------------------------------------------------------------------------------- /apps/OpenSign/src/hook/useWindowSize.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | 3 | export function useWindowSize() { 4 | const [size, setSize] = useState({ 5 | width: 0, 6 | height: 0 7 | }); 8 | 9 | const onResize = () => { 10 | setSize({ 11 | width: window.innerWidth, 12 | height: window.innerHeight 13 | }); 14 | }; 15 | 16 | useEffect(() => { 17 | onResize(); 18 | window.addEventListener("resize", onResize); 19 | return () => { 20 | window.removeEventListener("resize", onResize); 21 | }; 22 | }, []); 23 | 24 | return size; 25 | } 26 | -------------------------------------------------------------------------------- /apps/OpenSign/src/i18n.js: -------------------------------------------------------------------------------- 1 | import i18n from "i18next"; 2 | import { initReactI18next } from "react-i18next"; 3 | import Backend from "i18next-http-backend"; 4 | import LanguageDetector from "i18next-browser-languagedetector"; 5 | 6 | i18n 7 | .use(Backend) 8 | .use(LanguageDetector) // Use LanguageDetector directly without creating an instance 9 | .use(initReactI18next) 10 | .init({ 11 | backend: { 12 | loadPath: "/locales/{{lng}}/{{ns}}.json" 13 | }, 14 | fallbackLng: "en", // Fallback to English if no other language is detected 15 | detection: { 16 | // Specifies the default language to fall back to if the detected language is not available. 17 | order: ["localStorage", "navigator"], 18 | // Defines where the detected language should be cached. 19 | caches: ["localStorage"] 20 | }, 21 | ns: ["translation"], // default namespace 22 | defaultNS: "translation", // default namespace 23 | //Enables debug mode, which outputs detailed logs to the console about the translation process. 24 | debug: false, 25 | interpolation: { 26 | escapeValue: false // Not needed for react as it escapes by default 27 | }, 28 | whitelist: ["en", "es", "fr", "it", "de", "hi"] // List of allowed languages 29 | }); 30 | 31 | export default i18n; 32 | -------------------------------------------------------------------------------- /apps/OpenSign/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body { 6 | background-color: white; 7 | } 8 | 9 | /* Custom CSS to hide scrollbar */ 10 | .hide-scrollbar::-webkit-scrollbar { 11 | display: none; 12 | } 13 | 14 | .hide-scrollbar { 15 | -ms-overflow-style: none; 16 | /* IE and Edge */ 17 | scrollbar-width: none; 18 | /* Firefox */ 19 | } 20 | .react-datepicker-popper { 21 | z-index: 9999 !important; 22 | } 23 | 24 | @media screen and (max-width: 766px) { 25 | .reactour__close { 26 | width: 17px !important; 27 | padding-left: 3px !important; 28 | } 29 | } 30 | 31 | /* below css class replace bootstrap background color with daisy */ 32 | .op-bg-primary { 33 | @apply bg-primary; 34 | } 35 | 36 | .op-bg-secondary { 37 | @apply bg-secondary; 38 | } 39 | 40 | .op-bg-info { 41 | @apply bg-info; 42 | } 43 | 44 | .op-bg-success { 45 | @apply bg-success; 46 | } 47 | 48 | .op-bg-warning { 49 | @apply bg-warning; 50 | } 51 | 52 | /* below css class replace bootstrap text color with daisy */ 53 | .op-text-primary { 54 | @apply text-primary; 55 | } 56 | 57 | .op-text-secondary { 58 | @apply text-secondary; 59 | } 60 | 61 | .op-text-info { 62 | @apply text-info; 63 | } 64 | 65 | .op-text-accent { 66 | @apply text-accent; 67 | } 68 | 69 | .op-text-success { 70 | @apply text-success; 71 | } 72 | 73 | .op-text-warning { 74 | @apply text-warning; 75 | } 76 | 77 | .op-border-primary { 78 | @apply border-primary; 79 | } 80 | 81 | /* CSS for scrollbar customization */ 82 | * { 83 | scrollbar-width: thin; 84 | /* For Firefox */ 85 | scrollbar-color: gray; 86 | /* For Firefox */ 87 | } 88 | 89 | /* For webkit-based browsers like Chrome, Safari */ 90 | *::-webkit-scrollbar { 91 | width: 5px; 92 | } 93 | 94 | *::-webkit-scrollbar-track { 95 | background-color: transparent; 96 | } 97 | 98 | *::-webkit-scrollbar-thumb { 99 | background-color: gray; 100 | border-radius: 10px; 101 | } -------------------------------------------------------------------------------- /apps/OpenSign/src/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | import { Provider } from "react-redux"; 6 | import { store } from "./redux/store"; 7 | import { HTML5Backend } from "react-dnd-html5-backend"; 8 | import { TouchBackend } from "react-dnd-touch-backend"; 9 | import { 10 | DndProvider, 11 | TouchTransition, 12 | MouseTransition, 13 | Preview 14 | } from "react-dnd-multi-backend"; 15 | import DragElement from "./components/pdf/DragElement.jsx"; 16 | import Parse from "parse"; 17 | import "./polyfills"; 18 | import { serverUrl_fn } from "./constant/appinfo"; 19 | import "./i18n"; 20 | 21 | const appId = 22 | import.meta.env.VITE_APPID || process.env.REACT_APP_APPID || "opensign"; 23 | const serverUrl = serverUrl_fn(); 24 | Parse.initialize(appId); 25 | Parse.serverURL = serverUrl; 26 | 27 | const HTML5toTouch = { 28 | backends: [ 29 | { 30 | id: "html5", 31 | backend: HTML5Backend, 32 | transition: MouseTransition 33 | }, 34 | { 35 | id: "touch", 36 | backend: TouchBackend, 37 | options: { enableMouseEvents: true }, 38 | preview: true, 39 | transition: TouchTransition 40 | } 41 | ] 42 | }; 43 | const generatePreview = (props) => { 44 | const { item, style } = props; 45 | const newStyle = { 46 | ...style 47 | }; 48 | 49 | return ( 50 |
    51 | 52 |
    53 | ); 54 | }; 55 | 56 | 57 | const root = ReactDOM.createRoot(document.getElementById("root")); 58 | root.render( 59 | 60 | 61 | {generatePreview} 62 | 63 | 64 | 65 | ); 66 | -------------------------------------------------------------------------------- /apps/OpenSign/src/json/FormJson.js: -------------------------------------------------------------------------------- 1 | import { documentCls, templateCls } from "../constant/const"; 2 | export const formJson = { 3 | //json form for signYourself 4 | sHAnZphf69: { 5 | title: "Sign Yourself", 6 | redirectRoute: "signaturePdf", 7 | msgVar: "Document", 8 | Cls: documentCls 9 | }, 10 | 11 | //json form for request signatures 12 | "8mZzFxbG1z": { 13 | title: "Request Signatures", 14 | msgVar: "Document", 15 | redirectRoute: "placeHolderSign", 16 | Cls: documentCls, 17 | signers: true, 18 | bcc: true 19 | }, 20 | //json form for template 21 | template: { 22 | title: "New Template", 23 | redirectRoute: "template", 24 | msgVar: "Template", 25 | Cls: templateCls, 26 | bcc: true 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /apps/OpenSign/src/pages/Dashboard.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import GetDashboard from "../components/dashboard/GetDashboard"; 3 | import { useNavigate, useParams } from "react-router"; 4 | import Title from "../components/Title"; 5 | import { useDispatch } from "react-redux"; 6 | import { saveTourSteps } from "../redux/reducers/TourStepsReducer"; 7 | import dashboardJson from "../json/dashboardJson"; 8 | import Loader from "../primitives/Loader"; 9 | import { useTranslation } from "react-i18next"; 10 | 11 | const Dashboard = () => { 12 | const { t } = useTranslation(); 13 | const navigate = useNavigate(); 14 | const dispatch = useDispatch(); 15 | const { id } = useParams(); 16 | const [dashboard, setdashboard] = useState({}); 17 | const [loading, setloading] = useState(true); 18 | 19 | useEffect(() => { 20 | if (localStorage.getItem("accesstoken")) { 21 | if (id !== undefined) { 22 | getDashboard(id); 23 | } else { 24 | getDashboard(localStorage.getItem("PageLanding")); 25 | } 26 | } else { 27 | navigate("/", { replace: true, state: { from: "" } }); 28 | } 29 | // eslint-disable-next-line 30 | }, [id]); 31 | 32 | const getDashboard = async (id) => { 33 | try { 34 | const dashboard = dashboardJson.find((x) => x.id === id); 35 | setdashboard(dashboard); 36 | const dashboardTour = dashboard.columns 37 | .filter((col) => { 38 | if (col.widget.data && col.widget.data.tourSection) { 39 | return col; 40 | } 41 | }) 42 | .map((col) => { 43 | return { 44 | selector: `[data-tut=${col.widget.data.tourSection}]`, 45 | content: t(`tour-mssg.${col.widget.label}`), 46 | position: "top" 47 | // style: { backgroundColor: "#abd4d2" }, 48 | }; 49 | }); 50 | dispatch(saveTourSteps(dashboardTour)); 51 | setloading(false); 52 | } catch (e) { 53 | console.error("Problem", e); 54 | setloading(false); 55 | } 56 | }; 57 | 58 | return ( 59 | 60 | 61 | {loading ? ( 62 | <div className="h-[300px] w-full bg-white flex justify-center items-center rounded-md"> 63 | <Loader /> 64 | </div> 65 | ) : ( 66 | <GetDashboard dashboard={dashboard} /> 67 | )} 68 | </React.Fragment> 69 | ); 70 | }; 71 | 72 | export default Dashboard; 73 | -------------------------------------------------------------------------------- /apps/OpenSign/src/pages/PageNotFound.jsx: -------------------------------------------------------------------------------- 1 | // NotFound.js 2 | import React from "react"; 3 | import Title from "../components/Title"; 4 | 5 | const PageNotFound = ({ prefix }) => { 6 | return ( 7 | <div className="flex items-center justify-center h-screen w-full bg-base-100 text-base-content rounded-box"> 8 | <Title title={"Page Not Found"} /> 9 | <div className="text-center"> 10 | <h1 className="text-[60px] lg:text-[120px] font-semibold">404</h1> 11 | <p className="text-[30px] lg:text-[50px]"> 12 | {prefix ? prefix : "Page"} Not Found 13 | </p> 14 | </div> 15 | </div> 16 | ); 17 | }; 18 | 19 | export default PageNotFound; 20 | -------------------------------------------------------------------------------- /apps/OpenSign/src/polyfills.js: -------------------------------------------------------------------------------- 1 | // below code is used to handle undefined Promise.withResolvers for safari 2 | if (typeof Promise.withResolvers === "undefined") { 3 | Promise.withResolvers = function () { 4 | let resolve, reject; 5 | const promise = new Promise((res, rej) => { 6 | resolve = res; 7 | reject = rej; 8 | }); 9 | return { promise, resolve, reject }; 10 | }; 11 | } 12 | 13 | // Usage: 14 | // const { promise, resolve, reject } = Promise.withResolvers() 15 | // console.log(promise, resolve, reject) // Promise { <pending> } [Function (anonymous)] [Function (anonymous)] 16 | // ... Do something async and then call resolve or reject! -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/Alert.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Alert = ({ children, type, className }) => { 4 | const textcolor = type ? theme(type) : theme(); 5 | function theme(color) { 6 | switch (color) { 7 | case "success": 8 | return "op-alert-success"; 9 | case "info": 10 | return "op-alert-info"; 11 | case "danger": 12 | return "op-alert-error"; 13 | case "warning": 14 | return "op-alert-warning text-black"; 15 | default: 16 | return ""; 17 | } 18 | } 19 | return ( 20 | children && ( 21 | <div 22 | className={`${ 23 | className 24 | ? className 25 | : "z-[1000] fixed top-20 left-1/2 transform -translate-x-1/2 text-sm" 26 | } `} 27 | > 28 | <div className={`op-alert ${textcolor} flex justify-center`}> 29 | <span className="px-1">{children}</span> 30 | </div> 31 | </div> 32 | ) 33 | ); 34 | }; 35 | 36 | export default Alert; 37 | -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/CheckCircle.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const CheckCircle = ({ size = 56, color = "text-green-500" }) => { 4 | return ( 5 | <div className={`flex items-center justify-center ${color}`}> 6 | <svg 7 | xmlns="http://www.w3.org/2000/svg" 8 | className={`w-${size / 4} h-${size / 4}`} 9 | fill="none" 10 | viewBox="0 0 24 24" 11 | stroke="currentColor" 12 | strokeWidth={2} 13 | > 14 | <circle 15 | cx="12" 16 | cy="12" 17 | r="10" 18 | stroke="currentColor" 19 | strokeWidth="2" 20 | fill="none" 21 | /> 22 | <path 23 | stroke="currentColor" 24 | strokeLinecap="round" 25 | strokeLinejoin="round" 26 | strokeWidth="2" 27 | d="M9 12l2 2l4-4" 28 | /> 29 | </svg> 30 | </div> 31 | ); 32 | }; 33 | 34 | export default CheckCircle; 35 | -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/DotLottieReact.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { DotLottieReact } from "@lottiefiles/dotlottie-react"; 3 | import Loader from "./Loader"; 4 | import { useTranslation } from "react-i18next"; 5 | 6 | const LottieWithLoader = () => { 7 | const { t } = useTranslation(); 8 | const [isLoaded, setIsLoaded] = useState(false); 9 | const [hasError, setHasError] = useState(false); 10 | const [animationSrc, setAnimationSrc] = useState(null); 11 | const src = 12 | "https://lottie.host/00a72a09-f2d4-493a-9b2d-2843bf067638/Ic7jJ44wLJ.json"; 13 | useEffect(() => { 14 | fetch(src) 15 | .then((response) => { 16 | if (!response.ok) { 17 | throw new Error("Network response was not ok"); 18 | } 19 | return response.blob(); 20 | }) 21 | .then((blob) => { 22 | const objectURL = URL.createObjectURL(blob); 23 | setAnimationSrc(objectURL); 24 | setIsLoaded(true); 25 | }) 26 | .catch((error) => { 27 | console.error("faild to load animation of send request:", error); 28 | setHasError(true); 29 | }); 30 | }, [src]); 31 | 32 | return ( 33 | <div> 34 | {!isLoaded && !hasError && ( 35 | <div className="w-[120px] h-[120px] mx-auto"> 36 | <Loader /> 37 | </div> 38 | )} 39 | {hasError && <div className="error">{t("faild-animation")}</div>} 40 | {isLoaded && animationSrc && ( 41 | <DotLottieReact 42 | src={animationSrc} 43 | autoplay 44 | className="w-[120px] h-[120px] md:w-[200px] md:h-[200px] mx-auto" 45 | /> 46 | )} 47 | </div> 48 | ); 49 | }; 50 | 51 | export default LottieWithLoader; 52 | -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/HandleError.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function HandleError({ handleError }) { 4 | return ( 5 | <div className="flex justify-center items-center h-[100vh]"> 6 | <span className="p-[15px] md:p-0 text-[20px] text-[gray]"> 7 | {handleError} 8 | </span> 9 | </div> 10 | ); 11 | } 12 | 13 | export default HandleError; 14 | -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/LazyPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from "react"; 2 | import Loader from "./Loader"; 3 | 4 | const LazyPage = ({ Page }) => { 5 | return ( 6 | <Suspense 7 | fallback={ 8 | <div className="flex justify-center items-center h-[100vh]"> 9 | <Loader /> 10 | </div> 11 | } 12 | > 13 | <Page /> 14 | </Suspense> 15 | ); 16 | }; 17 | 18 | export default LazyPage; 19 | -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/LinkUserModal.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import SelectSigners from "../components/shared/fields/SelectSigners"; 3 | import AddContact from "./AddContact"; 4 | import ModalUi from "./ModalUi"; 5 | import { useTranslation } from "react-i18next"; 6 | 7 | const LinkUserModal = (props) => { 8 | const { t } = useTranslation(); 9 | const [isContact, setIsContact] = useState(false); 10 | const [isExistSigner, setIsExistSIgner] = useState(false); 11 | 12 | useEffect(() => { 13 | if (props.uniqueId) { 14 | const isExistSigner = props.signerPos.find( 15 | (x) => x.Id === props.uniqueId && x.signerObjId 16 | ); 17 | setIsExistSIgner(isExistSigner); 18 | } 19 | // eslint-disable-next-line react-hooks/exhaustive-deps 20 | }, [props.signerPos]); 21 | return ( 22 | <ModalUi 23 | title={isExistSigner ? t("change-signer") : t("add/choose-signer")} 24 | isOpen={true} 25 | handleClose={props.closePopup} 26 | > 27 | <SelectSigners 28 | {...props} 29 | closePopup={props.closePopup} 30 | isContact={isContact} 31 | setIsContact={setIsContact} 32 | isExistSigner={isExistSigner} 33 | /> 34 | {isContact && ( 35 | <> 36 | <div className="op-divider text-base-content mx-[25%] my-1"> 37 | {t("or")} 38 | </div> 39 | <AddContact 40 | details={props.handleAddUser} 41 | closePopup={props.closePopup} 42 | /> 43 | </> 44 | )} 45 | </ModalUi> 46 | ); 47 | }; 48 | 49 | export default LinkUserModal; 50 | -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/Loader.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Loader = () => { 4 | return ( 5 | <div className="op-loading op-loading-infinity w-[4rem] text-neutral"></div> 6 | ); 7 | }; 8 | 9 | export default Loader; 10 | -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/LoaderWithMsg.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Loader from "./Loader"; 3 | 4 | function LoaderWithMsg({ isLoading }) { 5 | return ( 6 | <div className="flex flex-col justify-center items-center h-[100vh]"> 7 | <Loader /> 8 | <span className="text-[13px] text-base-cotent">{isLoading.message}</span> 9 | </div> 10 | ); 11 | } 12 | 13 | export default LoaderWithMsg; 14 | -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/Modal.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | const Modal = ({ children, Title }) => { 4 | const [isOpen, SetIsOpen] = useState(false); 5 | return ( 6 | <> 7 | {children && ( 8 | <div 9 | className={`fixed top-20 left-1/2 transform -translate-x-1/2 border-[1px] text-sm bg-white rounded `} 10 | > 11 | <div className="flex justify-between items-center py-[.75rem] px-[1.25rem] "> 12 | <div className="font-semibold text-xl text-black">{Title}</div> 13 | <div 14 | onClick={() => SetIsOpen()} 15 | className="px-2 py-1 bg-gray-400 rounded cursor-pointer" 16 | > 17 | <i className="fa-light fa-xmark"></i> 18 | </div> 19 | </div> 20 | <hr /> 21 | {isOpen && <div>{children}</div>} 22 | </div> 23 | )} 24 | </> 25 | ); 26 | }; 27 | 28 | export default Modal; 29 | -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/ModalUi.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "../styles/signature.css"; 3 | 4 | const ModalUi = ({ 5 | children, 6 | title, 7 | isOpen, 8 | handleClose, 9 | showHeader = true, 10 | showClose = true, 11 | reduceWidth, 12 | position 13 | }) => { 14 | const width = reduceWidth; 15 | const isBottom = position === "bottom" ? "items-end pb-2" : ""; 16 | return ( 17 | <> 18 | {isOpen && ( 19 | <dialog 20 | id="selectSignerModal" 21 | className={`${isBottom} op-modal op-modal-open`} 22 | > 23 | <div 24 | className={`${ 25 | width || "md:min-w-[500px]" 26 | } op-modal-box p-0 max-h-90 overflow-y-auto hide-scrollbar text-sm`} 27 | > 28 | {showHeader && ( 29 | <> 30 | {title && ( 31 | <h3 className="text-base-content font-bold text-lg pt-[15px] px-[20px]"> 32 | {title} 33 | </h3> 34 | )} 35 | {showClose && ( 36 | <button 37 | className="op-btn op-btn-sm op-btn-circle op-btn-ghost text-base-content absolute right-2 top-2" 38 | onClick={() => handleClose && handleClose()} 39 | > 40 | ✕ 41 | </button> 42 | )} 43 | </> 44 | )} 45 | <div>{children}</div> 46 | </div> 47 | </dialog> 48 | )} 49 | </> 50 | ); 51 | }; 52 | 53 | export default ModalUi; 54 | -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/Tooltip.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Tooltip as ReactTooltip } from "react-tooltip"; 3 | import { openInNewTab } from "../constant/Utils"; 4 | const Tooltip = ({ 5 | id, 6 | message, 7 | url, 8 | iconColor, 9 | maxWidth, 10 | handleOnlickHelp 11 | }) => 12 | url || handleOnlickHelp ? ( 13 | <button 14 | onClick={() => 15 | handleOnlickHelp ? handleOnlickHelp() : openInNewTab(url) 16 | } 17 | className={"text-center cursor-pointer"} 18 | > 19 | <sup> 20 | <i 21 | className="fa-light fa-question rounded-full border-[1px] py-[1.5px] px-[4px] text-[13px]" 22 | style={{ 23 | borderColor: iconColor ? iconColor : "#33bbff", 24 | color: iconColor ? iconColor : "#33bbff" 25 | }} 26 | ></i> 27 | </sup> 28 | </button> 29 | ) : ( 30 | <> 31 | <a 32 | data-tooltip-id={id ? id : "my-tooltip"} 33 | data-tooltip-content={message} 34 | className="z-50" 35 | > 36 | <sup> 37 | <i 38 | className="fa-light fa-question rounded-full border-[1px] py-[1.5px] px-[4px] text-[13px]" 39 | style={{ 40 | borderColor: iconColor ? iconColor : "#33bbff", 41 | color: iconColor ? iconColor : "#33bbff" 42 | }} 43 | ></i> 44 | </sup> 45 | </a> 46 | <ReactTooltip 47 | id={id ? id : "my-tooltip"} 48 | className={`${maxWidth ? maxWidth : "max-w-[200px]"} z-[200]`} 49 | /> 50 | </> 51 | ); 52 | 53 | export default Tooltip; 54 | -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/Tour.jsx: -------------------------------------------------------------------------------- 1 | import ReactTour from "reactour"; 2 | 3 | export default function Tour({ 4 | steps, 5 | isOpen, 6 | rounded, 7 | className, 8 | showNumber, 9 | closeWithMask, 10 | onRequestClose, 11 | showNavigation, 12 | showCloseButton, 13 | showNavigationNumber, 14 | disableKeyboardNavigation 15 | }) { 16 | const radius = rounded || 5; 17 | return ( 18 | <ReactTour 19 | className={className} 20 | steps={steps} 21 | isOpen={isOpen} 22 | rounded={radius} 23 | scrollOffset={-100} 24 | closeWithMask={closeWithMask} 25 | disableKeyboardNavigation={disableKeyboardNavigation} 26 | showCloseButton={showCloseButton} 27 | onRequestClose={onRequestClose} 28 | showNumber={showNumber} 29 | showNavigation={showNavigation} 30 | showNavigationNumber={showNavigationNumber} 31 | /> 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/TourContentWithBtn.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { useTranslation } from "react-i18next"; 3 | export default function TourContentWithBtn({ message, isChecked, video }) { 4 | const { t } = useTranslation(); 5 | const [isCheck, setIsCheck] = useState(false); 6 | 7 | const handleCheck = () => { 8 | setIsCheck(!isCheck); 9 | if (isChecked) { 10 | isChecked(!isCheck); 11 | } 12 | }; 13 | return ( 14 | <div> 15 | <p className="p-1 md:p-0">{message}</p> 16 | {video && ( 17 | <div className="flex items-center justify-center w-full md:h-[300px] my-[10px] border-[1.3px] border-gray-400 rounded-sm"> 18 | <iframe 19 | className="w-full h-full" 20 | src={video} 21 | title="YouTube video player" 22 | allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" 23 | allowFullScreen 24 | ></iframe> 25 | </div> 26 | )} 27 | <label className="flex items-center justify-center mt-3 mb-0"> 28 | <input 29 | type="checkbox" 30 | className="op-checkbox op-checkbox-xs mr-1" 31 | checked={isCheck} 32 | onChange={handleCheck} 33 | /> 34 | <span className="#787878 text-[12px]">{t("tour-content")}</span> 35 | </label> 36 | </div> 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/Validate.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import Parse from "parse"; 3 | import { Outlet, useNavigate, useLocation } from "react-router"; 4 | import ModalUi from "./ModalUi"; 5 | import { useTranslation } from "react-i18next"; 6 | const Validate = () => { 7 | const navigate = useNavigate(); 8 | const { t } = useTranslation(); 9 | const location = useLocation(); 10 | const [isUserValid, setIsUserValid] = useState(true); 11 | useEffect(() => { 12 | (async () => { 13 | if (localStorage.getItem("accesstoken")) { 14 | try { 15 | const userDetails = JSON.parse( 16 | localStorage.getItem( 17 | `Parse/${localStorage.getItem("parseAppId")}/currentUser` 18 | ) 19 | ); 20 | // Use the session token to validate the user 21 | const userQuery = new Parse.Query(Parse.User); 22 | const user = await userQuery.get(userDetails?.objectId, { 23 | sessionToken: localStorage.getItem("accesstoken") 24 | }); 25 | if (user) { 26 | setIsUserValid(true); 27 | } else { 28 | setIsUserValid(false); 29 | } 30 | } catch (error) { 31 | // Session token is invalid or there was an error 32 | setIsUserValid(false); 33 | } 34 | } 35 | })(); 36 | 37 | // eslint-disable-next-line react-hooks/exhaustive-deps 38 | }, []); 39 | 40 | const handleLoginBtn = async () => { 41 | try { 42 | await Parse.User.logOut(); 43 | } catch (err) { 44 | console.log("err ", err); 45 | } finally { 46 | localStorage.removeItem("accesstoken"); 47 | navigate("/", { replace: true, state: { from: location } }); 48 | } 49 | }; 50 | return isUserValid ? ( 51 | <div> 52 | <Outlet /> 53 | </div> 54 | ) : ( 55 | <ModalUi showHeader={false} isOpen={true} showClose={false}> 56 | <div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5"> 57 | <p className="text-xl font-medium">{t("session-expired")}</p> 58 | <button onClick={handleLoginBtn} className="op-btn op-btn-neutral"> 59 | {t("login")} 60 | </button> 61 | </div> 62 | </ModalUi> 63 | ); 64 | }; 65 | 66 | export default Validate; 67 | -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/ValidateRoute.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import Parse from "parse"; 3 | import { Outlet } from "react-router"; 4 | import { saveLanguageInLocal } from "../constant/Utils"; 5 | import { useTranslation } from "react-i18next"; 6 | const ValidateRoute = () => { 7 | const { i18n } = useTranslation(); 8 | useEffect(() => { 9 | (async () => { 10 | if (localStorage.getItem("accesstoken")) { 11 | try { 12 | // Use the session token to validate the user 13 | const userQuery = new Parse.Query(Parse.User); 14 | const user = await userQuery.get(Parse?.User?.current()?.id, { 15 | sessionToken: localStorage.getItem("accesstoken") 16 | }); 17 | if (!user) { 18 | handlelogout(); 19 | } 20 | } catch (error) { 21 | console.log("err in validate route", error); 22 | handlelogout(); 23 | } 24 | } 25 | })(); 26 | // eslint-disable-next-line react-hooks/exhaustive-deps 27 | }, []); 28 | const handlelogout = async () => { 29 | let appdata = localStorage.getItem("userSettings"); 30 | let applogo = localStorage.getItem("appLogo"); 31 | let defaultmenuid = localStorage.getItem("defaultmenuid"); 32 | let PageLanding = localStorage.getItem("PageLanding"); 33 | let baseUrl = localStorage.getItem("baseUrl"); 34 | let appid = localStorage.getItem("parseAppId"); 35 | 36 | localStorage.clear(); 37 | saveLanguageInLocal(i18n); 38 | 39 | localStorage.setItem("appLogo", applogo); 40 | localStorage.setItem("defaultmenuid", defaultmenuid); 41 | localStorage.setItem("PageLanding", PageLanding); 42 | localStorage.setItem("userSettings", appdata); 43 | localStorage.setItem("baseUrl", baseUrl); 44 | localStorage.setItem("parseAppId", appid); 45 | }; 46 | return <div>{<Outlet />}</div>; 47 | }; 48 | 49 | export default ValidateRoute; 50 | -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/ValidateSession.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useTranslation } from "react-i18next"; 3 | import ModalUi from "./ModalUi"; 4 | import { useNavigate } from "react-router"; 5 | import Parse from "parse"; 6 | 7 | const ValidateSession = ({ children }) => { 8 | const { t } = useTranslation(); 9 | const navigate = useNavigate(); 10 | 11 | const handleLoginBtn = async () => { 12 | try { 13 | await Parse?.User?.logOut(); 14 | } catch (err) { 15 | console.log("err ", err); 16 | } finally { 17 | localStorage.removeItem("accesstoken"); 18 | navigate("/", { replace: true }); 19 | } 20 | }; 21 | return localStorage.getItem("accesstoken") ? ( 22 | children 23 | ) : ( 24 | <ModalUi showHeader={false} isOpen={true} showClose={false}> 25 | <div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5"> 26 | <p className="text-xl font-medium">Your session has expired.</p> 27 | <button onClick={handleLoginBtn} className="op-btn op-btn-neutral"> 28 | {t("login")} 29 | </button> 30 | </div> 31 | </ModalUi> 32 | ); 33 | }; 34 | 35 | export default ValidateSession; 36 | -------------------------------------------------------------------------------- /apps/OpenSign/src/primitives/sanitizeFileName.js: -------------------------------------------------------------------------------- 1 | function sanitizeFileName(fileName) { 2 | // Remove spaces and invalid characters 3 | const file = fileName?.replace(/[^a-zA-Z0-9._-]/g, ""); 4 | const removedot = file?.replace(/\.(?=.*\.)/g, ""); 5 | return removedot?.replace(/[^a-zA-Z0-9._-]/g, ""); 6 | } 7 | export default sanitizeFileName; 8 | -------------------------------------------------------------------------------- /apps/OpenSign/src/redux/reducers/ShowTenant.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | 3 | const showTenantSlice = createSlice({ 4 | name: "showTenant", 5 | initialState: "", 6 | reducers: { 7 | showTenant: (state, action) => { 8 | return action.payload; 9 | }, 10 | }, 11 | }); 12 | 13 | export const { showTenant } = showTenantSlice.actions; 14 | export default showTenantSlice.reducer; -------------------------------------------------------------------------------- /apps/OpenSign/src/redux/reducers/TourStepsReducer.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | 3 | const tourStepSlice = createSlice({ 4 | name: "tourStep", 5 | initialState: [], 6 | reducers: { 7 | saveTourSteps: (state, action) => { 8 | return action.payload; 9 | }, 10 | removeTourSteps: () => { 11 | return []; 12 | } 13 | } 14 | }); 15 | 16 | export const { saveTourSteps, removeTourSteps } = tourStepSlice.actions; 17 | export default tourStepSlice.reducer; 18 | -------------------------------------------------------------------------------- /apps/OpenSign/src/redux/reducers/infoReducer.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | import { appInfo } from "../../constant/appinfo"; 3 | 4 | const infoSlice = createSlice({ 5 | name: "info", 6 | initialState: {}, 7 | reducers: { 8 | fetchAppInfo: () => { 9 | localStorage.setItem("baseUrl", `${appInfo.baseUrl}/`); 10 | localStorage.setItem("parseAppId", appInfo.appId); 11 | localStorage.setItem("appLogo", appInfo.applogo); 12 | localStorage.removeItem("userSettings"); 13 | localStorage.setItem("userSettings", JSON.stringify(appInfo.settings)); 14 | localStorage.setItem("fev_Icon", appInfo.fev_Icon); 15 | return appInfo; 16 | } 17 | } 18 | }); 19 | export const { fetchAppInfo } = infoSlice.actions; 20 | export default infoSlice.reducer; 21 | -------------------------------------------------------------------------------- /apps/OpenSign/src/redux/reducers/showHeader.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | 3 | const showHeaderSlice = createSlice({ 4 | name: "showTenant", 5 | initialState: "", 6 | reducers: { 7 | showHeader: (state, action) => { 8 | return action.payload; 9 | } 10 | } 11 | }); 12 | 13 | export const { showHeader } = showHeaderSlice.actions; 14 | export default showHeaderSlice.reducer; 15 | -------------------------------------------------------------------------------- /apps/OpenSign/src/redux/reducers/widgetSlice.js: -------------------------------------------------------------------------------- 1 | // reducers/widgetSlice.js 2 | import { createSlice } from "@reduxjs/toolkit"; 3 | 4 | const initialState = { 5 | isShowModal: false, 6 | saveSignCheckbox: { 7 | isVisible: false, 8 | signId: "" 9 | }, 10 | signatureTypes: null, 11 | defaultSignImg: null, 12 | myInitial: null, 13 | lastIndex: "" 14 | }; 15 | 16 | const widgetSlice = createSlice({ 17 | name: "widget", 18 | initialState, 19 | reducers: { 20 | setIsShowModal: (state, action) => { 21 | state.isShowModal = action.payload; 22 | }, 23 | setSaveSignCheckbox: (state, action) => { 24 | state.saveSignCheckbox = action.payload; 25 | }, 26 | setSignatureTypes: (state, action) => { 27 | state.signatureTypes = action.payload; 28 | }, 29 | setDefaultSignImg: (state, action) => { 30 | state.defaultSignImg = action.payload; 31 | }, 32 | setMyInitial: (state, action) => { 33 | state.myInitial = action.payload; 34 | }, 35 | setLastIndex: (state, action) => { 36 | state.lastIndex = action.payload; 37 | }, 38 | resetWidgetState: () => initialState 39 | } 40 | }); 41 | 42 | export const { 43 | setIsShowModal, 44 | setSaveSignCheckbox, 45 | setSignatureTypes, 46 | setMyInitial, 47 | resetWidgetState, 48 | setDefaultSignImg, 49 | setLastIndex 50 | } = widgetSlice.actions; 51 | 52 | export default widgetSlice.reducer; 53 | -------------------------------------------------------------------------------- /apps/OpenSign/src/redux/store.js: -------------------------------------------------------------------------------- 1 | import { configureStore } from "@reduxjs/toolkit"; 2 | import infoReducer from "./reducers/infoReducer"; 3 | import ShowTenant from "./reducers/ShowTenant"; 4 | import TourStepsReducer from "./reducers/TourStepsReducer"; 5 | import showHeader from "./reducers/showHeader"; 6 | import widgetReducer from "./reducers/widgetSlice"; 7 | 8 | export const store = configureStore({ 9 | reducer: { 10 | appInfo: infoReducer, 11 | TourSteps: TourStepsReducer, 12 | ShowTenant, 13 | showHeader, 14 | widget: widgetReducer 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /apps/OpenSign/src/styles/AddUser.css: -------------------------------------------------------------------------------- 1 | .validationlist { 2 | position: absolute; 3 | left: 0; 4 | z-index: 1; 5 | appearance: none; 6 | -moz-appearance: "none"; 7 | -webkit-appearance: "none"; 8 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAYAAADE6YVjAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAnUlEQVR4nO2QMQrCQBBFX51NbWMp2uox7Gw8h22uYEguIZNKyB0lMIElTNzdGBvdB8PC8Gces5DJ/Cxn4Aa4QM5pbsgncQF6rQYoZ3IFcPey11jByRsaqzYuctqfZo8xki3QBURuRtDpfBQ74GEsaYHN5It8wYFEhgExlj2NniwRhC7qP70gRSRrCN6JZE2BJZJvCEb2QKVvJvMvvAD2WzaK/35kGAAAAABJRU5ErkJggg=="); 9 | background-repeat: no-repeat; 10 | background-position: right 0.7rem top 50%; 11 | background-size: 1rem auto; 12 | } 13 | 14 | @media (max-width: 375px) { 15 | .validationlist { 16 | background-position: right 0.5rem top 50%; 17 | } 18 | } 19 | 20 | @media (min-width:375px) and (max-width: 767px) { 21 | .validationlist { 22 | background-position: right 1rem top 50%; 23 | } 24 | } 25 | 26 | .warning { 27 | position: absolute; 28 | padding: 5px; 29 | border: 1px solid black; 30 | background: white; 31 | width: 250px; 32 | border-radius: 5px; 33 | animation: inAnimation 1s ease-in-out; 34 | z-index: 1; 35 | } 36 | 37 | .defaultvalueWarning { 38 | position: absolute; 39 | left: 20px; 40 | } 41 | 42 | .defaultvalueWarning::before { 43 | content: ""; 44 | width: 0px; 45 | height: 0px; 46 | border-style: outset; 47 | border-width: 0px 10px 10px 10px; 48 | border-color: transparent transparent black transparent; 49 | display: inline-block; 50 | position: absolute; 51 | top: -29%; 52 | left: 0; 53 | right: 75%; 54 | margin: 0 auto; 55 | } 56 | 57 | @keyframes inAnimation { 58 | 0% { 59 | opacity: 0; 60 | visibility: hidden; 61 | } 62 | 63 | 25% { 64 | opacity: 1; 65 | visibility: visible; 66 | } 67 | 68 | 50% { 69 | opacity: 1; 70 | visibility: visible; 71 | } 72 | 73 | 75% { 74 | opacity: 1; 75 | visibility: visible; 76 | } 77 | 78 | 100% { 79 | opacity: 1; 80 | visibility: visible; 81 | } 82 | } -------------------------------------------------------------------------------- /apps/OpenSign/src/styles/managesign.css: -------------------------------------------------------------------------------- 1 | .customwarning { 2 | position: absolute; 3 | padding: 8px; 4 | border: 1px solid black; 5 | background: white; 6 | border-radius: 5px; 7 | z-index: 1; 8 | } 9 | 10 | .signWarning { 11 | bottom: 70px; 12 | left: 50px; 13 | } 14 | 15 | .signWarning::before { 16 | content: ""; 17 | width: 0px; 18 | height: 0px; 19 | border-style: outset; 20 | border-width: 0px 10px 10px 10px; 21 | border-color: transparent transparent black transparent; 22 | display: inline-block; 23 | position: absolute; 24 | top: -29%; 25 | left: 0; 26 | right: 75%; 27 | margin: 0 auto; 28 | } 29 | 30 | @keyframes inAnimation { 31 | 0% { 32 | opacity: 0; 33 | visibility: hidden; 34 | } 35 | 36 | 100% { 37 | opacity: 1; 38 | visibility: visible; 39 | } 40 | } 41 | 42 | .signature { 43 | color: white; 44 | cursor: pointer; 45 | padding: 10px 14px 10px; 46 | background: #15b4e9; 47 | font-size: 12px; 48 | font-weight: 600; 49 | border-radius: 2px; 50 | box-shadow: 51 | 0 1px 3px rgba(0, 0, 0, 0.1), 52 | 0 1px 2px rgba(0, 0, 0, 0.18); 53 | } 54 | 55 | .signature:hover { 56 | box-shadow: 57 | 0 2px 4px rgba(0, 0, 0, 0.1), 58 | 0 2px 4px rgba(0, 0, 0, 0.18); 59 | background: #13a8da; 60 | color: white; 61 | } 62 | -------------------------------------------------------------------------------- /apps/OpenSign/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./src/**/*.{js,jsx,ts,tsx}"], 4 | theme: { 5 | extend: {} 6 | }, 7 | plugins: [ 8 | require("daisyui"), 9 | function ({ addUtilities }) { 10 | addUtilities({ 11 | // Prevent iOS long-press popup 12 | ".touch-callout-none": { 13 | "-webkit-touch-callout": "none" 14 | } 15 | }); 16 | } 17 | ], 18 | daisyui: { 19 | // themes: true, 20 | themes: [ 21 | "dark", 22 | { 23 | opensigncss: { 24 | primary: "#002864", 25 | "primary-content": "#cacccf", 26 | secondary: "#29354a", 27 | "secondary-content": "#c8d1e0", 28 | accent: "#E10032", 29 | "accent-content": "#ffd8d5", 30 | neutral: "#c1ccdb", 31 | "neutral-content": "#111312", 32 | "base-100": "#ffffff", 33 | "base-200": "#dedede", 34 | "base-300": "#bebebe", 35 | "base-content": "#161616", 36 | info: "#00b6ff", 37 | "info-content": "#f5f5f4", 38 | success: "#00a96e", 39 | "success-content": "#f5f5f4", 40 | warning: "#ffbe00", 41 | "warning-content": "#ccd9e8", 42 | error: "#ffa1a7", 43 | "error-content": "#16090a", 44 | "--rounded-btn": "1.9rem", 45 | "--tab-border": "2px", 46 | "--tab-radius": "0.7rem" 47 | } 48 | } 49 | ], 50 | prefix: "op-" 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /apps/OpenSign/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import svgr from "vite-plugin-svgr"; 4 | import { resolve } from "path"; 5 | import dotenv from "dotenv"; 6 | 7 | // Load env file based on mode 8 | dotenv.config(); 9 | 10 | // https://vitejs.dev/config/ 11 | export default defineConfig(({ command, mode }) => { 12 | return { 13 | plugins: [ 14 | react(), 15 | svgr() // Transform SVGs into React components 16 | ], 17 | resolve: { 18 | alias: { 19 | // Add any path aliases here if needed 20 | } 21 | }, 22 | define: { 23 | // Replace process.env.REACT_APP_* with import.meta.env.VITE_* 24 | "process.env": { 25 | ...Object.keys(process.env).reduce((env, key) => { 26 | if (key.startsWith("REACT_APP_")) { 27 | env[key] = process.env[key]; 28 | } 29 | return env; 30 | }, {}) 31 | } 32 | }, 33 | build: { 34 | outDir: "build", // Keep the same output directory as CRA for compatibility 35 | rollupOptions: { 36 | // For public template as separate chunk 37 | input: { 38 | main: resolve(__dirname, "index.html") 39 | } 40 | } 41 | }, 42 | server: { 43 | port: process.env.PORT || 3000, // Same port as CRA 44 | open: true 45 | }, 46 | test: { 47 | environment: "jsdom", 48 | globals: true, 49 | setupFiles: "./setuptest.js" // if you have one 50 | } 51 | }; 52 | }); 53 | -------------------------------------------------------------------------------- /apps/OpenSignServer/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /apps/OpenSignServer/.ebextensions/app.config: -------------------------------------------------------------------------------- 1 | option_settings: 2 | aws:elasticbeanstalk:application:environment: 3 | PARSE_MOUNT: "/parse" 4 | APP_ID: "ReplaceWithAppID" 5 | MASTER_KEY: "ReplaceWithMasterKey" 6 | DATABASE_URI: "ReplaceWithDatabaseURI" 7 | NODE_ENV: "production" 8 | SERVER_URL: "http://myappname.elasticbeanstalk.com/parse" 9 | -------------------------------------------------------------------------------- /apps/OpenSignServer/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "env": { 4 | "node": true, 5 | "es6": true 6 | }, 7 | "parserOptions": { 8 | "ecmaVersion": 2021, 9 | "sourceType": "module", 10 | "requireConfigFile": false 11 | }, 12 | "parser": "@babel/eslint-parser", 13 | "rules": { 14 | "indent": ["error", 2, { "SwitchCase": 1 }], 15 | "linebreak-style": ["error", "unix"], 16 | "no-trailing-spaces": "error", 17 | "eol-last": "error", 18 | "space-in-parens": ["error", "never"], 19 | "no-multiple-empty-lines": "warn", 20 | "prefer-const": "error", 21 | "space-infix-ops": "error", 22 | "no-useless-escape": "off", 23 | "require-atomic-updates": "off", 24 | "no-var": "warn", 25 | "no-await-in-loop" : "warn" 26 | }, 27 | "globals" : { 28 | "Parse" : true 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /apps/OpenSignServer/.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Node.js 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - '**' 9 | workflow_dispatch: 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | node-version: [16.x] 16 | name: ${{ matrix.name }} 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v3 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - name: Install Dependancies 24 | run: npm install 25 | - name: Check Linting 26 | run: npm run lint 27 | - name: Run Tests 28 | run: npm run test -------------------------------------------------------------------------------- /apps/OpenSignServer/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | .nyc_output 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 28 | node_modules 29 | 30 | # Emacs 31 | *~ 32 | .eslintcache 33 | -------------------------------------------------------------------------------- /apps/OpenSignServer/.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "reporter": [ 3 | "lcov", 4 | "text-summary" 5 | ], 6 | "exclude": [ 7 | "**/spec/**" 8 | ] 9 | } 10 | 11 | -------------------------------------------------------------------------------- /apps/OpenSignServer/.prettierrc: -------------------------------------------------------------------------------- 1 | semi: true 2 | trailingComma: "es5" 3 | singleQuote: true 4 | arrowParens: "avoid" 5 | printWidth: 100 -------------------------------------------------------------------------------- /apps/OpenSignServer/Dockerhubfile: -------------------------------------------------------------------------------- 1 | # Use an official Node runtime as the base image 2 | FROM node:22.14.0 3 | 4 | 5 | # Set the working directory inside the container 6 | WORKDIR /usr/src/app 7 | 8 | # Copy package.json and package-lock.json first to leverage Docker cache 9 | COPY apps/OpenSignServer/package*.json ./ 10 | 11 | # Install application dependencies 12 | RUN npm install 13 | 14 | # If you have native dependencies, you'll need extra tools. Uncomment the following line if needed. 15 | # RUN apk add --no-cache make gcc g++ python3 16 | 17 | # Copy the current directory contents into the container 18 | COPY apps/OpenSignServer/ . 19 | 20 | # Make port 8080 available to the world outside this container 21 | EXPOSE 8080 22 | 23 | # Define environment variables if needed 24 | # ENV NODE_ENV production 25 | # ENV DATABASE_URL mongodb://db:27017 26 | 27 | # Run the application 28 | CMD ["npm", "start"] 29 | -------------------------------------------------------------------------------- /apps/OpenSignServer/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Parse Server Example", 3 | "description": "An example Parse API server using the parse-server module", 4 | "repository": "https://github.com/ParsePlatform/parse-server-example", 5 | "logo": "https://avatars0.githubusercontent.com/u/1294580?v=3&s=200", 6 | "keywords": ["node", "express", "parse"], 7 | "env": { 8 | "PARSE_MOUNT": { 9 | "description": "Configure Parse API route.", 10 | "value": "/parse" 11 | }, 12 | "APP_ID": { 13 | "description": "A unique identifier for your app.", 14 | "value": "myAppId" 15 | }, 16 | "MASTER_KEY": { 17 | "description": "A key that overrides all permissions. Keep this secret.", 18 | "value": "myMasterKey" 19 | }, 20 | "SERVER_URL": { 21 | "description": "URL to connect to your Heroku instance (update with your app's name + PARSE_MOUNT)", 22 | "value": "http://yourappname.herokuapp.com/parse" 23 | } 24 | }, 25 | "image": "heroku/nodejs", 26 | "addons": ["mongolab"] 27 | } 28 | -------------------------------------------------------------------------------- /apps/OpenSignServer/app.yaml: -------------------------------------------------------------------------------- 1 | runtime: nodejs 2 | env: flex 3 | 4 | env_variables: 5 | # --REQUIRED-- 6 | DATABASE_URI: mongodb://localhost:27017/dev 7 | APP_ID: <your-app-id> 8 | MASTER_KEY: <your-master-key> 9 | SERVER_URL: https://your-project-id.appspot.com/parse 10 | # --OPTIONAL-- 11 | # FILE_KEY: <your-file-key> 12 | # PARSE_MOUNT: /parse 13 | # CLOUD_CODE_MAIN: 14 | -------------------------------------------------------------------------------- /apps/OpenSignServer/auth/authadapter.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import dotenv from 'dotenv'; 3 | dotenv.config(); 4 | const ssoApiUrl = process.env.SSO_API_URL || 'https://sso.opensignlabs.com/api'; //'https://osl-jacksonv2.vercel.app/api'; 5 | export const SSOAuth = { 6 | // Returns a promise that fulfills if this user mail is valid. 7 | validateAuthData: async authData => { 8 | try { 9 | const response = await axios.get(ssoApiUrl + '/oauth/userinfo', { 10 | headers: { 11 | Authorization: `Bearer ${authData.access_token}`, 12 | }, 13 | }); 14 | if ( 15 | response.data && 16 | response.data.id && 17 | response.data.email?.toLowerCase()?.replace(/\s/g, '') === authData.id 18 | ) { 19 | return; 20 | } 21 | throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'SSO auth is invalid for this user.'); 22 | } catch (error) { 23 | console.log('error in sso adapter', error?.response); 24 | throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'SSO auth is invalid for this user.'); 25 | } 26 | }, 27 | 28 | // Returns a promise that fulfills if this app id is valid. 29 | validateAppId: () => { 30 | return Promise.resolve(); 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/customRoute/customApp.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import cors from 'cors'; 3 | import dotenv from 'dotenv'; 4 | import uploadFile from './uploadFile.js'; 5 | 6 | export const app = express(); 7 | 8 | dotenv.config(); 9 | app.use(cors()); 10 | app.use(express.json({ limit: '50mb' })); 11 | app.use(express.urlencoded({ limit: '50mb', extended: true })); 12 | 13 | app.post('/file_upload', uploadFile); 14 | 15 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/CheckAdminExist.js: -------------------------------------------------------------------------------- 1 | // `CheckAdminExist` is used to check is admin with org exist or not in db 2 | export default async function CheckAdminExist() { 3 | try { 4 | const extClsQuery = new Parse.Query('contracts_Users'); 5 | extClsQuery.equalTo('UserRole', 'contracts_Admin'); 6 | extClsQuery.notEqualTo('IsDisabled', true); 7 | const extAdminRes = await extClsQuery.find({ useMasterKey: true }); 8 | // must be only one admin 9 | if (extAdminRes && extAdminRes.length === 1 && extAdminRes?.[0]?.get('OrganizationId')) { 10 | return 'exist'; 11 | } else { 12 | return 'not_exist'; 13 | } 14 | } catch (err) { 15 | console.log('err in isAdminExist', err); 16 | const code = err?.code || 400; 17 | const msg = err?.message || 'something went wrong.'; 18 | throw new Parse.Error(code, msg); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/ContactBookAftersave.js: -------------------------------------------------------------------------------- 1 | async function ContactbookAftersave(request) { 2 | /* In beforesave or aftersave if you want to check if an object is being inserted or updated 3 | you can check as follows */ 4 | if (!request.original) { 5 | const user = request.user; 6 | const object = request.object; 7 | if (object.get('UserId')) { 8 | // Retrieve the current ACL 9 | const acl = new Parse.ACL(); 10 | // Ensure the current user has read access 11 | if (acl && request?.user) { 12 | const object = request.object; 13 | acl.setReadAccess(user, true); 14 | acl.setWriteAccess(user, true); 15 | acl.setReadAccess(object.get('UserId'), true); 16 | acl.setWriteAccess(object.get('UserId'), true); 17 | 18 | object.setACL(acl); 19 | object.set('IsDeleted', false); 20 | // Continue saving the object 21 | return object.save(null, { useMasterKey: true }); 22 | } 23 | } else { 24 | const Name = object.get('Name'); 25 | const Email = object.get('Email'); 26 | const Phone = object.get('Phone'); 27 | try { 28 | const _users = Parse.Object.extend('User'); 29 | const _user = new _users(); 30 | _user.set('name', Name); 31 | _user.set('username', Email); 32 | _user.set('email', Email); 33 | _user.set('password', Email); 34 | if (Email) { 35 | _user.set('phone', Phone); 36 | } 37 | const user = await _user.save(); 38 | if (user) { 39 | object.set('UserId', user); 40 | const acl = object.getACL() || new Parse.ACL(); 41 | acl.setReadAccess(user.id, true); 42 | acl.setWriteAccess(user.id, true); 43 | object.setACL(acl); 44 | await object.save(null, { useMasterKey: true }); 45 | // console.log('res update new user with contac', res); 46 | } 47 | } catch (err) { 48 | // console.log('err ', err); 49 | if (err.code === 202) { 50 | const userQuery = new Parse.Query(Parse.User); 51 | userQuery.equalTo('email', Email); 52 | const userRes = await userQuery.first({ useMasterKey: true }); 53 | object.set('UserId', { 54 | __type: 'Pointer', 55 | className: '_User', 56 | objectId: userRes.id, 57 | }); 58 | const acl = object.getACL() || new Parse.ACL(); 59 | acl.setReadAccess(userRes.id, true); 60 | acl.setWriteAccess(userRes.id, true); 61 | object.setACL(acl); 62 | await object.save(null, { useMasterKey: true }); 63 | // console.log('res update existing user with contact', res); 64 | } 65 | } 66 | } 67 | } else { 68 | console.log('Object being update'); 69 | } 70 | } 71 | export default ContactbookAftersave; 72 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/DocumentAfterFind.js: -------------------------------------------------------------------------------- 1 | import { useLocal } from '../../Utils.js'; 2 | import getPresignedUrl, { presignedlocalUrl } from './getSignedUrl.js'; 3 | 4 | async function DocumentAfterFind(request) { 5 | if (request.objects.length === 1) { 6 | if (request.objects) { 7 | const obj = request.objects[0]; 8 | if ( 9 | useLocal !== 'true' 10 | ) { 11 | const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl'); 12 | const Url = obj?.get('URL') && obj?.get('URL'); 13 | const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl'); 14 | if (SignedUrl) { 15 | obj.set( 16 | 'SignedUrl', 17 | getPresignedUrl( 18 | SignedUrl, 19 | ) 20 | ); 21 | } 22 | if (Url) { 23 | obj.set( 24 | 'URL', 25 | getPresignedUrl( 26 | Url, 27 | ) 28 | ); 29 | } 30 | if (certificateUrl) { 31 | obj.set( 32 | 'CertificateUrl', 33 | getPresignedUrl( 34 | certificateUrl, 35 | ) 36 | ); 37 | } 38 | return [obj]; 39 | } else if (useLocal == 'true') { 40 | const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl'); 41 | const Url = obj?.get('URL') && obj?.get('URL'); 42 | const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl'); 43 | if (SignedUrl) { 44 | obj.set('SignedUrl', presignedlocalUrl(SignedUrl)); 45 | } 46 | if (Url) { 47 | obj.set('URL', presignedlocalUrl(Url)); 48 | } 49 | if (certificateUrl) { 50 | obj.set('CertificateUrl', presignedlocalUrl(certificateUrl)); 51 | } 52 | return [obj]; 53 | } 54 | } 55 | } 56 | } 57 | export default DocumentAfterFind; 58 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/DocumentBeforesave.js: -------------------------------------------------------------------------------- 1 | import { MAX_DESCRIPTION_LENGTH, MAX_NAME_LENGTH, MAX_NOTE_LENGTH } from '../../Utils.js'; 2 | 3 | async function DocumentBeforesave(request) { 4 | if (!request.original) { 5 | const validations = [ 6 | { field: 'Name', max: MAX_NAME_LENGTH }, 7 | { field: 'Note', max: MAX_NOTE_LENGTH }, 8 | { field: 'Description', max: MAX_DESCRIPTION_LENGTH }, 9 | ]; 10 | 11 | for (const { field, max } of validations) { 12 | const value = request?.object?.get(field); 13 | if (value && value.length > max) { 14 | throw new Parse.Error( 15 | Parse.Error.VALIDATION_ERROR, 16 | `The "${field}" field must be at most ${max} characters long.` 17 | ); 18 | } 19 | } 20 | 21 | const TimeToCompleteDays = request?.object?.get('TimeToCompleteDays') || 15; 22 | const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5; 23 | const AutoReminder = request?.object?.get('AutomaticReminders') || false; 24 | const reminderCount = TimeToCompleteDays / RemindOnceInEvery; 25 | if (AutoReminder && reminderCount > 15) { 26 | throw new Parse.Error(Parse.Error.INVALID_QUERY, 'only 15 reminder allowed'); 27 | } 28 | } 29 | try { 30 | // below code is used to update document when user sent document or self signed 31 | const document = request.object; 32 | const oldDocument = request.original; 33 | 34 | // Check if SignedUrl field has been added (transition from undefined to defined) 35 | if (oldDocument && !oldDocument?.get('SignedUrl') && document?.get('SignedUrl')) { 36 | // Update count in contracts_Users class 37 | const query = new Parse.Query('contracts_Users'); 38 | query.equalTo('objectId', oldDocument.get('ExtUserPtr').id); 39 | 40 | try { 41 | const contractUser = await query.first({ useMasterKey: true }); 42 | if (contractUser) { 43 | contractUser.increment('DocumentCount', 1); 44 | await contractUser.save(null, { useMasterKey: true }); 45 | } else { 46 | // Create new entry if not found 47 | const ContractsUsers = Parse.Object.extend('contracts_Users'); 48 | const newContractUser = new ContractsUsers(); 49 | newContractUser.set('DocumentCount', 1); 50 | await newContractUser.save(null, { useMasterKey: true }); 51 | } 52 | } catch (error) { 53 | console.log('Error updating document count in contracts_Users: ' + error.message); 54 | } 55 | if (document?.get('Signers') && document.get('Signers').length > 0) { 56 | document.set('DocSentAt', new Date()); 57 | } 58 | } 59 | } catch (err) { 60 | console.log('err in document beforesave', err.message); 61 | } 62 | } 63 | export default DocumentBeforesave; 64 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/GetLogoByDomain.js: -------------------------------------------------------------------------------- 1 | import { appName } from '../../Utils.js'; 2 | 3 | // `GetLogoByDomain` is used to get logo by domain as well as check any tenant exist or not in db 4 | export default async function GetLogoByDomain(request) { 5 | const domain = request.params.domain; 6 | try { 7 | const tenantCreditsQuery = new Parse.Query('partners_Tenant'); 8 | tenantCreditsQuery.equalTo('Domain', domain); 9 | const res = await tenantCreditsQuery.first({ useMasterKey: true }); 10 | if (res) { 11 | const updateRes = JSON.parse(JSON.stringify(res)); 12 | return { 13 | logo: updateRes?.Logo, 14 | appname: appName, 15 | user: 'exist', 16 | }; 17 | } else { 18 | const tenantCreditsQuery = new Parse.Query('partners_Tenant'); 19 | const tenantRes = await tenantCreditsQuery.first({ useMasterKey: true }); 20 | if (tenantRes) { 21 | return { logo: '', appname: appName, user: 'exist' }; 22 | } else { 23 | return { logo: '', appname: appName, user: 'not_exist' }; 24 | } 25 | } 26 | } catch (err) { 27 | const code = err.code || 400; 28 | const msg = err.message || 'Something went wrong.'; 29 | throw new Parse.Error(code, msg); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/Newsletter.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | export default async function Newsletter(request) { 3 | const name = request.params.name; 4 | const email = request.params?.email?.toLowerCase()?.replace(/\s/g, ''); 5 | const domain = request.params.domain; 6 | try { 7 | const envAppId = 'opensign'; 8 | const headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': envAppId }; 9 | const envProdServer = 'https://app.opensignlabs.com/api/app'; 10 | const newsletter = await axios.post( 11 | `${envProdServer}/classes/Newsletter`, 12 | { Name: name, Email: email, Domain: domain }, 13 | { headers: headers } 14 | ); 15 | return 'success'; 16 | } catch (err) { 17 | const code = err?.response?.data?.code || err?.response?.status || err?.code || 400; 18 | const message = 19 | err?.response?.data?.error || err?.response?.data || err?.message || 'Something went wrong.'; 20 | console.log('err in savenewsletter', err); 21 | throw new Parse.Error(code, message); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/SignatureAfterFind.js: -------------------------------------------------------------------------------- 1 | import { useLocal } from '../../Utils.js'; 2 | import getPresignedUrl, { presignedlocalUrl } from './getSignedUrl.js'; 3 | 4 | async function SignatureAfterFind(request) { 5 | if (useLocal !== 'true') { 6 | if (request.objects.length === 1) { 7 | if (request.objects) { 8 | const obj = request.objects[0]; 9 | const ImageURL = obj?.get('ImageURL') && obj?.get('ImageURL'); 10 | const Initials = obj?.get('Initials') && obj?.get('Initials'); 11 | if (ImageURL) { 12 | obj.set('ImageURL', getPresignedUrl(ImageURL)); 13 | } 14 | if (Initials) { 15 | obj.set('Initials', getPresignedUrl(Initials)); 16 | } 17 | return [obj]; 18 | } 19 | } 20 | } else if (useLocal == 'true') { 21 | if (request.objects.length === 1) { 22 | if (request.objects) { 23 | const obj = request.objects[0]; 24 | const ImageURL = obj?.get('ImageURL') && obj?.get('ImageURL'); 25 | const Initials = obj?.get('Initials') && obj?.get('Initials'); 26 | if (ImageURL) { 27 | obj.set('ImageURL', presignedlocalUrl(ImageURL)); 28 | } 29 | if (Initials) { 30 | obj.set('Initials', presignedlocalUrl(Initials)); 31 | } 32 | return [obj]; 33 | } 34 | } 35 | } 36 | } 37 | export default SignatureAfterFind; 38 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/TeamsAftersave.js: -------------------------------------------------------------------------------- 1 | export default async function TeamsAftersave(req) { 2 | if (!req.original) { 3 | try { 4 | const Ancestors = req.object.get('Ancestors'); 5 | let updatedAncestors; 6 | if (Ancestors && Ancestors.length > 0) { 7 | updatedAncestors = [ 8 | ...Ancestors, 9 | { 10 | __type: 'Pointer', 11 | className: 'contracts_Teams', 12 | objectId: req.object.id, 13 | }, 14 | ]; 15 | } else { 16 | updatedAncestors = [ 17 | { 18 | __type: 'Pointer', 19 | className: 'contracts_Teams', 20 | objectId: req.object.id, 21 | }, 22 | ]; 23 | } 24 | req.object.set('Ancestors', updatedAncestors); 25 | await req.object.save(null, { useMasterKey: true }); 26 | } catch (err) { 27 | console.log('Err in team aftersave', err); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/TemplateAfterFind.js: -------------------------------------------------------------------------------- 1 | import { useLocal } from '../../Utils.js'; 2 | import getPresignedUrl, { presignedlocalUrl } from './getSignedUrl.js'; 3 | 4 | async function TemplateAfterFind(request) { 5 | if (request.objects.length === 1) { 6 | if (request.objects) { 7 | const obj = request.objects[0]; 8 | if ( 9 | useLocal !== 'true' 10 | ) { 11 | const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl'); 12 | const Url = obj?.get('URL') && obj?.get('URL'); 13 | const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl'); 14 | if (SignedUrl) { 15 | obj.set( 16 | 'SignedUrl', 17 | getPresignedUrl( 18 | SignedUrl, 19 | ) 20 | ); 21 | } 22 | if (Url) { 23 | obj.set( 24 | 'URL', 25 | getPresignedUrl( 26 | Url, 27 | ) 28 | ); 29 | } 30 | if (certificateUrl) { 31 | obj.set( 32 | 'CertificateUrl', 33 | getPresignedUrl( 34 | certificateUrl, 35 | ) 36 | ); 37 | } 38 | return [obj]; 39 | } else if (useLocal == 'true') { 40 | const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl'); 41 | const Url = obj?.get('URL') && obj?.get('URL'); 42 | const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl'); 43 | if (SignedUrl) { 44 | obj.set('SignedUrl', presignedlocalUrl(SignedUrl)); 45 | } 46 | if (Url) { 47 | obj.set('URL', presignedlocalUrl(Url)); 48 | } 49 | if (certificateUrl) { 50 | obj.set('CertificateUrl', presignedlocalUrl(certificateUrl)); 51 | } 52 | return [obj]; 53 | } 54 | } 55 | } 56 | } 57 | export default TemplateAfterFind; 58 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/TemplateBeforesave.js: -------------------------------------------------------------------------------- 1 | import { MAX_DESCRIPTION_LENGTH, MAX_NAME_LENGTH, MAX_NOTE_LENGTH } from '../../Utils.js'; 2 | 3 | async function TemplateBeforeSave(request) { 4 | if (!request.original) { 5 | const validations = [ 6 | { field: 'Name', max: MAX_NAME_LENGTH }, 7 | { field: 'Note', max: MAX_NOTE_LENGTH }, 8 | { field: 'Description', max: MAX_DESCRIPTION_LENGTH }, 9 | ]; 10 | 11 | for (const { field, max } of validations) { 12 | const value = request.object?.get(field); 13 | if (value && value.length > max) { 14 | throw new Parse.Error( 15 | Parse.Error.VALIDATION_ERROR, 16 | `The "${field}" field must be at most ${max} characters long.` 17 | ); 18 | } 19 | } 20 | 21 | const TimeToCompleteDays = request.object.get('TimeToCompleteDays') || 15; 22 | const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5; 23 | const AutoReminder = request?.object?.get('AutomaticReminders') || false; 24 | const reminderCount = TimeToCompleteDays / RemindOnceInEvery; 25 | if (AutoReminder && reminderCount > 15) { 26 | throw new Parse.Error(Parse.Error.INVALID_QUERY, 'only 15 reminder allowed'); 27 | } 28 | } 29 | try { 30 | if (!request.original) { 31 | // below code is used to update template when user sent template or self signed 32 | const template = request.object; 33 | 34 | // Update count in contracts_Users class 35 | const query = new Parse.Query('contracts_Users'); 36 | query.equalTo('objectId', template.get('ExtUserPtr').id); 37 | 38 | try { 39 | const contractUser = await query.first({ useMasterKey: true }); 40 | if (contractUser) { 41 | contractUser.increment('TemplateCount', 1); 42 | await contractUser.save(null, { useMasterKey: true }); 43 | } else { 44 | // Create new entry if not found 45 | const ContractsUsers = Parse.Object.extend('contracts_Users'); 46 | const newContractUser = new ContractsUsers(); 47 | newContractUser.set('TemplateCount', 1); 48 | await newContractUser.save(null, { useMasterKey: true }); 49 | } 50 | } catch (error) { 51 | console.log('Error updating template count in contracts_Users: ' + error.message); 52 | } 53 | } 54 | } catch (err) { 55 | console.log('err in template beforesave', err.message); 56 | } 57 | } 58 | export default TemplateBeforeSave; 59 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/TenantAfterFind.js: -------------------------------------------------------------------------------- 1 | import { useLocal } from '../../Utils.js'; 2 | import getPresignedUrl, { presignedlocalUrl } from './getSignedUrl.js'; 3 | 4 | async function TenantAterFind(request) { 5 | if (useLocal !== 'true') { 6 | if (request.objects.length === 1) { 7 | if (request.objects) { 8 | const obj = request.objects[0]; 9 | const Logo = obj?.get('Logo') && obj?.get('Logo'); 10 | if (Logo) { 11 | obj.set('Logo', getPresignedUrl(Logo)); 12 | } 13 | return [obj]; 14 | } 15 | } 16 | } else if (useLocal == 'true') { 17 | if (request.objects.length === 1) { 18 | if (request.objects) { 19 | const obj = request.objects[0]; 20 | const Logo = obj?.get('Logo') && obj?.get('Logo'); 21 | if (Logo) { 22 | obj.set('Logo', presignedlocalUrl(Logo)); 23 | } 24 | return [obj]; 25 | } 26 | } 27 | } 28 | } 29 | export default TenantAterFind; 30 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/UserAfterFInd.js: -------------------------------------------------------------------------------- 1 | import { useLocal } from '../../Utils.js'; 2 | import getPresignedUrl, { presignedlocalUrl } from './getSignedUrl.js'; 3 | 4 | async function UserAfterFind(request) { 5 | if (useLocal !== 'true') { 6 | if (request.objects.length === 1) { 7 | if (request.objects) { 8 | const obj = request.objects[0]; 9 | const ProfilePic = obj?.get('ProfilePic') && obj?.get('ProfilePic'); 10 | if (ProfilePic) { 11 | obj.set('ProfilePic', getPresignedUrl(ProfilePic)); 12 | } 13 | return [obj]; 14 | } 15 | } 16 | } else if (useLocal == 'true') { 17 | if (request.objects.length === 1) { 18 | if (request.objects) { 19 | const obj = request.objects[0]; 20 | const ProfilePic = obj?.get('ProfilePic') && obj?.get('ProfilePic'); 21 | if (ProfilePic) { 22 | obj.set('ProfilePic', presignedlocalUrl(ProfilePic)); 23 | } 24 | return [obj]; 25 | } 26 | } 27 | } 28 | } 29 | export default UserAfterFind; 30 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/VerifyEmail.js: -------------------------------------------------------------------------------- 1 | export default async function VerifyEmail(request) { 2 | try { 3 | if (!request?.user) { 4 | throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); 5 | } else { 6 | let otpN = request.params.otp; 7 | let otp = parseInt(otpN); 8 | let email = request.params.email; 9 | 10 | //checking otp is correct or not which already save in defaultdata_Otp class 11 | const checkOtp = new Parse.Query('defaultdata_Otp'); 12 | checkOtp.equalTo('Email', email); 13 | checkOtp.equalTo('OTP', otp); 14 | 15 | const res = await checkOtp.first({ useMasterKey: true }); 16 | if (res) { 17 | // Fetch the user by their objectId 18 | const isEmailVerified = request?.user?.get('emailVerified'); 19 | if (isEmailVerified) { 20 | return { message: 'Email is already verified.' }; 21 | } else { 22 | const userQuery = new Parse.Query(Parse.User); 23 | const user = await userQuery.get(request?.user.id, { 24 | sessionToken: request?.user.getSessionToken(), 25 | }); 26 | 27 | // Update the emailVerified field to true 28 | user.set('emailVerified', true); 29 | // Save the user object 30 | const res = await user.save(null, { useMasterKey: true }); 31 | if (res) { 32 | return { message: 'Email is verified.' }; 33 | } else { 34 | const error = new Error('Something went wrong, please try again later!'); 35 | error.code = 400; // Set the error code (e.g., 400 for bad request) 36 | throw error; 37 | } 38 | } 39 | } else { 40 | const error = new Error('OTP is invalid.'); 41 | error.code = 400; // Set the error code (e.g., 400 for bad request) 42 | throw error; 43 | } 44 | } 45 | } catch (err) { 46 | console.log('err ', err.code + ' ' + err.message); 47 | throw err; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/createBatchContact.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { cloudServerUrl } from '../../Utils.js'; 3 | const appId = process.env.APP_ID; 4 | const masterkey = process.env.MASTER_KEY; 5 | export default async function createBatchContact(req) { 6 | if (!req?.user) { 7 | throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); 8 | } 9 | if (!req.params?.contacts) { 10 | throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide parameter.'); 11 | } 12 | const contactData = JSON.parse(req.params.contacts); 13 | if (contactData?.length > 0) { 14 | try { 15 | const requests = contactData.map(x => { 16 | return { 17 | method: 'POST', 18 | path: '/app/classes/contracts_Contactbook', 19 | body: { 20 | UserRole: 'contracts_Guest', 21 | TenantId: { __type: 'Pointer', className: 'partners_Tenant', objectId: x.TenantId }, 22 | CreatedBy: { __type: 'Pointer', className: '_User', objectId: req.user.id }, 23 | Name: x.Name, 24 | Email: x.Email?.toLowerCase()?.replace(/\s/g, ''), 25 | IsDeleted: false, 26 | IsImported: true, 27 | ...(x?.Phone ? { Phone: `${x?.Phone}` } : {}), 28 | ACL: { [req.user.id]: { read: true, write: true } }, 29 | }, 30 | }; 31 | }); 32 | const parseConfig = { 33 | baseURL: cloudServerUrl, 34 | headers: { 35 | 'X-Parse-Application-Id': appId, 36 | 'X-Parse-Master-Key': masterkey, 37 | 'Content-Type': 'application/json', 38 | }, 39 | }; 40 | const response = await axios.post('batch', { requests: requests }, parseConfig); 41 | // Handle the batch query response 42 | // console.info('createbatchcontact ', response.data); 43 | const successCount = response?.data?.filter(item => item.success).length; 44 | const failedCount = requests.length - successCount; 45 | console.log( 46 | `createbatchcontact query response: success: ${successCount}, failed: ${failedCount}` 47 | ); 48 | return { success: successCount, failed: failedCount }; 49 | } catch (err) { 50 | console.log('err while create batch contact', err); 51 | throw new Parse.Error(400, 'Something went wrong, please try again later'); 52 | } 53 | } else { 54 | throw new Parse.Error(Parse.Error.INVALID_CONTENT_LENGTH, 'Please provide parameter'); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/declinedocument.js: -------------------------------------------------------------------------------- 1 | export default async function declinedocument(request) { 2 | const docId = request.params.docId; 3 | const reason = request.params?.reason || ''; 4 | const declineBy = { 5 | __type: 'Pointer', 6 | className: '_User', 7 | objectId: request.params?.userId, 8 | }; 9 | 10 | if (!docId) { 11 | throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'missing parameter docId.'); 12 | } 13 | try { 14 | const docCls = new Parse.Query('contracts_Document'); 15 | docCls.include('ExtUserPtr.TenantId'); 16 | const updateDoc = await docCls.get(docId, { useMasterKey: true }); 17 | if (updateDoc) { 18 | const isEnableOTP = updateDoc?.get('IsEnableOTP') || false; 19 | if (!isEnableOTP) { 20 | updateDoc.set('IsDeclined', true); 21 | updateDoc.set('DeclineReason', reason); 22 | updateDoc.set('DeclineBy', declineBy); 23 | await updateDoc.save(null, { useMasterKey: true }); 24 | return 'document declined'; 25 | } else { 26 | if (!request?.user) { 27 | throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); 28 | } 29 | updateDoc.set('IsDeclined', true); 30 | updateDoc.set('DeclineReason', reason); 31 | updateDoc.set('DeclineBy', declineBy); 32 | await updateDoc.save(null, { useMasterKey: true }); 33 | return 'document declined'; 34 | } 35 | } else { 36 | throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found.'); 37 | } 38 | } catch (err) { 39 | console.log('err while decling doc', err); 40 | throw err; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/fileUpload.js: -------------------------------------------------------------------------------- 1 | import { getSignedLocalUrl } from './getSignedUrl.js'; 2 | 3 | export default async function fileUpload(request) { 4 | const url = request.params.url; 5 | 6 | try { 7 | const urlwithjwt = getSignedLocalUrl(url, 200); 8 | return { url: urlwithjwt }; 9 | } catch (err) { 10 | console.log('Err ', err); 11 | throw err; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/getContact.js: -------------------------------------------------------------------------------- 1 | export default async function getContact(request) { 2 | const contactId = request.params.contactId; 3 | try { 4 | const contactCls = new Parse.Query('contracts_Contactbook'); 5 | const contactRes = await contactCls.get(contactId, { useMasterKey: true }); 6 | return contactRes; 7 | } catch (err) { 8 | console.log('Err in contracts_Contactbook class ', err); 9 | throw err; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/getDrive.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { cloudServerUrl } from '../../Utils.js'; 3 | export default async function getDrive(request) { 4 | const serverUrl = cloudServerUrl; //process.env.SERVER_URL; 5 | const appId = process.env.APP_ID; 6 | const limit = request.params.limit; 7 | const skip = request.params.skip; 8 | const docId = request.params.docId; 9 | try { 10 | const userRes = await axios.get(serverUrl + '/users/me', { 11 | headers: { 12 | 'X-Parse-Application-Id': appId, 13 | 'X-Parse-Session-Token': request.headers['sessiontoken'], 14 | }, 15 | }); 16 | const userId = userRes.data && userRes.data.objectId; 17 | if (userId) { 18 | try { 19 | const query = new Parse.Query('contracts_Document'); 20 | if (docId) { 21 | query.equalTo('Folder', { 22 | __type: 'Pointer', 23 | className: 'contracts_Document', 24 | objectId: docId, 25 | }); 26 | query.include('Folder'); 27 | } else { 28 | query.doesNotExist('Folder', true); 29 | } 30 | query.equalTo('CreatedBy', { __type: 'Pointer', className: '_User', objectId: userId }); 31 | query.include('ExtUserPtr'); 32 | query.include('ExtUserPtr.TenantId'); 33 | query.include('Signers'); 34 | query.notEqualTo('IsArchive', true); 35 | query.descending('updatedAt'); 36 | query.skip(skip); 37 | query.limit(limit); 38 | query.exclude('AuditTrail'); 39 | const res = await query.find({ useMasterKey: true }); 40 | return res; 41 | } catch (err) { 42 | console.log('err', err); 43 | return { error: "You don't have access to drive" }; 44 | } 45 | } else { 46 | return { error: 'Please provide required parameter!' }; 47 | } 48 | } catch (err) { 49 | console.log('err', err); 50 | if (err.code == 209) { 51 | return { error: 'Invalid session token' }; 52 | } else { 53 | return { error: "You don't have access!" }; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/getSigners.js: -------------------------------------------------------------------------------- 1 | // Function to escape special characters in the search string 2 | function escapeRegExp(string) { 3 | return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // Escape special characters 4 | } 5 | 6 | async function getContacts(searchObj, isJWT) { 7 | try { 8 | const escapedSearch = escapeRegExp(searchObj.search); // Escape the search input 9 | const searchRegex = new RegExp(escapedSearch, 'i'); // Create regex once to reuse 10 | const contactNameQuery = new Parse.Query('contracts_Contactbook'); 11 | contactNameQuery.matches('Name', searchRegex); 12 | 13 | const conatctEmailQuery = new Parse.Query('contracts_Contactbook'); 14 | conatctEmailQuery.matches('Email', searchRegex); 15 | 16 | // Combine the two queries with OR 17 | const mainQuery = Parse.Query.or(contactNameQuery, conatctEmailQuery); 18 | 19 | // Add the common condition for 'CreatedBy' 20 | mainQuery.equalTo('CreatedBy', searchObj.CreatedBy); 21 | mainQuery.notEqualTo('IsDeleted', true); 22 | const findOpt = isJWT ? { useMasterKey: true } : { sessionToken: searchObj.sessionToken }; 23 | const contactRes = await mainQuery.find(findOpt); 24 | const _contactRes = JSON.parse(JSON.stringify(contactRes)); 25 | return _contactRes; 26 | } catch (err) { 27 | console.log('err while fetch contacts', err); 28 | throw err; 29 | } 30 | } 31 | export default async function getSigners(request) { 32 | const searchObj = { search: request.params.search || '', sessionToken: '' }; 33 | try { 34 | if (request.user) { 35 | searchObj.CreatedBy = { __type: 'Pointer', className: '_User', objectId: request?.user?.id }; 36 | searchObj.sessionToken = request.user.getSessionToken(); 37 | return await getContacts(searchObj); 38 | } 39 | else { 40 | throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token'); 41 | } 42 | } catch (err) { 43 | console.log('err in get signers', err); 44 | throw err; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/getTeams.js: -------------------------------------------------------------------------------- 1 | export default async function getTeams(request) { 2 | const activeTeams = request.params.active; 3 | if (!request?.user) { 4 | throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); 5 | } 6 | try { 7 | const extUserQuery = new Parse.Query('contracts_Users'); 8 | extUserQuery.equalTo('UserId', { 9 | __type: 'Pointer', 10 | className: '_User', 11 | objectId: request.user.id, 12 | }); 13 | extUserQuery.notEqualTo('IsDisabled', true); 14 | const resExt = await extUserQuery.first({ useMasterKey: true }); 15 | const extUser = JSON.parse(JSON.stringify(resExt)); 16 | const teamCls = new Parse.Query('contracts_Teams'); 17 | teamCls.equalTo('OrganizationId', { 18 | __type: 'Pointer', 19 | className: 'contracts_Organizations', 20 | objectId: extUser.OrganizationId.objectId, 21 | }); 22 | if (activeTeams) { 23 | teamCls.equalTo('IsActive', true); 24 | } 25 | teamCls.descending('createdAt'); 26 | const teamRes = await teamCls.find({ useMasterKey: true }); 27 | if (teamRes && teamRes.length > 0) { 28 | return teamRes; 29 | } else { 30 | return []; 31 | } 32 | } catch (err) { 33 | console.log('err in getTeams', err); 34 | const code = err?.code || 400; 35 | const msg = err?.message || 'Something went wrong.'; 36 | throw new Parse.Error(code, msg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/getTenant.js: -------------------------------------------------------------------------------- 1 | 2 | async function getTenantByUserId(userId, contactId) { 3 | try { 4 | if (contactId) { 5 | const contactquery = new Parse.Query('contracts_Contactbook'); 6 | contactquery.equalTo('objectId', contactId); 7 | const contactuser = await contactquery.first({ useMasterKey: true }); 8 | if (contactuser) { 9 | const tenantId = contactuser?.get('TenantId')?.id; 10 | if (tenantId) { 11 | const tenantCreditsQuery = new Parse.Query('partners_Tenant'); 12 | tenantCreditsQuery.equalTo('objectId', tenantId); 13 | tenantCreditsQuery.exclude('FileAdapters,PfxFile,ContactNumber'); 14 | const res = await tenantCreditsQuery.first({ useMasterKey: true }); 15 | return res; 16 | } else { 17 | return {}; 18 | } 19 | } else { 20 | return {}; 21 | } 22 | } else { 23 | const query = new Parse.Query('contracts_Users'); 24 | query.equalTo('UserId', { 25 | __type: 'Pointer', 26 | className: '_User', 27 | objectId: userId, 28 | }); 29 | const extuser = await query.first({ useMasterKey: true }); 30 | if (extuser) { 31 | const user = extuser?.get('CreatedBy')?.id || userId; 32 | const tenantCreditsQuery = new Parse.Query('partners_Tenant'); 33 | tenantCreditsQuery.equalTo('UserId', { 34 | __type: 'Pointer', 35 | className: '_User', 36 | objectId: user, 37 | }); 38 | tenantCreditsQuery.exclude('FileAdapters,PfxFile'); 39 | const res = await tenantCreditsQuery.first({ useMasterKey: true }); 40 | return res; 41 | } else { 42 | return {}; 43 | } 44 | } 45 | } catch (err) { 46 | console.log('err in getTenant ', err); 47 | return 'user does not exist!'; 48 | } 49 | } 50 | export default async function getTenant(request) { 51 | const userId = request.params.userId || ''; 52 | const contactId = request.params.contactId || ''; 53 | 54 | if (userId || contactId) { 55 | return await getTenantByUserId(userId, contactId); 56 | } 57 | else { 58 | return {}; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/getUserDetails.js: -------------------------------------------------------------------------------- 1 | async function getUserDetails(request) { 2 | const reqEmail = request.params.email; 3 | if (reqEmail || request.user) { 4 | try { 5 | const userId = request.params.userId; 6 | const userQuery = new Parse.Query('contracts_Users'); 7 | if (reqEmail) { 8 | userQuery.equalTo('Email', reqEmail); 9 | } else { 10 | const email = request.user.get('email'); 11 | userQuery.equalTo('Email', email); 12 | } 13 | userQuery.include('TenantId'); 14 | userQuery.include('UserId'); 15 | userQuery.include('CreatedBy'); 16 | userQuery.exclude('CreatedBy.authData'); 17 | userQuery.exclude('TenantId.FileAdapters'); 18 | userQuery.exclude('google_refresh_token'); 19 | userQuery.exclude('TenantId.PfxFile'); 20 | if (userId) { 21 | userQuery.equalTo('CreatedBy', { __type: 'Pointer', className: '_User', objectId: userId }); 22 | } 23 | const res = await userQuery.first({ useMasterKey: true }); 24 | if (res) { 25 | if (reqEmail) { 26 | return { objectId: res.id }; 27 | } else { 28 | return res; 29 | } 30 | } else { 31 | return ''; 32 | } 33 | } catch (err) { 34 | console.log('Err ', err); 35 | const code = err?.code || 400; 36 | const msg = err?.message || 'Something went wrong.'; 37 | throw new Parse.Error(code, msg); 38 | } 39 | } 40 | else { 41 | throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); 42 | } 43 | } 44 | export default getUserDetails; 45 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/getUserId.js: -------------------------------------------------------------------------------- 1 | async function getUserId(request) { 2 | try { 3 | const username = request.params.username; 4 | const email = request.params.email; 5 | const query = new Parse.Query(Parse.User); 6 | if (username) { 7 | query.equalTo('username', username); 8 | } else { 9 | query.equalTo('email', email); 10 | } 11 | const user = await query.first({ useMasterKey: true }); 12 | return { id: user.id }; 13 | } catch (err) { 14 | console.log('err', err); 15 | return err; 16 | } 17 | } 18 | export default getUserId; 19 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/getUserListByOrg.js: -------------------------------------------------------------------------------- 1 | export default async function getUserListByOrg(req) { 2 | const OrganizationId = req.params.organizationId; 3 | const orgPtr = { 4 | __type: 'Pointer', 5 | className: 'contracts_Organizations', 6 | objectId: OrganizationId, 7 | }; 8 | if (!req?.user) { 9 | throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); 10 | } else { 11 | try { 12 | const extUser = new Parse.Query('contracts_Users'); 13 | extUser.equalTo('OrganizationId', orgPtr); 14 | extUser.include('TeamIds'); 15 | extUser.descending('createdAt'); 16 | const userRes = await extUser.find({ useMasterKey: true }); 17 | if (userRes.length > 0) { 18 | const _userRes = JSON.parse(JSON.stringify(userRes)); 19 | return _userRes; 20 | } else { 21 | return []; 22 | } 23 | } catch (err) { 24 | console.log('err in getuserlist', err); 25 | throw err; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/isUserInContactBook.js: -------------------------------------------------------------------------------- 1 | 2 | export default async function isUserInContactBook(request) { 3 | try { 4 | if (request.user) { 5 | const email = request.user.get('email'); 6 | const userPtr = { __type: 'Pointer', className: '_User', objectId: request.user?.id }; 7 | const query = new Parse.Query('contracts_Contactbook'); 8 | query.equalTo('CreatedBy', userPtr); 9 | query.notEqualTo('IsDeleted', true); 10 | query.equalTo('Email', email); 11 | const res = await query.first({ sessionToken: request.user.getSessionToken() }); 12 | return res; 13 | } 14 | } catch (err) { 15 | console.log('err', err); 16 | throw err; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/isextenduser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks if a user exists in the extended class 'contracts_Users' based on the provided email. 3 | * @param email - The request contains parameters, such as the user's email. 4 | * @returns {Object} - Returns an object indicating whether the user exists. 5 | */ 6 | export default async function isextenduser(request) { 7 | try { 8 | // Query the 'contracts_Users' class in the database based on the provided email 9 | const userQuery = new Parse.Query('contracts_Users'); 10 | userQuery.equalTo('Email', request.params.email); 11 | 12 | // Execute the query 13 | const res = await userQuery.first({ useMasterKey: true }); 14 | 15 | // Check if a user was found 16 | if (res) { 17 | // If user exists, return object with 'isUserExist' set to true 18 | return { isUserExist: true }; 19 | } else { 20 | // If user does not exist, return object with 'isUserExist' set to false 21 | return { isUserExist: false }; 22 | } 23 | } catch (err) { 24 | // Handle errors 25 | console.log('Error in userexist', err); 26 | const code = err?.code || 400; 27 | const message = err?.message || 'Something went wrong.'; 28 | throw new Parse.Error(code, message); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/loginUser.js: -------------------------------------------------------------------------------- 1 | import crypto from 'node:crypto'; 2 | export default async function loginUser(request) { 3 | const username = request.params.email; 4 | const password = request.params.password; 5 | 6 | if (username && password) { 7 | try { 8 | // Pass the username and password to logIn function 9 | const user = await Parse.User.logIn(username, password); 10 | // console.log('user ', user); 11 | if (user) { 12 | const _user = user?.toJSON(); 13 | return { 14 | ..._user, 15 | }; 16 | } else { 17 | throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'user not found.'); 18 | } 19 | } catch (err) { 20 | console.log('err in login user', err); 21 | throw err; 22 | } 23 | } else { 24 | throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'username/password is missing.'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/pdf/PDFArrayCustom.js: -------------------------------------------------------------------------------- 1 | import { PDFArray, CharCodes } from 'pdf-lib'; 2 | 3 | /** 4 | * Extends PDFArray class in order to make ByteRange look like this: 5 | * /ByteRange [0 /********** /********** /**********] 6 | * Not this: 7 | * /ByteRange [ 0 /********** /********** /********** ] 8 | */ 9 | export default class PDFArrayCustom extends PDFArray { 10 | static withContext(context) { 11 | return new PDFArrayCustom(context); 12 | } 13 | 14 | clone(context) { 15 | const clone = PDFArrayCustom.withContext(context || this.context); 16 | for (let idx = 0, len = this.size(); idx < len; idx++) { 17 | clone.push(this.array[idx]); 18 | } 19 | return clone; 20 | } 21 | 22 | toString() { 23 | let arrayString = '['; 24 | for (let idx = 0, len = this.size(); idx < len; idx++) { 25 | arrayString += this.get(idx).toString(); 26 | if (idx < len - 1) arrayString += ' '; 27 | } 28 | arrayString += ']'; 29 | return arrayString; 30 | } 31 | 32 | sizeInBytes() { 33 | let size = 2; 34 | for (let idx = 0, len = this.size(); idx < len; idx++) { 35 | size += this.get(idx).sizeInBytes(); 36 | if (idx < len - 1) size += 1; 37 | } 38 | return size; 39 | } 40 | 41 | copyBytesInto(buffer, offset) { 42 | const initialOffset = offset; 43 | 44 | buffer[offset++] = CharCodes.LeftSquareBracket; 45 | for (let idx = 0, len = this.size(); idx < len; idx++) { 46 | offset += this.get(idx).copyBytesInto(buffer, offset); 47 | if (idx < len - 1) buffer[offset++] = CharCodes.Space; 48 | } 49 | buffer[offset++] = CharCodes.RightSquareBracket; 50 | 51 | return offset - initialOffset; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/recreateDocument.js: -------------------------------------------------------------------------------- 1 | export default async function recreateDocument(request) { 2 | const { docId } = request.params; 3 | if (!docId) { 4 | throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Missing docId parameter'); 5 | } 6 | if (!request.user) { 7 | throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User not aunthenticated'); 8 | } 9 | 10 | try { 11 | const docQuery = new Parse.Query('contracts_Document'); 12 | docQuery.equalTo('objectId', docId); 13 | const doc = await docQuery.first({ useMasterKey: true }); 14 | if (!doc) { 15 | throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found'); 16 | } 17 | const _docRes = doc?.toJSON(); 18 | const { objectId, SignedUrl, AuditTrail, ACL, DeclineBy, DeclineReason, ...docRes } = _docRes; 19 | const createDoc = new Parse.Object('contracts_Document'); 20 | Object.entries(docRes).forEach(([key, value]) => { 21 | if (key === 'IsDeclined') { 22 | createDoc.set(key, false); 23 | } else { 24 | createDoc.set(key, value); 25 | } 26 | // console.log(`${key}: ${value}`); 27 | }); 28 | const createDocRes = await createDoc.save(null, { useMasterKey: true }); 29 | // console.log('createDocRes', createDocRes); 30 | const newDoc = JSON.parse(JSON.stringify(createDocRes)); 31 | return { objectId: newDoc.objectId, createdAt: newDoc.createdAt, updatedAt: newDoc.updatedAt }; 32 | } catch (err) { 33 | console.log('err in recreate document', err); 34 | throw err; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/saveFile.js: -------------------------------------------------------------------------------- 1 | import { 2 | flattenPdf, 3 | getSecureUrl, 4 | } from '../../Utils.js'; 5 | export default async function saveFile(request) { 6 | if (!request.params.fileBase64) { 7 | throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide file.'); 8 | } 9 | const fileBase64 = request.params.fileBase64; 10 | const id = request.params.id; 11 | try { 12 | if (request.user) { 13 | const extCls = new Parse.Query('contracts_Users'); 14 | extCls.equalTo('UserId', request.user); 15 | extCls.include('TenantId'); 16 | const resExt = await extCls.first({ useMasterKey: true }); 17 | if (resExt) { 18 | const _resExt = JSON.parse(JSON.stringify(resExt)); 19 | const fileName = request.params.fileName; 20 | const ext = request.params.fileName?.split('.')?.pop(); 21 | let mimeType; 22 | let file; 23 | if (ext === 'pdf') { 24 | mimeType = 'application/pdf'; 25 | const flatPdf = await flattenPdf(fileBase64); 26 | file = [...flatPdf]; 27 | } else if (ext === 'png' || ext === 'jpeg' || ext === 'jpg') { 28 | mimeType = `image/${ext}`; 29 | file = { base64: fileBase64 }; 30 | } 31 | const pdfFile = new Parse.File(fileName, file, mimeType); 32 | // Save the Parse File if needed 33 | const pdfData = await pdfFile.save({ useMasterKey: true }); 34 | const presignedUrl = pdfData.url(); 35 | const fileRes = getSecureUrl(presignedUrl); 36 | return { url: fileRes.url }; 37 | } else { 38 | throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.'); 39 | } 40 | } 41 | else { 42 | throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); 43 | } 44 | } catch (err) { 45 | console.log('err in savetoS3', err); 46 | throw err; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/updateContactTour.js: -------------------------------------------------------------------------------- 1 | export default async function updateContactTour(request) { 2 | const contactId = request.params.contactId; 3 | try { 4 | const contactCls = new Parse.Query('contracts_Contactbook'); 5 | const contactRes = await contactCls.get(contactId, { useMasterKey: true }); 6 | if (contactRes) { 7 | const _contactRes = JSON.parse(JSON.stringify(contactRes)); 8 | const tourStatus = _contactRes?.TourStatus?.length > 0 ? _contactRes.TourStatus : []; 9 | let updatedTourStatus = []; 10 | if (tourStatus.length > 0) { 11 | updatedTourStatus = [...tourStatus]; 12 | const requestSignIndex = tourStatus.findIndex( 13 | obj => obj['requestSign'] === false || obj['requestSign'] === true 14 | ); 15 | if (requestSignIndex !== -1) { 16 | updatedTourStatus[requestSignIndex] = { requestSign: true }; 17 | } else { 18 | updatedTourStatus.push({ requestSign: true }); 19 | } 20 | } else { 21 | updatedTourStatus = [{ requestSign: true }]; 22 | } 23 | contactRes.set('TourStatus', updatedTourStatus); 24 | const updateRes = await contactRes.save(null, { useMasterKey: true }); 25 | return updateRes; 26 | } else { 27 | throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'contact not found.'); 28 | } 29 | } catch (err) { 30 | console.log('Err in contracts_Contactbook class ', err); 31 | throw err; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/updateTenant.js: -------------------------------------------------------------------------------- 1 | export default async function updateTenant(request) { 2 | const { tenantId, details } = request.params; 3 | 4 | if (!tenantId || !details) { 5 | throw new Parse.Error(400, 'Missing tenantId or details.'); 6 | } 7 | 8 | if (!request.user) { 9 | throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'unauthorized'); 10 | } 11 | try { 12 | const tenant = new Parse.Object('partners_Tenant'); 13 | tenant.id = tenantId; 14 | // Update tenant details 15 | Object.keys(details).forEach(key => { 16 | tenant.set(key, details?.[key]); 17 | }); 18 | 19 | const tenantRes = await tenant.save(null, { useMasterKey: true }); 20 | if (tenantRes) { 21 | const res = JSON.parse(JSON.stringify(tenantRes)); 22 | return res; 23 | } 24 | } catch (error) { 25 | throw error; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/updateTourStatus.js: -------------------------------------------------------------------------------- 1 | export default async function updateTourStatus(request) { 2 | const tourstatus = request.params.TourStatus; 3 | const extUserId = request.params.ExtUserId; 4 | 5 | if (request.user) { 6 | try { 7 | const updateUser = new Parse.Object('contracts_Users'); 8 | updateUser.id = extUserId; 9 | updateUser.set('TourStatus', tourstatus); 10 | const res = await updateUser.save(); 11 | return res; 12 | } catch (err) { 13 | console.log('Err ', err); 14 | const code = err?.code || 400; 15 | const msg = err?.message || 'Something went wrong.'; 16 | throw new Parse.Error(code, msg); 17 | } 18 | } 19 | else { 20 | throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /apps/OpenSignServer/cloud/parsefunction/updatesignaturetype.js: -------------------------------------------------------------------------------- 1 | export default async function updateSignatureType(request) { 2 | if (!request?.user) { 3 | throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); 4 | } 5 | const SignatureType = request.params.SignatureType || []; 6 | if (SignatureType.length > 0) { 7 | const enabledSignTypes = SignatureType?.filter(x => x.enabled); 8 | const isDefaultSignTypeOnly = 9 | enabledSignTypes?.length === 1 && enabledSignTypes[0]?.name === 'default'; 10 | 11 | if (enabledSignTypes.length === 0) { 12 | throw new Parse.Error( 13 | Parse.Error.INVALID_QUERY, 14 | 'At least one signature type should be enabled.' 15 | ); 16 | } else if (isDefaultSignTypeOnly) { 17 | throw new Parse.Error( 18 | Parse.Error.INVALID_QUERY, 19 | 'At least one signature type other than the default should be enabled.' 20 | ); 21 | } else { 22 | try { 23 | const orgQuery = new Parse.Query('contracts_Users'); 24 | orgQuery.equalTo('UserId', { 25 | __type: 'Pointer', 26 | className: '_User', 27 | objectId: request.user.id, 28 | }); 29 | const resUser = await orgQuery.first({ useMasterKey: true }); 30 | if (resUser) { 31 | const newOrg = new Parse.Object('contracts_Users'); 32 | newOrg.id = resUser.id; 33 | newOrg.set('SignatureType', SignatureType); 34 | const updateUserRes = await newOrg.save(null, { useMasterKey: true }); 35 | if (updateUserRes) { 36 | const _updateUserRes = JSON.parse(JSON.stringify(updateUserRes)); 37 | return _updateUserRes; 38 | } 39 | } else { 40 | throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Premission denied.'); 41 | } 42 | } catch (err) { 43 | console.log('err in addorganization', err); 44 | const code = err?.code || 400; 45 | const msg = err?.message || 'Something went wrong.'; 46 | throw new Parse.Error(code, msg); 47 | } 48 | } 49 | } else { 50 | throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide signature types.'); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20231220171155-create_template_cls.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const className = 'contracts_Template'; 7 | const schema = new Parse.Schema(className); 8 | 9 | schema.addString('Name'); 10 | schema.addString('URL'); 11 | schema.addString('Note'); 12 | schema.addString('Description'); 13 | schema.addArray('Signers'); 14 | schema.addBoolean('IsArchive'); 15 | schema.addArray('Placeholders'); 16 | schema.addPointer('Folder', 'contracts_Template'); 17 | schema.addString('Type'); 18 | schema.addPointer('CreatedBy', '_User'); 19 | schema.addPointer('ExtUserPtr', 'contracts_Users'); 20 | schema.addBoolean('EnablePhoneOTP') 21 | schema.addBoolean('EnableEmailOTP') 22 | schema.addBoolean('SendinOrder') 23 | schema.addBoolean('SentToOthers') 24 | schema.addBoolean('AutomaticReminders') 25 | 26 | 27 | 28 | return schema.save(); 29 | }; 30 | 31 | /** 32 | * 33 | * @param {Parse} Parse 34 | */ 35 | exports.down = async Parse => { 36 | const className = 'contracts_Template'; 37 | const schema = new Parse.Schema(className); 38 | 39 | return schema.purge().then(() => schema.delete()); 40 | }; -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20240110100110-update_document_cls_cjs.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const className = 'contracts_Document'; 7 | const schema = new Parse.Schema(className); 8 | schema.addBoolean('IsArchive'); 9 | return schema.update(); 10 | }; 11 | 12 | /** 13 | * 14 | * @param {Parse} Parse 15 | */ 16 | exports.down = async Parse => { 17 | const className = 'contracts_Document'; 18 | const schema = new Parse.Schema(className); 19 | schema.deleteField('IsArchive'); 20 | return schema.update(); 21 | }; -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20240306123606-add_fields_contracts_users_cjs.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const className = 'contracts_Users'; 7 | const schema = new Parse.Schema(className); 8 | schema.addBoolean('HeaderDocId'); 9 | schema.addString('Webhook'); 10 | return schema.update(); 11 | }; 12 | 13 | /** 14 | * 15 | * @param {Parse} Parse 16 | */ 17 | exports.down = async Parse => { 18 | const className = 'contracts_Users'; 19 | const schema = new Parse.Schema(className); 20 | schema.deleteField('HeaderDocId'); 21 | schema.deleteField('Webhook'); 22 | return schema.update(); 23 | }; 24 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20240408133151-update_contracts_document_cls.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const className = 'contracts_Document'; 7 | const schema = new Parse.Schema(className); 8 | schema.addString('RequestBody'); 9 | schema.addString('RequestSubject'); 10 | return schema.update(); 11 | }; 12 | 13 | /** 14 | * 15 | * @param {Parse} Parse 16 | */ 17 | exports.down = async Parse => { 18 | const className = 'contracts_Document'; 19 | const schema = new Parse.Schema(className); 20 | schema.deleteField('RequestBody'); 21 | schema.deleteField('RequestSubject'); 22 | return schema.update(); 23 | }; 24 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20240521184801-update_contracts_document_cls_cjs.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const className = 'contracts_Document'; 7 | const schema = new Parse.Schema(className); 8 | schema.addBoolean('SendMail'); 9 | schema.addBoolean('SendCompletionMail'); 10 | return schema.update(); 11 | }; 12 | 13 | /** 14 | * 15 | * @param {Parse} Parse 16 | */ 17 | exports.down = async Parse => { 18 | const className = 'contracts_Document'; 19 | const schema = new Parse.Schema(className); 20 | schema.deleteField('SendMail'); 21 | schema.deleteField('SendCompletionMail'); 22 | return schema.update(); 23 | }; 24 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20240527194216-update_contracts_template_cls_cjs.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const className = 'contracts_Template'; 7 | const schema = new Parse.Schema(className); 8 | schema.addArray('PublicRole'); 9 | schema.addBoolean('IsPublic'); 10 | return schema.update(); 11 | }; 12 | 13 | /** 14 | * 15 | * @param {Parse} Parse 16 | */ 17 | exports.down = async Parse => { 18 | const className = 'contracts_Template'; 19 | const schema = new Parse.Schema(className); 20 | schema.deleteField('PublicRole'); 21 | schema.deleteField('IsPublic'); 22 | return schema.update(); 23 | }; 24 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20240701151539-update_contracts_users_cls_cjs.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const className = 'contracts_Users'; 7 | const schema = new Parse.Schema(className); 8 | schema.addString('UserName'); 9 | schema.addString('Tagline'); 10 | schema.addBoolean('SearchIndex'); 11 | return schema.update(); 12 | }; 13 | 14 | /** 15 | * 16 | * @param {Parse} Parse 17 | */ 18 | exports.down = async Parse => { 19 | const className = 'contracts_Users'; 20 | const schema = new Parse.Schema(className); 21 | schema.deleteField('UserName'); 22 | schema.deleteField('Tagline'); 23 | schema.deleteField('SearchIndex'); 24 | return schema.update(); 25 | }; 26 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20240708200454-create_multiuser_classes.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const teamschema = new Parse.Schema('contracts_Teams'); 7 | teamschema.addString('Name'); 8 | teamschema.addBoolean('IsActive', { defaultValue: true }); 9 | teamschema.addPointer('ParentId', 'contracts_Teams'); 10 | teamschema.addPointer('OrganizationId', 'contracts_Organizations'); 11 | teamschema.addArray('Ancestors'); 12 | await teamschema.save(); 13 | 14 | const orgSchema = new Parse.Schema('contracts_Organizations'); 15 | orgSchema.addString('Name'); 16 | orgSchema.addBoolean('IsActive', { defaultValue: true }); 17 | orgSchema.addPointer('TenantId', 'partners_Tenant'); 18 | orgSchema.addPointer('CreatedBy', '_User'); 19 | orgSchema.addPointer('ExtUserId', 'contracts_Users'); 20 | await orgSchema.save(); 21 | 22 | const schema = new Parse.Schema('contracts_Users'); 23 | schema.addBoolean('IsDisabled', { defaultValue: false }); 24 | schema.addPointer('OrganizationId', 'contracts_Organizations'); 25 | schema.addArray('TeamIds'); 26 | await schema.update(); 27 | 28 | const templateschema = new Parse.Schema('contracts_Template'); 29 | templateschema.addArray('SharedWith'); 30 | return templateschema.update(); 31 | }; 32 | 33 | /** 34 | * 35 | * @param {Parse} Parse 36 | */ 37 | exports.down = async Parse => { 38 | const teamschema = new Parse.Schema('contracts_Teams'); 39 | await teamschema.purge().then(() => teamschema.delete()); 40 | 41 | const orgSchema = new Parse.Schema('contracts_Organizations'); 42 | await orgSchema.purge().then(() => orgSchema.delete()); 43 | 44 | const schema = new Parse.Schema('contracts_Users'); 45 | schema.deleteField('IsDisabled'); 46 | schema.deleteField('OrganizationId'); 47 | schema.deleteField('TeamIds'); 48 | await schema.update(); 49 | 50 | const templateschema = new Parse.Schema('contracts_Template'); 51 | templateschema.deleteField('SharedWith'); 52 | return templateschema.update(); 53 | }; 54 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20240710163936-addfield_declinereason_doc_cls.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const className = 'contracts_Document'; 7 | const schema = new Parse.Schema(className); 8 | schema.addString('DeclineReason'); 9 | return schema.update(); 10 | }; 11 | 12 | /** 13 | * 14 | * @param {Parse} Parse 15 | */ 16 | exports.down = async Parse => { 17 | const className = 'contracts_Document'; 18 | const schema = new Parse.Schema(className); 19 | schema.deleteField('DeclineReason'); 20 | return schema.update(); 21 | }; 22 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20240723162947-update_teams_org_clp.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const teamSchema = new Parse.Schema('contracts_Teams'); 7 | teamSchema.setCLP({ 8 | get: {}, 9 | find: {}, 10 | count: {}, 11 | create: {}, 12 | update: {}, 13 | delete: {}, 14 | addField: {}, 15 | }); 16 | await teamSchema.update(); 17 | 18 | const orgSchema = new Parse.Schema('contracts_Organizations'); 19 | orgSchema.setCLP({ 20 | get: {}, 21 | find: {}, 22 | count: {}, 23 | create: {}, 24 | update: {}, 25 | delete: {}, 26 | addField: {}, 27 | }); 28 | 29 | return orgSchema.update(); 30 | }; 31 | 32 | /** 33 | * 34 | * @param {Parse} Parse 35 | */ 36 | exports.down = async Parse => { 37 | const teamSchema = new Parse.Schema('contracts_Teams'); 38 | teamSchema.setCLP({ 39 | get: { '*': true }, 40 | find: { '*': true }, 41 | count: { '*': true }, 42 | create: { '*': true }, 43 | update: { '*': true }, 44 | delete: { '*': true }, 45 | addField: { '*': true }, 46 | }); 47 | await teamSchema.update(); 48 | 49 | const orgSchema = new Parse.Schema('contracts_Organizations'); 50 | orgSchema.setCLP({ 51 | get: { '*': true }, 52 | find: { '*': true }, 53 | count: { '*': true }, 54 | create: { '*': true }, 55 | update: { '*': true }, 56 | delete: { '*': true }, 57 | addField: { '*': true }, 58 | }); 59 | 60 | return orgSchema.update(); 61 | }; 62 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20240726114557-update_contracts_template_cjs.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const className = 'contracts_Template'; 7 | const schema = new Parse.Schema(className); 8 | schema.addString('OriginIp'); 9 | return schema.update(); 10 | }; 11 | 12 | /** 13 | * 14 | * @param {Parse} Parse 15 | */ 16 | exports.down = async Parse => { 17 | const className = 'contracts_Template'; 18 | const schema = new Parse.Schema(className); 19 | schema.deleteField('OriginIp'); 20 | return schema.update(); 21 | }; 22 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20240806141611-update_contracts_users_cjs.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const className = 'contracts_Users'; 7 | const schema = new Parse.Schema(className); 8 | schema.addString('Language'); 9 | return schema.update(); 10 | }; 11 | 12 | /** 13 | * 14 | * @param {Parse} Parse 15 | */ 16 | exports.down = async Parse => { 17 | const className = 'contracts_Users'; 18 | const schema = new Parse.Schema(className); 19 | schema.deleteField('Language'); 20 | return schema.update(); 21 | }; 22 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20240822201043-addfield_doccls_cjs.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const className = 'contracts_Document'; 7 | const schema = new Parse.Schema(className); 8 | schema.addBoolean('IsSignyourself'); 9 | return schema.update(); 10 | }; 11 | 12 | /** 13 | * 14 | * @param {Parse} Parse 15 | */ 16 | exports.down = async Parse => { 17 | const className = 'contracts_Document'; 18 | const schema = new Parse.Schema(className); 19 | schema.deleteField('IsSignyourself'); 20 | return schema.update(); 21 | }; 22 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20240903170628-update_contracts_document_cls_cjs.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const className = 'contracts_Document'; 7 | const schema = new Parse.Schema(className); 8 | schema.addPointer('DeclineBy', '_User'); 9 | return schema.update(); 10 | }; 11 | 12 | /** 13 | * 14 | * @param {Parse} Parse 15 | */ 16 | exports.down = async Parse => { 17 | const className = 'contracts_Document'; 18 | const schema = new Parse.Schema(className); 19 | schema.deleteField('DeclineBy'); 20 | return schema.update(); 21 | }; 22 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20240913121404-add_isenableotp_doc.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const templateSchema = new Parse.Schema('contracts_Template'); 7 | templateSchema.addBoolean('IsEnableOTP'); 8 | await templateSchema.update(); 9 | 10 | const className = 'contracts_Document'; 11 | const schema = new Parse.Schema(className); 12 | schema.addBoolean('IsEnableOTP'); 13 | return schema.update(); 14 | }; 15 | 16 | /** 17 | * 18 | * @param {Parse} Parse 19 | */ 20 | exports.down = async Parse => { 21 | const templateSchema = new Parse.Schema('contracts_Template'); 22 | templateSchema.deleteField('IsEnableOTP'); 23 | await templateSchema.update(); 24 | 25 | const className = 'contracts_Document'; 26 | const schema = new Parse.Schema(className); 27 | schema.deleteField('IsEnableOTP'); 28 | return schema.update(); 29 | }; 30 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20240926163209-add_istourenabled_doc_cjs.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async (Parse) => { 6 | const templateSchema = new Parse.Schema('contracts_Template'); 7 | templateSchema.addBoolean('IsTourEnabled'); 8 | await templateSchema.update(); 9 | 10 | const className = 'contracts_Document'; 11 | const schema = new Parse.Schema(className); 12 | schema.addBoolean('IsTourEnabled'); 13 | return schema.update(); 14 | }; 15 | 16 | /** 17 | * 18 | * @param {Parse} Parse 19 | */ 20 | exports.down = async (Parse) => { 21 | const templateSchema = new Parse.Schema('contracts_Template'); 22 | templateSchema.deleteField('IsTourEnabled'); 23 | await templateSchema.update(); 24 | 25 | const className = 'contracts_Document'; 26 | const schema = new Parse.Schema(className); 27 | schema.deleteField('IsTourEnabled'); 28 | return schema.update(); 29 | }; 30 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20241004104551-fileadapterId_doccls.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const className = 'contracts_Document'; 7 | const schema = new Parse.Schema(className); 8 | schema.addString('FileAdapterId'); 9 | return schema.update(); 10 | }; 11 | 12 | /** 13 | * 14 | * @param {Parse} Parse 15 | */ 16 | exports.down = async Parse => { 17 | const className = 'contracts_Document'; 18 | const schema = new Parse.Schema(className); 19 | schema.deleteField('FileAdapterId'); 20 | return schema.update(); 21 | }; 22 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20241106180102-add_signaturetype_field.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const docSchema = new Parse.Schema('contracts_Document'); 7 | docSchema.addArray('SignatureType'); 8 | await docSchema.update(); 9 | 10 | const templateSchema = new Parse.Schema('contracts_Template'); 11 | templateSchema.addArray('SignatureType'); 12 | await templateSchema.update(); 13 | 14 | const extUserSchema = new Parse.Schema('contracts_Users'); 15 | extUserSchema.addArray('SignatureType'); 16 | await extUserSchema.update(); 17 | 18 | const tenantSchema = new Parse.Schema('partners_Tenant'); 19 | tenantSchema.addArray('SignatureType'); 20 | return tenantSchema.update(); 21 | }; 22 | 23 | /** 24 | * 25 | * @param {Parse} Parse 26 | */ 27 | exports.down = async Parse => { 28 | const docSchema = new Parse.Schema('contracts_Document'); 29 | docSchema.deleteField('SignatureType'); 30 | await docSchema.update(); 31 | 32 | const templateSchema = new Parse.Schema('contracts_Template'); 33 | docSchema.deleteField('SignatureType'); 34 | await templateSchema.update(); 35 | 36 | const extUserSchema = new Parse.Schema('contracts_Users'); 37 | docSchema.deleteField('SignatureType'); 38 | await extUserSchema.update(); 39 | 40 | const tenantSchema = new Parse.Schema('partners_Tenant'); 41 | docSchema.deleteField('SignatureType'); 42 | return tenantSchema.update(); 43 | }; 44 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20241114175425-add_notifyonsignatures_field.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const docSchema = new Parse.Schema('contracts_Document'); 7 | docSchema.addBoolean('NotifyOnSignatures'); 8 | await docSchema.update(); 9 | 10 | const templateSchema = new Parse.Schema('contracts_Template'); 11 | templateSchema.addBoolean('NotifyOnSignatures'); 12 | await templateSchema.update(); 13 | 14 | const extUserSchema = new Parse.Schema('contracts_Users'); 15 | extUserSchema.addBoolean('NotifyOnSignatures'); 16 | return extUserSchema.update(); 17 | }; 18 | 19 | /** 20 | * 21 | * @param {Parse} Parse 22 | */ 23 | exports.down = async Parse => { 24 | const docSchema = new Parse.Schema('contracts_Document'); 25 | docSchema.deleteField('NotifyOnSignatures'); 26 | await docSchema.update(); 27 | 28 | const templateSchema = new Parse.Schema('contracts_Template'); 29 | templateSchema.deleteField('NotifyOnSignatures'); 30 | await templateSchema.update(); 31 | 32 | const extUserSchema = new Parse.Schema('contracts_Users'); 33 | docSchema.deleteField('NotifyOnSignatures'); 34 | return extUserSchema.update(); 35 | }; 36 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20241205115937-add_bcc_field_cjs.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const docSchema = new Parse.Schema('contracts_Document'); 7 | docSchema.addArray('Bcc'); 8 | await docSchema.update(); 9 | 10 | const templateSchema = new Parse.Schema('contracts_Template'); 11 | templateSchema.addArray('Bcc'); 12 | await templateSchema.update(); 13 | 14 | }; 15 | 16 | /** 17 | * 18 | * @param {Parse} Parse 19 | */ 20 | exports.down = async Parse => { 21 | const docSchema = new Parse.Schema('contracts_Document'); 22 | docSchema.deleteField('Bcc'); 23 | await docSchema.update(); 24 | 25 | const templateSchema = new Parse.Schema('contracts_Template'); 26 | templateSchema.deleteField('Bcc'); 27 | await templateSchema.update(); 28 | }; 29 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20241205163817-add_redirecturl_field_cjs.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const docSchema = new Parse.Schema('contracts_Document'); 7 | docSchema.addString('RedirectUrl'); 8 | await docSchema.update(); 9 | 10 | const templateSchema = new Parse.Schema('contracts_Template'); 11 | templateSchema.addString('RedirectUrl'); 12 | await templateSchema.update(); 13 | 14 | }; 15 | 16 | /** 17 | * 18 | * @param {Parse} Parse 19 | */ 20 | exports.down = async Parse => { 21 | const docSchema = new Parse.Schema('contracts_Document'); 22 | docSchema.deleteField('RedirectUrl'); 23 | await docSchema.update(); 24 | 25 | const templateSchema = new Parse.Schema('contracts_Template'); 26 | templateSchema.deleteField('RedirectUrl'); 27 | await templateSchema.update(); 28 | }; 29 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20241227112339-update_contracts_document_cls_cjs.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const className = 'contracts_Document'; 7 | const schema = new Parse.Schema(className); 8 | schema.addBoolean('AllowModifications'); 9 | return schema.update(); 10 | }; 11 | 12 | /** 13 | * 14 | * @param {Parse} Parse 15 | */ 16 | exports.down = async Parse => { 17 | const className = 'contracts_Document'; 18 | const schema = new Parse.Schema(className); 19 | schema.deleteField('AllowModifications'); 20 | return schema.update(); 21 | }; 22 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20241227125916-update_contracts_template_cls_cjs.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const className = 'contracts_Template'; 7 | const schema = new Parse.Schema(className); 8 | schema.addBoolean('AllowModifications'); 9 | return schema.update(); 10 | }; 11 | 12 | /** 13 | * 14 | * @param {Parse} Parse 15 | */ 16 | exports.down = async Parse => { 17 | const className = 'contracts_Template'; 18 | const schema = new Parse.Schema(className); 19 | schema.deleteField('AllowModifications'); 20 | return schema.update(); 21 | }; 22 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20250411095519-add_templateid_field.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const className = 'contracts_Document'; 7 | const schema = new Parse.Schema(className); 8 | schema.addPointer('TemplateId', 'contracts_Template'); 9 | return schema.update(); 10 | }; 11 | 12 | /** 13 | * 14 | * @param {Parse} Parse 15 | */ 16 | exports.down = async Parse => { 17 | const className = 'contracts_Document'; 18 | const schema = new Parse.Schema(className); 19 | schema.deleteField('TemplateId'); 20 | return schema.update(); 21 | }; 22 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/migrations/20250427105912-add_name_field_user_cls.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * @param {Parse} Parse 4 | */ 5 | exports.up = async Parse => { 6 | const userschema = new Parse.Schema('_User'); 7 | userschema.addString('name'); 8 | return userschema.update(null, { useMasterKey: true }); 9 | }; 10 | 11 | /** 12 | * 13 | * @param {Parse} Parse 14 | */ 15 | exports.down = async Parse => { 16 | const userschema = new Parse.Schema('_User'); 17 | userschema.deleteField('name'); 18 | return userschema.update(null, { useMasterKey: true }); 19 | }; 20 | -------------------------------------------------------------------------------- /apps/OpenSignServer/databases/seeders/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSignServer/databases/seeders/.gitkeep -------------------------------------------------------------------------------- /apps/OpenSignServer/exports/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSignServer/exports/.gitkeep -------------------------------------------------------------------------------- /apps/OpenSignServer/files/custom_email.html: -------------------------------------------------------------------------------- 1 | <html> 2 | 3 | <head> 4 | <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 5 | </head> 6 | 7 | <body style="text-align: center;"> 8 | <p style="font-weight: bolder; font-size: large;">Hello!</p> 9 | <p>This is a notice regarding your account:</p> 10 | <p>{{username}}</p> 11 | <br /> 12 | <p>{{appName}}</p> 13 | </body> 14 | 15 | </html> -------------------------------------------------------------------------------- /apps/OpenSignServer/files/custom_email.txt: -------------------------------------------------------------------------------- 1 | Hello! 2 | 3 | This is a notice regarding your account: 4 | {{username}} 5 | 6 | {{appName}} -------------------------------------------------------------------------------- /apps/OpenSignServer/files/custom_email_subject.txt: -------------------------------------------------------------------------------- 1 | Account Notice - {{appName}} -------------------------------------------------------------------------------- /apps/OpenSignServer/files/password_reset_email.html: -------------------------------------------------------------------------------- 1 | <html> 2 | 3 | <head> 4 | <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 5 | </head> 6 | 7 | <body style="text-align: center;"> 8 | <p style="font-weight: bolder; font-size: large;">Hello!</p> 9 | <p>You requested to reset the password for your account:</p> 10 | <p>{{username}}</p> 11 | <p>Click the button below to reset your password:</p> 12 | <br /> 13 | <p><a href="{{{link}}}" style="background-color: lightskyblue; cursor: pointer; border-radius: 5px; padding: 10px; border-style: solid; border-width: 2px; text-decoration: none; font-weight: bolder; color:blue">Verify email</a></p> 14 | <br /> 15 | </body> 16 | 17 | </html> -------------------------------------------------------------------------------- /apps/OpenSignServer/files/password_reset_email.txt: -------------------------------------------------------------------------------- 1 | Hello! 2 | 3 | You requested to reset the password for your account: 4 | 5 | {{username}} 6 | 7 | Click the link below to reset your password: 8 | 9 | {{{link}}} 10 | -------------------------------------------------------------------------------- /apps/OpenSignServer/files/password_reset_email_subject.txt: -------------------------------------------------------------------------------- 1 | Password Reset 2 | -------------------------------------------------------------------------------- /apps/OpenSignServer/files/verification_email.html: -------------------------------------------------------------------------------- 1 | <html> 2 | 3 | <head> 4 | <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 5 | </head> 6 | 7 | <body style="text-align: center;"> 8 | <p style="font-weight: bolder; font-size: large;">Welcome!</p> 9 | <p>Click the button below to verify your email address:</p> 10 | <br /> 11 | <p><a href="{{{link}}}" style="background-color: lightskyblue; cursor: pointer; border-radius: 5px; padding: 10px; border-style: solid; border-width: 2px; text-decoration: none; font-weight: bolder; color:blue">Verify email</a></p> 12 | <br /> 13 | <p>{{appName}}</p> 14 | </body> 15 | 16 | </html> 17 | -------------------------------------------------------------------------------- /apps/OpenSignServer/files/verification_email.txt: -------------------------------------------------------------------------------- 1 | Welcome! 2 | 3 | Click the link below to verify your email address: 4 | 5 | {{{link}}} 6 | 7 | {{appName}} -------------------------------------------------------------------------------- /apps/OpenSignServer/files/verification_email_subject.txt: -------------------------------------------------------------------------------- 1 | {{appName}} Email Address Verification -------------------------------------------------------------------------------- /apps/OpenSignServer/font/times.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSignServer/font/times.ttf -------------------------------------------------------------------------------- /apps/OpenSignServer/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "commonjs" 5 | } 6 | } -------------------------------------------------------------------------------- /apps/OpenSignServer/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSignServer/logo.png -------------------------------------------------------------------------------- /apps/OpenSignServer/migrationdb/createContactIndex.js: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv'; 2 | import { MongoClient } from 'mongodb'; 3 | import { generateId } from '../Utils.js'; 4 | dotenv.config(); 5 | 6 | export default async function createContactIndex() { 7 | // Provide the complete MongoDB connection URL with the database name 8 | const uri = process.env.MONGODB_URI || 'mongodb://localhost:27017/dev'; // Replace with your MongoDB URI 9 | const client = new MongoClient(uri); 10 | try { 11 | await client.connect(); 12 | const database = client.db(); 13 | 14 | const migrationCollection = database.collection('Migrationdb'); 15 | const migrationName = 'contactIndex_1'; 16 | 17 | // Check if the migration has already been executed 18 | const migrationExists = await migrationCollection.findOne({ name: migrationName }); 19 | 20 | if (migrationExists) { 21 | console.log(' INFO No migrations were executed, database schema was already up to date.'); 22 | console.log(' SUCCESS Successfully ran indexed migrations directly on db.'); 23 | return; 24 | } 25 | 26 | const collection = database.collection('contracts_Contactbook'); 27 | 28 | // Create the unique index, but only on documents where IsImported is true 29 | const query = { 30 | IsImported: { $eq: true }, 31 | $or: [{ IsDeleted: false }, { IsDeleted: { $eq: false } }], 32 | }; // Include documents with IsImported: true and IsDeleted not true 33 | await collection.createIndex( 34 | { _p_CreatedBy: 1, Email: 1, IsImported: 1 }, 35 | { unique: true, partialFilterExpression: query } 36 | ); 37 | 38 | // Save the migration record in the migrationdb collection 39 | await migrationCollection.insertOne({ 40 | _id: generateId(10), 41 | name: migrationName, 42 | _created_at: new Date(), 43 | _updated_at: new Date(), 44 | executedAt: new Date(), 45 | details: 'Created unique index on CreatedBy, IsImported, Email', 46 | }); 47 | 48 | const migrationdb = database.collection('_SCHEMA'); 49 | // create migrationdb SCHEM migrationdb 50 | 51 | // Document to be inserted 52 | const schemaDocument = { 53 | _id: 'Migrationdb', 54 | objectId: 'string', 55 | name: 'string', 56 | updatedAt: 'date', 57 | createdAt: 'date', 58 | executedAt: 'date', 59 | details: 'string', 60 | }; 61 | 62 | // Insert the document 63 | await migrationdb.insertOne(schemaDocument); 64 | console.log(' Unique index created successfully.'); 65 | console.log(' SUCCESS Successfully ran indexed migrations directly on db.'); 66 | } catch (error) { 67 | console.log(' ERROR running indexed migration:', error); 68 | } finally { 69 | await client.close(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /apps/OpenSignServer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "open_sign_server", 3 | "version": "2.21.1", 4 | "description": "An example Parse API server using the parse-server module", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/ParsePlatform/parse-server-example" 8 | }, 9 | "license": "MIT", 10 | "main": "index.js", 11 | "scripts": { 12 | "coverage": "TESTING=true nyc jasmine", 13 | "lint": "eslint --cache ./cloud && eslint --cache index.js && eslint --cache ./spec", 14 | "lint-fix": "eslint --cache --fix ./cloud && eslint --cache --fix index.js && eslint --cache --fix ./spec", 15 | "prettier": "prettier --write '{cloud,spec}/{**/*,*}.js' 'index.js'", 16 | "start": "node index.js", 17 | "test": "mongodb-runner start && TESTING=true jasmine", 18 | "watch": "nodemon index.js" 19 | }, 20 | "dependencies": { 21 | "@aws-sdk/client-s3": "^3.824.0", 22 | "@aws-sdk/s3-request-presigner": "^3.824.0", 23 | "@parse/fs-files-adapter": "^3.0.0", 24 | "@parse/s3-files-adapter": "^4.1.1", 25 | "@pdf-lib/fontkit": "^1.1.1", 26 | "@signpdf/placeholder-pdf-lib": "^3.2.6", 27 | "@signpdf/signer-p12": "^3.2.4", 28 | "@signpdf/signpdf": "^3.2.5", 29 | "aws-sdk": "^2.1692.0", 30 | "axios": "^1.9.0", 31 | "cors": "^2.8.5", 32 | "date-fns-tz": "^3.2.0", 33 | "dotenv": "^16.5.0", 34 | "express": "^5.1.0", 35 | "form-data": "^4.0.2", 36 | "generate-api-key": "^1.0.2", 37 | "googleapis": "^149.0.0", 38 | "mailgun.js": "^12.0.2", 39 | "mongodb": "^6.17.0", 40 | "multer": "^2.0.1", 41 | "multer-s3": "^3.0.1", 42 | "node-forge": "^1.3.1", 43 | "nodemailer": "^7.0.3", 44 | "parse": "^6.1.1", 45 | "parse-dbtool": "^1.2.0", 46 | "parse-server": "^8.2.1", 47 | "parse-server-api-mail-adapter": "^4.1.0", 48 | "pdf-lib": "^1.17.1", 49 | "posthog-node": "^4.18.0", 50 | "qrcode": "^1.5.4", 51 | "rate-limiter-flexible": "^7.1.1", 52 | "speakeasy": "^2.0.0", 53 | "ws": "^8.18.2" 54 | }, 55 | "type": "module", 56 | "devDependencies": { 57 | "@babel/eslint-parser": "^7.27.5", 58 | "eslint": "^9.28.0", 59 | "jasmine": "^5.7.1", 60 | "mongodb-runner": "^5.8.3", 61 | "nodemon": "^3.1.10", 62 | "nyc": "^17.1.0", 63 | "prettier": "^3.5.3" 64 | }, 65 | "overrides": { 66 | "ws": "$ws", 67 | "parse": "$parse" 68 | }, 69 | "engines": { 70 | "node": "18 || 20 || 22" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /apps/OpenSignServer/pdfFile/emudhra-test-class2.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSignServer/pdfFile/emudhra-test-class2.pfx -------------------------------------------------------------------------------- /apps/OpenSignServer/pdfFile/sample.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSignServer/pdfFile/sample.pdf -------------------------------------------------------------------------------- /apps/OpenSignServer/public/assets/images/parse-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/apps/OpenSignServer/public/assets/images/parse-logo.png -------------------------------------------------------------------------------- /apps/OpenSignServer/scalingo.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Parse Server Example", 3 | "description": "An example Parse API server using the parse-server module", 4 | "repository": "https://github.com/ParsePlatform/parse-server-example", 5 | "logo": "https://avatars0.githubusercontent.com/u/1294580?v=3&s=200", 6 | "env": { 7 | "PARSE_MOUNT": { 8 | "description": "Configure Parse API route.", 9 | "value": "/parse" 10 | }, 11 | "APP_ID": { 12 | "description": "A unique identifier for your app.", 13 | "value": "" 14 | }, 15 | "MASTER_KEY": { 16 | "description": "A key that overrides all permissions. Keep this secret.", 17 | "value": "" 18 | }, 19 | "DATABASE_URI": { 20 | "description": "Connection string for your database.", 21 | "value": "$SCALINGO_MONGO_URL" 22 | } 23 | }, 24 | "addons": ["scalingo-mongodb"] 25 | } 26 | -------------------------------------------------------------------------------- /apps/OpenSignServer/spec/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jasmine": true 4 | }, 5 | "globals": { 6 | "Parse": true 7 | }, 8 | "rules": { 9 | "no-console": [0], 10 | "no-var": "error" 11 | } 12 | } -------------------------------------------------------------------------------- /apps/OpenSignServer/spec/Tests.spec.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | describe('Parse Server example', () => { 3 | Parse.User.enableUnsafeCurrentUser(); 4 | it('call function', async () => { 5 | const result = await Parse.Cloud.run('hello'); 6 | expect(result).toBe('Hi'); 7 | }); 8 | it('call async function', async () => { 9 | const result = await Parse.Cloud.run('asyncFunction'); 10 | expect(result).toBe('Hi async'); 11 | }); 12 | it('failing test', async () => { 13 | const obj = new Parse.Object('Test'); 14 | try { 15 | await obj.save(); 16 | fail('should not have been able to save test object.'); 17 | } catch (e) { 18 | expect(e).toBeDefined(); 19 | expect(e.code).toBe(9001); 20 | expect(e.message).toBe('Saving test objects is not available.'); 21 | } 22 | }); 23 | it('coverage for /', async () => { 24 | const { data, headers } = await axios.get('http://localhost:30001/'); 25 | expect(headers['content-type']).toContain('text/html'); 26 | expect(data).toBe('I dream of being a website. Please star the parse-server repo on GitHub!'); 27 | }); 28 | it('coverage for /test', async () => { 29 | const { data, headers } = await axios.get('http://localhost:30001/test'); 30 | expect(headers['content-type']).toContain('text/html'); 31 | expect(data).toContain('<title>Parse Server Example'); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /apps/OpenSignServer/spec/helper.js: -------------------------------------------------------------------------------- 1 | import { startParseServer, stopParseServer, dropDB } from './utils/test-runner.js'; 2 | beforeAll(async () => { 3 | await startParseServer(); 4 | }, 100 * 60 * 2); 5 | 6 | afterAll(async () => { 7 | await dropDB(); 8 | await stopParseServer(); 9 | }); 10 | -------------------------------------------------------------------------------- /apps/OpenSignServer/spec/support/jasmine.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec_dir": "spec", 3 | "spec_files": [ 4 | "**/*[sS]pec.js" 5 | ], 6 | "helpers": ["helper.js"], 7 | "stopSpecOnExpectationFailure": false, 8 | "random": false 9 | } 10 | -------------------------------------------------------------------------------- /apps/OpenSignServer/spec/utils/test-runner.js: -------------------------------------------------------------------------------- 1 | import http from 'http'; 2 | import { ParseServer } from 'parse-server'; 3 | import { app, config } from '../../index.js'; 4 | 5 | export const dropDB = async () => { 6 | await Parse.User.logOut(); 7 | return await Parse.Server.database.deleteEverything(true); 8 | }; 9 | let parseServerState = {}; 10 | 11 | /** 12 | * Starts the ParseServer instance 13 | * @param {Object} parseServerOptions Used for creating the `ParseServer` 14 | * @return {Promise} Runner state 15 | */ 16 | export async function startParseServer() { 17 | delete config.databaseAdapter; 18 | const parseServerOptions = Object.assign(config, { 19 | databaseURI: 'mongodb://localhost:27017/parse-test', 20 | masterKey: 'test', 21 | javascriptKey: 'test', 22 | appId: 'test', 23 | port: 30001, 24 | mountPath: '/test', 25 | serverURL: `http://localhost:30001/test`, 26 | logLevel: 'error', 27 | silent: true, 28 | }); 29 | const parseServer = new ParseServer(parseServerOptions); 30 | await parseServer.start(); 31 | app.use(parseServerOptions.mountPath, parseServer.app); 32 | const httpServer = http.createServer(app); 33 | await new Promise(resolve => httpServer.listen(parseServerOptions.port, resolve)); 34 | Object.assign(parseServerState, { 35 | parseServer, 36 | httpServer, 37 | parseServerOptions, 38 | }); 39 | return parseServerOptions; 40 | } 41 | 42 | /** 43 | * Stops the ParseServer instance 44 | * @return {Promise} 45 | */ 46 | export async function stopParseServer() { 47 | await new Promise(resolve => parseServerState.httpServer.close(resolve)); 48 | parseServerState = {}; 49 | } 50 | -------------------------------------------------------------------------------- /apps/localstack/localstack-script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | awslocal s3api \ 4 | create-bucket --bucket my-bucket \ 5 | --create-bucket-configuration LocationConstraint=eu-central-1 \ 6 | --region eu-central-1 -------------------------------------------------------------------------------- /apps/mongo/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official MongoDB imagee 2 | FROM mongo:latest 3 | 4 | # Create a directory to host initialization scripts 5 | # RUN mkdir -p /docker-entrypoint-initdb.d 6 | 7 | # Copy default data JSON files to the initialization directory 8 | # COPY ./default-data/*.json /docker-entrypoint-initdb.d/ 9 | 10 | # Execute the initialization script during container startup 11 | CMD ["mongod"] -------------------------------------------------------------------------------- /cert/local_dev.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDxTCCAq2gAwIBAgIUAZQ9QST2eBZz5k9pCa//mHQVm1QwDQYJKoZIhvcNAQEL 3 | BQAwcjELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldyBZb3JrMRYwFAYDVQQKDA1P 4 | cGVuU2lnbiBMYWJzMREwDwYDVQQDDAhPcGVuU2lnbjElMCMGCSqGSIb3DQEJARYW 5 | aGVsbG9Ab3BlbnNpZ25sYWJzLmNvbTAeFw0yMzExMTcxOTQ0MzZaFw0yNDExMTYx 6 | OTQ0MzZaMHIxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazEWMBQGA1UE 7 | CgwNT3BlblNpZ24gTGFiczERMA8GA1UEAwwIT3BlblNpZ24xJTAjBgkqhkiG9w0B 8 | CQEWFmhlbGxvQG9wZW5zaWdubGFicy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB 9 | DwAwggEKAoIBAQDLRKLjCSrvV1zAY2leWxCvdKTUaThpQO8nWPE/4v5ilMU2aRON 10 | krAnli0LUfQ/tIRWNurxwVjJ7HrW3ICLn05GBLjm2AUCfuMbJCvOEEdmFJqRY04n 11 | 12fBZWz2KtzbZEIBVxLhc4E4g+Rz68hQA2fqwNuFdyGkkIbhVXpZIePEppqRmw+2 12 | uuDvIzJ77rdDaLqwwPoHWenZ2uZTedKXx7PGYs98psg35GvcpssqAJGAIAi9Rzg/ 13 | ZV4oSpGXCjn/TJy61zTDbmPFCS/rC1XBWzBQfwDeBPDI18W6gPVX6N4dp0Ao+n8Q 14 | txeI6g4FzMH91KPbdbZXxm8rZqXj87VQs9trAgMBAAGjUzBRMB0GA1UdDgQWBBRN 15 | NtRPagnqovdmbrdhSUON9Rwd3TAfBgNVHSMEGDAWgBRNNtRPagnqovdmbrdhSUON 16 | 9Rwd3TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBrcPyA7GHq 17 | +ol9qjy9HijpoU5MaEiV9v9H8/0v7KoXUND3rMl37yFyjxGN8uda8WxpEhE2eFnk 18 | Xjtm2NgPrv8qeYHor/A3y5RfQhnlCY4kE1bREZGhcuKihR4rOtmComwFjx4Ibwwj 19 | 4nKsflj6rXcWKSfSNHLkXFXHPYwyqRsKdtClrD/8YJn6LeeMlMjIYGGP4qyi2WwS 20 | Z8hExWGEMaMWiA9rxTMX6iu/parGH/pWU/55Os3WlcHPhlvqchBkQFJ+f8b+NYz0 21 | vIHQgwn4K5WMsZVLxoiUcU7cSqE/qhnP+pzBf8lRaYwd+zUznDvzpKKz6nFgeHok 22 | s8BK7JAKkIsl 23 | -----END CERTIFICATE----- 24 | -------------------------------------------------------------------------------- /cert/local_dev.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-EDE3-CBC,5932FA9F02938D1B 4 | 5 | 6gkkZP80WrVnR3dVwkimtfirSeYNXZlrXXBv+Ddjyq7FScqca7svJFd8ObC0xXjg 6 | nbxChDKS8SXcSjsNCTzixG0QReO5vaqovrpQoYbkSQ9GRMIVV8WuWNzlyMnypNJ4 7 | Z6hdpd5nkc1hIP8A6WcY3lA8msyHceU9IidEEb75CE743qsvUMko+XvqntZx/LNe 8 | 0uK8Bs+cXeDzolpNX8ORKBTAOMii+WPlN2l+9ldsWTECEk4GmWa51QfpKouLxz5E 9 | u7fuGuku/nPKGb4Yj7sCgaD91UabRksDLS9UGDBqeNL+mogG3gY/Iu0/q2cUs6XR 10 | x1W6KW1eJHnOf1pAp7+G/zNqzdsxI3pnoR+nsV6/G0glBLar+Xqc99B568lnUWWv 11 | 5NGN3Buz1axx1jPKIknDcT4Skmvt6TcmgrjoCQ9XvmNKVClpExQ+dKPmJI/EVNoU 12 | jjDZyfghcJ6BEMcTgoOBDiUrPM0273sb89u/AzyUA1tTciW2KARz6cOUTPWVRSRn 13 | 4yXpkA4BJDpxRmK68BXorZLNxtvHk+on80c9tu5e+IVa3Gu1K7gc269TPmlIolAM 14 | v//i+jpwJ4Pe2IGVYdojMr15useUv7JC6/Kconnkx4q9rkMriguuXoQt8qW3kvEs 15 | Ur8KuibtDqhz2zGbsKboQFMOd6cdd+63dJCGAUqk4hFrSikTw3LhkXNcg9GZHq1z 16 | WFAfaA3x2jd+kJJ5xg8xMVEHax5IAOXDL2ektyPFpI1UZOuTc2i7FK6Y0NnXPkSN 17 | G/+DsKdLWT943mPfZj01mTRi3MgseDXsHMHGmmPxW9J4jv+WW4OdpHvd474QzJ8b 18 | vQmyiKq4XPHsuQUkzHVPbTrGe1B9z4p97YQKdEfXP2ChGFsUHCYQdpHyXwie2+pq 19 | E2rc7Bai0OuL0anYMSIvDrJ0DE8mVVaFur1b2Uz6ieCDlrOXnzD1BrcleiZem7og 20 | on/3oCUK+qxibCCcd5Hg2HtPusa43U0/UbCrqb2sww9iN36DLdtef/jlozKNiBx9 21 | e8ZztF6GtAKhIdgGW/M9TXMUZOYr7aaXO71MsqhwpVWZ5dNqagOBzhmd7Etokf1H 22 | O0WxJZ5XPDsnlKwYq86ssK+2RCNYAuvZoM3dRCysPvXoBFmsmHDzKdbKcckd8Vxr 23 | 6P1yaCkB/iX55dUqwKfIuqxY3m4MA2Us9DD0BRxZOSUqHhzHHSogsLhqLQEQENA4 24 | U9N/v/za81UaU8nfkAswsGlTdTKjWH6Fzc0NeagbEFtpUaG/WIHPesUzU7ZNvMzR 25 | SQACVWl62Dley2g2eAgcrsIn78MbwNJI2YN5eXIw0K2MiMlGXWOEvkjIIVfjc0/d 26 | QxLFJwqShZBVO+jnAJ8jti7mP6PgskjtfHNAIphRJ4fm9h/Ns1YMpipBRVBDhbwY 27 | AOVT4bRw9Q3O5I0pe10ZdViAhexegQ8yGt3pUlKaUf9GRwIFEknjZ5Pc98V/R/wC 28 | INSIOZzDHj2uyZ3p7ToF1Z6kVVbbyfqDOnxUiV2ykR8ZqQMXNWcEJKH8qRqI+VHf 29 | oszD2BQvXgkWdrJtHuHjJCdDl03Y+daS5yP8IKTDucHvRv4ZSMghZ34SJTw5MaBP 30 | -----END RSA PRIVATE KEY----- 31 | -------------------------------------------------------------------------------- /cert/local_dev.pfx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSignLabs/OpenSign/b0e0b38e8f976cebfc23a4954d764179f7a9bcec/cert/local_dev.pfx -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | server: 3 | image: opensign/opensignserver:main 4 | container_name: OpenSignServer-container 5 | volumes: 6 | - opensign-files:/usr/src/app/files 7 | ports: 8 | - "8080:8080" 9 | depends_on: 10 | - mongo 11 | env_file: .env.prod 12 | environment: 13 | - NODE_ENV=production 14 | - SERVER_URL=${HOST_URL:-https://localhost:3001}/api/app 15 | - PUBLIC_URL=${HOST_URL:-https://localhost:3001} 16 | networks: 17 | - app-network 18 | mongo: 19 | image: mongo:latest 20 | container_name: mongo-container 21 | volumes: 22 | - data-volume:/data/db 23 | ports: 24 | - "27018:27017" 25 | networks: 26 | - app-network 27 | client: 28 | image: opensign/opensign:main 29 | container_name: OpenSign-container 30 | depends_on: 31 | - server 32 | env_file: .env.prod 33 | ports: 34 | - "3000:3000" 35 | networks: 36 | - app-network 37 | caddy: 38 | image: caddy:latest 39 | container_name: caddy-container 40 | ports: 41 | - "3001:3001" 42 | - "80:80" 43 | - "443:443" 44 | - "443:443/udp" 45 | volumes: 46 | - ./Caddyfile:/etc/caddy/Caddyfile 47 | - caddy_data:/data 48 | - caddy_config:/config 49 | networks: 50 | - app-network 51 | environment: 52 | - HOST_URL=${HOST_URL:-localhost:3001} 53 | networks: 54 | app-network: 55 | driver: bridge 56 | 57 | volumes: 58 | data-volume: 59 | web-root: 60 | caddy_data: 61 | caddy_config: 62 | opensign-files: 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "opensign", 3 | "version": "1.0.6", 4 | "description": "Free and open source alternative to DocuSign", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "prepare": "husky install", 9 | "lint-staged-changes": "lint-staged" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "github.com/opensignlabs/opensign" 14 | }, 15 | "keywords": [ 16 | "digital", 17 | "signature", 18 | "e-signature", 19 | "docusign", 20 | "electronic", 21 | "signature" 22 | ], 23 | "author": "OpenSignLabs", 24 | "license": "AGPL-3.0", 25 | "devDependencies": { 26 | "husky": "^8.0.0", 27 | "lint-staged": "^15.1.0", 28 | "prettier": "3.1.0" 29 | } 30 | } 31 | --------------------------------------------------------------------------------