├── .env.tpl
├── .github
├── ACKNOWLEDGMENTS.md
├── AUTHORS.md
├── CHANGELOG.md
├── CODEOWNERS.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── CONTRIBUTORS.md
├── FUNDING.md
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.yaml
│ ├── config.yml
│ ├── documentation_change.yaml
│ ├── feature_request.yaml
│ └── housekeeping.yaml
├── PULL_REQUEST_TEMPLATE.md
├── SECURITY.md
├── SUPPORT.md
├── dependabot.yml
├── labels.yml
└── workflows
│ ├── codeql-analysis.yml
│ ├── deployment-testing.yml
│ ├── image-build.yml
│ ├── lock.yml
│ ├── mega-linter.yml
│ ├── project-release-test.yml
│ ├── project-release.yml
│ ├── python-build.yml
│ └── stale.yml
├── .gitignore
├── LICENSE
├── README.md
├── conf
└── config.tpl.yml
├── deployment
├── bare-metal
│ ├── linux
│ │ └── debian.sh
│ └── shared
│ │ ├── collect_inputs.sh
│ │ ├── create_config.sh
│ │ └── detect_os.sh
└── setup.sh
├── docs
├── README.md
├── announcements
│ ├── 2022-12-09-docker-hub-repository-moved.md
│ ├── 2022-12-09-pda-project-update.md
│ ├── 2023-03-10-docker-images-and-repository-branches.md
│ ├── 2023-03-11-release-v0.4.0.md
│ ├── 2023-03-19-new-contribution-policy.md
│ ├── 2023-04-11-release-0.4.1.md
│ ├── 2023-11-25-pda-project-update.md
│ └── 2024-01-31-release-0.4.2.md
└── wiki
│ ├── README.md
│ ├── configuration
│ ├── README.md
│ └── settings
│ │ ├── README.md
│ │ ├── environment-settings.md
│ │ └── runtime-settings.md
│ ├── contributing
│ ├── README.md
│ └── labeling-standards.md
│ ├── deployment
│ ├── README.md
│ ├── bare-metal
│ │ ├── README.md
│ │ ├── bsd
│ │ │ ├── README.md
│ │ │ └── freebsd.md
│ │ ├── linux
│ │ │ ├── README.md
│ │ │ ├── alma-linux.md
│ │ │ ├── alpine-linux.md
│ │ │ ├── arch-linux.md
│ │ │ ├── centos-linux.md
│ │ │ ├── debian-linux.md
│ │ │ ├── fedora-linux.md
│ │ │ ├── opensuse-linux.md
│ │ │ ├── oracle-linux.md
│ │ │ ├── redhat-enterprise-linux.md
│ │ │ ├── rocky-linux.md
│ │ │ ├── suse-linux-enterprise-server.md
│ │ │ └── ubuntu-linux.md
│ │ ├── macos
│ │ │ └── README.md
│ │ ├── solaris
│ │ │ └── README.md
│ │ └── windows
│ │ │ └── README.md
│ ├── docker
│ │ ├── README.md
│ │ ├── docker-compose.md
│ │ ├── docker-swarm.md
│ │ └── docker.md
│ └── kubernetes
│ │ ├── README.md
│ │ ├── helm-charts.md
│ │ ├── k3s.md
│ │ ├── kubeadm.md
│ │ ├── minikube.md
│ │ └── rancher.md
│ ├── project
│ ├── README.md
│ ├── features.md
│ ├── milestones.md
│ ├── releases.md
│ └── roadmap.md
│ ├── support
│ └── README.md
│ └── testing
│ └── README.md
├── setup.py
└── src
├── .babelrc
├── .gitignore
├── apps
├── __init__.py
├── api
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── helpers.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── permissions.py
│ ├── schema.py
│ └── tests
│ │ ├── __init__.py
│ │ └── test_schema.py
├── users
│ ├── __init__.py
│ ├── adapter.py
│ ├── admin.py
│ ├── apps.py
│ ├── forms.py
│ ├── helpers.py
│ ├── mailing_list.py
│ ├── management
│ │ ├── __init__.py
│ │ └── commands
│ │ │ ├── __init__.py
│ │ │ └── promote_user_to_superuser.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_customuser_language.py
│ │ └── __init__.py
│ ├── models.py
│ ├── serializers.py
│ ├── signals.py
│ ├── urls.py
│ └── views.py
├── utils
│ ├── __init__.py
│ ├── decorators.py
│ ├── models.py
│ ├── slug.py
│ └── tests
│ │ ├── __init__.py
│ │ └── test_slugs.py
└── web
│ ├── __init__.py
│ ├── context_processors.py
│ ├── forms.py
│ ├── locale_middleware.py
│ ├── meta.py
│ ├── migrations
│ ├── 0001_initial.py
│ └── __init__.py
│ ├── sitemaps.py
│ ├── templatetags
│ ├── __init__.py
│ ├── form_tags.py
│ └── meta_tags.py
│ ├── tests
│ ├── __init__.py
│ └── test_api_schema.py
│ ├── urls.py
│ └── views.py
├── assets
├── javascript
│ ├── api-client
│ │ ├── .openapi-generator-ignore
│ │ ├── .openapi-generator
│ │ │ └── VERSION
│ │ ├── apis
│ │ │ └── index.ts
│ │ ├── index.ts
│ │ ├── models
│ │ │ └── index.ts
│ │ └── runtime.ts
│ ├── api.js
│ ├── app.js
│ ├── htmx.js
│ ├── site.js
│ └── utilities
│ │ ├── LoadError.js
│ │ ├── Loading.js
│ │ └── ValidationErrors.js
├── material-dashboard
│ ├── LICENSE.md
│ └── scss
│ │ └── material-dashboard
│ │ ├── _accordion.scss
│ │ ├── _alert.scss
│ │ ├── _avatars.scss
│ │ ├── _backgrounds.scss
│ │ ├── _badge.scss
│ │ ├── _breadcrumbs.scss
│ │ ├── _buttons.scss
│ │ ├── _cards-extend.scss
│ │ ├── _cards.scss
│ │ ├── _components.scss
│ │ ├── _dark-version.scss
│ │ ├── _dropdown-extend.scss
│ │ ├── _dropdown.scss
│ │ ├── _dropup.scss
│ │ ├── _fixed-plugin.scss
│ │ ├── _floating-elements.scss
│ │ ├── _footer.scss
│ │ ├── _forms.scss
│ │ ├── _gradients.scss
│ │ ├── _header.scss
│ │ ├── _icons.scss
│ │ ├── _info-areas.scss
│ │ ├── _list-check.scss
│ │ ├── _misc-extend.scss
│ │ ├── _misc.scss
│ │ ├── _nav.scss
│ │ ├── _navbar-vertical.scss
│ │ ├── _navbar.scss
│ │ ├── _pagination.scss
│ │ ├── _popovers.scss
│ │ ├── _progress.scss
│ │ ├── _ripple.scss
│ │ ├── _rtl-extend.scss
│ │ ├── _rtl.scss
│ │ ├── _social-buttons.scss
│ │ ├── _tables.scss
│ │ ├── _tilt.scss
│ │ ├── _timeline.scss
│ │ ├── _tooltips.scss
│ │ ├── _typography.scss
│ │ ├── _utilities-extend.scss
│ │ ├── _utilities.scss
│ │ ├── _variables.scss
│ │ ├── badges
│ │ ├── _badge-circle.scss
│ │ ├── _badge-dot.scss
│ │ ├── _badge-floating.scss
│ │ └── _badge.scss
│ │ ├── cards
│ │ ├── card-background.scss
│ │ ├── card-blog.scss
│ │ ├── card-horizontal.scss
│ │ ├── card-pricing.scss
│ │ ├── card-profile.scss
│ │ └── card-rotate.scss
│ │ ├── custom
│ │ ├── _styles.scss
│ │ └── _variables.scss
│ │ ├── forms
│ │ ├── _form-check.scss
│ │ ├── _form-select.scss
│ │ ├── _form-switch.scss
│ │ ├── _forms.scss
│ │ ├── _input-group.scss
│ │ ├── _inputs.scss
│ │ └── _labels.scss
│ │ ├── mixins
│ │ ├── _badge.scss
│ │ ├── _buttons.scss
│ │ ├── _colored-shadows.scss
│ │ ├── _hover.scss
│ │ ├── _social-buttons.scss
│ │ ├── _vendor.scss
│ │ └── mixins.scss
│ │ ├── theme.scss
│ │ └── variables
│ │ ├── _animations.scss
│ │ ├── _avatars.scss
│ │ ├── _badge.scss
│ │ ├── _breadcrumb.scss
│ │ ├── _cards-extend.scss
│ │ ├── _cards.scss
│ │ ├── _choices.scss
│ │ ├── _dark-version.scss
│ │ ├── _dropdowns.scss
│ │ ├── _fixed-plugin.scss
│ │ ├── _form-switch.scss
│ │ ├── _full-calendar.scss
│ │ ├── _header.scss
│ │ ├── _info-areas.scss
│ │ ├── _misc-extend.scss
│ │ ├── _misc.scss
│ │ ├── _navbar-vertical.scss
│ │ ├── _navbar.scss
│ │ ├── _pagination.scss
│ │ ├── _ripple.scss
│ │ ├── _rtl.scss
│ │ ├── _social-buttons.scss
│ │ ├── _table.scss
│ │ ├── _timeline.scss
│ │ ├── _utilities-extend.scss
│ │ ├── _utilities.scss
│ │ └── _virtual-reality.scss
├── site-base.js
├── site-bootstrap.js
└── styles
│ ├── app
│ ├── base.sass
│ ├── bootstrap
│ │ ├── _all.sass
│ │ └── layout.sass
│ ├── navbar.sass
│ ├── profile.sass
│ ├── progress.sass
│ └── utilities.sass
│ ├── site-base.scss
│ └── site-bootstrap.scss
├── config.py
├── lib
├── __init__.py
├── api
│ ├── __init__.py
│ └── search.py
├── cli
│ ├── __init__.py
│ ├── app.py
│ └── cmd
│ │ ├── __init__.py
│ │ ├── cmd_configure.py
│ │ ├── cmd_gen_salt.py
│ │ ├── cmd_make_migrations.py
│ │ ├── cmd_migrate.py
│ │ └── cmd_run.py
├── jinja
│ ├── __init__.py
│ ├── filters.py
│ └── tests.py
└── mutables.py
├── locale
├── en
│ └── LC_MESSAGES
│ │ ├── django.mo
│ │ ├── django.po
│ │ ├── djangojs.mo
│ │ └── djangojs.po
└── fr
│ └── LC_MESSAGES
│ ├── django.mo
│ ├── django.po
│ ├── djangojs.mo
│ └── djangojs.po
├── manage.py
├── pda
├── __init__.py
├── asgi.py
├── celery.py
├── jinja2.py
├── settings.py
├── urls.py
└── wsgi.py
├── requirements.txt
├── requirements
├── prod-requirements.in
├── prod-requirements.txt
├── requirements.in
└── requirements.txt
├── static
├── css
│ ├── site-base.css
│ └── site-bootstrap.css
├── images
│ └── undraw
│ │ ├── license.txt
│ │ ├── undraw_alert.svg
│ │ ├── undraw_analytics.svg
│ │ ├── undraw_coding.svg
│ │ ├── undraw_credit_card.svg
│ │ ├── undraw_empty.svg
│ │ ├── undraw_features_overview.svg
│ │ ├── undraw_instant_support.svg
│ │ ├── undraw_joyride.svg
│ │ ├── undraw_lighthouse.svg
│ │ ├── undraw_outer_space.svg
│ │ ├── undraw_portfolio_update.svg
│ │ ├── undraw_savings.svg
│ │ ├── undraw_site_stats.svg
│ │ ├── undraw_team.svg
│ │ └── undraw_to_the_stars.svg
└── js
│ ├── app-bundle.js
│ ├── site-base-bundle.js
│ ├── site-bootstrap-bundle.js
│ └── site-bundle.js
├── templates
├── 400.html
├── 403.html
├── 404.html
├── 500.html
├── account
│ ├── base.html
│ ├── components
│ │ ├── 2fa.html
│ │ ├── api_keys.html
│ │ ├── profile_form.html
│ │ └── social
│ │ │ ├── login_with_google_button.html
│ │ │ └── social_accounts.html
│ ├── email.html
│ ├── email
│ │ ├── email_confirmation_message.html
│ │ ├── email_confirmation_signup_message.html
│ │ ├── email_template_base.html
│ │ ├── password_reset_key_message.html
│ │ ├── password_reset_key_message.txt
│ │ └── password_reset_key_subject.txt
│ ├── email_confirm.html
│ ├── login.html
│ ├── logout.html
│ ├── password_change.html
│ ├── password_reset.html
│ ├── password_reset_done.html
│ ├── password_reset_from_key.html
│ ├── password_reset_from_key_done.html
│ ├── password_set.html
│ ├── profile.html
│ ├── signup.html
│ └── verification_sent.html
├── allauth_2fa
│ ├── 2fa_base.html
│ ├── authenticate.html
│ ├── backup_tokens.html
│ ├── remove.html
│ └── setup.html
├── django
│ └── forms
│ │ └── widgets
│ │ ├── input.html
│ │ ├── select.html
│ │ └── textarea.html
├── error_base.html
├── socialaccount
│ ├── connections.html
│ ├── login.html
│ └── signup.html
└── web
│ ├── app
│ └── app_base.html
│ ├── app_home.html
│ ├── base.html
│ ├── components
│ ├── app_nav.html
│ ├── favicon.html
│ ├── footer.html
│ ├── google_analytics.html
│ ├── hero.html
│ ├── language_select.html
│ ├── messages-js.html
│ ├── messages.html
│ ├── sidenav_js.html
│ ├── top_nav.html
│ └── top_nav_app.html
│ ├── icons
│ ├── google-icon.svg
│ └── twitter-icon.svg
│ ├── landing_page.html
│ └── terms.html
└── webpack.config.js
/.github/ACKNOWLEDGMENTS.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Acknowledgments
4 |
--------------------------------------------------------------------------------
/.github/AUTHORS.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Authors
4 |
5 | This is the list of the PDA application's significant contributors.
6 |
7 | This does not necessarily list everyone who has contributed code,
8 | especially since many employees of one corporation may be contributing.
9 |
10 | To see the full list of contributors, see the revision history in
11 | source control.
12 |
13 | - Azorian Solutions LLC <help@azorian.solutions>
--------------------------------------------------------------------------------
/.github/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Change Log
4 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Code Owners
4 |
5 | - Azorian Solutions LLC <help@azorian.solutions>
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | **Looking for help?** Try taking a look at the project's
4 | [Support Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/support/README.md) or joining
5 | our [Discord Server](https://discord.powerdnsadmin.org).
6 |
7 | ## [Contribution Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/contributing/README.md)
8 |
9 | Please see the project's [Contribution Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/contributing/README.md)
10 | for information on how to contribute to the project.
--------------------------------------------------------------------------------
/.github/CONTRIBUTORS.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Contributors
4 |
5 | This is a list of people / organizations that are currently contributing to the project in
6 | some way.
7 |
8 | This does not necessarily list everyone who has contributed code.
9 |
10 | To see the full list of contributors, see the revision history in
11 | source control.
12 |
13 | - Azorian Solutions LLC <help@azorian.solutions>
--------------------------------------------------------------------------------
/.github/FUNDING.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Funding
4 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [AzorianSolutions]
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # Reference: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser
3 | blank_issues_enabled: false
4 | contact_links:
5 | - name: 📖 Contribution Guide
6 | url: https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/contributing/README.md
7 | about: "Please read through our contribution guide before opening an issue or pull request"
8 | - name: ❓ Discussion
9 | url: https://github.com/PowerDNS-Admin/pda-next/discussions
10 | about: "If you're just looking for help, try starting a discussion instead"
11 | - name: 💬 Discord Server
12 | url: https://discord.powerdnsadmin.org
13 | about: "Join our Discord server to discuss the project with other users and developers"
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/documentation_change.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: 📖 Documentation Change
3 | description: Suggest an addition or modification to the PDA documentation
4 | labels: ["docs / request"]
5 | body:
6 | - type: dropdown
7 | attributes:
8 | label: Change Type
9 | description: What type of change are you proposing?
10 | options:
11 | - Addition
12 | - Correction
13 | - Removal
14 | - Cleanup (formatting, typos, etc.)
15 | validations:
16 | required: true
17 | - type: dropdown
18 | attributes:
19 | label: Area
20 | description: To what section of the documentation does this change primarily pertain?
21 | options:
22 | - Features
23 | - Installation/upgrade
24 | - Getting started
25 | - Configuration
26 | - Customization
27 | - Database Setup
28 | - Debug
29 | - Integrations/API
30 | - Administration
31 | - Development
32 | - Other
33 | validations:
34 | required: true
35 | - type: textarea
36 | attributes:
37 | label: Proposed Changes
38 | description: Describe the proposed changes and why they are necessary.
39 | validations:
40 | required: true
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/housekeeping.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🏡 Housekeeping
3 | description: A change pertaining to the codebase itself (developers only)
4 | labels: ["mod / change-request"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: >
9 | **NOTE:** This template is for use by maintainers only. Please do not submit
10 | an issue using this template unless you have been specifically asked to do so.
11 | - type: textarea
12 | attributes:
13 | label: Proposed Changes
14 | description: >
15 | Describe in detail the new feature or behavior you'd like to propose.
16 | Include any specific changes to work flows, data models, or the user interface.
17 | validations:
18 | required: true
19 | - type: textarea
20 | attributes:
21 | label: Justification
22 | description: Please provide justification for the proposed change(s).
23 | validations:
24 | required: true
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
10 | ### Fixes: #1234
11 |
12 |
--------------------------------------------------------------------------------
/.github/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## No Warranty
4 |
5 | Per the terms of the MIT license, PDA is offered "as is" and without any guarantee or warranty pertaining to its operation. While every reasonable effort is made by its maintainers to ensure the product remains free of security vulnerabilities, users are ultimately responsible for conducting their own evaluations of each software release.
6 |
7 | ## Recommendations
8 |
9 | Administrators are encouraged to adhere to industry best practices concerning the secure operation of software, such as:
10 |
11 | * Do not expose your PDA installation to the public Internet
12 | * Do not permit multiple users to share an account
13 | * Enforce minimum password complexity requirements for local accounts
14 | * Prohibit access to your database from clients other than the PDA application
15 | * Keep your deployment updated to the most recent stable release
16 |
17 | ## Reporting a Suspected Vulnerability
18 |
19 | If you believe you've uncovered a security vulnerability and wish to report it confidentially, you may do so via email. Please note that any reported vulnerabilities **MUST** meet all the following conditions:
20 |
21 | * Affects the most recent stable release of PDA, or a current beta release
22 | * Affects a PDA instance installed and configured per the official documentation
23 | * Is reproducible following a prescribed set of instructions
24 |
25 | Please note that we **DO NOT** accept reports generated by automated tooling which merely suggest that a file or file(s) _may_ be vulnerable under certain conditions, as these are most often innocuous.
26 |
27 | If you believe that you've found a vulnerability which meets all of these conditions, please [submit a draft security advisory](https://github.com/PowerDNS-Admin/pda-next/security/advisories/new) on GitHub, or email a brief description of the suspected bug and instructions for reproduction to **admin@powerdnsadmin.org**.
28 |
29 | ### Bug Bounties
30 |
31 | As PDA is provided as free open source software, we do not offer any monetary compensation for vulnerability or bug reports, however your contributions are greatly appreciated.
--------------------------------------------------------------------------------
/.github/SUPPORT.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | **Looking to help?** Try taking a look at the project's
4 | [Contribution Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/contributing/README.md).
5 |
6 | ## [Support Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/support/README.md)
7 |
8 | Please see the project's [Support Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/support/README.md)
9 | for information on how to obtain support for the project.
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | ---
2 | version: 2
3 | updates:
4 | - package-ecosystem: npm
5 | target-branch: dev
6 | directory: /
7 | schedule:
8 | interval: daily
9 | ignore:
10 | - dependency-name: "*"
11 | update-types: [ "version-update:semver-major" ]
12 | labels:
13 | - 'feature / dependency'
14 | - package-ecosystem: pip
15 | target-branch: dev
16 | directory: /
17 | schedule:
18 | interval: daily
19 | ignore:
20 | - dependency-name: "*"
21 | update-types: [ "version-update:semver-major" ]
22 | labels:
23 | - 'feature / dependency'
24 |
--------------------------------------------------------------------------------
/.github/workflows/deployment-testing.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: Deployment Testing
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches:
7 | - main-off
8 | jobs:
9 | deployment_test_ubuntu_linux_2204:
10 | runs-on: [self-hosted, linux, x64]
11 | environment: testing
12 | concurrency:
13 | group: deployment_test_ubuntu_linux_2204
14 | cancel-in-progress: true
15 | steps:
16 | - name: Repository Checkout
17 | uses: actions/checkout@v3
--------------------------------------------------------------------------------
/.github/workflows/lock.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # lock-threads (https://github.com/marketplace/actions/lock-threads)
3 | name: 'Lock Inactive Threads'
4 |
5 | on:
6 | workflow_dispatch:
7 | schedule:
8 | - cron: '0 3 * * *'
9 |
10 | permissions:
11 | issues: write
12 | pull-requests: write
13 |
14 | jobs:
15 | lock:
16 | runs-on: ubuntu-latest
17 | steps:
18 | - uses: dessant/lock-threads@v3
19 | with:
20 | issue-inactive-days: 90
21 | pr-inactive-days: 30
22 | issue-lock-reason: 'resolved'
23 | exclude-any-issue-labels: 'bug / security-vulnerability, mod / announcement, mod / accepted, mod / reviewing, mod / testing'
24 | exclude-any-pr-labels: 'bug / security-vulnerability, mod / announcement, mod / accepted, mod / reviewing, mod / testing'
--------------------------------------------------------------------------------
/.github/workflows/project-release-test.yml:
--------------------------------------------------------------------------------
1 | name: 'Project Release (Test)'
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | test-build-publish-package:
8 | name: Test, Build, & Publish Python Package
9 | runs-on: ubuntu-20.04
10 | permissions:
11 | id-token: write
12 | contents: write
13 |
14 | steps:
15 | # Checkout the repository
16 | - name: Checkout Repository
17 | uses: actions/checkout@v4
18 |
19 | # Set up the environment to run Python
20 | - name: Set up Python
21 | uses: actions/setup-python@v5
22 | with:
23 | python-version: '3.8'
24 |
25 | # Set up the environment to run the latest Python build, test, and publish tools
26 | - name: Set Up Environment
27 | run: |
28 | python3 -m pip install --upgrade pip
29 | python3 -m pip install --upgrade setuptools
30 | python3 -m pip install --upgrade wheel
31 | python3 -m pip install --upgrade pytest
32 | python3 -m pip install --upgrade twine
33 |
34 | # Install the package from source
35 | - name: Install Source Code
36 | run: |
37 | python3 -m pip install .
38 |
39 | # Run the package tests
40 | - name: Test Source Code
41 | run: |
42 | python3 -m pytest -v
43 |
44 | # Build the package
45 | - name: Build Package
46 | run: |
47 | python3 -m pip install --upgrade build
48 | python3 -m build
49 |
50 | # Check the distribution files with Twine
51 | - name: Check Package
52 | run: |
53 | python3 -m twine check dist/*
54 |
55 | # Store the distribution files as artifacts
56 | - name: Upload Package
57 | uses: actions/upload-artifact@v3
58 | with:
59 | name: python-package
60 | path: dist/
61 |
62 | # Upload the distribution artifacts to PyPi test environment for all (supported) scenarios
63 | - name: Publish Package (PyPi Test)
64 | uses: pypa/gh-action-pypi-publish@release/v1
65 | with:
66 | packages-dir: dist/
67 | password: ${{ secrets.PYPI_TEST_TOKEN }}
68 | repository-url: https://test.pypi.org/legacy/
69 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.db
2 | *.pyc
3 | *~
4 | .DS_Store
5 | *.egg-info
6 | .env
7 | .env.dev
8 | .env.docker
9 | .env.local
10 | .env.production
11 | .idea
12 | conf/config.yml
13 | docs/_build
14 | venv
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Azorian Solutions LLC - legal[at]azorian.solutions
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/conf/config.tpl.yml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/conf/config.tpl.yml
--------------------------------------------------------------------------------
/deployment/bare-metal/linux/debian.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Define system packages required for the project
4 | PDACLI_PKGS=(build-essential python3 python3-dev python3-venv)
5 |
6 | # Add additional packages based on the environment configuration
7 | if [[ "$PDA_ENV_TYPE" == 'production' ]]; then
8 | # Add Gunicorn packages if the server type is Gunicorn
9 | if [[ "$PDA_SERVER_TYPE" == 'gunicorn' ]]; then
10 | PDACLI_PKGS+=(gunicorn)
11 |
12 | # Add Uvicorn packages if the server type is Uvicorn
13 | elif [[ "$PDA_SERVER_TYPE" == 'uvicorn' ]]; then
14 | PDACLI_PKGS+=(uvicorn)
15 |
16 | # Add UWSGI packages if the server type is UWSGI
17 | elif [[ "$PDA_SERVER_TYPE" == 'uwsgi' ]]; then
18 | PDACLI_PKGS+=(uwsgi uwsgi-plugin-python3)
19 | fi
20 | fi
21 |
22 | # Add MySQL packages if the database engine is MySQL
23 | if [[ "$PDA_DB_ENGINE" == 'mysql' ]]; then
24 | PDACLI_PKGS+=(libmysqlclient-dev)
25 |
26 | # Add PostgreSQL packages if the database engine is PostgreSQL
27 | elif [[ "$PDA_DB_ENGINE" == 'postgres' ]]; then
28 | PDACLI_PKGS+=(libpq-dev)
29 | fi
30 |
31 | # Determine whether sudo needs to be added to commands based whether the current user is root
32 | PDACLI_CMD_PREFIX=
33 | if [ ! "$EUID" -eq 0 ]; then
34 | PDACLI_CMD_PREFIX=sudo
35 | fi
36 |
37 | # Install missing system packages
38 | $PDACLI_CMD_PREFIX apt update
39 | $PDACLI_CMD_PREFIX apt-get -y --ignore-missing install "${PDACLI_PKGS[@]}"
40 |
41 | # Setup Python virtual environment and activate it only if the environment type is development
42 | if [[ "$PDA_VENV_ENABLED" == '1' ]]; then
43 | # Create a Python virtual environment
44 | $(which env) python3 -m venv "$PDA_VENV_PATH"
45 |
46 | # Activate the Python virtual environment
47 | . "$PDA_VENV_PATH/bin/activate"
48 | fi
49 |
50 | # Install the required pip modules based on the configuration in setup.py
51 | $(which pip) install --editable .
52 |
--------------------------------------------------------------------------------
/deployment/bare-metal/shared/create_config.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Set the default path to the environment configuration file to be created
4 | PDA_ENV_FILE=${PDA_ENV_FILE:-.env}
5 |
6 | # Define the environment variables to be set in the environment configuration file
7 | PDACLI_ENV_VARS_SET=(
8 | 'PDA_DB_ENGINE'
9 | 'PDA_ENV_TYPE'
10 | 'PDA_SECRET_KEY'
11 | 'PDA_SERVER_TYPE'
12 | 'PDA_VENV_ENABLED'
13 | 'PDA_VENV_PATH'
14 | )
15 |
16 | # Generate a new secret_key setting value
17 | echo "Generating new secret key for the environment configuration file..."
18 | # shellcheck disable=SC2034
19 | PDA_SECRET_KEY=$(pda gen_salt -r)
20 |
21 | # Create a variable to hold the environment variable settings that will be saved to the environment configuration file
22 | env_data=''
23 | for var_name in "${PDACLI_ENV_VARS_SET[@]}"
24 | do
25 | env_data+="$var_name='${!var_name}'\n"
26 | done
27 |
28 | # Save the environment variables to the configured file path
29 | echo "Saving environment configuration to: $PDA_ENV_FILE"
30 | echo -e "\n$env_data" >> "$PDA_ENV_FILE"
31 |
--------------------------------------------------------------------------------
/deployment/setup.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Load OS meta
4 | if ! . "deployment/bare-metal/shared/detect_os.sh"; then
5 | echo "Failed to match supported OS. Halting execution."
6 | return 1
7 | fi
8 |
9 | # Collect inputs from the user before preparing the environment
10 | . "deployment/bare-metal/shared/collect_inputs.sh"
11 |
12 | # Prepare the system for the project using an OS specific script if it exists, otherwise use a distribution script
13 | if [ -f "deployment/bare-metal/$PDACLI_PLATFORM/$PDACLI_OS.sh" ]; then
14 | # shellcheck source=deployment/bare-metal/linux/debian.sh
15 | . "deployment/bare-metal/$PDACLI_PLATFORM/$PDACLI_OS.sh"
16 | else
17 | # shellcheck source=deployment/bare-metal/linux/debian.sh
18 | . "deployment/bare-metal/$PDACLI_PLATFORM/$PDACLI_DISTRO.sh"
19 | fi
20 |
21 | # Create a starting environment config file that can be updated by the app's `configure` command
22 | . "deployment/bare-metal/shared/create_config.sh"
23 |
24 | # Run the environment configuration wizard
25 | pda configure
26 |
27 | # Capture the exit status of the configure command
28 | config_status=$?
29 |
30 | if [ "$config_status" -ne 0 ]; then
31 | echo ""
32 | echo "Failed to configure the environment. Halting execution."
33 | echo ""
34 | echo "Please try to run the \"pda configure\" command manually to troubleshoot the issue."
35 | echo ""
36 | return 1
37 | else
38 | echo ""
39 | echo "The environment is ready to run!"
40 | echo ""
41 | echo "Please run the \"pda\" command to get started."
42 | echo ""
43 | echo "For more information, please visit:"
44 | echo "https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/README.md"
45 | echo ""
46 | fi
47 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Project Documentation
4 |
5 | Hopefully you will find that the project documentation is vast and answers most if not all of your questions. A key
6 | directive of the next-generation application is to reduce barrier to entry as much as possible within reason. To
7 | accomplish this goal, the project has provided great documentation surrounding the common topics of use such as
8 | application configuration, deployment, testing, and planning.
9 |
10 | ### Project Information
11 |
12 | For information about the project such as feature planning, the roadmap, and milestones, then please see the
13 | [Project Information](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/project/README.md) section of the wiki.
14 |
15 | ### Support
16 |
17 | **Looking for help?** Try taking a look at the project's
18 | [Support Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/support/README.md) or joining
19 | our [Discord Server](https://discord.powerdnsadmin.org).
20 |
21 | ### Contributing
22 |
23 | If you're interested in participating in the project design discussions, or you want to actively submit work to the
24 | project then you should check out the
25 | [Contribution Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/contributing/README.md)!
26 |
27 | ### Application Configuration
28 |
29 | For information about all the ways this application can be configured and what each setting does, please visit the
30 | [Configuration Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/configuration/README.md) section of the wiki.
31 |
32 | ### Application Deployment
33 |
34 | For information about how to deploy the application in various environments, please visit the
35 | [Deployment Guides](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/README.md) section of the wiki.
36 |
37 | ### Application Testing
38 |
39 | For information on how to create and execute automated application tests, please visit the
40 | [Testing Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/testing/README.md) section of the wiki.
41 |
--------------------------------------------------------------------------------
/docs/announcements/2022-12-09-docker-hub-repository-moved.md:
--------------------------------------------------------------------------------
1 | Hi everyone,
2 |
3 | I wanted to share a brief but important announcement with the community. The project's associated Docker Hub repository has changed to a more permanent location to reflect the organization's branding and ongoing future efforts.
4 |
5 | The Docker Hub repository for the current PDA application is now located here: https://hub.docker.com/r/powerdnsadmin/pda-legacy
6 |
7 | This means that all legacy references to "ngoduykhanh/powerdns-admin" should be changed to "powerdnsadmin/pda-legacy" to continue receiving updates for images.
8 |
9 | It's important to take notice that all previous official releases **have NOT been been transferred** to this Docker Hub repository at the time of this post. I will continue working on a solution to get all of the previous releases built and published to the new repository as soon as possible. In the mean time, the best solution if you're using an image from a specific release, would be to continue using the old references until a further announcement has been made for this. The old repository should remain for quite some time until the transition is complete.
10 |
11 | Thank you all again for being loyal users of the PDA project!
--------------------------------------------------------------------------------
/docs/announcements/2023-03-10-docker-images-and-repository-branches.md:
--------------------------------------------------------------------------------
1 | As part of the clean-up process for the project, the project moving to a new development strategy based on the "[OneFlow](https://www.endoflineblog.com/oneflow-a-git-branching-model-and-workflow)" methodology. As a result, I have created a "dev" branch which will now contain ongoing prerelease code base changes. This means that all PRs moving forward should be based on the "dev" branch and not the "master" branch.
2 |
3 | Following the next stable production release of version 0.4.0, the "master" branch will become tied to the current production release version. Accordingly, the "latest" tag of the project's Docker image (powerdnsadmin/pda-legacy) will continue to follow the "master" branch of the repository. This means that following the next production release of version 0.4.0, the "latest" Docker image tag will begin representing the latest production release.
4 |
5 | Additionally, the new "dev" branch of the repository has been setup to provide automatic Docker image builds under the "dev" tag of the official project Docker image powerdnsadmin/pda-legacy.
6 |
7 | Thank you all for participating in the community and/or being a loyal PDA user!
--------------------------------------------------------------------------------
/docs/announcements/2023-03-19-new-contribution-policy.md:
--------------------------------------------------------------------------------
1 | Please be aware that the project has implemented a new contribution policy, effective immediately!
2 |
3 | Please read the [new policy here](https://github.com/PowerDNS-Admin/PowerDNS-Admin/blob/master/docs/CONTRIBUTING.md).
--------------------------------------------------------------------------------
/docs/wiki/README.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Wiki Index
4 |
5 | ### Project Information
6 |
7 | - [Features](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/project/features.md)
8 | - [Releases](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/project/releases.md)
9 | - [Roadmap](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/project/roadmap.md)
10 | - [Milestones](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/project/milestones.md)
11 |
12 | For more information, see the [Project Information](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/project/README.md) section of the wiki.
13 |
14 | ### Support
15 |
16 | **Looking for help?** Please see the project's
17 | [Support Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/support/README.md)
18 | for more information.
19 |
20 | ### Contributing
21 |
22 | If you're interested in participating in the project design discussions, or you want to actively submit work to the
23 | project then you should check out the
24 | [Contribution Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/contributing/README.md)!
25 |
26 | ### Configuration
27 |
28 | For information on configuring the application, please visit the
29 | [Configuration Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/configuration/README.md).
30 |
31 | ### Deployment
32 |
33 | For information about how to deploy the application in various environments, please visit the
34 | [Deployment Guides](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/README.md).
35 |
36 | ### Testing
37 |
38 | For information on how to create and execute automated application tests, please visit the
39 | [Testing Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/testing/README.md).
40 |
--------------------------------------------------------------------------------
/docs/wiki/configuration/settings/README.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Configuration Guide
4 |
5 | ### Application Settings Guide
6 |
7 | The application has two general categories of settings to be managed: environment configuration and
8 | runtime configuration. Environment configuration is used to configure the application during initialization.
9 | Runtime configuration is used to configure application features during runtime.
10 |
11 | #### Environment Configuration
12 |
13 | To view the alphabetical list of environment configuration settings, see the
14 | [Environment Configuration Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/configuration/settings/environment-settings.md).
15 |
16 | #### Runtime Configuration
17 |
18 | To view the alphabetical list of environment configuration settings, see the
19 | [Runtime Configuration Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/configuration/settings/runtime-settings.md).
20 |
--------------------------------------------------------------------------------
/docs/wiki/configuration/settings/runtime-settings.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Configuration Guide
4 |
5 | ### Runtime Configuration Guide
6 |
7 | The configuration settings listed in this section are used during runtime by various application features. Configuration
8 | settings are only placed here if the setting isn't required to bootstrap the application during initialization.
9 |
10 | **More coming soon!**
--------------------------------------------------------------------------------
/docs/wiki/deployment/README.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## TL;DR
4 |
5 | To get started quickly with a simple deployment, execute the following commands on a *nix based system
6 | with `bash` and `git` installed:
7 |
8 | ```
9 | git clone https://github.com/PowerDNS-Admin/pda-next.git
10 | cd pda-next
11 | deployment/setup.sh
12 | ```
13 |
14 | ## Deployment Guides
15 |
16 | ### Bare Metal
17 |
18 | If you wish to deploy the application on bare metal, the project contains a plethora of guides for doing so.
19 |
20 | Please refer to the
21 | [Bare Metal Deployment Guides](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/bare-metal/README.md)
22 | for an index of currently available guides.
23 |
24 | ### Docker
25 |
26 | The project currently provides a few guides of common Docker deployment methods and templates.
27 |
28 | Please refer to the
29 | [Docker Deployment Guides](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/docker/README.md)
30 | for an index of currently available guides.
31 |
32 | ### Kubernetes
33 |
34 | The project currently provides a few guides of common Kubernetes deployment methods and templates.
35 |
36 | Please refer to the
37 | [Kubernetes Deployment Guides](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/kubernetes/README.md)
38 | for an index of currently available guides.
39 |
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/bsd/README.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## BSD Bare Metal Deployment Guides
4 |
5 | - [FreeBSD](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/bare-metal/bsd/freebsd.md)
6 |
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/bsd/freebsd.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## FreeBSD Linux Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/linux/README.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Linux Bare Metal Deployment Guides
4 |
5 | - [Alma Linux](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/bare-metal/linux/alma-linux.md)
6 | - [Alpine Linux](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/bare-metal/linux/alpine-linux.md)
7 | - [Arch Linux](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/bare-metal/linux/arch-linux.md)
8 | - [CentOS Linux](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/bare-metal/linux/centos-linux.md)
9 | - [Debian Linux](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/bare-metal/linux/debian-linux.md)
10 | - [Fedora Linux](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/bare-metal/linux/fedora-linux.md)
11 | - [FreeBSD Linux](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/bare-metal/linux/freebsd-linux.md)
12 | - [openSUSE Linux](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/bare-metal/linux/opensuse-linux.md)
13 | - [Oracle Linux](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/bare-metal/linux/oracle-linux.md)
14 | - [Redhat Enterprise Linux](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/bare-metal/linux/redhat-enterprise-linux.md)
15 | - [Rocky Linux](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/bare-metal/linux/rocky-linux.md)
16 | - [SUSE Linux Enterprise Server](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/bare-metal/linux/suse-linux-enterprise-server.md)
17 | - [Ubuntu Linux](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/bare-metal/linux/ubuntu-linux.md)
18 |
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/linux/alma-linux.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Alma Linux Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/linux/alpine-linux.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Alpine Linux Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/linux/arch-linux.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Arch Linux Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/linux/centos-linux.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## CentOS Linux Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/linux/debian-linux.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Debian Linux Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/linux/fedora-linux.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Fedora Linux Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/linux/opensuse-linux.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## openSUSE Linux Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/linux/oracle-linux.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Oracle Linux Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/linux/redhat-enterprise-linux.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Redhat Enterprise Linux Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/linux/rocky-linux.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Rocky Linux Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/linux/suse-linux-enterprise-server.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## SUSE Linux Enterprise Server Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/linux/ubuntu-linux.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Ubuntu Linux Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/macos/README.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## macOS Bare Metal Installation Guide
4 |
5 | Good luck!
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/solaris/README.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Solaris Bare Metal Deployment Guides
4 |
5 | Coming soon!
6 |
--------------------------------------------------------------------------------
/docs/wiki/deployment/bare-metal/windows/README.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Windows Bare Metal Installation Guide
4 |
5 | Good luck!
--------------------------------------------------------------------------------
/docs/wiki/deployment/docker/README.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Docker Deployment Guides
4 |
5 | - [Docker](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/docker/docker.md)
6 | - [Docker Compose](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/docker/docker-compose.md)
7 | - [Docker Swarm](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/docker/docker-swarm.md)
8 |
--------------------------------------------------------------------------------
/docs/wiki/deployment/docker/docker-compose.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Docker Compose Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/docker/docker-swarm.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Docker Swarm Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/docker/docker.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Docker Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/kubernetes/README.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Kubernetes Deployment Guides
4 |
5 | - [Helm Charts](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/kubernetes/helm-charts.md)
6 | - [K3s](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/kubernetes/k3s.md)
7 | - [Kubeadm](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/kubernetes/kubeadm.md)
8 | - [Minikube](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/kubernetes/minikube.md)
9 | - [Rancher](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/deployment/kubernetes/rancher.md)
10 |
--------------------------------------------------------------------------------
/docs/wiki/deployment/kubernetes/helm-charts.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Kubernetes Helm Charts Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/kubernetes/k3s.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Kubernetes K3S Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/kubernetes/kubeadm.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Kubernetes Kubeadm Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/kubernetes/minikube.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Kubernetes Minikube Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/deployment/kubernetes/rancher.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Kubernetes Rancher Deployment Guide
4 |
5 | Coming soon!
--------------------------------------------------------------------------------
/docs/wiki/project/README.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Project Information
4 |
5 | - [Features](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/project/features.md)
6 | - [Releases](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/project/releases.md)
7 | - [Roadmap](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/project/roadmap.md)
8 | - [Milestones](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/project/milestones.md)
9 |
10 | ### Contributing
11 |
12 | If you're interested in participating in the project design discussions, or you want to actively submit work to the
13 | project then you should check out the
14 | [Contribution Guide](https://github.com/PowerDNS-Admin/pda-next/blob/main/docs/wiki/contributing/README.md)!
15 |
--------------------------------------------------------------------------------
/docs/wiki/project/milestones.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Project Milestones
4 |
5 | Coming eventually!
--------------------------------------------------------------------------------
/docs/wiki/project/releases.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Project Releases
4 |
5 | Coming eventually!
--------------------------------------------------------------------------------
/docs/wiki/project/roadmap.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Project Roadmap
4 |
5 | Coming eventually!
--------------------------------------------------------------------------------
/docs/wiki/support/README.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Project Support
4 |
5 | **Looking for help?** PDA has a somewhat active community of fellow users that may be able to provide assistance.
6 | Just [start a discussion](https://github.com/PowerDNS-Admin/pda-next/discussions/new) right here on GitHub!
7 |
8 | Looking to chat with someone? Join our [Discord Server](https://discord.powerdnsadmin.org).
9 |
10 | Some general tips for engaging here on GitHub:
11 |
12 | * Register for a free [GitHub account](https://github.com/signup) if you haven't already.
13 | * You can use [GitHub Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) for formatting text and adding images.
14 | * To help mitigate notification spam, please avoid "bumping" issues with no activity. (To vote an issue up or down, use a :thumbsup: or :thumbsdown: reaction.)
15 | * Please avoid pinging members with `@` unless they've previously expressed interest or involvement with that particular issue.
16 |
--------------------------------------------------------------------------------
/docs/wiki/testing/README.md:
--------------------------------------------------------------------------------
1 | # PDA Next
2 |
3 | ## Testing Guide
4 |
5 | Coming eventually!
--------------------------------------------------------------------------------
/src/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/env",
4 | "@babel/preset-react",
5 | "@babel/typescript"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/src/.gitignore:
--------------------------------------------------------------------------------
1 | .sass-cache/
2 | pda/localsettings.py
3 | pda/local.py
4 | static_root/
5 | openapitools.json
6 | media/
7 | _dist/
8 | # uncomment these lines after setting up the front-end build pipeline in dev and production
9 | # static/css/
10 | # static/js/
11 |
--------------------------------------------------------------------------------
/src/apps/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/apps/__init__.py
--------------------------------------------------------------------------------
/src/apps/api/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/apps/api/__init__.py
--------------------------------------------------------------------------------
/src/apps/api/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from rest_framework_api_key.admin import APIKeyModelAdmin
3 |
4 | from .models import UserAPIKey
5 |
6 |
7 | @admin.register(UserAPIKey)
8 | class UserAPIKeyModelAdmin(APIKeyModelAdmin):
9 | list_display = [*APIKeyModelAdmin.list_display, "user"]
10 |
--------------------------------------------------------------------------------
/src/apps/api/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class APIConfig(AppConfig):
5 | name = "apps.api"
6 | label = "api"
7 | default_auto_field = "django.db.models.BigAutoField"
8 |
--------------------------------------------------------------------------------
/src/apps/api/helpers.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from django.http import HttpRequest
4 | from rest_framework_api_key.permissions import KeyParser
5 |
6 | from apps.api.models import UserAPIKey
7 | from apps.users.models import CustomUser
8 |
9 |
10 | def get_user_from_request(request: HttpRequest) -> Optional[CustomUser]:
11 | if request is None:
12 | return None
13 | if request.user.is_anonymous:
14 | user_api_key = _get_api_key_object(request, UserAPIKey)
15 | return user_api_key.user
16 | else:
17 | return request.user
18 |
19 |
20 | def _get_api_key_object(request, model_class):
21 | return model_class.objects.get_from_key(_get_api_key(request))
22 |
23 |
24 | def _get_api_key(request):
25 | # inspired by / copied from BaseHasAPIKey.get_key()
26 | # loosely based on this issue: https://github.com/florimondmanca/djangorestframework-api-key/issues/98
27 | return KeyParser().get(request)
28 |
--------------------------------------------------------------------------------
/src/apps/api/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.13 on 2022-05-25 11:37
2 |
3 | import django.db.models.deletion
4 | from django.conf import settings
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | initial = True
11 |
12 | dependencies = [
13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
14 | ]
15 |
16 | operations = [
17 | migrations.CreateModel(
18 | name='UserAPIKey',
19 | fields=[
20 | ('id', models.CharField(editable=False, max_length=150, primary_key=True, serialize=False, unique=True)),
21 | ('prefix', models.CharField(editable=False, max_length=8, unique=True)),
22 | ('hashed_key', models.CharField(editable=False, max_length=150)),
23 | ('created', models.DateTimeField(auto_now_add=True, db_index=True)),
24 | ('name', models.CharField(default=None, help_text='A free-form name for the API key. Need not be unique. 50 characters max.', max_length=50)),
25 | ('revoked', models.BooleanField(blank=True, default=False, help_text='If the API key is revoked, clients cannot use it anymore. (This cannot be undone.)')),
26 | ('expiry_date', models.DateTimeField(blank=True, help_text='Once API key expires, clients cannot use it anymore.', null=True, verbose_name='Expires')),
27 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='api_keys', to=settings.AUTH_USER_MODEL)),
28 | ],
29 | options={
30 | 'verbose_name': 'User API key',
31 | 'verbose_name_plural': 'User API keys',
32 | 'ordering': ('-created',),
33 | 'abstract': False,
34 | },
35 | ),
36 | ]
37 |
--------------------------------------------------------------------------------
/src/apps/api/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/apps/api/migrations/__init__.py
--------------------------------------------------------------------------------
/src/apps/api/models.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.db import models
3 | from rest_framework_api_key.models import AbstractAPIKey
4 |
5 |
6 | class UserAPIKey(AbstractAPIKey):
7 | """
8 | API Key associated with a User, allowing you to scope the key's API access based on what the user
9 | is allowed to view/do.
10 | """
11 |
12 | user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="api_keys")
13 |
14 | class Meta(AbstractAPIKey.Meta):
15 | verbose_name = "User API key"
16 | verbose_name_plural = "User API keys"
17 |
--------------------------------------------------------------------------------
/src/apps/api/permissions.py:
--------------------------------------------------------------------------------
1 | import typing
2 |
3 | from django.http import HttpRequest
4 | from rest_framework.permissions import IsAuthenticated
5 | from rest_framework_api_key.permissions import BaseHasAPIKey
6 |
7 | from .helpers import get_user_from_request
8 | from .models import UserAPIKey
9 |
10 |
11 | class HasUserAPIKey(BaseHasAPIKey):
12 | model = UserAPIKey
13 |
14 | def has_permission(self, request: HttpRequest, view: typing.Any) -> bool:
15 | has_perm = super().has_permission(request, view)
16 | if has_perm:
17 | # if they have permission, also populate the request.user object for convenience
18 | request.user = get_user_from_request(request)
19 | return has_perm
20 |
21 |
22 | # hybrid permission class that can check for API keys or authentication
23 | IsAuthenticatedOrHasUserAPIKey = IsAuthenticated | HasUserAPIKey
24 |
--------------------------------------------------------------------------------
/src/apps/api/schema.py:
--------------------------------------------------------------------------------
1 | def filter_schema_apis(endpoints):
2 | """
3 | Used to filter out certain API endpoints from the auto-generated docs / clients
4 | """
5 | return [e for e in endpoints if include_in_schema(e)]
6 |
7 |
8 | def include_in_schema(endpoint):
9 | urL_path = endpoint[0]
10 | return not urL_path.startswith("/cms/") # filter out wagtail URLs if present
11 |
--------------------------------------------------------------------------------
/src/apps/api/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/apps/api/tests/__init__.py
--------------------------------------------------------------------------------
/src/apps/api/tests/test_schema.py:
--------------------------------------------------------------------------------
1 | from django.test import Client, TestCase
2 |
3 |
4 | class ApiSchemaTestCase(TestCase):
5 | def test_schema_filters(self):
6 | c = Client()
7 | response = c.get("/api/schema/")
8 | response_yaml = response.content.decode("utf-8")
9 | self.assertFalse("/cms/" in response_yaml)
10 |
--------------------------------------------------------------------------------
/src/apps/users/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/apps/users/__init__.py
--------------------------------------------------------------------------------
/src/apps/users/adapter.py:
--------------------------------------------------------------------------------
1 | from allauth.account import app_settings
2 | from allauth.account.adapter import DefaultAccountAdapter
3 | from allauth.account.utils import user_email, user_field
4 | from allauth_2fa.adapter import OTPAdapter as AllAuthOtpAdapter
5 |
6 |
7 | class EmailAsUsernameAdapter(DefaultAccountAdapter):
8 | """
9 | Adapter that always sets the username equal to the user's email address.
10 | """
11 |
12 | def populate_username(self, request, user):
13 | # override the username population to always use the email
14 | user_field(user, app_settings.USER_MODEL_USERNAME_FIELD, user_email(user))
15 |
16 |
17 | class NoNewUsersAccountAdapter(DefaultAccountAdapter):
18 | """
19 | Adapter that can be used to disable public sign-ups for your app.
20 | """
21 |
22 | def is_open_for_signup(self, request):
23 | # see https://stackoverflow.com/a/29799664/8207
24 | return False
25 |
26 |
27 | class AccountAdapter(EmailAsUsernameAdapter, AllAuthOtpAdapter):
28 | pass
29 |
--------------------------------------------------------------------------------
/src/apps/users/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django.contrib.auth.admin import UserAdmin
3 |
4 | from .models import CustomUser
5 |
6 |
7 | @admin.register(CustomUser)
8 | class CustomUserAdmin(UserAdmin):
9 | list_display = UserAdmin.list_display
10 |
11 | fieldsets = UserAdmin.fieldsets + (("Custom Fields", {"fields": ("avatar", "language")}),)
12 |
--------------------------------------------------------------------------------
/src/apps/users/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class UserConfig(AppConfig):
5 | name = "apps.users"
6 | label = "users"
7 | default_auto_field = "django.db.models.BigAutoField"
8 |
9 | def ready(self):
10 | from . import signals
11 |
--------------------------------------------------------------------------------
/src/apps/users/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from django.conf import settings
3 | from django.contrib.auth.forms import UserChangeForm
4 | from django.utils.translation import gettext
5 |
6 | from .models import CustomUser
7 |
8 |
9 | class CustomUserChangeForm(UserChangeForm):
10 | email = forms.EmailField(label=gettext("Email"), required=True)
11 | language = forms.ChoiceField(label=gettext("Language"))
12 |
13 | class Meta:
14 | model = CustomUser
15 | fields = ("email", "first_name", "last_name", "language")
16 |
17 | def __init__(self, *args, **kwargs):
18 | super().__init__(*args, **kwargs)
19 | if settings.USE_I18N and len(settings.LANGUAGES) > 1:
20 | language = self.fields.get("language")
21 | language.choices = settings.LANGUAGES
22 | else:
23 | self.fields.pop("language")
24 |
25 |
26 | class UploadAvatarForm(forms.Form):
27 | avatar = forms.FileField()
28 |
--------------------------------------------------------------------------------
/src/apps/users/helpers.py:
--------------------------------------------------------------------------------
1 | from allauth.account import app_settings
2 | from allauth.account.models import EmailAddress
3 | from django.conf import settings
4 |
5 |
6 | def require_email_confirmation():
7 | return settings.ACCOUNT_EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY
8 |
9 |
10 | def user_has_confirmed_email_address(user, email):
11 | try:
12 | email_obj = EmailAddress.objects.get_for_user(user, email)
13 | return email_obj.verified
14 | except EmailAddress.DoesNotExist:
15 | return False
16 |
--------------------------------------------------------------------------------
/src/apps/users/mailing_list.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 |
3 | try:
4 | from mailchimp3 import MailChimp
5 | from mailchimp3.mailchimpclient import MailChimpError
6 |
7 | MAILCHIMP_AVAILABLE = True
8 | except ImportError:
9 | MAILCHIMP_AVAILABLE = False
10 |
11 |
12 | def get_mailchimp_client():
13 | if getattr(settings, "MAILCHIMP_API_KEY", None) and getattr(settings, "MAILCHIMP_LIST_ID", None):
14 | return MailChimp(mc_api=settings.MAILCHIMP_API_KEY)
15 | else:
16 | return None
17 |
18 |
19 | def is_mailchimp_available():
20 | return MAILCHIMP_AVAILABLE and get_mailchimp_client() is not None
21 |
22 |
23 | def subscribe_to_mailing_list(email_address):
24 | if not is_mailchimp_available():
25 | return
26 |
27 | try:
28 | get_mailchimp_client().lists.members.create(
29 | settings.MAILCHIMP_LIST_ID,
30 | {
31 | "email_address": email_address,
32 | "status": "subscribed",
33 | },
34 | )
35 | except MailChimpError as e:
36 | # likely it's just that they were already subscribed so don't worry about it
37 | try:
38 | # but do log to sentry if available
39 | from sentry_sdk import capture_exception
40 |
41 | capture_exception(e)
42 | except ImportError:
43 | pass
44 |
--------------------------------------------------------------------------------
/src/apps/users/management/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/apps/users/management/__init__.py
--------------------------------------------------------------------------------
/src/apps/users/management/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/apps/users/management/commands/__init__.py
--------------------------------------------------------------------------------
/src/apps/users/management/commands/promote_user_to_superuser.py:
--------------------------------------------------------------------------------
1 | from django.core.management.base import BaseCommand, CommandError
2 |
3 | from apps.users.models import CustomUser
4 |
5 |
6 | class Command(BaseCommand):
7 | help = "Promotes the given user to a superuser and provides admin access."
8 |
9 | def add_arguments(self, parser):
10 | parser.add_argument("username", type=str)
11 |
12 | def handle(self, username, **options):
13 | try:
14 | user = CustomUser.objects.get(username=username)
15 | except CustomUser.DoesNotExist:
16 | raise CommandError(f"No user with username/email {username} found!")
17 | user.is_superuser = True
18 | user.is_staff = True
19 | user.save()
20 | print(f"{username} successfully promoted to superuser and can now access the admin site")
21 |
--------------------------------------------------------------------------------
/src/apps/users/migrations/0002_customuser_language.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.1.7 on 2023-02-24 17:46
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('users', '0001_initial'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='customuser',
15 | name='language',
16 | field=models.CharField(blank=True, max_length=10, null=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/src/apps/users/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/apps/users/migrations/__init__.py
--------------------------------------------------------------------------------
/src/apps/users/models.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 |
3 | from django.contrib.auth.models import AbstractUser
4 | from django.db import models
5 |
6 |
7 | class CustomUser(AbstractUser):
8 | """
9 | Add additional fields to the user model here.
10 | """
11 |
12 | avatar = models.FileField(upload_to="profile-pictures/", blank=True)
13 | language = models.CharField(max_length=10, blank=True, null=True)
14 |
15 | def __str__(self):
16 | return f"{self.get_full_name()} <{self.email or self.username}>"
17 |
18 | def get_display_name(self) -> str:
19 | if self.get_full_name().strip():
20 | return self.get_full_name()
21 | return self.email or self.username
22 |
23 | @property
24 | def avatar_url(self) -> str:
25 | if self.avatar:
26 | return self.avatar.url
27 | else:
28 | return "https://www.gravatar.com/avatar/{}?s=128&d=identicon".format(self.gravatar_id)
29 |
30 | @property
31 | def gravatar_id(self) -> str:
32 | # https://en.gravatar.com/site/implement/hash/
33 | return hashlib.md5(self.email.lower().strip().encode("utf-8")).hexdigest()
34 |
--------------------------------------------------------------------------------
/src/apps/users/serializers.py:
--------------------------------------------------------------------------------
1 | from rest_framework import serializers
2 |
3 | from .models import CustomUser
4 |
5 |
6 | class CustomUserSerializer(serializers.ModelSerializer):
7 | """
8 | Basic serializer to pass CustomUser details to the front end.
9 | Extend with any fields your app needs.
10 | """
11 |
12 | class Meta:
13 | model = CustomUser
14 | fields = ("id", "first_name", "last_name", "email")
15 |
--------------------------------------------------------------------------------
/src/apps/users/signals.py:
--------------------------------------------------------------------------------
1 | from allauth.account.signals import email_confirmed, user_signed_up
2 | from django.conf import settings
3 | from django.core.mail import mail_admins
4 | from django.dispatch import receiver
5 |
6 | from apps.users.mailing_list import subscribe_to_mailing_list
7 |
8 |
9 | @receiver(user_signed_up)
10 | def handle_sign_up(request, user, **kwargs):
11 | # customize this function to do custom logic on sign up, e.g. send a welcome email
12 | # or subscribe them to your mailing list.
13 | # This example notifies the admins, in case you want to keep track of sign ups
14 | _notify_admins_of_signup(user)
15 | # and subscribes them to a mailchimp mailing list
16 | subscribe_to_mailing_list(user.email)
17 |
18 |
19 | @receiver(email_confirmed)
20 | def update_user_email(sender, request, email_address, **kwargs):
21 | """
22 | When an email address is confirmed make it the primary email.
23 | """
24 | # This also sets user.email to the new email address.
25 | # hat tip: https://stackoverflow.com/a/29661871/8207
26 | email_address.set_as_primary()
27 |
28 |
29 | def _notify_admins_of_signup(user):
30 | mail_admins(
31 | f"Yowsers, someone signed up for {settings.PROJECT_METADATA['NAME']}!",
32 | "Email: {}".format(user.email),
33 | fail_silently=True,
34 | )
35 |
--------------------------------------------------------------------------------
/src/apps/users/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from . import views
4 |
5 | app_name = "users"
6 | urlpatterns = [
7 | path("profile/", views.profile, name="user_profile"),
8 | path("profile/upload-image/", views.upload_profile_image, name="upload_profile_image"),
9 | path("api-keys/create/", views.create_api_key, name="create_api_key"),
10 | path("api-keys/revoke/", views.revoke_api_key, name="revoke_api_key"),
11 | ]
12 |
--------------------------------------------------------------------------------
/src/apps/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/apps/utils/__init__.py
--------------------------------------------------------------------------------
/src/apps/utils/decorators.py:
--------------------------------------------------------------------------------
1 | from django.http import JsonResponse
2 | from stripe.error import CardError
3 |
4 |
5 | class catch_stripe_errors(object):
6 | """
7 | Meant to be used with django views only.
8 | """
9 |
10 | def __init__(self, f):
11 | self.f = f
12 |
13 | def __call__(self, *args, **kwargs):
14 | try:
15 | return self.f(*args, **kwargs)
16 | except CardError as e:
17 | return JsonResponse(
18 | {
19 | "error": {
20 | "message": e._message,
21 | }
22 | },
23 | status=400,
24 | )
25 |
--------------------------------------------------------------------------------
/src/apps/utils/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 |
4 | class BaseModel(models.Model):
5 | """
6 | Base model that includes default created / updated timestamps.
7 | """
8 |
9 | created_at = models.DateTimeField(auto_now_add=True)
10 | updated_at = models.DateTimeField(auto_now=True)
11 |
12 | class Meta:
13 | abstract = True
14 |
--------------------------------------------------------------------------------
/src/apps/utils/slug.py:
--------------------------------------------------------------------------------
1 | from django.utils.text import slugify
2 |
3 |
4 | def get_next_unique_slug(model_class, display_name, slug_field_name):
5 | """
6 | Gets the next unique slug based on the name. Appends -1, -2, etc. until it finds
7 | a unique value.
8 | """
9 | base_value = slugify(display_name)
10 | if model_class.objects.filter(slug=base_value).exists():
11 | # todo make this do fewer queries
12 | suffix = 2
13 | while True:
14 | next_slug = get_next_slug(base_value, suffix)
15 | if not model_class.objects.filter(**{slug_field_name: next_slug}).exists():
16 | return next_slug
17 | else:
18 | suffix += 1
19 | else:
20 | return base_value
21 |
22 |
23 | def get_next_slug(base_value, suffix, max_length=100):
24 | """
25 | Gets the next slug from base_value such that "base_value-suffix" will not exceed max_length characters.
26 | """
27 | suffix_length = len(str(suffix)) + 1 # + 1 for the "-" character
28 | if suffix_length >= max_length:
29 | raise ValueError("Suffix {} is too long to create a unique slug! ".format(suffix))
30 |
31 | return "{}-{}".format(base_value[: max_length - suffix_length], suffix)
32 |
--------------------------------------------------------------------------------
/src/apps/utils/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/apps/utils/tests/__init__.py
--------------------------------------------------------------------------------
/src/apps/utils/tests/test_slugs.py:
--------------------------------------------------------------------------------
1 | from django.test import SimpleTestCase
2 |
3 | from ..slug import get_next_slug
4 |
5 |
6 | class NextSlugTest(SimpleTestCase):
7 | def test_next_slug_basic(self):
8 | self.assertEqual("slug-11", get_next_slug("slug", 11))
9 |
10 | def test_next_slug_truncate(self):
11 | self.assertEqual("slug-11", get_next_slug("slug", 11, max_length=7))
12 | self.assertEqual("slu-11", get_next_slug("slug", 11, max_length=6))
13 | self.assertEqual("slu-100", get_next_slug("slug", 100, max_length=7))
14 | self.assertEqual("sl-100", get_next_slug("slug", 100, max_length=6))
15 |
16 | def test_next_slug_fail(self):
17 | with self.assertRaises(ValueError):
18 | get_next_slug("slug", 11111, max_length=6)
19 |
--------------------------------------------------------------------------------
/src/apps/web/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/apps/web/__init__.py
--------------------------------------------------------------------------------
/src/apps/web/context_processors.py:
--------------------------------------------------------------------------------
1 | from copy import copy
2 |
3 | from django.conf import settings
4 |
5 | from .meta import absolute_url, get_server_root
6 |
7 |
8 | def project_meta(request):
9 | # modify these values as needed and add whatever else you want globally available here
10 | project_data = copy(settings.PROJECT_METADATA)
11 | project_data["TITLE"] = "{} | {}".format(project_data["NAME"], project_data["DESCRIPTION"])
12 | return {
13 | "project_meta": project_data,
14 | "server_url": get_server_root(),
15 | "page_url": absolute_url(request.path),
16 | "page_title": "",
17 | "page_description": "",
18 | "page_image": "",
19 | # put any settings you want made available to all templates here
20 | # then reference them as {{ project_settings.MY_VALUE }} in templates
21 | "project_settings": {
22 | "ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE": settings.ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE,
23 | },
24 | "use_i18n": getattr(settings, "USE_I18N", False) and len(getattr(settings, "LANGUAGES", [])) > 1,
25 | }
26 |
27 |
28 | def google_analytics_id(request):
29 | """
30 | Adds google analytics id to all requests
31 | """
32 | if settings.GOOGLE_ANALYTICS_ID:
33 | return {
34 | "GOOGLE_ANALYTICS_ID": settings.GOOGLE_ANALYTICS_ID,
35 | }
36 | else:
37 | return {}
38 |
--------------------------------------------------------------------------------
/src/apps/web/forms.py:
--------------------------------------------------------------------------------
1 | from django.forms import BaseForm
2 |
3 |
4 | def set_form_fields_disabled(form: BaseForm, disabled: bool = True) -> None:
5 | """
6 | For a given form, disable (or enable) all fields.
7 | """
8 | for field in form.fields:
9 | form.fields[field].disabled = disabled
10 |
--------------------------------------------------------------------------------
/src/apps/web/locale_middleware.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.utils import translation
3 | from django.utils.deprecation import MiddlewareMixin
4 |
5 |
6 | class UserLocaleMiddleware(MiddlewareMixin):
7 | def process_request(self, request):
8 | """Activate logged-in users' preferred language based on their profile setting."""
9 | user = getattr(request, "user", None)
10 | if not (user and user.is_authenticated):
11 | return
12 |
13 | if user.language and user.language != translation.get_language():
14 | translation.activate(user.language)
15 |
16 | def process_response(self, request, response):
17 | cookie_lang_code = request.COOKIES.get(settings.LANGUAGE_COOKIE_NAME)
18 | if not cookie_lang_code or cookie_lang_code != translation.get_language():
19 | response.set_cookie(settings.LANGUAGE_COOKIE_NAME, translation.get_language())
20 | return response
21 |
--------------------------------------------------------------------------------
/src/apps/web/meta.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.contrib.sites.models import Site
3 |
4 |
5 | def get_protocol(is_secure: bool = settings.USE_HTTPS_IN_ABSOLUTE_URLS) -> str:
6 | """
7 | Returns the default protocol for the server ("http" or "https").
8 | """
9 | return f'http{"s" if is_secure else ""}'
10 |
11 |
12 | def get_server_root(is_secure: bool = settings.USE_HTTPS_IN_ABSOLUTE_URLS) -> str:
13 | """
14 | Returns the default server root, with protocol. E.g. https://www.example.com
15 | """
16 | return f"{get_protocol(is_secure)}://{Site.objects.get_current().domain}"
17 |
18 |
19 | def absolute_url(relative_url: str, is_secure: bool = settings.USE_HTTPS_IN_ABSOLUTE_URLS):
20 | """
21 | Returns the complete absolute url for a given path - for use in emails or API integrations.
22 | """
23 | return f"{get_server_root(is_secure)}{relative_url}"
24 |
--------------------------------------------------------------------------------
/src/apps/web/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.7 on 2019-03-21 12:18
2 | from django.conf import settings
3 | from django.db import migrations
4 |
5 |
6 | def create_site(apps, schema_editor):
7 | Site = apps.get_model('sites', 'Site')
8 | site = Site.objects.get_or_create(
9 | id=settings.SITE_ID
10 | )[0]
11 | # strip leading http:// from site url since django sites framework doesn't expect it
12 | site.domain = settings.PROJECT_METADATA['URL'].replace('http://', '').replace('https://', '')
13 | site.name = settings.PROJECT_METADATA['NAME']
14 | site.save()
15 |
16 |
17 | class Migration(migrations.Migration):
18 |
19 | dependencies = [
20 | ]
21 |
22 | operations = [
23 | migrations.RunPython(create_site),
24 | ]
25 |
--------------------------------------------------------------------------------
/src/apps/web/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/apps/web/migrations/__init__.py
--------------------------------------------------------------------------------
/src/apps/web/sitemaps.py:
--------------------------------------------------------------------------------
1 | from django.contrib import sitemaps
2 | from django.urls import reverse
3 |
4 | from .meta import get_protocol
5 |
6 |
7 | class StaticViewSitemap(sitemaps.Sitemap):
8 | """
9 | Sitemap for serving any static content you want.
10 | """
11 |
12 | @property
13 | def protocol(self):
14 | return get_protocol()
15 |
16 | def items(self):
17 | # add any urls (by name) for static content you want to appear in your sitemap to this list
18 | return [
19 | "web:home",
20 | ]
21 |
22 | def location(self, item):
23 | return reverse(item)
24 |
--------------------------------------------------------------------------------
/src/apps/web/templatetags/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/apps/web/templatetags/__init__.py
--------------------------------------------------------------------------------
/src/apps/web/templatetags/meta_tags.py:
--------------------------------------------------------------------------------
1 | from django import template
2 | from django.templatetags.static import static
3 |
4 | from ..meta import absolute_url
5 |
6 | register = template.Library()
7 |
8 |
9 | @register.filter
10 | def get_title(project_meta, page_title=None):
11 | if page_title:
12 | return "{} | {}".format(page_title, project_meta["NAME"])
13 | else:
14 | return project_meta["TITLE"]
15 |
16 |
17 | @register.filter
18 | def get_description(project_meta, page_description=None):
19 | return page_description or project_meta["DESCRIPTION"]
20 |
21 |
22 | @register.filter
23 | def get_image_url(project_meta, page_image=None):
24 | if page_image and page_image.startswith("/"):
25 | page_image = absolute_url(static(page_image))
26 | return page_image or project_meta["IMAGE"]
27 |
--------------------------------------------------------------------------------
/src/apps/web/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/apps/web/tests/__init__.py
--------------------------------------------------------------------------------
/src/apps/web/tests/test_api_schema.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 |
4 | class TestApiSchema(TestCase):
5 | def test_schema_returns_success(self):
6 | response = self.client.get("/api/schema/")
7 | self.assertEqual(200, response.status_code)
8 |
--------------------------------------------------------------------------------
/src/apps/web/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from django.views.generic import TemplateView
3 |
4 | from . import views
5 |
6 | app_name = "web"
7 | urlpatterns = [
8 | path("", views.home, name="home"),
9 | path("terms/", TemplateView.as_view(template_name="web/terms.html"), name="terms"),
10 | # these views are just for testing error pages
11 | # actual error handling is handled by Django: https://docs.djangoproject.com/en/4.1/ref/views/#error-views
12 | path("400/", TemplateView.as_view(template_name="400.html"), name="400"),
13 | path("403/", TemplateView.as_view(template_name="403.html"), name="403"),
14 | path("404/", TemplateView.as_view(template_name="404.html"), name="404"),
15 | path("500/", TemplateView.as_view(template_name="500.html"), name="500"),
16 | path("send_test_email/", views.send_test_email),
17 | path("simulate_error/", views.simulate_error),
18 | ]
19 |
--------------------------------------------------------------------------------
/src/apps/web/views.py:
--------------------------------------------------------------------------------
1 | from django.contrib import messages
2 | from django.http import HttpResponseRedirect
3 | from django.shortcuts import render
4 | from django.urls import reverse
5 | from django.utils.translation import gettext_lazy as _
6 |
7 |
8 | def home(request):
9 | if request.user.is_authenticated:
10 | return render(
11 | request,
12 | 'web/app_home.html',
13 | context={
14 | 'active_tab': 'dashboard',
15 | 'page_title': _('Dashboard'),
16 | },
17 | )
18 | else:
19 | return render(request, 'web/landing_page.html')
20 |
21 |
22 | def send_test_email(request):
23 | from django.core.mail import send_mail
24 | from config import settings
25 |
26 | send_mail(
27 | subject='This is a test email',
28 | message='This is a test email.',
29 | from_email=settings.site_from_email,
30 | recipient_list=[settings.admin_email],
31 | )
32 | messages.success(request, 'Test email sent.')
33 | return HttpResponseRedirect(reverse('home'))
34 |
35 |
36 | def simulate_error(request):
37 | raise Exception('This is a simulated error.')
38 |
--------------------------------------------------------------------------------
/src/assets/javascript/api-client/.openapi-generator-ignore:
--------------------------------------------------------------------------------
1 | # OpenAPI Generator Ignore
2 | # Generated by openapi-generator https://github.com/openapitools/openapi-generator
3 |
4 | # Use this file to prevent files from being overwritten by the generator.
5 | # The patterns follow closely to .gitignore or .dockerignore.
6 |
7 | # As an example, the C# client generator defines ApiClient.cs.
8 | # You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
9 | #ApiClient.cs
10 |
11 | # You can match any string of characters against a directory, file or extension with a single asterisk (*):
12 | #foo/*/qux
13 | # The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
14 |
15 | # You can recursively match patterns against a directory, file or extension with a double asterisk (**):
16 | #foo/**/qux
17 | # This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
18 |
19 | # You can also negate patterns with an exclamation (!).
20 | # For example, you can ignore all files in a docs folder with the file extension .md:
21 | #docs/*.md
22 | # Then explicitly reverse the ignore rule for a single file:
23 | #!docs/README.md
24 |
--------------------------------------------------------------------------------
/src/assets/javascript/api-client/.openapi-generator/VERSION:
--------------------------------------------------------------------------------
1 | 6.2.1
--------------------------------------------------------------------------------
/src/assets/javascript/api-client/apis/index.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable */
2 | /* eslint-disable */
3 |
--------------------------------------------------------------------------------
/src/assets/javascript/api-client/index.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable */
2 | /* eslint-disable */
3 | export * from './runtime';
4 | export * from './apis';
5 | export * from './models';
6 |
--------------------------------------------------------------------------------
/src/assets/javascript/api-client/models/index.ts:
--------------------------------------------------------------------------------
1 | /* tslint:disable */
2 | /* eslint-disable */
3 |
--------------------------------------------------------------------------------
/src/assets/javascript/api.js:
--------------------------------------------------------------------------------
1 | import Cookies from "js-cookie";
2 | import {Configuration} from "./api-client";
3 |
4 |
5 | export function getApiConfiguration(serverBaseUrl) {
6 | return new Configuration({
7 | basePath: serverBaseUrl,
8 | headers: getApiHeaders(),
9 | });
10 | }
11 |
12 | export function getApiHeaders() {
13 | return {
14 | 'X-CSRFToken': Cookies.get('csrftoken'),
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/assets/javascript/app.js:
--------------------------------------------------------------------------------
1 | import * as JsCookie from "js-cookie"; // generated
2 |
3 | export { Payments } from './payments';
4 |
5 | // pass-through for Cookies API
6 | export const Cookies = JsCookie.default;
7 |
--------------------------------------------------------------------------------
/src/assets/javascript/htmx.js:
--------------------------------------------------------------------------------
1 | window.htmx = require('htmx.org');
2 |
--------------------------------------------------------------------------------
/src/assets/javascript/site.js:
--------------------------------------------------------------------------------
1 | // put site-wide dependencies here.
2 | // HTMX setup: https://htmx.org/docs/#installing
3 | import 'htmx.org';
4 | import './htmx';
5 |
--------------------------------------------------------------------------------
/src/assets/javascript/utilities/LoadError.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const LoadError = function() {
4 | return (
5 |
9 | {gettext("Check your internet connection and try reloading the page.")}
10 |
12 | {gettext("If you are the site administrator and setting up your site for the first time, see the documentation to resolve this: ")}
13 |
14 | {gettext("Troubleshooting API errors.")}
15 |
16 | {gettext("Sorry, there was an error loading your data.")}
7 |
Loading...
8 |7 | { props.errors.map((error, i) => { 8 | return {error} 9 | })} 10 |
11 | ); 12 | } 13 | return ''; 14 | }; 15 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Creative Tim (https://www.creative-tim.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_accordion.scss: -------------------------------------------------------------------------------- 1 | .accordion { 2 | .accordion-button { 3 | margin: 0 auto; 4 | font-size: inherit !important; 5 | } 6 | 7 | div { 8 | button[aria-expanded='true'] { 9 | .collapse-close { 10 | display: none; 11 | } 12 | 13 | .collapse-open { 14 | display: block; 15 | } 16 | } 17 | 18 | button[aria-expanded='false'] { 19 | .collapse-open { 20 | display: none; 21 | } 22 | 23 | .collapse-close { 24 | display: block; 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_alert.scss: -------------------------------------------------------------------------------- 1 | @each $state, $value in $theme-gradient-colors { 2 | .alert-#{$state} { 3 | @include gradient-directional(nth($value, 1) 0%, nth($value, -1) 100%, $deg: 195deg); 4 | } 5 | } 6 | 7 | // Pegasus removed these from the default files, to restore the close button on messages 8 | //.btn-close { 9 | // &:focus { 10 | // box-shadow: none; 11 | // } 12 | //} 13 | 14 | //remove 15 | //.alert-dismissible { 16 | // .btn-close { 17 | // background-image: none; 18 | // } 19 | //} 20 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_backgrounds.scss: -------------------------------------------------------------------------------- 1 | .bg-primary-soft{ 2 | background: rgba($primary-gradient, .03); 3 | } 4 | 5 | .bg-info-soft{ 6 | background: rgba($info-gradient, .03); 7 | } 8 | 9 | .bg-success-soft{ 10 | background: rgba($success-gradient, .03); 11 | } 12 | 13 | .bg-warning-soft{ 14 | background: rgba($warning-gradient, .03); 15 | } 16 | 17 | .bg-danger-soft{ 18 | background: rgba($danger-gradient, .03); 19 | } 20 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_badge.scss: -------------------------------------------------------------------------------- 1 | @each $prop, $value in $theme-colors { 2 | .badge.bg-#{$prop} { 3 | background: $value; 4 | } 5 | } 6 | .badge { 7 | text-transform: uppercase; 8 | } 9 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_breadcrumbs.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Breadcrumb 3 | // 4 | 5 | .breadcrumb-item { 6 | font-size: $font-size-sm; 7 | &.text-white{ 8 | &::before { 9 | color: $white; 10 | } 11 | } 12 | 13 | } 14 | 15 | .breadcrumb-dark { 16 | background-color: $breadcrumb-dark-bg; 17 | 18 | .breadcrumb-item { 19 | font-weight: 600; 20 | 21 | a { 22 | color: $breadcrumb-dark-color; 23 | 24 | &:hover { 25 | color: $breadcrumb-dark-hover-color; 26 | } 27 | } 28 | 29 | + .breadcrumb-item { 30 | &::before { 31 | color: $breadcrumb-dark-divider-color; 32 | } 33 | } 34 | 35 | &.active { 36 | color: $breadcrumb-dark-active-color; 37 | } 38 | } 39 | } 40 | 41 | 42 | // Links 43 | 44 | .breadcrumb-links { 45 | padding: 0; 46 | margin: 0; 47 | background: transparent; 48 | } 49 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_cards-extend.scss: -------------------------------------------------------------------------------- 1 | @import 'cards/card-blog'; 2 | @import 'cards/card-horizontal'; 3 | @import 'cards/card-profile'; 4 | @import 'cards/card-pricing'; 5 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_cards.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | box-shadow: $card-box-shadow; 3 | 4 | &[data-animation="true"] { 5 | .card-header { 6 | @include transform-translate-y(0); 7 | -webkit-transition: $header-data-animation-transition; 8 | -moz-transition: $header-data-animation-transition; 9 | -o-transition: $header-data-animation-transition; 10 | -ms-transition: $header-data-animation-transition; 11 | transition: $header-data-animation-transition; 12 | } 13 | } 14 | 15 | @include hover { 16 | &[data-animation="true"] { 17 | .card-header { 18 | @include transform-translate-y(-50px); 19 | } 20 | } 21 | } 22 | 23 | .card-header { 24 | padding: $card-header-padding; 25 | } 26 | 27 | .card-body { 28 | font-family: $font-family-sans-serif; 29 | padding: $card-body-padding; 30 | } 31 | 32 | &.card-plain { 33 | background-color: $card-plain-bg-color; 34 | box-shadow: $card-plain-box-shadow; 35 | } 36 | 37 | .card-footer { 38 | padding: $card-footer-padding; 39 | background-color: transparent; 40 | } 41 | } 42 | 43 | .author { 44 | display: $card-author-display; 45 | 46 | .name > span { 47 | line-height: $card-author-name-line-height; 48 | font-weight: $font-weight-bold; 49 | font-size: $font-size-sm; 50 | color: $card-author-name-color; 51 | } 52 | 53 | .stats { 54 | font-size: $font-size-sm; 55 | font-weight: $font-weight-normal; 56 | } 57 | } 58 | 59 | @import 'cards/card-background'; 60 | @import 'cards/card-rotate'; 61 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_components.scss: -------------------------------------------------------------------------------- 1 | // Badges 2 | @import "badges/badge"; 3 | @import "badges/badge-circle"; 4 | @import "badges/badge-dot"; 5 | @import "badges/badge-floating"; 6 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_dropdown-extend.scss: -------------------------------------------------------------------------------- 1 | // MultiLevel Dropdown Style 2 | 3 | .dropdown-menu li { 4 | position: relative; 5 | } 6 | 7 | // End MultiLevel Dropdown Style 8 | 9 | .dropdown-image { 10 | background-size: cover; 11 | } 12 | 13 | @include media-breakpoint-up(lg) { 14 | .dropdown-xl { 15 | min-width: $dropdown-xl-min-width; 16 | } 17 | 18 | .dropdown-lg { 19 | min-width: $dropdown-lg-min-width; 20 | } 21 | 22 | .dropdown-md { 23 | min-width: $dropdown-md-min-width; 24 | } 25 | } 26 | 27 | @include media-breakpoint-down(xl) { 28 | .dropdown-lg-responsive { 29 | min-width: $dropdown-lg-width-responsive; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_dropup.scss: -------------------------------------------------------------------------------- 1 | .dropup { 2 | .dropdown-menu { 3 | box-shadow: $dropdown-box-shadow; 4 | transition: $dropdown-transition; 5 | cursor: pointer; 6 | top: auto !important; 7 | bottom: 100% !important; 8 | margin-bottom: $dropup-mb !important; 9 | display: block; 10 | opacity: 0; 11 | transform-origin: $dropup-transform-origin; 12 | pointer-events: none; 13 | transform: $dropup-transform; 14 | -webkit-backface-visibility: hidden; 15 | backface-visibility: hidden; 16 | will-change: transform,box-shadow; 17 | 18 | &.show{ 19 | pointer-events: auto; 20 | transform: $dropup-transform-show; 21 | opacity: 1; 22 | 23 | &:after { 24 | bottom: -($dropup-animation-arrow-bottom-position - 2); 25 | } 26 | } 27 | 28 | &:after { 29 | font-family: "FontAwesome"; 30 | content: "\f0d7"; 31 | position: absolute; 32 | z-index: -1; 33 | bottom: $dropup-animation-arrow-bottom-position; 34 | left: $dropdown-animation-arrow-left-position; 35 | right: auto; 36 | font-size: $dropdown-animation-arrow-font-size; 37 | color: $white; 38 | transition: $dropup-animation-arrow-transition; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_fixed-plugin.scss: -------------------------------------------------------------------------------- 1 | .fixed-plugin{ 2 | .fixed-plugin-button{ 3 | background: $white; 4 | border-radius: $fixed-plugin-radius; 5 | bottom: $fixed-plugin-bottom; 6 | right: $fixed-plugin-right; 7 | font-size: $font-size-xl; 8 | z-index: $fixed-plugin-button-z-index; 9 | box-shadow: $fixed-plugin-box-shadow; 10 | cursor: pointer; 11 | i{ 12 | pointer-events: none; 13 | } 14 | } 15 | .card{ 16 | position: fixed !important; 17 | right: -$fixed-plugin-card-width; 18 | top: 0; 19 | height: 100%; 20 | left: auto!important; 21 | transform: unset !important; 22 | width: $fixed-plugin-card-width; 23 | border-radius: 0; 24 | padding: 0 10px; 25 | transition: .2s ease; 26 | z-index: $fixed-plugin-card-z-index; 27 | } 28 | 29 | .badge{ 30 | border: 1px solid $white; 31 | border-radius: 50%; 32 | cursor: pointer; 33 | display: inline-block; 34 | height: 23px; 35 | margin-right: 5px; 36 | position: relative; 37 | width: 23px; 38 | transition: $transition-base; 39 | &:hover, 40 | &.active{ 41 | border-color: $dark; 42 | } 43 | } 44 | 45 | .btn.bg-gradient-dark:not(:disabled):not(.disabled) { 46 | border: 1px solid transparent; 47 | &:not(.active) { 48 | background-color: transparent; 49 | background-image: none; 50 | border: 1px solid $dark; 51 | color: $dark; 52 | } 53 | } 54 | 55 | &.show{ 56 | .card{ 57 | right: 0; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_floating-elements.scss: -------------------------------------------------------------------------------- 1 | .floating-man { 2 | width: $floating-man-width; 3 | } 4 | 5 | .fadeIn1 { 6 | animation-duration: $fade-in-1-animation-duration; 7 | } 8 | .fadeIn2 { 9 | animation-duration: $fade-in-2-animation-duration; 10 | } 11 | .fadeIn3 { 12 | animation-duration: $fade-in-3-animation-duration; 13 | } 14 | .fadeIn4 { 15 | animation-duration: $fade-in-4-animation-duration; 16 | } 17 | .fadeIn5 { 18 | animation-duration: $fade-in-5-animation-duration; 19 | } 20 | .fadeIn1, 21 | .fadeIn2, 22 | .fadeIn3, 23 | .fadeIn4, 24 | .fadeIn5 { 25 | animation-fill-mode: both; 26 | } 27 | .fadeInBottom { 28 | animation-name: $fade-in-animation-name; 29 | } 30 | 31 | @keyframes fadeInBottom { 32 | from { 33 | opacity: 0; 34 | transform: $fade-in-bottom-transform; 35 | } 36 | to { 37 | opacity: 1 38 | } 39 | } 40 | 41 | 42 | 43 | 44 | // Headers 45 | 46 | .header-rounded-images { 47 | .shape-1 { 48 | width: $shape-1-width; 49 | left: $shape-1-left; 50 | } 51 | 52 | .shape-2 { 53 | width: $shape-2-width; 54 | left: $shape-2-left; 55 | } 56 | 57 | .shape-3 { 58 | width: $shape-3-width; 59 | margin-top: $shape-3-left; 60 | } 61 | 62 | .img-1 { 63 | right: $shape-img-1-right; 64 | width: $shape-img-1-width; 65 | margin-top: $shape-img-1-margin-top; 66 | } 67 | 68 | .img-2 { 69 | left: $shape-img-2-left; 70 | width: $shape-img-2-width; 71 | margin-top: $shape-img-2-margin-top; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_footer.scss: -------------------------------------------------------------------------------- 1 | .footer { 2 | .nav-link { 3 | color: $dark; 4 | font-weight: $font-weight-normal; 5 | font-size: $font-size-sm; 6 | padding-top: 0; 7 | padding-bottom: $nav-link-footer-padding; 8 | 9 | &:hover { 10 | opacity: 1 !important; 11 | transition: $footer-link-animation; 12 | } 13 | } 14 | .footer-logo { 15 | max-width: 2rem; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_forms.scss: -------------------------------------------------------------------------------- 1 | .input-group { 2 | @include border-radius($input-border-radius, 0); 3 | 4 | &, 5 | .input-group-text { 6 | transition: $input-transition; 7 | } 8 | 9 | & > :not(:first-child):not(.dropdown-menu) { 10 | margin-left: 0; 11 | } 12 | 13 | .form-control { 14 | box-shadow: none; 15 | 16 | &:not(:first-child) { 17 | border-left: 0; 18 | padding-left: 0; 19 | } 20 | &:not(:last-child) { 21 | border-right: 0; 22 | padding-right: 0; 23 | } 24 | 25 | & + .input-group-text { 26 | border-left: 0; 27 | border-right: $input-border-width solid $input-border-color; 28 | } 29 | } 30 | 31 | .input-group-text { 32 | border-right: 0; 33 | } 34 | 35 | 36 | 37 | &.focused { 38 | box-shadow: $input-focus-box-shadow; 39 | } 40 | 41 | &.focused .input-group-text { 42 | border-color: $input-focus-border-color; 43 | } 44 | } 45 | 46 | 47 | .form-group { 48 | margin-bottom: 1rem; 49 | } 50 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_gradients.scss: -------------------------------------------------------------------------------- 1 | @each $prop, $value in $theme-gradient-colors { 2 | .bg-gradient-#{$prop} { 3 | @include gradient-directional(nth($value, 1) 0%, nth($value, -1) 100%, $deg: 195deg); 4 | } 5 | } 6 | 7 | @each $prop, $value in $theme-colors { 8 | .bg-gradient-faded-#{$prop} { 9 | background-image: radial-gradient(370px circle at 80% 50%,rgba($value, .6) 0,darken($value, 10%) 100%) 10 | } 11 | } 12 | 13 | 14 | @each $prop, $value in $theme-colors { 15 | // Pegasus removed these lines because they generated a compilation error 16 | //&.bg-gradient-faded-#{$prop}-vertical{ 17 | // background-image: radial-gradient(200px circle at 50% 70%, rgba($value, .3) 0, $value 100%); 18 | //} 19 | } 20 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_header.scss: -------------------------------------------------------------------------------- 1 | .page-header { 2 | padding: $page-header-padding; 3 | position: $page-header-position; 4 | overflow: $page-header-overflow; 5 | display: $page-header-display; 6 | align-items: $page-header-align-items; 7 | background-size: $page-header-bg-size; 8 | background-position: $page-header-bg-position; 9 | 10 | .container { 11 | z-index: $page-header-conteiner-index; 12 | } 13 | 14 | video { 15 | position: absolute; 16 | top: $header-video-top; 17 | left: $header-video-left; 18 | min-width: $header-video-min-width; 19 | min-height: $header-video-min-height; 20 | width: auto; 21 | height: auto; 22 | z-index: 0; 23 | transform: $header-video-min-transform; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_icons.scss: -------------------------------------------------------------------------------- 1 | .material-icons { 2 | font-family: 'Material Icons Round'; 3 | font-weight: normal; 4 | font-style: normal; 5 | font-size: 20px; /* Preferred icon size */ 6 | display: inline-block; 7 | line-height: 1; 8 | text-transform: none; 9 | letter-spacing: normal; 10 | word-wrap: normal; 11 | white-space: nowrap; 12 | direction: ltr; 13 | 14 | /* Support for all WebKit browsers. */ 15 | -webkit-font-smoothing: antialiased; 16 | /* Support for Safari and Chrome. */ 17 | text-rendering: optimizeLegibility; 18 | 19 | /* Support for Firefox. */ 20 | -moz-osx-font-smoothing: grayscale; 21 | 22 | /* Support for IE. */ 23 | font-feature-settings: 'liga'; 24 | } 25 | 26 | .nav.nav-pills { 27 | .nav-link { 28 | .material-icons { 29 | top: 3px; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_list-check.scss: -------------------------------------------------------------------------------- 1 | // Checklist item 2 | // contains the checklist entry info and checkbox 3 | 4 | .checklist-item { 5 | &:before { 6 | content: ""; 7 | position: absolute; 8 | width: 3px; 9 | height: 100%; 10 | top: 0; 11 | left: 0; 12 | background-color: $primary; 13 | border-radius: $border-radius-sm; 14 | } 15 | } 16 | 17 | // Color variations 18 | 19 | @each $color, $value in $theme-colors { 20 | .checklist-item-#{$color} { 21 | &:before { 22 | background-color: $value; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_pagination.scss: -------------------------------------------------------------------------------- 1 | .page-item { 2 | &.active .page-link { 3 | box-shadow: $pagination-active-box-shadow; 4 | } 5 | 6 | .page-link, 7 | span { 8 | display: $page-link-display; 9 | align-items: $page-link-align-items; 10 | justify-content: $page-link-justify-content; 11 | color: $secondary; 12 | padding: 0; 13 | margin: $page-link-margin; 14 | border-radius: $page-link-radius !important; 15 | width: $page-link-width; 16 | height: $page-link-height; 17 | font-size: $font-size-sm; 18 | } 19 | } 20 | 21 | .pagination-lg { 22 | .page-item { 23 | .page-link, 24 | span { 25 | width: $page-link-width-lg; 26 | height: $page-link-height-lg; 27 | line-height: $page-link-line-height-lg; 28 | } 29 | } 30 | } 31 | 32 | .pagination-sm { 33 | .page-item { 34 | .page-link, 35 | span { 36 | width: $page-link-width-sm; 37 | height: $page-link-height-sm; 38 | line-height: $page-link-line-height-sm; 39 | } 40 | } 41 | } 42 | 43 | 44 | // Colors 45 | .pagination { 46 | @each $name, $value in $theme-gradient-colors { 47 | &.pagination-#{$name} { 48 | .page-item.active > .page-link { 49 | &, 50 | &:focus, 51 | &:hover { 52 | @include gradient-directional(nth($value, 1) 0%, nth($value, -1) 100%, $deg: 195deg); 53 | border: none; 54 | } 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_popovers.scss: -------------------------------------------------------------------------------- 1 | .popover { 2 | box-shadow: $popover-box-shadow; 3 | } 4 | 5 | // popover title 6 | .popover { 7 | .popover-header { 8 | font-weight: 600; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_progress.scss: -------------------------------------------------------------------------------- 1 | .progress-bar { 2 | height: $progress-bar-height; 3 | border-radius: $border-radius-sm; 4 | } 5 | 6 | .progress { 7 | overflow: visible; 8 | 9 | &.progress-sm { 10 | height: $progress-height-sm; 11 | } 12 | &.progress-lg { 13 | height: $progress-height-lg; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_ripple.scss: -------------------------------------------------------------------------------- 1 | // Ripple effect 2 | 3 | .ripple { 4 | display: block; 5 | position: absolute; 6 | background: rgba($white, .3); 7 | border-radius: 100%; 8 | transform:scale(0); 9 | animation:ripple 0.65s linear; 10 | 11 | } 12 | 13 | @keyframes ripple { 14 | 100% {opacity: 0; transform: scale(2.5);} 15 | } 16 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_rtl-extend.scss: -------------------------------------------------------------------------------- 1 | @include media-breakpoint-up(lg) { 2 | .rtl { 3 | .navbar-vertical { 4 | .navbar-nav { 5 | .collapse, 6 | .collapsing { 7 | .nav-item { 8 | .nav-link { 9 | &:before { 10 | right: $rtl-sidebar-bullet-right !important; 11 | left: auto !important; 12 | } 13 | } 14 | 15 | .collapse, 16 | .collapsing { 17 | .nav { 18 | padding-right: 0; 19 | } 20 | } 21 | } 22 | } 23 | } 24 | } 25 | 26 | &.g-sidenav-hidden { 27 | .navbar-vertical { 28 | .navbar-nav { 29 | .nav-item { 30 | .collapse, 31 | .collapsing { 32 | .nav { 33 | padding-right: 0; 34 | } 35 | } 36 | } 37 | } 38 | 39 | &:hover { 40 | .navbar-nav { 41 | > .nav-item { 42 | .collapse, 43 | .collapsing { 44 | .nav { 45 | padding-right: $rtl-sidebar-hover-padding-right; 46 | 47 | .nav-item { 48 | .collapse, 49 | .collapsing { 50 | .nav { 51 | padding-right: 0 !important; 52 | } 53 | } 54 | } 55 | } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_social-buttons.scss: -------------------------------------------------------------------------------- 1 | .btn { 2 | // social buttons 3 | &.btn-facebook { 4 | @include social-buttons-color($facebook, $facebook-states); 5 | } 6 | &.btn-twitter { 7 | @include social-buttons-color($twitter, $twitter-states); 8 | } 9 | &.btn-pinterest { 10 | @include social-buttons-color($pinterest, $pinterest-states); 11 | } 12 | &.btn-linkedin { 13 | @include social-buttons-color($linkedin, $linkedin-states); 14 | } 15 | &.btn-dribbble { 16 | @include social-buttons-color($dribbble, $dribbble-states); 17 | } 18 | &.btn-github { 19 | @include social-buttons-color($github, $github-states); 20 | } 21 | &.btn-youtube { 22 | @include social-buttons-color($youtube, $youtube-states); 23 | } 24 | &.btn-instagram { 25 | @include social-buttons-color($instagram, $instagram-states); 26 | } 27 | &.btn-reddit { 28 | @include social-buttons-color($reddit, $reddit-states); 29 | } 30 | &.btn-tumblr { 31 | @include social-buttons-color($tumblr, $tumblr-states); 32 | } 33 | &.btn-behance { 34 | @include social-buttons-color($behance, $behance-states); 35 | } 36 | &.btn-vimeo { 37 | @include social-buttons-color($vimeo, $vimeo-states); 38 | } 39 | &.btn-slack { 40 | @include social-buttons-color($slack, $slack-states); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_tables.scss: -------------------------------------------------------------------------------- 1 | // General styles 2 | 3 | .table { 4 | thead th { 5 | padding: $table-head-spacer-y $table-head-spacer-x; 6 | text-transform: $table-head-text-transform; 7 | letter-spacing: $table-head-letter-spacing; 8 | border-bottom: $table-border-width solid $table-border-color; 9 | } 10 | 11 | th { 12 | font-weight: $table-head-font-weight; 13 | } 14 | 15 | td { 16 | .progress { 17 | height: $table-progress-height; 18 | width: $table-progress-width; 19 | margin: $table-progress-margin; 20 | 21 | .progress-bar { 22 | height: $table-progress-height; 23 | } 24 | } 25 | } 26 | 27 | td, 28 | th { 29 | white-space: nowrap; 30 | } 31 | // Vetical align table content 32 | &.align-items-center { 33 | td, 34 | th { 35 | vertical-align: middle; 36 | } 37 | } 38 | tbody{ 39 | tr:last-child{ 40 | td{ 41 | border-width: 0; 42 | } 43 | } 44 | } 45 | 46 | > :not(:last-child) > :last-child > * { 47 | // pegasus removed due to styling issues in htmx example 48 | // border-bottom-color: $light; 49 | } 50 | 51 | > :not(:first-child) { 52 | // pegasus removed due to styling issues in htmx example 53 | // border-top: (1 * $table-border-width) solid $table-group-separator-color; 54 | border-top: 0; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_tilt.scss: -------------------------------------------------------------------------------- 1 | // Tilt Animation 2 | .tilt { 3 | -webkit-transform-style: $tilt-transform-style; 4 | transform-style: $tilt-transform-style; 5 | 6 | .up { 7 | -webkit-transform: $tilt-transform-up-transform; 8 | transform: $tilt-transform-up-transform !important; 9 | transition: $tilt-transform-up-transition; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_tooltips.scss: -------------------------------------------------------------------------------- 1 | .bs-tooltip-auto[x-placement^=right] .tooltip-arrow, 2 | .bs-tooltip-right .tooltip-arrow { 3 | left: $tooltip-arrow-left; 4 | } 5 | 6 | .bs-tooltip-auto[x-placement^=left] .tooltip-arrow, 7 | .bs-tooltip-left .tooltip-arrow { 8 | right: $tooltip-arrow-right; 9 | } 10 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/_utilities-extend.scss: -------------------------------------------------------------------------------- 1 | @each $name, $value in $max-width-dim{ 2 | .max-width-#{$name} { 3 | max-width: $value !important; 4 | } 5 | } 6 | 7 | @each $name, $value in $width-dim{ 8 | .width-#{$name} { 9 | width: $value !important; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/badges/_badge-circle.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Circle badge 3 | // 4 | 5 | 6 | // General styles 7 | 8 | .badge-circle { 9 | text-align: center; 10 | display: inline-flex; 11 | align-items: center; 12 | justify-content: center; 13 | border-radius: $badge-circle-border-radius; 14 | padding: 0 !important; 15 | width: $badge-circle-width; 16 | height: $badge-circle-height; 17 | font-size: $badge-circle-font-size; 18 | font-weight: $badge-circle-font-weight; 19 | 20 | &.badge-md { 21 | width: $badge-circle-md-width; 22 | height: $badge-circle-md-height; 23 | } 24 | 25 | &.badge-lg { 26 | width: $badge-circle-lg-width; 27 | height: $badge-circle-lg-height; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/badges/_badge-dot.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Dot badge 3 | // 4 | 5 | 6 | // General styles 7 | 8 | .badge-dot { 9 | padding-left: 0; 10 | padding-right: 0; 11 | background: transparent; 12 | font-weight: $font-weight-normal; 13 | font-size: $font-size-sm; 14 | text-transform: none; 15 | 16 | strong { 17 | color: $gray-800; 18 | } 19 | 20 | i { 21 | display: inline-block; 22 | vertical-align: middle; 23 | width: $badge-dot-icon-width; 24 | height: $badge-dot-icon-height; 25 | border-radius: $badge-dot-icon-radius; 26 | margin-right: $badge-dot-icon-margin-right; 27 | } 28 | 29 | &.badge-md { 30 | i { 31 | width: $badge-dot-md-icon-width; 32 | height: $badge-dot-md-icon-height; 33 | } 34 | } 35 | 36 | &.badge-lg { 37 | i { 38 | width: $badge-dot-lg-icon-width; 39 | height: $badge-dot-lg-icon-height; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/badges/_badge-floating.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Badge floating 3 | // 4 | 5 | 6 | .btn { 7 | .badge-floating { 8 | position: absolute; 9 | top: -$badge-floating-top; 10 | transform: translateY($badge-floating-top); 11 | border: $badge-floating-border solid; 12 | 13 | &.badge:not(.badge-circle) { 14 | transform: $badge-floating-transform; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/badges/_badge.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Badge 3 | // 4 | 5 | 6 | // General styles 7 | 8 | .badge { 9 | 10 | 11 | a { 12 | color: $white; 13 | } 14 | } 15 | 16 | 17 | // Size variations 18 | 19 | .badge-sm{ 20 | padding: $badge-sm-padding; 21 | font-size: $badge-sm-font-size; 22 | border-radius: $border-radius-md; 23 | } 24 | 25 | .badge-md { 26 | padding: $badge-md-padding; 27 | } 28 | 29 | .badge-lg { 30 | padding: $badge-lg-padding; 31 | } 32 | 33 | 34 | // Multiple inline badges 35 | 36 | .badge-inline { 37 | margin-right: $badge-inline-margin-right; 38 | 39 | + span { 40 | top: $badge-inline-span-top; 41 | position: relative; 42 | 43 | > a { 44 | text-decoration: underline; 45 | } 46 | } 47 | } 48 | 49 | 50 | // Color fixes 51 | 52 | .badge-default { 53 | color: $white; 54 | } 55 | 56 | .badge.badge-secondary { 57 | background-color: lighten($secondary, 32%); 58 | color: $gray-600; 59 | } 60 | 61 | 62 | // Badge spacing inside a btn with some text 63 | 64 | .btn { 65 | .badge { 66 | &:not(:first-child) { 67 | margin-left: $badge-btn-margin; 68 | } 69 | &:not(:last-child) { 70 | margin-right: $badge-btn-margin; 71 | } 72 | } 73 | } 74 | 75 | // Colors 76 | // 77 | // Contextual variations (linked badges get darker on :hover). 78 | 79 | @each $color, $value in $theme-colors { 80 | .badge-#{$color} { 81 | @include badge-variant($value); 82 | } 83 | } 84 | 85 | 86 | 87 | // card-pricing badge position 88 | 89 | .card { 90 | >.badge { 91 | margin-top: $card-badge-position !important; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/cards/card-blog.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | &.card-blog { 3 | .card-image { 4 | box-shadow: $box-shadow; 5 | 6 | .img { 7 | width: 100%; 8 | } 9 | } 10 | 11 | .card-title { 12 | a { 13 | color: $dark; 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/cards/card-horizontal.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | &.card-horizontal { 3 | box-shadow: none; 4 | 5 | .card-image { 6 | border-radius: $card-border-radius; 7 | 8 | .img { 9 | width: 100%; 10 | border-radius: $card-border-radius; 11 | } 12 | } 13 | 14 | .card-body { 15 | .card-title { 16 | font-size: $h4-font-size; 17 | 18 | a { 19 | color: $dark; 20 | } 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/cards/card-pricing.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | &.card-pricing { 3 | .card-body { 4 | padding: $card-pricing-body-padding; 5 | } 6 | .table { 7 | tr { 8 | border-image: $card-pricing-border-color; 9 | } 10 | td { 11 | .badge { 12 | padding: $card-pricing-badge-padding; 13 | font-size: $card-pricing-badge-font-size; 14 | position: $card-pricing-badge-position; 15 | top: $card-pricing-badge-top; 16 | } 17 | } 18 | } 19 | &[data-feature="icon"] { 20 | 21 | .icon { 22 | height: $card-pricing-icon-height; 23 | width: $card-pricing-icon-width; 24 | position: $card-pricing-icon-position; 25 | top: $card-pricing-icon-top; 26 | left: 0; 27 | right: 0; 28 | 29 | i { 30 | color: $white; 31 | padding: $card-pricing-i-padding; 32 | } 33 | } 34 | } 35 | 36 | .plans { 37 | i { 38 | font-size: $card-pricing-icon-font-size; 39 | } 40 | .card-category { 41 | text-transform: uppercase; 42 | } 43 | } 44 | .icon-lg i { 45 | font-size: $card-pricing-icon-lg-font-size; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/cards/card-profile.scss: -------------------------------------------------------------------------------- 1 | .card { 2 | &.card-profile { 3 | .card-body { 4 | padding: $card-profile-body-padding; 5 | } 6 | 7 | .card-avatar { 8 | margin: $card-profile-avatar-margin; 9 | 10 | .img { 11 | margin-top: $card-profile-img-mt; 12 | border-radius: $card-profile-img-radius; 13 | width: $card-profile-img-width; 14 | } 15 | } 16 | 17 | p.lead { 18 | font-weight: $font-weight-bold; 19 | font-size: $h6-font-size; 20 | line-height: $card-profile-p-line-height; 21 | } 22 | .table { 23 | td { 24 | font-weight: $font-weight-light; 25 | font-size: $font-size-base; 26 | } 27 | } 28 | 29 | .card-before { 30 | &:before { 31 | position: absolute; 32 | bottom: 0; 33 | left: 0; 34 | width: 100%; 35 | height: 50%; 36 | display: block; 37 | z-index: 0; 38 | content: ''; 39 | transition: opacity .65s cubic-bezier(.05,.2,.1,1); 40 | } 41 | 42 | &.mask-primary:before { 43 | background: linear-gradient(to bottom,rgba(15,15,15,0),$primary 100%); 44 | } 45 | &.mask-info:before { 46 | background: linear-gradient(to bottom,rgba(15,15,15,0),$info 100%); 47 | } 48 | &.mask-warning:before { 49 | background: linear-gradient(to bottom,rgba(15,15,15,0),$warning-gradient-state 100%); 50 | } 51 | &.mask-danger:before { 52 | background: linear-gradient(to bottom,rgba(15,15,15,0),$danger 100%); 53 | } 54 | &.mask-success:before { 55 | background: linear-gradient(to bottom,rgba(15,15,15,0),$success 100%); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/custom/_styles.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | .floating-alerts { 5 | position: fixed; 6 | top: 10px; 7 | left: 50%; 8 | transform: translateX(-50%); 9 | width: 90%; 10 | z-index: 9999; 11 | } 12 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/custom/_variables.scss: -------------------------------------------------------------------------------- 1 | // make the navbar a bit bigger so it fits everything by default 2 | $navbar-vertical-inner: calc(100vh - 200px) !default; 3 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/forms/_form-select.scss: -------------------------------------------------------------------------------- 1 | .form-select { 2 | transition: $input-transition; 3 | } 4 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/forms/_form-switch.scss: -------------------------------------------------------------------------------- 1 | .form-switch{ 2 | .form-check-input{ 3 | position: relative; 4 | background-color: $form-switch-bg-color; 5 | height: $form-switch-height; 6 | width: $form-switch-width; 7 | 8 | &:after { 9 | transition: transform $form-check-transition-time ease-in-out, background-color $form-check-transition-time ease-in-out; 10 | content: ""; 11 | width: $form-switch-check-after-width; 12 | height: $form-switch-check-after-width; 13 | border-radius: 50%; 14 | border: 1px solid $form-switch-check-after-border-color; 15 | position: absolute; 16 | background-color: $white; 17 | transform: translateX($form-switch-translate-x-start); 18 | box-shadow: $form-switch-round-box-shadow; 19 | top: $form-switch-check-top; 20 | left: $form-switch-check-left; 21 | } 22 | 23 | &:checked:after { 24 | transform: translateX($form-switch-translate-x-end); 25 | border-color: $dark-gradient; 26 | } 27 | 28 | &:checked { 29 | border-color: $dark-gradient; 30 | background-color: $dark-gradient; 31 | &:active{ 32 | &:after{ 33 | box-shadow: $form-switch-check-active-checked-after-shadow; 34 | } 35 | } 36 | } 37 | &:active{ 38 | &:after{ 39 | box-shadow: $form-switch-check-active-after-shadow; 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/forms/_forms.scss: -------------------------------------------------------------------------------- 1 | @import 'input-group'; 2 | @import 'form-check'; 3 | @import 'form-switch'; 4 | @import 'form-select'; 5 | @import 'labels'; 6 | @import 'inputs'; 7 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/forms/_inputs.scss: -------------------------------------------------------------------------------- 1 | .form-control { 2 | border: none; 3 | 4 | &.is-invalid { 5 | border: $input-border-width solid $input-border-color; 6 | padding: $form-control-outline-padding; 7 | line-height: 1.3 !important; 8 | 9 | &:focus { 10 | box-shadow: 0 0 0 2px rgba($form-feedback-invalid-color, .6); 11 | } 12 | } 13 | 14 | &.is-valid { 15 | border: $input-border-width solid $input-border-color; 16 | padding: $form-control-outline-padding; 17 | line-height: 1.3 !important; 18 | 19 | &:focus { 20 | box-shadow: 0 0 0 2px rgba($form-feedback-valid-color, .65); 21 | } 22 | } 23 | 24 | &[disabled] { 25 | padding: $form-control-outline-padding; 26 | line-height: 1.45 !important; 27 | } 28 | } 29 | 30 | .input-group { 31 | .input-group-text { 32 | position: absolute; 33 | padding: .75rem 0; 34 | right: 0; 35 | border-right: 0 !important; 36 | 37 | i { 38 | color: $gray-600; 39 | } 40 | } 41 | 42 | &.input-group-static { 43 | .input-group-text { 44 | bottom: 0; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/forms/_labels.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Labels 3 | // 4 | 5 | label, 6 | .form-label { 7 | font-size: $form-label-font-size; 8 | font-weight: $form-label-font-weight; 9 | margin-bottom: $form-label-margin-bottom; 10 | color: $form-label-color; 11 | margin-left: $form-label-margin-left; 12 | } 13 | 14 | .input-group{ 15 | .form-label{ 16 | position: $form-label-position; 17 | top: $form-label-top; 18 | margin-left: 0; 19 | transition: $form-label-transition; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/mixins/_badge.scss: -------------------------------------------------------------------------------- 1 | @mixin badge-variant($bg) { 2 | color: saturate(darken($bg, 10%), 10); 3 | background-color: lighten($bg, 32%); 4 | 5 | &[href] { 6 | @include hover-focus { 7 | color: color-yiq($bg); 8 | text-decoration: none; 9 | background-color: darken($bg, 12%); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/mixins/_buttons.scss: -------------------------------------------------------------------------------- 1 | @mixin colored-shadows($value){ 2 | // new box shadow optimized for Tablets and Phones 3 | box-shadow: 0 3px 3px 0 rgba($value, .15), 4 | 0 3px 1px -2px rgba($value, .2), 5 | 0 1px 5px 0 rgba($value, .15); 6 | } 7 | 8 | @mixin colored-shadows-hover($value){ 9 | box-shadow: 0 14px 26px -12px rgba($value, .4), 10 | 0 4px 23px 0 rgba($value, .15), 11 | 0 8px 10px -5px rgba($value, .2); 12 | } 13 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/mixins/_colored-shadows.scss: -------------------------------------------------------------------------------- 1 | @mixin shadow-big-color($color){ 2 | // new box shadow optimized for Tablets and Phones 3 | box-shadow: 0 4px 20px 0px rgba(0, 0, 0, .14), 4 | 0 7px 10px -5px rgba($color, 0.4) 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/mixins/_hover.scss: -------------------------------------------------------------------------------- 1 | 2 | @mixin hover() { 3 | &:hover { @content; } 4 | } 5 | 6 | @mixin hover-focus() { 7 | &:hover, 8 | &:focus { 9 | @content; 10 | } 11 | } 12 | 13 | @mixin plain-hover-focus() { 14 | &, 15 | &:hover, 16 | &:focus { 17 | @content; 18 | } 19 | } 20 | 21 | @mixin hover-focus-active() { 22 | &:hover, 23 | &:focus, 24 | &:active { 25 | @content; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/mixins/_social-buttons.scss: -------------------------------------------------------------------------------- 1 | // for social buttons 2 | @mixin social-buttons-color ($color, $state-color) { 3 | background-color: $color; 4 | color: $white; 5 | 6 | &:focus, 7 | &:hover { 8 | background-color: $state-color; 9 | color: $white; 10 | } 11 | &:active, 12 | &:focus, 13 | &:active:focus { 14 | box-shadow: none; 15 | } 16 | 17 | &.btn-simple { 18 | color: $state-color; 19 | background-color: transparent; 20 | background-image: none !important; 21 | box-shadow: none; 22 | border: none; 23 | 24 | &:hover, 25 | &:focus, 26 | &:hover:focus, 27 | &:active, 28 | &:hover:focus:active { 29 | color: $state-color; 30 | background: transparent !important; 31 | box-shadow: none !important; 32 | } 33 | } 34 | 35 | 36 | &.btn-neutral { 37 | color: $color; 38 | background-color: $white; 39 | 40 | &:hover, 41 | &:focus, 42 | &:active { 43 | color: $state-color; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/mixins/_vendor.scss: -------------------------------------------------------------------------------- 1 | @mixin transform-translate-y($value) { 2 | -webkit-transform: translate3d(0, $value, 0); 3 | -moz-transform: translate3d(0, $value, 0); 4 | -o-transform: translate3d(0, $value, 0); 5 | -ms-transform: translate3d(0, $value, 0); 6 | transform: translate3d(0, $value, 0); 7 | } 8 | @mixin perspective($value) { 9 | -webkit-perspective: $value; 10 | -moz-perspective: $value; 11 | -o-perspective: $value; 12 | -ms-perspective: $value; 13 | perspective: $value; 14 | } 15 | @mixin transitions($time, $type) { 16 | -webkit-transition: all $time $type; 17 | -moz-transition: all $time $type; 18 | -o-transition: all $time $type; 19 | -ms-transition: all $time $type; 20 | transition: all $time $type; 21 | } 22 | @mixin transitions-property($property, $time, $type) { 23 | -webkit-transition: $property $time $type; 24 | -moz-transition: $property $time $type; 25 | -o-transition: $property $time $type; 26 | -ms-transition: $property $time $type; 27 | transition: $property $time $type; 28 | } 29 | @mixin transform-style($type){ 30 | -webkit-transform-style: $type; 31 | -moz-transform-style: $type; 32 | -o-transform-style: $type; 33 | -ms-transform-style: $type; 34 | transform-style: $type; 35 | } 36 | 37 | @mixin backface-visibility($type){ 38 | -webkit-backface-visibility: $type; 39 | -moz-backface-visibility: $type; 40 | -o-backface-visibility: $type; 41 | -ms-backface-visibility: $type; 42 | backface-visibility: $type; 43 | } 44 | 45 | @mixin rotateY-180() { 46 | -webkit-transform: rotateY( 180deg ); 47 | -moz-transform: rotateY( 180deg ); 48 | -o-transform: rotateY( 180deg ); 49 | -ms-transform: rotateY(180deg); 50 | transform: rotateY( 180deg ); 51 | } 52 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/mixins/mixins.scss: -------------------------------------------------------------------------------- 1 | @import "badge"; 2 | @import "buttons"; 3 | @import "hover"; 4 | @import "colored-shadows"; 5 | @import "social-buttons"; 6 | @import "vendor"; 7 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/theme.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | 3 | ========================================================= 4 | * Material Dashboard - v3.0.0 5 | ========================================================= 6 | 7 | * Product Page: https://www.creative-tim.com/product/material-dashboard 8 | * Copyright 2021 Creative Tim (https://www.creative-tim.com) 9 | * Licensed under MIT (site.license) 10 | 11 | * Coded by www.creative-tim.com 12 | 13 | ========================================================= 14 | 15 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 16 | 17 | */ 18 | 19 | // ( site.product.name ) components 20 | 21 | // Variables 22 | @import "variables/navbar-vertical"; 23 | @import "variables/social-buttons"; 24 | @import "variables/breadcrumb"; 25 | 26 | // Mixin 27 | @import "mixins/mixins"; 28 | 29 | // Core Components - extra styling 30 | @import "alert"; 31 | @import "avatars"; 32 | @import "badge"; 33 | @import "buttons"; 34 | @import "breadcrumbs"; 35 | @import "cards"; 36 | @import "dark-version"; 37 | @import "dropdown"; 38 | @import "dropup"; 39 | @import "header"; 40 | @import "fixed-plugin"; 41 | @import "forms/forms"; 42 | @import "footer"; 43 | @import "gradients"; 44 | @import "icons"; 45 | @import "info-areas"; 46 | @import "misc"; 47 | @import "navbar"; 48 | @import "navbar-vertical"; 49 | @import "nav"; 50 | @import "pagination"; 51 | @import "popovers"; 52 | @import "progress"; 53 | @import "rtl"; 54 | @import "ripple"; 55 | @import "social-buttons"; 56 | @import "tables"; 57 | @import "timeline"; 58 | @import "tilt"; 59 | @import "tooltips"; 60 | @import "typography"; 61 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_avatars.scss: -------------------------------------------------------------------------------- 1 | // Avatar 2 | $avatar-height: 48px !default; 3 | $avatar-width: 48px !default; 4 | 5 | $avatar-xs-height: 24px !default; 6 | $avatar-xs-width: 24px !default; 7 | 8 | $avatar-sm-height: 36px !default; 9 | $avatar-sm-width: 36px !default; 10 | 11 | $avatar-lg-height: 58px !default; 12 | $avatar-lg-width: 58px !default; 13 | 14 | $avatar-xl-height: 74px !default; 15 | $avatar-xl-width: 74px !default; 16 | 17 | $avatar-xxl-height: 110px !default; 18 | $avatar-xxl-width: 110px !default; 19 | 20 | $avatar-font-size: 1rem !default; 21 | $avatar-content-margin: .75rem !default; 22 | 23 | 24 | // Avatar Group 25 | $avatar-group-border: 2px !default; 26 | $avatar-group-zindex: 2 !default; 27 | $avatar-group-zindex-hover: 3 !default; 28 | $avatar-group-double: -1rem !default; 29 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_badge.scss: -------------------------------------------------------------------------------- 1 | // Badge 2 | $badge-sm-padding: .45em .775em !default; 3 | $badge-sm-font-size: .65em !default; 4 | $badge-md-padding: .65em 1em !default; 5 | $badge-lg-padding: .85em 1.375em !default; 6 | $badge-inline-margin-right: .625rem !default; 7 | $badge-inline-span-top: 2px !default; 8 | $badge-btn-margin: .5rem !default; 9 | 10 | // Badge Circle 11 | $badge-circle-border-radius: 50% !default; 12 | $badge-circle-width: 1.25rem !default; 13 | $badge-circle-height: 1.25rem !default; 14 | $badge-circle-font-size: .75rem !default; 15 | $badge-circle-font-weight: 600 !default; 16 | 17 | $badge-circle-md-width: 1.5rem !default; 18 | $badge-circle-md-height: 1.5rem !default; 19 | 20 | $badge-circle-lg-width: 2rem !default; 21 | $badge-circle-lg-height: 2rem !default; 22 | 23 | //Badge Dot 24 | $badge-dot-icon-width: .375rem !default; 25 | $badge-dot-icon-height: .375rem !default; 26 | $badge-dot-icon-radius: 50% !default; 27 | $badge-dot-icon-margin-right: .375rem !default; 28 | 29 | $badge-dot-md-icon-width: .5rem !default; 30 | $badge-dot-md-icon-height: .5rem !default; 31 | 32 | $badge-dot-lg-icon-width: .625rem !default; 33 | $badge-dot-lg-icon-height: .625rem !default; 34 | 35 | //Badge Floating 36 | $badge-floating-top: -50% !default; 37 | $badge-floating-border: 3px !default; 38 | $badge-floating-transform: translate(147%, 50%) !default; 39 | 40 | $card-badge-position: -.6875rem !default; 41 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_breadcrumb.scss: -------------------------------------------------------------------------------- 1 | $breadcrumb-dark-bg: $dark !default; 2 | $breadcrumb-dark-color: $gray-100 !default; 3 | $breadcrumb-dark-hover-color: $white !default; 4 | $breadcrumb-dark-active-color: $gray-300 !default; 5 | $breadcrumb-dark-divider-color: $gray-500 !default; 6 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_cards-extend.scss: -------------------------------------------------------------------------------- 1 | // Card Profile 2 | $card-profile-body-text-align: center !default; 3 | $card-profile-body-padding: 1.25rem 2rem !default; 4 | $card-profile-avatar-margin: 0 auto !default; 5 | $card-profile-img-mt: 32px !default; 6 | $card-profile-img-radius: 50% !default; 7 | $card-profile-img-width: 130px !default; 8 | $card-profile-btn-mt: 24px !default; 9 | $card-profile-p-line-height: 1.778 !default; 10 | 11 | // Card Pricing 12 | $card-pricing-body-padding: 2.25rem !default; 13 | $card-pricing-line-height: 1.111 !default; 14 | $card-pricing-title-mb: $card-bg-description-margin !default; 15 | $card-pricing-td-line-height: 1.429 !default; 16 | $card-pricing-icon-height: 4rem !default; 17 | $card-pricing-icon-width: $card-pricing-icon-height !default; 18 | $card-pricing-icon-position: absolute !default; 19 | $card-pricing-icon-top: -22px !default; 20 | $card-pricing-icon-font-size: 1.25rem !default; 21 | $card-pricing-icon-lg-font-size: 1.75rem !default; 22 | $card-pricing-i-padding: 18px !default; 23 | $card-pricing-badge-padding: 5px !default; 24 | $card-pricing-badge-font-size: 6px !default; 25 | $card-pricing-badge-position: relative !default; 26 | $card-pricing-badge-top: -2px !default; 27 | $card-pricing-border-color: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1), rgba(255, 255, 255, 0)); 28 | $card-pricing-border-color-dark: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, .4), rgba(0, 0, 0, 0)); 29 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_choices.scss: -------------------------------------------------------------------------------- 1 | $choices-box-shadow: $dropdown-box-shadow !default; 2 | $choices-border-radius: .5rem !default; 3 | $choices-animation: .3s cubic-bezier(.23,1,.32,1) !default; 4 | $choices-transition: $dropdown-transition !default; 5 | $choices-transform: scale(.95) !important !default; 6 | $choices-transform-show: scale(1) !important !default; 7 | $choices-padding-y: 1rem !default; 8 | $choices-padding-x: .5rem !default; 9 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_dark-version.scss: -------------------------------------------------------------------------------- 1 | $dark-version-body-color: rgba(255, 255, 255, .8) !default; 2 | $dark-version-bg-color: #1a2035 !default; 3 | $dark-version-sidenav-bg-color: #1f283e !default; 4 | $dark-version-card-bg-color: #202940 !default; 5 | $dark-version-card-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .20), 0 1px 5px 0 rgba(0, 0, 0, .12) !default; 6 | $dark-version-input-bg-image: linear-gradient(0deg, #e91e63 2px, rgba(156, 39, 176, 0) 0), linear-gradient(0deg, rgba(210, 210, 210, .6) 1px, rgba(209, 209, 209, 0) 0) !default; 7 | $dark-version-border-color: rgba(255, 255, 255, .4) !default; 8 | $dark-version-table-color: rgba(255, 255, 255, .6) !default; 9 | $dark-version-caret-sidebar-color: rgba(206, 212, 218, .7) !default; 10 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_fixed-plugin.scss: -------------------------------------------------------------------------------- 1 | $fixed-plugin-bottom: 30px !default; 2 | $fixed-plugin-right: $fixed-plugin-bottom !default; 3 | $fixed-plugin-radius: 50% !default; 4 | $fixed-plugin-box-shadow: $navbar-box-shadow !default; 5 | $fixed-plugin-button-z-index: 990 !default; 6 | $fixed-plugin-card-z-index: 1020 !default; 7 | $fixed-plugin-card-width: 360px !default; 8 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_form-switch.scss: -------------------------------------------------------------------------------- 1 | $slider-dim: 15px !default; 2 | $slider-position: 2px !default; 3 | $moving-circle: translateX(21px) !default; 4 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_full-calendar.scss: -------------------------------------------------------------------------------- 1 | $fc-event-title-padding-y: .2rem !default; 2 | $fc-event-title-padding-x: .3rem !default; 3 | 4 | $fc-daygrid-event-border-radius: .35rem !default; 5 | 6 | $fc-theme-standard-dark-border-color: rgba($secondary, .3) !default; 7 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_header.scss: -------------------------------------------------------------------------------- 1 | $page-header-padding: 0 !default; 2 | $page-header-position: relative !default; 3 | $page-header-overflow: hidden !default; 4 | $page-header-display: flex !default; 5 | $page-header-align-items: center !default; 6 | $page-header-bg-size: cover !default; 7 | $page-header-bg-position: 50% !default; 8 | 9 | $mask-position: absolute !default; 10 | $mask-bg-size: cover !default; 11 | $mask-bg-position: center center !default; 12 | $mask-top: 0 !default; 13 | $mask-left: $mask-top !default; 14 | $mask-width: 100% !default; 15 | $mask-height: $mask-width !default; 16 | $mask-opacity: .8 !default; 17 | 18 | $page-header-conteiner-index: 1 !default; 19 | 20 | $header-video-top: 50% !default; 21 | $header-video-left: $header-video-top !default; 22 | $header-video-min-width: 100% !default; 23 | $header-video-min-height: $header-video-min-width !default; 24 | $header-video-min-transform: translateX(-50%) translateY(-50%) !default; 25 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_info-areas.scss: -------------------------------------------------------------------------------- 1 | $icon-shape-bg-image: linear-gradient(195deg,#7928CA,#FF0080) !default; 2 | $icon-shape-bg-position: center !default; 3 | 4 | $icon-striped-bg-md: 85px !default; 5 | $icon-striped-bg-lg: 111px !default; 6 | $icon-striped-bg-xl: 80px !default; 7 | 8 | $icon-striped-icon-mt: 25% !default; 9 | $icon-striped-icon-ml: -24% !default; 10 | 11 | $icon-shape-icon-opacity: .8 !default; 12 | $info-icon-top: 11px !default; 13 | $info-icon-top-xxs: 0 !default; 14 | $info-icon-top-xs: -1px !default; 15 | $info-icon-top-sm: 4px !default; 16 | $info-icon-top-md: 30% !default; 17 | $info-icon-top-lg: 31% !default; 18 | $info-icon-top-xl: 35% !default; 19 | $info-icon-position: relative !default; 20 | 21 | $icon-xxs-width: 20px !default; 22 | $icon-xxs-height: $icon-xxs-width !default; 23 | $icon-xs-width: 24px !default; 24 | $icon-xs-height: $icon-xs-width !default; 25 | $icon-sm-width: 32px !default; 26 | $icon-sm-height: $icon-sm-width !default; 27 | $icon-md-width: 48px !default; 28 | $icon-md-height: $icon-md-width !default; 29 | $icon-lg-width: 64px !default; 30 | $icon-lg-height: $icon-lg-width !default; 31 | $icon-xl-width: 100px !default; 32 | $icon-xl-height: $icon-xl-width !default; 33 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_navbar.scss: -------------------------------------------------------------------------------- 1 | // Navbar toggler icon on responsive styling 2 | 3 | $navbar-toggler-bar-display: block !default; 4 | $navbar-toggler-bar-position: relative !default; 5 | $navbar-toggler-bar-width: 22px !default; 6 | $navbar-toggler-bar-height: 1px !default; 7 | $navbar-toggler-bar-radius: 1px !default; 8 | $navbar-toggler-bar-transition: all 0.2s !default; 9 | $navbar-toggler-bar-margin-top: 7px !default; 10 | 11 | $navbar-toggler-bar-1-transform: rotate(45deg) !default; 12 | $navbar-toggler-bar-1-transform-origin: 10% 10% !default; 13 | $navbar-toggler-bar-1-margin-top: 4px !default; 14 | $navbar-toggler-bar-2-opacity: 0 !default; 15 | $navbar-toggler-bar-3-transform: rotate(-45deg) !default; 16 | $navbar-toggler-bar-3-transform-origin: 10% 90% !default; 17 | $navbar-toggler-bar-3-margin-top: 3px !default; 18 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_pagination.scss: -------------------------------------------------------------------------------- 1 | // Pagination 2 | 3 | $pagination-active-box-shadow: $btn-hover-box-shadow !default; 4 | 5 | $page-link-display: flex !default; 6 | $page-link-align-items: center !default; 7 | $page-link-justify-content: $page-link-align-items !default; 8 | $page-link-margin: 0 3px !default; 9 | $page-link-radius: 50% !default; 10 | $page-link-width: 36px !default; 11 | $page-link-height: $page-link-width !default; 12 | 13 | $page-link-width-lg: 46px !default; 14 | $page-link-height-lg: $page-link-width-lg !default; 15 | $page-link-line-height-lg: $page-link-width-lg !default; 16 | 17 | $page-link-width-sm: 30px !default; 18 | $page-link-height-sm: $page-link-width-sm !default; 19 | $page-link-line-height-sm: $page-link-width-sm !default; 20 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_ripple.scss: -------------------------------------------------------------------------------- 1 | // RIPPLE Effect 2 | 3 | $ripple-position: relative !default; 4 | 5 | $ripple-container-position: absolute !default; 6 | $ripple-container-top: 0 !default; 7 | $ripple-container-left: $ripple-container-top !default; 8 | $ripple-container-z-index: 1 !default; 9 | $ripple-container-width: 100% !default; 10 | $ripple-container-height: $ripple-container-width !default; 11 | $ripple-container-overflow: hidden !default; 12 | $ripple-container-pointer: none !default; 13 | $ripple-container-radius: inherit !default; 14 | 15 | $ripple-decorator-position: $ripple-container-position !default; 16 | $ripple-decorator-width: 20px !default; 17 | $ripple-decorator-height: $ripple-decorator-width !default; 18 | $ripple-decorator-mt: -10px !default; 19 | $ripple-decorator-ml: $ripple-decorator-mt !default; 20 | $ripple-decorator-pointer: $ripple-container-pointer !default; 21 | $ripple-decorator-bg-color: rgba($black, 0.05) !default; 22 | $ripple-decorator-radius: 100% !default; 23 | $ripple-decorator-opacity: 0 !default; 24 | $ripple-decorator-transform: scale(1) !default; 25 | $ripple-decorator-transform-origin: 50% !default; 26 | 27 | $ripple-on-opacity: .1 !default; 28 | $ripple-on-transition: opacity 0.15s ease-in 0s, 29 | transform 0.5s cubic-bezier(0.4, 0, 0.2, 1) 0.1s !default; 30 | 31 | $ripple-out-opacity: 0 !default; 32 | $ripple-out-transition: opacity 0.1s linear 0s !default; 33 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_rtl.scss: -------------------------------------------------------------------------------- 1 | $timeline-step-transform-rtl: translateX(50%) !default; 2 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_social-buttons.scss: -------------------------------------------------------------------------------- 1 | // Social Buttons 2 | 3 | $facebook: #3b5998 !default; 4 | $facebook-states: darken($facebook, 5%) !default; 5 | $twitter: #55acee !default; 6 | $twitter-states: darken($twitter, 5%) !default; 7 | $instagram: #125688 !default; 8 | $instagram-states: darken($instagram, 6%) !default; 9 | $linkedin: #0077B5 !default; 10 | $linkedin-states: darken($linkedin, 5%) !default; 11 | $pinterest: #cc2127 !default; 12 | $pinterest-states: darken($pinterest, 6%) !default; 13 | $youtube: #e52d27 !default; 14 | $youtube-states: darken($youtube, 6%) !default; 15 | $dribbble: #ea4c89 !default; 16 | $dribbble-states: darken($dribbble, 6%) !default; 17 | $github: #24292E !default; 18 | $github-states: darken($github, 6%) !default; 19 | $reddit: #ff4500 !default; 20 | $reddit-states: darken($reddit, 6%) !default; 21 | $tumblr: #35465c !default; 22 | $tumblr-states: darken($tumblr, 6%) !default; 23 | $behance: #1769ff !default; 24 | $behance-states: darken($behance, 6%) !default; 25 | $vimeo: #1AB7EA !default; 26 | $vimeo-states: darken($vimeo, 6%) !default; 27 | $slack: #3aaf85 !default; 28 | $slack-states: darken($slack, 6%) !default; 29 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_table.scss: -------------------------------------------------------------------------------- 1 | $table-head-spacer-y: .75rem !default; 2 | $table-head-spacer-x: 1rem !default; 3 | $table-head-font-size: .65rem !default; 4 | $table-head-font-weight: $font-weight-bold !default; 5 | $table-head-text-transform: uppercase !default; 6 | $table-head-letter-spacing: 1px !default; 7 | $table-head-bg: $gray-100 !default; 8 | $table-head-color: $gray-600 !default; 9 | $table-action-color: $gray-500 !default; 10 | 11 | $table-body-font-size: .8125rem !default; 12 | 13 | $table-dark-bg: theme-color("default") !default; 14 | $table-dark-accent-bg: rgba($white, .05) !default; 15 | $table-dark-hover-bg: rgba($white, .075) !default; 16 | 17 | $table-dark-head-bg: lighten($dark, 4%) !default; 18 | $table-dark-head-color: lighten($dark, 35%) !default; 19 | $table-dark-action-color: lighten($dark, 35%) !default; 20 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_timeline.scss: -------------------------------------------------------------------------------- 1 | // Timeline 2 | 3 | $timeline-axis-width: 2px !default; 4 | $timeline-axis-color: #e5e5e5 !default; 5 | $timeline-axis-color-dark: #4a4a4a !default; 6 | $timeline-left: 1rem !default; 7 | 8 | $timeline-step-bg: $white !default; 9 | $timeline-step-width: 26px !default; 10 | $timeline-step-height: $timeline-step-width !default; 11 | $timeline-step-radius: 50% !default; 12 | $timeline-step-transform: translateX(-50%) !default; 13 | $timeline-step-line-height: 1.4 !default; 14 | 15 | $timeline-step-border-width: 2px !default; 16 | $timeline-step-border-color: $timeline-axis-color !default; 17 | 18 | $timeline-content-margin-left: 45px !default; 19 | $timeline-content-padding-top: .35rem !default; 20 | $timeline-content-top: -6px !default; 21 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_utilities-extend.scss: -------------------------------------------------------------------------------- 1 | // Colores Shadow on cards 2 | $colored-shadow-top: 3.5% !default; 3 | $colored-shadow-blur: 12px !default; 4 | $colored-shadow-scale: .94 !default; 5 | $colored-shadow-scale-avatar: .87 !default; 6 | 7 | // Card Projects 8 | $card-project-transition: .4s cubic-bezier(.215,.61,.355,1) !default; 9 | $card-project-avatar-transform: scale(.8) translateY(-45px) !default; 10 | $card-project-hover-transform: translateY(-2px) !default; 11 | 12 | // Transform Perspective effect 13 | $transform-perspective: scale(1) perspective(1040px) rotateY(-11deg) rotateX(2deg) rotate(2deg) !default; 14 | $transform-perspective-inverse: scale(1) perspective(1040px) rotateY(11deg) rotateX(-2deg) rotate(-2deg) !default; 15 | 16 | // Z index 17 | $z-index2: 2 !default; 18 | 19 | // Width in PX 20 | $width-32-px: 32px !default; 21 | $width-48-px: 48px !default; 22 | $width-64-px: 64px !default; 23 | 24 | // Wizard variants 25 | $multistep-progress-primary-color: #f48aaa !default; 26 | $multistep-progress-success-color: #9ed1a0 !default; 27 | $multistep-progress-danger-color: #f79592 !default; 28 | $multistep-progress-warning-color: #ffc483 !default; 29 | $multistep-progress-info-color: #88d9e4 !default; 30 | $multistep-progress-dark-color: #848486 !default; 31 | $multistep-progress-secondary-color: #a4a9b2 !default; 32 | $multistep-progress-light-color: #ebeef1 !default; 33 | -------------------------------------------------------------------------------- /src/assets/material-dashboard/scss/material-dashboard/variables/_virtual-reality.scss: -------------------------------------------------------------------------------- 1 | $animation-name: fadeInBottom !default; 2 | $animation-mode: both !default; 3 | $animation-duration: 1.5s !default; 4 | $transform-scale: scale(0.6) !default; 5 | $position-left: 18% !default; 6 | -------------------------------------------------------------------------------- /src/assets/site-base.js: -------------------------------------------------------------------------------- 1 | require('./styles/site-base.scss'); 2 | 3 | -------------------------------------------------------------------------------- /src/assets/site-bootstrap.js: -------------------------------------------------------------------------------- 1 | require('./styles/site-bootstrap.scss'); 2 | 3 | // load all of bootstrap's javascript. 4 | // require('bootstrap'); 5 | 6 | // you can also explicitly include individual bootstrap plugins here like this. 7 | // this is the minimum needed for the app's default functionality 8 | require('bootstrap/js/dist/collapse'); // mobile menus 9 | require('bootstrap/js/dist/dropdown'); // navigation dropdowns 10 | require('bootstrap/js/dist/alert'); // alerts (close button) 11 | require('bootstrap/js/dist/modal'); // modals (used by teams) 12 | -------------------------------------------------------------------------------- /src/assets/styles/app/base.sass: -------------------------------------------------------------------------------- 1 | @charset "utf-8" 2 | 3 | @import "navbar.sass" 4 | @import "profile.sass" 5 | @import "progress.sass" 6 | @import "stripe.sass" 7 | @import "utilities.sass" 8 | -------------------------------------------------------------------------------- /src/assets/styles/app/bootstrap/_all.sass: -------------------------------------------------------------------------------- 1 | @import "layout" 2 | -------------------------------------------------------------------------------- /src/assets/styles/app/bootstrap/layout.sass: -------------------------------------------------------------------------------- 1 | 2 | .app-card 3 | @extend .shadow-sm 4 | margin-bottom: 1em 5 | padding: 2em 6 | width: 100% 7 | border-radius: .3em 8 | // allow using "notification" class to override color styles 9 | &:not(.notification) 10 | background-color: white 11 | -------------------------------------------------------------------------------- /src/assets/styles/app/navbar.sass: -------------------------------------------------------------------------------- 1 | 2 | .navbar-avatar 3 | object-fit: cover 4 | width: 32px 5 | height: 32px 6 | max-height: inherit !important 7 | border-radius: 50% 8 | -------------------------------------------------------------------------------- /src/assets/styles/app/profile.sass: -------------------------------------------------------------------------------- 1 | 2 | 3 | #profile-picture 4 | display: flex 5 | justify-content: center 6 | img.avatar 7 | object-fit: cover 8 | width: 128px 9 | height: 128px 10 | border-radius: 50% 11 | 12 | 13 | #profile-upload-holder 14 | padding-top: .5em 15 | 16 | input[type="file"] 17 | display: none 18 | -------------------------------------------------------------------------------- /src/assets/styles/app/progress.sass: -------------------------------------------------------------------------------- 1 | 2 | .progress-bar 3 | background-color: var(--primary) 4 | width: 2px 5 | 6 | 7 | #progress-bar-message 8 | color: var(--gray) 9 | -------------------------------------------------------------------------------- /src/assets/styles/app/utilities.sass: -------------------------------------------------------------------------------- 1 | 2 | .h-100 3 | height: 100% 4 | 5 | 6 | .muted-link 7 | color: hsl(0, 0%, 71%) 8 | &:hover 9 | color: hsl(0, 0%, 48%) 10 | text-decoration: underline 11 | 12 | // this is the default class assigned to errors by django forms 13 | .errorlist 14 | color: var(--danger) 15 | 16 | 17 | img.socialicon 18 | padding-right: .5em 19 | max-width: 24px 20 | max-height: 24px 21 | 22 | 23 | // css loader https://loading.io/css/ 24 | 25 | .lds-ripple 26 | display: inline-block 27 | position: relative 28 | width: 80px 29 | height: 80px 30 | 31 | .lds-ripple div 32 | position: absolute 33 | border: 4px solid var(--primary) 34 | opacity: 1 35 | border-radius: 50% 36 | animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite 37 | 38 | .lds-ripple div:nth-child(2) 39 | animation-delay: -0.5s 40 | 41 | @keyframes lds-ripple 42 | 0% 43 | top: 36px 44 | left: 36px 45 | width: 0 46 | height: 0 47 | opacity: 1 48 | 49 | 100% 50 | top: 0px 51 | left: 0px 52 | width: 72px 53 | height: 72px 54 | opacity: 0 55 | 56 | -------------------------------------------------------------------------------- /src/assets/styles/site-base.scss: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | 4 | @import "app/base"; 5 | -------------------------------------------------------------------------------- /src/lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/lib/__init__.py -------------------------------------------------------------------------------- /src/lib/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/lib/api/__init__.py -------------------------------------------------------------------------------- /src/lib/cli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/lib/cli/__init__.py -------------------------------------------------------------------------------- /src/lib/cli/cmd/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/lib/cli/cmd/__init__.py -------------------------------------------------------------------------------- /src/lib/cli/cmd/cmd_configure.py: -------------------------------------------------------------------------------- 1 | import click 2 | from lib.cli.app import Environment, pass_environment 3 | 4 | 5 | @click.command('configure', short_help='Builds an environment configuration file based on user input.') 6 | @click.option('-d', '--dry-run', is_flag=True, help='Performs a dry-run of the command without writing any files.') 7 | @pass_environment 8 | def cli(ctx: Environment, dry_run: bool = False): 9 | """ Builds an environment configuration file based on user input. """ 10 | 11 | click.clear() 12 | click.echo('') 13 | click.echo('Welcome to the PDA environment configuration wizard!') 14 | click.echo('') 15 | click.echo('This wizard will guide you through the process of creating a configuration file for your environment.') 16 | click.echo('') 17 | click.echo('What configuration category would you like to work on?') 18 | click.echo('') 19 | click.echo(' [1] Environment Type') 20 | click.echo(' [2] Database') 21 | click.echo(' [3] Security') 22 | click.echo(' [4] Email') 23 | click.echo(' [5] Authentication') 24 | click.echo(' [7] Other') 25 | click.echo(' [6] Exit') 26 | click.echo('') 27 | click.echo('Enter the number of the category you would like to work on, or press [Enter] to exit.') 28 | click.echo('') 29 | click.echo('Your choice: ', nl=False) 30 | click.echo('') 31 | click.echo('') 32 | 33 | c = click.getchar() 34 | 35 | click.echo(f'Choice: {c}') 36 | click.echo('') 37 | click.echo('This configuration tool is not ready for use yet and thus does not save any files.') 38 | click.echo('') 39 | -------------------------------------------------------------------------------- /src/lib/cli/cmd/cmd_gen_salt.py: -------------------------------------------------------------------------------- 1 | import click 2 | from cryptography.fernet import Fernet 3 | from lib.cli.app import Environment, pass_environment 4 | 5 | 6 | @click.command('gen-salt', short_help='Generates a Fernet encryption key to be used for data encryption.') 7 | @click.option('-r', '--raw', is_flag=True, help='Prints the raw value without a label. (Default: False)') 8 | @pass_environment 9 | def cli(ctx: Environment, raw: bool = False): 10 | """ Generates a Fernet encryption key to be used for data encryption. """ 11 | key: str = Fernet.generate_key().decode("utf-8") 12 | if raw: 13 | print(key) 14 | else: 15 | print(f'Generated Key: {Fernet.generate_key().decode("utf-8")}') 16 | -------------------------------------------------------------------------------- /src/lib/cli/cmd/cmd_make_migrations.py: -------------------------------------------------------------------------------- 1 | import click 2 | import os 3 | from lib.cli.app import Environment, pass_environment 4 | 5 | 6 | @click.command("make-migrations", short_help="Run the Django makemigrations command.") 7 | @pass_environment 8 | def cli(ctx: Environment): 9 | """Run the Django makemigrations command.""" 10 | 11 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pda.settings') 12 | 13 | try: 14 | from django.core.management import execute_from_command_line 15 | except ImportError as exc: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) from exc 21 | 22 | execute_from_command_line( 23 | ['src/manage.py', 'makemigrations'] 24 | ) 25 | -------------------------------------------------------------------------------- /src/lib/cli/cmd/cmd_migrate.py: -------------------------------------------------------------------------------- 1 | import click 2 | import os 3 | from lib.cli.app import Environment, pass_environment 4 | 5 | 6 | @click.command("migrate", short_help="Run the Django migrate command.") 7 | @pass_environment 8 | def cli(ctx: Environment): 9 | """Run the Django migrate command.""" 10 | 11 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pda.settings') 12 | 13 | try: 14 | from django.core.management import execute_from_command_line 15 | except ImportError as exc: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) from exc 21 | 22 | execute_from_command_line( 23 | ['src/manage.py', 'migrate'] 24 | ) 25 | -------------------------------------------------------------------------------- /src/lib/jinja/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/lib/jinja/__init__.py -------------------------------------------------------------------------------- /src/lib/jinja/filters.py: -------------------------------------------------------------------------------- 1 | 2 | def action_label(value: int): 3 | return 'Update' if value else 'Create' 4 | 5 | 6 | def no_value(value): 7 | return '' if value is None else value 8 | 9 | 10 | def none_value(value): 11 | return 'None' if value is None or not len(str(value).strip()) else value 12 | 13 | 14 | def selected(value, match): 15 | return 'selected' if value == match else '' 16 | 17 | 18 | def checked(value, match): 19 | return 'checked' if value == match else '' 20 | 21 | 22 | def format_phone(value): 23 | formatted: str = str(value) 24 | length: int = len(value) 25 | 26 | if length == 7: 27 | formatted = f'{value[:3]}-{value[3:]}' 28 | elif length == 10: 29 | formatted = f'({value[:3]}) {value[3:6]}-{value[6:]}' 30 | elif length == 11: 31 | formatted = f'{value[:1]} ({value[1:4]}) {value[4:7]}-{value[7:]}' 32 | elif length == 12: 33 | formatted = f'{value[:2]} ({value[2:5]}) {value[5:8]}-{value[8:]}' 34 | elif length == 13: 35 | formatted = f'{value[:3]} ({value[3:6]}) {value[6:9]}-{value[9:]}' 36 | 37 | return formatted 38 | -------------------------------------------------------------------------------- /src/lib/jinja/tests.py: -------------------------------------------------------------------------------- 1 | 2 | def in_list(value: str, search: str, separator: str = ','): 3 | return search in value.split(separator) 4 | -------------------------------------------------------------------------------- /src/lib/mutables.py: -------------------------------------------------------------------------------- 1 | 2 | class Mutable: 3 | """ Mutable objects are objects that can be modified after creation. """ 4 | 5 | def __init__(self, *args, **kwargs): 6 | """ Initialize a mutable object. """ 7 | super().__init__() 8 | for attr_name, attr_value in kwargs.items(): 9 | if hasattr(self, attr_name): 10 | setattr(self, attr_name, attr_value) 11 | elif hasattr(self, f'_{attr_name}'): 12 | setattr(self, f'_{attr_name}', attr_value) 13 | 14 | def __getattr__(self, item: str): 15 | """ Return the value of an attribute. """ 16 | if item.startswith('__') and item.endswith('__'): 17 | return getattr(super(), item) 18 | 19 | if item.startswith('_'): 20 | item = item[1:] 21 | if not hasattr(self, f'_{item}'): 22 | raise AttributeError(f'{self.__class__.__name__} has no attribute {item}') 23 | return getattr(self, f'_{item}') 24 | 25 | def __repr__(self): 26 | """ Return a string representation of the object. """ 27 | props: list = [k for k in dir(self) 28 | if not (k.startswith('__') and k.endswith('__')) and not callable(getattr(self, k))] 29 | return f'{self.__class__.__name__}({", ".join([f"{k}={getattr(self, k)}" for k in props])})' 30 | 31 | @classmethod 32 | def factory(cls, **kwargs): 33 | """ Create a new instance of the class. """ 34 | return cls(**kwargs) 35 | -------------------------------------------------------------------------------- /src/locale/en/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/locale/en/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /src/locale/en/LC_MESSAGES/djangojs.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerDNS-Admin/pda-next/35b0b502dd096e003e257d05a48bdd5b942d1ee2/src/locale/en/LC_MESSAGES/djangojs.mo -------------------------------------------------------------------------------- /src/locale/en/LC_MESSAGES/djangojs.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: PACKAGE VERSION\n" 4 | "Report-Msgid-Bugs-To: \n" 5 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 6 | "Last-Translator: FULL NAME{% translate "You already have two-factor authentication enabled." %}
6 | {% translate "Configure" %} 7 | {% else %} 8 |{% translate "You do not have two-factor authentication enabled." %}
9 | {% translate "Enable" %} 10 | {% endif %} 11 |{% translate "Prefix" %} | 11 |{% translate "Created" %} | 12 |13 | |
---|---|---|
{{ key.prefix }} | 19 |{{ key.created.date }} | 20 |21 | 28 | | 29 |
{% translate "You haven't created any API keys yet. Create one below." %}
37 | {% endfor %} 38 | 44 |{% translate "You haven't linked any social accounts yet. Create one below." %}
25 | {% endfor %} 26 | Manage Accounts 27 | {% if not user.has_usable_password %}Add a Password{% endif %} 28 | -------------------------------------------------------------------------------- /src/templates/account/email/email_confirmation_message.html: -------------------------------------------------------------------------------- 1 | {% extends 'account/email/email_template_base.html' %} 2 | {% load i18n %} 3 | {% block message_body %}{% blocktranslate with site_name=current_site.name %} 4 | Please click the confirmation link below to activate your {{ site_name }} account. 5 | {% endblocktranslate %}{% endblock %} 6 | {% block cta_link %}{{ activate_url }}{% endblock %} 7 | {% block cta_text %}{% translate "Confirm Email Address" %}{% endblock %} 8 | -------------------------------------------------------------------------------- /src/templates/account/email/email_confirmation_signup_message.html: -------------------------------------------------------------------------------- 1 | {% extends 'account/email/email_template_base.html' %} 2 | {% load i18n %} 3 | {% block message_body %}{% blocktranslate with site_name=current_site.name %} 4 | Welcome to {{ site_name }}! 5 | Click the confirmation link below to activate your account. 6 | {% endblocktranslate %}{% endblock %} 7 | {% block cta_link %}{{ activate_url }}{% endblock %} 8 | {% block cta_text %}{% translate "Confirm Email Address" %}{% endblock %} 9 | -------------------------------------------------------------------------------- /src/templates/account/email/password_reset_key_message.html: -------------------------------------------------------------------------------- 1 | {% extends 'account/email/email_template_base.html' %} 2 | {% load i18n %} 3 | {% block message_body %}{% blocktranslate with site_name=current_site.name %} 4 | Click the link below to reset your {{ site_name }} password. 5 | If you didn't request this, you can safely ignore this email. 6 | {% endblocktranslate %}{% endblock %} 7 | {% block cta_link %}{{ password_reset_url }}{% endblock %} 8 | {% block cta_text %}{% translate "Reset Password" %}{% endblock %} 9 | -------------------------------------------------------------------------------- /src/templates/account/email/password_reset_key_message.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %}{% autoescape off %}{% blocktranslate with site_name=current_site.name site_domain=current_site.domain %}Hello, 2 | 3 | Please click the link below to reset your {{ site_name }} password. 4 | 5 | {{ password_reset_url }} 6 | 7 | If you did not try to reset your password you can safely ignore this email. 8 | {% endblocktranslate %} 9 | {% blocktranslate with site_name=current_site.name site_domain=current_site.domain %}Thanks! 10 | 11 | The {{ site_name }} team 12 | {% endblocktranslate %} 13 | {% endautoescape %} 14 | -------------------------------------------------------------------------------- /src/templates/account/email/password_reset_key_subject.txt: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% autoescape off %} 3 | {% blocktranslate %}Reset Password{% endblocktranslate %} 4 | {% endautoescape %} 5 | -------------------------------------------------------------------------------- /src/templates/account/email_confirm.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | {% load i18n %} 3 | {% load account %} 4 | {% block content %} 5 |9 | {% blocktranslate with confirmation.email_address.email as email %} 10 | Please confirm that {{ email }} 11 | is an e-mail address for user {{ user_display }}. 12 | {% endblocktranslate %} 13 |
14 | 20 | {% else %} 21 | {% url 'account_email' as email_url %} 22 |23 | {% blocktranslate %} 24 | This e-mail confirmation link expired or is invalid. 25 | Please issue a new e-mail confirmation request. 26 | {% endblocktranslate %} 27 |
28 | {% endif %} 29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /src/templates/account/logout.html: -------------------------------------------------------------------------------- 1 | {% extends "web/app/app_base.html" %} 2 | {% load static %} 3 | {% load i18n %} 4 | {% block app %} 5 |{% translate 'Are you sure you want to sign out?' %}
8 | 25 |10 | {% blocktranslate %} 11 | The password reset link was invalid, possibly because it has already been used. 12 | {% endblocktranslate %} 13 |
14 |15 | {% blocktranslate %} 16 | Please request a new password reset. 17 | {% endblocktranslate %} 18 |
19 |{% translate 'Your password is now changed.' %}
33 | {% endif %} 34 | {% endif %} 35 | {% endblock %} 36 | -------------------------------------------------------------------------------- /src/templates/account/password_reset_from_key_done.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | {% load i18n %} 3 | {% block content %} 4 |7 | {% blocktranslate %} 8 | Your password has been changed. 9 | {% endblocktranslate %} 10 |
11 |12 | {% url 'account_login' as login_url %} 13 | {% blocktranslate %} 14 | You can now login with your new password. 15 | {% endblocktranslate %} 16 |
17 |6 | {% blocktranslate %} 7 | We have sent an e-mail to you for verification. 8 | Follow the link provided to finalize the signup process. 9 | {% endblocktranslate %} 10 |
11 |12 | {% blocktranslate %} 13 | Please contact us if you do not receive it within a few minutes. 14 | {% endblocktranslate %} 15 |
16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /src/templates/allauth_2fa/2fa_base.html: -------------------------------------------------------------------------------- 1 | {% extends "web/app/app_base.html" %} 2 | {% block sidebar-nav %} 3 | {% with 'profile' as active_tab %} 4 | {{ block.super }} 5 | {% endwith %} 6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /src/templates/allauth_2fa/authenticate.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | {% load i18n %} 3 | {% load form_tags %} 4 | {% block content %} 5 |{% translate "Please enter a valid authentication token to disable two-factor authentication:" %}
9 |10 | {% translate 'Scan the QR code below with a token generator of your choice (e.g. Google Authenticator).' %} 11 |
12 |{% translate 'Input a token generated by the app:' %}
16 |{% translate '← Return to Profile' %}
9 | 10 |{% blocktranslate %}You can sign in to your account using any of the following third party accounts:{% endblocktranslate %}
14 | 15 | 16 | 40 |{% translate 'You currently have no social network accounts connected to this account.' %}
43 | {% endif %} 44 | 45 |{% blocktranslate with provider.name as provider %}You are about to connect a new third party account from {{ provider }}.{% endblocktranslate %}
9 | {% else %} 10 |{% blocktranslate with provider.name as provider %}You are about to sign in using a third party account from {{ provider }}.{% endblocktranslate %}
13 | {% endif %} 14 | 15 | 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /src/templates/socialaccount/signup.html: -------------------------------------------------------------------------------- 1 | {% extends "socialaccount/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load form_tags %} 5 | 6 | {% block page_title %}{% translate "Sign Up" %}{% endblock %} 7 | 8 | {% block content %} 9 |{% blocktranslate with provider_name=account.get_provider.name site_name=site.name %}You are about to use your {{provider_name}} account to login to 12 | {{site_name}}. As a final step, please complete the following form:{% endblocktranslate %}
13 | 14 | 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /src/templates/web/app/app_base.html: -------------------------------------------------------------------------------- 1 | {% extends "web/base.html" %} 2 | {% load static %} 3 | {# remove top nav and messages and reinsert into body #} 4 | {% block top_nav %}{% endblock %} 5 | {% block messages %}{% endblock %} 6 | {% block body %} 7 | {% block notifications %} 8 | {% endblock %} 9 | {% block sidebar-nav %} 10 | {% include "web/components/app_nav.html" %} 11 | {% endblock %} 12 |16 | Now go make something great! 17 |
18 |To add more to this page you can make new sections like this one.
24 |Tip: the source of this page can be found at templates/web/app_home.html
{{project_meta.DESCRIPTION}} 12 |
Your site's terms go here.
9 |