├── .dockerignore
├── .editorconfig
├── .env.example
├── .git-blame-ignore-revs
├── .gitguardian.yaml
├── .github
├── CONTRIBUTING.MD
├── FUNDING.YML
├── WORKFLOW.MD
└── workflows
│ ├── backend_check.yml
│ └── build-image.yml
├── .gitignore
├── .gitmodules
├── .idea
├── .gitignore
├── icon.svg
└── runConfigurations
│ ├── build.xml
│ ├── collectstatic.xml
│ ├── compilemessages.xml
│ ├── dev.xml
│ ├── dev_server.xml
│ ├── generate_causes.xml
│ ├── generate_donations.xml
│ ├── generate_orgs.xml
│ ├── generate_orgs_valid.xml
│ ├── makemessages.xml
│ ├── makemigrations.xml
│ ├── migrate.xml
│ ├── qcluster.xml
│ ├── seed_groups.xml
│ ├── server.xml
│ └── shell.xml
├── .vscode
├── launch.json
└── settings.json
├── LICENSE
├── Makefile
├── README.md
├── TODO.md
├── backend
├── .nvmrc
├── assets
│ ├── combobox.js
│ ├── imageUpload.js
│ ├── main.css
│ ├── main.js
│ ├── ngoSearch.js
│ ├── select.js
│ ├── signature.js
│ └── twoPercentForm.js
├── donations
│ ├── __init__.py
│ ├── admin
│ │ ├── __init__.py
│ │ ├── byof.py
│ │ ├── causes.py
│ │ ├── common.py
│ │ ├── donors.py
│ │ ├── download_jobs.py
│ │ ├── jobs.py
│ │ └── ngos.py
│ ├── apps.py
│ ├── callbacks
│ │ └── __init__.py
│ ├── common
│ │ ├── __init__.py
│ │ ├── models_hashing.py
│ │ └── validation
│ │ │ ├── __init__.py
│ │ │ ├── clean_slug.py
│ │ │ ├── phone_number.py
│ │ │ ├── registration_number.py
│ │ │ └── validate_slug.py
│ ├── context_processors.py
│ ├── forms
│ │ ├── __init__.py
│ │ ├── account.py
│ │ ├── common.py
│ │ ├── ngo_account.py
│ │ └── redirection.py
│ ├── management
│ │ ├── __init__.py
│ │ └── commands
│ │ │ ├── __init__.py
│ │ │ ├── clean_ngo_county_region.py
│ │ │ ├── download_donations.py
│ │ │ ├── generate_donations.py
│ │ │ ├── generate_orgs.py
│ │ │ ├── generate_other_causes.py
│ │ │ └── registration_numbers_cleanup.py
│ ├── middleware.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_initial.py
│ │ ├── 0003_alter_ngo_name_alter_ngo_slug.py
│ │ ├── 0004_alter_donor_date_created_alter_ngo_date_created.py
│ │ ├── 0005_alter_donor_ngo.py
│ │ ├── 0006_remove_job_url_job_date_finished_job_zip.py
│ │ ├── 0007_alter_donor_date_created_alter_ngo_date_created.py
│ │ ├── 0008_alter_job_options_alter_job_ngo_alter_job_owner.py
│ │ ├── 0009_alter_donor_date_created_alter_ngo_date_created.py
│ │ ├── 0010_alter_donor_date_created_alter_ngo_date_created.py
│ │ ├── 0011_alter_donor_first_name_alter_donor_last_name.py
│ │ ├── 0012_remove_ngo_form_url_remove_ngo_image_and_more.py
│ │ ├── 0013_rename_has_special_status_ngo_is_social_service_viable_and_more.py
│ │ ├── 0014_rename_first_name_donor_l_name.py
│ │ ├── 0015_rename_last_name_donor_f_name.py
│ │ ├── 0016_remove_donor_pdf_url.py
│ │ ├── 0017_donor_anaf_gdpr.py
│ │ ├── 0018_alter_ngo_is_accepting_forms.py
│ │ ├── 0019_add_romanian_unaccent.py
│ │ ├── 0020_ngo_display_email_ngo_display_phone.py
│ │ ├── 0021_ngo_locality.py
│ │ ├── 0022_cause_donor_cause_job_cause.py
│ │ ├── 0023_auto_20250218_1306.py
│ │ ├── 0024_alter_cause_prefilled_form.py
│ │ ├── 0025_alter_donor_f_name_alter_donor_l_name_and_more.py
│ │ ├── 0026_job_number_of_donations.py
│ │ ├── 0027_donor_is_available.py
│ │ ├── 0028_cause_is_main_cause_ngo_main_cause_unique.py
│ │ ├── 0029_cause_visibility.py
│ │ ├── 0030_alter_cause_is_main.py
│ │ ├── 0031_cause_notification_email.py
│ │ ├── 0032_initialize_notifications_email.py
│ │ ├── 0033_auto_20250409_1226.py
│ │ ├── 0034_cause_filename_cache.py
│ │ ├── 0035_alter_ngo_bank_account_alter_ngo_description_and_more.py
│ │ ├── 0036_ownformsupload_redirectionsdownloadjob.py
│ │ ├── 0037_remove_ngo_slug__unique_remove_ngo_bank_account_and_more.py
│ │ └── __init__.py
│ ├── models
│ │ ├── __init__.py
│ │ ├── byof.py
│ │ ├── common.py
│ │ ├── donors.py
│ │ ├── downloads.py
│ │ ├── jobs.py
│ │ └── ngos.py
│ ├── pdf.py
│ ├── templatetags
│ │ ├── __init__.py
│ │ ├── cause_action_menu.py
│ │ ├── dictionary_management.py
│ │ ├── elided_pagination.py
│ │ ├── redirection_action_menu.py
│ │ ├── redirection_helpers.py
│ │ ├── text_format.py
│ │ └── url_extras.py
│ ├── tests
│ │ ├── __init__.py
│ │ ├── test_donor_encryption.py
│ │ ├── test_ngo_cif_validation.py
│ │ ├── test_ngo_updates_remove_prefilled_forms.py
│ │ └── test_redirection_form_signature.py
│ ├── urls_ngo_account.py
│ ├── views
│ │ ├── __init__.py
│ │ ├── account_management.py
│ │ ├── api.py
│ │ ├── api_download.py
│ │ ├── base.py
│ │ ├── common
│ │ │ ├── __init__.py
│ │ │ ├── misc.py
│ │ │ └── search.py
│ │ ├── dashboard
│ │ │ ├── __init__.py
│ │ │ ├── admin_dashboard.py
│ │ │ ├── dashboard.py
│ │ │ ├── helpers.py
│ │ │ └── ngo_dashboard.py
│ │ ├── download_donations
│ │ │ ├── __init__.py
│ │ │ ├── build_xml.py
│ │ │ ├── byof.py
│ │ │ ├── common.py
│ │ │ └── main.py
│ │ ├── errors.py
│ │ ├── ngo_account
│ │ │ ├── __init__.py
│ │ │ ├── byof.py
│ │ │ ├── causes.py
│ │ │ ├── common.py
│ │ │ ├── my_organization.py
│ │ │ ├── redirections.py
│ │ │ └── user_settings.py
│ │ ├── ngo_account_filters.py
│ │ ├── redirections.py
│ │ └── site.py
│ └── workers
│ │ ├── __init__.py
│ │ └── update_organization.py
├── frequent_questions
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── data
│ │ ├── seed-data.html
│ │ └── seed-data.json
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_alter_question_options_alter_section_options.py
│ │ └── __init__.py
│ ├── models.py
│ └── views.py
├── importer
│ ├── __init__.py
│ ├── apps.py
│ ├── extract.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_alter_importjob_import_type.py
│ │ ├── 0003_alter_importjob_csv_file.py
│ │ ├── 0004_alter_importjob_options_remove_importjob_csv_file_and_more.py
│ │ ├── 0005_delete_importjob.py
│ │ └── __init__.py
│ └── tasks
│ │ ├── __init__.py
│ │ ├── donor_forms.py
│ │ ├── repair_addresses.py
│ │ └── utils.py
├── locale
│ ├── en
│ │ └── LC_MESSAGES
│ │ │ └── django.po
│ ├── hy
│ │ └── LC_MESSAGES
│ │ │ └── django.po
│ └── ro
│ │ └── LC_MESSAGES
│ │ └── django.po
├── locale_localflavor
│ ├── en
│ │ └── LC_MESSAGES
│ │ │ └── django.po
│ └── ro
│ │ └── LC_MESSAGES
│ │ └── django.po
├── manage.py
├── package-lock.json
├── package.json
├── partners
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── context_processors.py
│ ├── management
│ │ ├── __init__.py
│ │ └── commands
│ │ │ ├── __init__.py
│ │ │ └── generate_partners.py
│ ├── middleware.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_alter_partner_ngos.py
│ │ ├── 0003_partner_display_ordering_alter_partner_subdomain.py
│ │ ├── 0004_partnerngo.py
│ │ ├── 0005_auto_20250211_1317.py
│ │ ├── 0006_remove_partner_ngos.py
│ │ ├── 0007_partner_ngos.py
│ │ ├── 0008_alter_partner_display_ordering.py
│ │ ├── 0009_partnercause_partner_causes_and_more.py
│ │ ├── 0010_auto_20250219_1900.py
│ │ ├── 0011_partner_custom_cta.py
│ │ ├── 0012_auto_20250512_1408.py
│ │ └── __init__.py
│ ├── models.py
│ ├── tests
│ │ ├── __init__.py
│ │ └── test_middleware.py
│ └── views.py
├── postcss.config.js
├── pyproject.toml
├── q_heartbeat
│ ├── __init__.py
│ ├── apps.py
│ ├── management
│ │ ├── __init__.py
│ │ └── commands
│ │ │ ├── __init__.py
│ │ │ ├── qheartbeat.py
│ │ │ └── schedule_qheartbeat.py
│ └── migrations
│ │ └── __init__.py
├── redirectioneaza
│ ├── __init__.py
│ ├── asgi.py
│ ├── callbacks.py
│ ├── common
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ ├── app_url.py
│ │ ├── async_wrapper.py
│ │ ├── cache.py
│ │ ├── clean.py
│ │ ├── filters.py
│ │ ├── messaging.py
│ │ ├── testing.py
│ │ └── validators.py
│ ├── context_processors
│ │ ├── __init__.py
│ │ ├── feature_flags.py
│ │ ├── headers.py
│ │ └── variables.py
│ ├── settings
│ │ ├── __init__.py
│ │ ├── app_configs.py
│ │ ├── auth.py
│ │ ├── base.py
│ │ ├── cache.py
│ │ ├── captcha_analytics.py
│ │ ├── constants.py
│ │ ├── database.py
│ │ ├── django_q.py
│ │ ├── email.py
│ │ ├── environment.py
│ │ ├── feature_flags.py
│ │ ├── i18n.py
│ │ ├── logging.py
│ │ ├── storages.py
│ │ ├── templates.py
│ │ └── unfold.py
│ ├── social_adapters.py
│ ├── urls.py
│ ├── views.py
│ └── wsgi.py
├── requirements-dev.in
├── requirements-dev.txt
├── requirements.in
├── requirements.txt
├── static_extras
│ ├── files
│ │ └── sample.csv
│ ├── font
│ │ └── opensans.ttf
│ ├── images
│ │ ├── checkmark-verified.svg
│ │ ├── code4romania-inversed.png
│ │ ├── code4romania.png
│ │ ├── envelope-received.svg
│ │ ├── favicon
│ │ │ ├── android-chrome-192x192.png
│ │ │ ├── android-chrome-512x512.png
│ │ │ ├── apple-touch-icon.png
│ │ │ ├── favicon-16x16.png
│ │ │ ├── favicon-32x32.png
│ │ │ ├── favicon.ico
│ │ │ └── site.webmanifest
│ │ ├── first_page.jpg
│ │ ├── footer-logo.png
│ │ ├── formular-2019.jpg
│ │ ├── formular-2021.jpg
│ │ ├── formular-2025.jpg
│ │ ├── hero-partner.svg
│ │ ├── hero.svg
│ │ ├── hub.png
│ │ ├── icons
│ │ │ ├── envelope.svg
│ │ │ ├── question-mark-circle.svg
│ │ │ ├── search.svg
│ │ │ └── star.svg
│ │ ├── logo-homepage.png
│ │ ├── logo-smaller.png
│ │ ├── logo.png
│ │ ├── logo.svg
│ │ ├── logo_bw.png
│ │ ├── logo_gray.png
│ │ ├── ngohub-screenshot.png
│ │ ├── placeholder.png
│ │ ├── second_page.jpg
│ │ ├── semnatura.png
│ │ ├── social-icons
│ │ │ ├── facebook.png
│ │ │ ├── github.png
│ │ │ └── instagram.png
│ │ └── thank_you.jpg
│ └── js
│ │ └── pristine.min.js
├── tailwind.config.js
├── templates
│ ├── v2
│ │ ├── DESCRIERE.html
│ │ ├── account
│ │ │ ├── components
│ │ │ │ ├── email-input.html
│ │ │ │ └── password-input.html
│ │ │ ├── errors
│ │ │ │ ├── base.html
│ │ │ │ └── login
│ │ │ │ │ ├── app_missing.html
│ │ │ │ │ ├── base_login.html
│ │ │ │ │ ├── multiple_ngos.html
│ │ │ │ │ ├── unknown_error.html
│ │ │ │ │ └── unknown_role.html
│ │ │ ├── login.html
│ │ │ ├── register.html
│ │ │ ├── reset-password.html
│ │ │ ├── set-password.html
│ │ │ ├── signup-confirmation.html
│ │ │ ├── signup-verification.html
│ │ │ └── snippets
│ │ │ │ ├── cta-ngohub.html
│ │ │ │ ├── errors.html
│ │ │ │ ├── login-form.html
│ │ │ │ ├── register-form.html
│ │ │ │ └── third-party.html
│ │ ├── admin
│ │ │ └── base.html
│ │ ├── allauth
│ │ │ └── layouts
│ │ │ │ └── base.html
│ │ ├── base.html
│ │ ├── components
│ │ │ ├── action-menu
│ │ │ │ ├── item-action.html
│ │ │ │ ├── item_href.html
│ │ │ │ └── main.html
│ │ │ ├── badge.html
│ │ │ ├── buttons
│ │ │ │ └── link.html
│ │ │ ├── copy-to-clipboard
│ │ │ │ ├── base.html
│ │ │ │ ├── icon.html
│ │ │ │ └── text.html
│ │ │ ├── cta-redirection.html
│ │ │ ├── form
│ │ │ │ └── legend.html
│ │ │ ├── info-banner.html
│ │ │ ├── input
│ │ │ │ ├── base.html
│ │ │ │ ├── checkbox.html
│ │ │ │ ├── combobox.html
│ │ │ │ ├── county.html
│ │ │ │ ├── filter.html
│ │ │ │ ├── help-text.html
│ │ │ │ ├── image.html
│ │ │ │ ├── input.html
│ │ │ │ ├── search-box.html
│ │ │ │ ├── select.html
│ │ │ │ └── textarea.html
│ │ │ ├── logo-or-default.html
│ │ │ └── notifications
│ │ │ │ ├── message.html
│ │ │ │ └── messages.html
│ │ ├── emails
│ │ │ ├── account
│ │ │ │ ├── activate-account
│ │ │ │ │ ├── main.html
│ │ │ │ │ └── main.txt
│ │ │ │ ├── ignore-email.html
│ │ │ │ ├── ignore-email.txt
│ │ │ │ ├── invite-partner
│ │ │ │ │ ├── main.html
│ │ │ │ │ └── main.txt
│ │ │ │ ├── ngohub-notification
│ │ │ │ │ ├── main.html
│ │ │ │ │ └── main.txt
│ │ │ │ └── reset-password
│ │ │ │ │ ├── main.html
│ │ │ │ │ └── main.txt
│ │ │ ├── admin
│ │ │ │ └── new-ngo
│ │ │ │ │ ├── main.html
│ │ │ │ │ └── main.txt
│ │ │ ├── base-footer.html
│ │ │ ├── base-header.html
│ │ │ ├── base.html
│ │ │ ├── components
│ │ │ │ ├── action.html
│ │ │ │ ├── extra-hand.html
│ │ │ │ ├── extra-hand.txt
│ │ │ │ ├── misdelivered.html
│ │ │ │ ├── misdelivered.txt
│ │ │ │ ├── panel.html
│ │ │ │ └── subcopy.html
│ │ │ ├── donor
│ │ │ │ ├── redirection-sans-signature
│ │ │ │ │ ├── main.html
│ │ │ │ │ └── main.txt
│ │ │ │ ├── redirection-with-signature
│ │ │ │ │ ├── main.html
│ │ │ │ │ └── main.txt
│ │ │ │ └── removed-redirection
│ │ │ │ │ ├── main.html
│ │ │ │ │ └── main.txt
│ │ │ ├── ngo
│ │ │ │ ├── download-archive
│ │ │ │ │ ├── main.html
│ │ │ │ │ └── main.txt
│ │ │ │ ├── download-csv
│ │ │ │ │ ├── main.html
│ │ │ │ │ └── main.txt
│ │ │ │ └── new-form-received
│ │ │ │ │ ├── main.html
│ │ │ │ │ └── main.txt
│ │ │ └── styles.css
│ │ ├── errors
│ │ │ ├── 400.html
│ │ │ ├── 403.html
│ │ │ ├── 404.html
│ │ │ ├── 500.html
│ │ │ ├── base.html
│ │ │ └── other.html
│ │ ├── form
│ │ │ ├── header
│ │ │ │ ├── main.html
│ │ │ │ └── other-form.html
│ │ │ ├── modals
│ │ │ │ ├── confirmation.html
│ │ │ │ └── signature.html
│ │ │ ├── redirection-closed.html
│ │ │ ├── redirection-header.html
│ │ │ ├── redirection-open.html
│ │ │ ├── redirection.html
│ │ │ └── success
│ │ │ │ ├── main.html
│ │ │ │ ├── signed.html
│ │ │ │ └── unsigned.html
│ │ ├── layouts
│ │ │ └── content-and-image-page.html
│ │ ├── ngo-account
│ │ │ ├── archives
│ │ │ │ ├── list-header.html
│ │ │ │ ├── list-items.html
│ │ │ │ ├── listing.html
│ │ │ │ └── main.html
│ │ │ ├── banners
│ │ │ │ ├── form-emails-banner.html
│ │ │ │ ├── form-ngohub.html
│ │ │ │ └── form-notifications-banner.html
│ │ │ ├── base-listing.html
│ │ │ ├── base.html
│ │ │ ├── byof
│ │ │ │ ├── list-header.html
│ │ │ │ ├── list-items.html
│ │ │ │ ├── listing.html
│ │ │ │ ├── main.html
│ │ │ │ ├── modal.html
│ │ │ │ └── upload.html
│ │ │ ├── cause
│ │ │ │ ├── form.html
│ │ │ │ └── main.html
│ │ │ ├── causes
│ │ │ │ ├── list-header.html
│ │ │ │ ├── list-items.html
│ │ │ │ ├── listing.html
│ │ │ │ ├── main.html
│ │ │ │ └── visibility-badge.html
│ │ │ ├── components
│ │ │ │ ├── filters.html
│ │ │ │ ├── form-title.html
│ │ │ │ ├── ngo-switch-tab-script.html
│ │ │ │ └── tab.html
│ │ │ ├── my-organization
│ │ │ │ ├── base.html
│ │ │ │ ├── ngo-form.html
│ │ │ │ └── ngo-presentation.html
│ │ │ ├── redirections-downloads
│ │ │ │ ├── list-header.html
│ │ │ │ ├── list-items.html
│ │ │ │ ├── listing.html
│ │ │ │ └── main.html
│ │ │ ├── redirections
│ │ │ │ ├── base.html
│ │ │ │ ├── download-filtered
│ │ │ │ │ └── button.html
│ │ │ │ ├── generate-archive
│ │ │ │ │ ├── button.html
│ │ │ │ │ ├── multiple-button.html
│ │ │ │ │ ├── multiple-dropdown.html
│ │ │ │ │ └── single-button.html
│ │ │ │ ├── list-header.html
│ │ │ │ ├── list-items.html
│ │ │ │ ├── listing.html
│ │ │ │ └── main.html
│ │ │ └── settings-account
│ │ │ │ └── main.html
│ │ ├── public
│ │ │ ├── all-causes.html
│ │ │ ├── all-ngos.html
│ │ │ ├── articles
│ │ │ │ ├── about.html
│ │ │ │ ├── base.html
│ │ │ │ ├── note.html
│ │ │ │ ├── policy.html
│ │ │ │ └── terms.html
│ │ │ ├── components
│ │ │ │ ├── cause-search.html
│ │ │ │ ├── faq-question.html
│ │ │ │ ├── grid-card.html
│ │ │ │ ├── home
│ │ │ │ │ ├── dropdown-search.html
│ │ │ │ │ ├── explore.html
│ │ │ │ │ ├── hero-stats.html
│ │ │ │ │ └── hero.html
│ │ │ │ └── pagination.html
│ │ │ ├── faq.html
│ │ │ └── home.html
│ │ ├── redirect
│ │ │ ├── commitglobal-banner.html
│ │ │ ├── footer.html
│ │ │ └── header
│ │ │ │ ├── desktop-item.html
│ │ │ │ ├── desktop.html
│ │ │ │ ├── dropdown-item.html
│ │ │ │ ├── main.html
│ │ │ │ ├── mobile-item.html
│ │ │ │ └── mobile.html
│ │ └── socialaccount
│ │ │ └── login.html
│ └── v3
│ │ ├── admin
│ │ ├── announcements
│ │ │ ├── base.html
│ │ │ └── work_in_progress.html
│ │ ├── base_admin.html
│ │ ├── dashboard_components
│ │ │ ├── header_stats.html
│ │ │ ├── monthly_forms_chart.html
│ │ │ ├── table_stats.html
│ │ │ └── yearly_stats.html
│ │ ├── forms
│ │ │ └── action.html
│ │ └── index.html
│ │ ├── redirect
│ │ └── components
│ │ │ ├── card.html
│ │ │ └── dropdown_navigation.html
│ │ └── robots.txt
├── users
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── context_processors.py
│ ├── groups_management.py
│ ├── management
│ │ ├── __init__.py
│ │ └── commands
│ │ │ ├── __init__.py
│ │ │ ├── _private
│ │ │ ├── __init__.py
│ │ │ └── seed_user.py
│ │ │ ├── schedule_session_cleanup.py
│ │ │ ├── seed_groups.py
│ │ │ ├── seed_superuser.py
│ │ │ └── wait_for_db.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_user_date_created_user_date_updated_and_more.py
│ │ ├── 0003_alter_user_date_created.py
│ │ ├── 0004_alter_user_date_created.py
│ │ ├── 0005_alter_user_date_created.py
│ │ ├── 0006_alter_user_date_created.py
│ │ ├── 0007_alter_user_options.py
│ │ ├── 0008_groupproxy.py
│ │ ├── 0009_user_is_ngohub_user.py
│ │ ├── 0010_user_partner.py
│ │ ├── 0011_alter_user_is_ngohub_user.py
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
└── vite.config.js
├── docker-compose.base.yml
├── docker-compose.dbless.yml
├── docker-compose.prod.yml
├── docker-compose.yml
├── docker
├── dockerfiles
│ ├── Dockerfile
│ └── Dockerfile.dev
├── nginx
│ └── nginx.conf
└── s6-rc.d
│ ├── backend
│ ├── dependencies
│ ├── run
│ └── type
│ ├── cron
│ ├── dependencies
│ ├── run
│ └── type
│ ├── frontend_dev
│ ├── dependencies
│ ├── run
│ └── type
│ ├── init
│ ├── init.sh
│ ├── type
│ └── up
│ ├── nginx
│ ├── dependencies
│ ├── run
│ └── type
│ ├── qcluster
│ ├── dependencies
│ ├── run
│ └── type
│ └── user
│ └── contents.d
│ ├── backend
│ ├── frontend_dev
│ ├── init
│ ├── nginx
│ └── qcluster
└── terraform
├── .gitignore
├── .terraform.lock.hcl
├── README.md
├── acm.tf
├── cloudfront.tf
├── data.tf
├── database.tf
├── ec2_bastion.tf
├── ecs.tf
├── functions
└── www-redirect.js
├── iam.tf
├── lb.tf
├── locals.tf
├── main.tf
├── modules
├── ecs-cluster
│ ├── assets
│ │ └── user_data.sh
│ ├── cloudwatch.tf
│ ├── data.tf
│ ├── iam.tf
│ ├── locals.tf
│ ├── main.tf
│ ├── outputs.tf
│ ├── service_discovery.tf
│ └── variables.tf
├── ecs-service
│ ├── autoscaling.tf
│ ├── data.tf
│ ├── ecs_task_definition.tf
│ ├── iam.tf
│ ├── lb.tf
│ ├── locals.tf
│ ├── main.tf
│ ├── outputs.tf
│ ├── route53.tf
│ ├── service_discovery.tf
│ └── variables.tf
└── s3
│ ├── main.tf
│ ├── outputs.tf
│ ├── random.tf
│ └── variables.tf
├── networking_eips.tf
├── networking_gateways.tf
├── networking_routing.tf
├── networking_subnets.tf
├── networking_vpc.tf
├── outputs.tf
├── providers.tf
├── random.tf
├── route53.tf
├── ses.tf
└── variables.tf
/.dockerignore:
--------------------------------------------------------------------------------
1 | _appengine_legacy
2 |
3 | .db_sqlite/*
4 |
5 | **/media
6 | **/static
7 | **/bower_components
8 | **/dist
9 |
10 | **/node_modules
11 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 2
7 | indent_style = space
8 | insert_final_newline = true
9 | max_line_length = 120
10 | tab_width = 2
11 | trim_trailing_whitespace = true
12 |
13 | [*.html]
14 | max_line_length = 240
15 |
16 | [Makefile]
17 | tab_width = 4
18 | indent_style = tab
19 | max_line_length = 240
20 |
21 | [*.py]
22 | indent_size = 4
23 | tab_width = 4
24 |
--------------------------------------------------------------------------------
/.git-blame-ignore-revs:
--------------------------------------------------------------------------------
1 | # Add all the commits you would like to be omitted from the `git blame` output here.
2 | # Add one commit per line and a comment with the reason for the omission.
3 |
4 | # In order to use this file, you either need to add it to your config:
5 | # git config blame.ignoreRevsFile ".git-blame-ignore-revs"
6 | # or pass it as an argument to `git blame`:
7 | # git blame --ignore-revs-file ".git-blame-ignore-revs"
8 |
9 | # To remove the settings from your config:
10 | # git config --unset blame.ignoreRevsFile
11 | # or pass the --ignore-revs-file option with an empty value:
12 | # git blame --ignore-revs-file=""
13 |
14 | # While this setting isn't automatically picked up git, it is used by GitHub based on the file name
15 |
16 | ######### COMMITS IGNORED FROM BLAME #########
17 |
18 | # General formatting of all the template files
19 | 6677d4a6b3a13c690fa4d22d3fb214fc151e8853
20 |
21 | # Remove closing tags
22 | 4e85a0ffdb47a1d1fceb81c46147ffc87fe1aa87
23 |
--------------------------------------------------------------------------------
/.gitguardian.yaml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | secret:
4 | ignored-paths:
5 | - '**/tests.py'
6 |
7 | ignored-matches:
8 | - name: reCAPTCHA Key
9 | match: "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe"
10 | - name: reCAPTCHA Key
11 | match: "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI"
12 | - name: Django Secret Key
13 | match: "django-insecure-#*_9knm02y(3)+mq41ns1%*!iz((u-wnbq62f(%i(qh7fyu9@+"
14 | - name: Django Secret Key
15 | match: "django-insecure-demo-key"
16 |
--------------------------------------------------------------------------------
/.github/FUNDING.YML:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | custom: https://code4.ro/en/donate/
4 |
--------------------------------------------------------------------------------
/.github/workflows/build-image.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches:
4 | - 'main'
5 | - 'refactor/frontend'
6 | - 'feature/multi-forms/main'
7 | tags:
8 | - 'v*'
9 | paths:
10 | - 'backend/**'
11 | - 'docker/**'
12 | - '.github/workflows/build-image.yml'
13 |
14 | name: Build Docker image
15 |
16 | jobs:
17 |
18 | build:
19 | name: Build Docker image
20 | uses: code4romania/.github/.github/workflows/build-push-image.yml@main
21 | with:
22 | images: code4romania/redirectioneaza
23 | context: ./
24 | dockerfile: ./docker/dockerfiles/Dockerfile
25 | secrets:
26 | username: ${{ secrets.DOCKER_HUB_USERNAME }}
27 | token: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
28 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/.gitmodules
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore all root XMLs
2 | /*.xml
3 |
4 | /codeStyles/
5 | /inspectionProfiles/
6 | /libraries/
7 |
8 | # Default ignored files, generated by the IDE
9 | /shelf/
10 | /workspace.xml
11 | # Editor-based HTTP Client requests
12 | /httpRequests/
13 | # Datasource local storage ignored files
14 | /dataSources/
15 | /dataSources.local.xml
16 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/dev.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/dev_server.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "configurations": [
3 | {
4 | "name": "Attach to runserver in Docker",
5 | "type": "debugpy",
6 | "request": "attach",
7 | "pathMappings": [
8 | {
9 | "localRoot": "${workspaceFolder}",
10 | "remoteRoot": "/var/www/redirect/"
11 | }
12 | ],
13 | "connect": {
14 | "port": 5678,
15 | "host": "127.0.0.1"
16 | }
17 | },
18 | {
19 | "name": "Attach to qcluster in Docker",
20 | "type": "debugpy",
21 | "request": "attach",
22 | "pathMappings": [
23 | {
24 | "localRoot": "${workspaceFolder}",
25 | "remoteRoot": "/var/www/redirect/"
26 | }
27 | ],
28 | "connect": {
29 | "port": 5677,
30 | "host": "127.0.0.1"
31 | }
32 | }
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": false
3 | }
4 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | # To-Do List for the Multi-Causes Feature
2 |
3 | - [ ] Use the main cause as a placeholder for the NGO's presentation
4 | - [ ] `name` for everywhere the display name of the NGO is used
5 | - [ ] `display_image` for the NGO Logo
6 |
--------------------------------------------------------------------------------
/backend/.nvmrc:
--------------------------------------------------------------------------------
1 | 22
2 |
--------------------------------------------------------------------------------
/backend/assets/imageUpload.js:
--------------------------------------------------------------------------------
1 | export default function() {
2 | return {
3 | generatePreview(event) {
4 | const file = event.target.files[0];
5 |
6 | if (!file) {
7 | return;
8 | }
9 |
10 | const reader = new FileReader();
11 | reader.onload = () => (this.$refs.preview.src = reader.result);
12 | reader.readAsDataURL(file);
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/backend/assets/main.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/backend/assets/ngoSearch.js:
--------------------------------------------------------------------------------
1 | export default function() {
2 | return {
3 | query: null,
4 | isOpen: false,
5 | results: [],
6 |
7 | open() {
8 | if (!this.results.length) {
9 | return;
10 | }
11 |
12 | this.isOpen = true;
13 | },
14 |
15 | close() {
16 | this.isOpen = false;
17 | },
18 |
19 | async search() {
20 | const url = new URL(window.location.origin + '/api/search');
21 | url.searchParams.set('q', this.query);
22 |
23 | this.results = await (await fetch(url)).json();
24 |
25 | this.open();
26 | },
27 |
28 | init() {
29 | this.$watch('query', () => this.search());
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/backend/assets/select.js:
--------------------------------------------------------------------------------
1 | export default function (options,selectedValue) {
2 |
3 | return {
4 | selectedOption: "",
5 | options: [],
6 |
7 | handleChange() {
8 | this.$refs.select.closest('form').submit();
9 | },
10 |
11 | init() {
12 | // Convert all option values to strings to ensure consistency
13 | this.options = options.map(option => ({
14 | ...option,
15 | value: String(option.value) // Ensure option values are strings
16 | }));
17 | this.options = [...this.options];
18 |
19 | // Convert selected value to string if not null
20 | this.selectedOption = selectedValue !== null ? String(selectedValue) : "";
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/backend/donations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/donations/__init__.py
--------------------------------------------------------------------------------
/backend/donations/admin/__init__.py:
--------------------------------------------------------------------------------
1 | from .causes import CauseAdmin
2 | from .donors import DonorAdmin
3 | from .download_jobs import DownloadJobAdmin
4 | from .jobs import JobAdmin
5 | from .ngos import NgoAdmin
6 | from .byof import OwnFormsUploadAdmin
7 |
8 |
9 | __all__ = [
10 | CauseAdmin,
11 | DonorAdmin,
12 | DownloadJobAdmin,
13 | JobAdmin,
14 | NgoAdmin,
15 | OwnFormsUploadAdmin,
16 | ]
17 |
--------------------------------------------------------------------------------
/backend/donations/admin/byof.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from unfold.admin import ModelAdmin
3 |
4 | from donations.models.byof import OwnFormsUpload
5 |
6 |
7 | @admin.register(OwnFormsUpload)
8 | class OwnFormsUploadAdmin(ModelAdmin):
9 | list_display = ("id", "ngo", "status", "date_created")
10 | list_display_links = ("id", "ngo")
11 | list_filter = ("date_created", "status")
12 |
13 | readonly_fields = ("date_created",)
14 | autocomplete_fields = ("ngo",)
15 |
--------------------------------------------------------------------------------
/backend/donations/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 | from django.utils.translation import gettext_lazy as _
3 |
4 |
5 | class DonationsConfig(AppConfig):
6 | default_auto_field = "django.db.models.BigAutoField"
7 | name = "donations"
8 | verbose_name = _("Donations")
9 |
--------------------------------------------------------------------------------
/backend/donations/callbacks/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/donations/callbacks/__init__.py
--------------------------------------------------------------------------------
/backend/donations/common/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/donations/common/__init__.py
--------------------------------------------------------------------------------
/backend/donations/common/models_hashing.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 |
3 | from django.conf import settings
4 |
5 |
6 | def hash_id_secret(prefix: str, pk: int) -> str:
7 | return hashlib.blake2s(
8 | f"{prefix}-{pk}-{settings.SECRET_KEY_HASH}".encode(), digest_size=16, usedforsecurity=False
9 | ).hexdigest()
10 |
--------------------------------------------------------------------------------
/backend/donations/common/validation/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/donations/common/validation/__init__.py
--------------------------------------------------------------------------------
/backend/donations/common/validation/clean_slug.py:
--------------------------------------------------------------------------------
1 | from django.utils.text import slugify
2 |
3 |
4 | def remove_unwanted_characters(s: str) -> str:
5 | """Remove all characters from a string except for letters, numbers, and dashes."""
6 |
7 | return "".join(c for c in s if c.isalnum() or c == "-")
8 |
9 |
10 | def clean_slug(slug: str) -> str:
11 | """Slugify the string but also remove underscores and other unwanted characters."""
12 | slug = slugify(slug)
13 | slug = remove_unwanted_characters(slug)
14 |
15 | return slug
16 |
--------------------------------------------------------------------------------
/backend/donations/context_processors.py:
--------------------------------------------------------------------------------
1 | from django.http import HttpRequest
2 | from django.templatetags.static import static
3 |
4 |
5 | def default_ngo_logo(request: HttpRequest):
6 | return {"default_ngo_logo": static("images/logo_gray.png")}
7 |
--------------------------------------------------------------------------------
/backend/donations/forms/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/donations/forms/__init__.py
--------------------------------------------------------------------------------
/backend/donations/management/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/donations/management/__init__.py
--------------------------------------------------------------------------------
/backend/donations/management/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/donations/management/commands/__init__.py
--------------------------------------------------------------------------------
/backend/donations/management/commands/clean_ngo_county_region.py:
--------------------------------------------------------------------------------
1 | from typing import List
2 |
3 | from django.core.management import BaseCommand
4 | from django.db.models import QuerySet
5 |
6 | from donations.models.ngos import Ngo
7 |
8 |
9 | class Command(BaseCommand):
10 | help = "Clean up the county and active_region for all NGOs. Move from S1-6 to București"
11 |
12 | def handle(self, *args, **options):
13 | sector_range: List[int] = list(range(1, 7))
14 | affected_ngos_region: QuerySet[Ngo] = Ngo.objects.filter(active_region__in=sector_range)
15 | for ngo in affected_ngos_region:
16 | ngo.active_region = "București"
17 | ngo.save()
18 |
19 | self.stdout.write(self.style.SUCCESS(f"Moved {len(affected_ngos_region)} NGOs from S1-6 to București region."))
20 |
21 | affected_ngos_county: QuerySet[Ngo] = Ngo.objects.filter(county__in=sector_range)
22 | for ngo in affected_ngos_county:
23 | ngo.locality = f"Sector {ngo.county}"
24 | ngo.county = "București"
25 | ngo.save()
26 |
27 | self.stdout.write(self.style.SUCCESS(f"Moved {len(affected_ngos_county)} NGOs from S1-6 to București county."))
28 |
--------------------------------------------------------------------------------
/backend/donations/middleware.py:
--------------------------------------------------------------------------------
1 | #
2 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0002_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.9 on 2024-02-02 12:06
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | initial = True
11 |
12 | dependencies = [
13 | ("donations", "0001_initial"),
14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15 | ]
16 |
17 | operations = [
18 | migrations.AddField(
19 | model_name="job",
20 | name="owner",
21 | field=models.ForeignKey(
22 | on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name="owner"
23 | ),
24 | ),
25 | migrations.AddField(
26 | model_name="donor",
27 | name="ngo",
28 | field=models.ForeignKey(
29 | on_delete=django.db.models.deletion.CASCADE, to="donations.ngo", verbose_name="NGO"
30 | ),
31 | ),
32 | ]
33 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0003_alter_ngo_name_alter_ngo_slug.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-02-19 08:54
2 |
3 | from django.db import migrations, models
4 |
5 | import donations.models.ngos
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ("donations", "0002_initial"),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name="ngo",
17 | name="name",
18 | field=models.CharField(db_index=True, max_length=200, verbose_name="Name"),
19 | ),
20 | migrations.AlterField(
21 | model_name="ngo",
22 | name="slug",
23 | field=models.SlugField(
24 | max_length=150, unique=True, validators=[donations.models.ngos.ngo_slug_validator], verbose_name="slug"
25 | ),
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0004_alter_donor_date_created_alter_ngo_date_created.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-02-20 10:26
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0003_alter_ngo_name_alter_ngo_slug"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="donor",
15 | name="date_created",
16 | field=models.DateTimeField(db_index=True, verbose_name="date created"),
17 | ),
18 | migrations.AlterField(
19 | model_name="ngo",
20 | name="date_created",
21 | field=models.DateTimeField(db_index=True, verbose_name="date created"),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0005_alter_donor_ngo.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-02-20 15:02
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("donations", "0004_alter_donor_date_created_alter_ngo_date_created"),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name="donor",
16 | name="ngo",
17 | field=models.ForeignKey(
18 | null=True, on_delete=django.db.models.deletion.SET_NULL, to="donations.ngo", verbose_name="NGO"
19 | ),
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0006_remove_job_url_job_date_finished_job_zip.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-02-21 10:40
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0005_alter_donor_ngo"),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name="job",
15 | name="url",
16 | ),
17 | migrations.AddField(
18 | model_name="job",
19 | name="date_finished",
20 | field=models.DateTimeField(blank=True, db_index=True, null=True, verbose_name="date finished"),
21 | ),
22 | migrations.AddField(
23 | model_name="job",
24 | name="zip",
25 | field=models.FileField(blank=True, null=True, upload_to="donation-zips/%Y/%m/%d/", verbose_name="ZIP"),
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0007_alter_donor_date_created_alter_ngo_date_created.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-02-21 14:58
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0006_remove_job_url_job_date_finished_job_zip"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="donor",
15 | name="date_created",
16 | field=models.DateTimeField(auto_now_add=True, db_index=True, verbose_name="date created"),
17 | ),
18 | migrations.AlterField(
19 | model_name="ngo",
20 | name="date_created",
21 | field=models.DateTimeField(auto_now_add=True, db_index=True, verbose_name="date created"),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0009_alter_donor_date_created_alter_ngo_date_created.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-02-23 09:21
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0008_alter_job_options_alter_job_ngo_alter_job_owner"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="donor",
15 | name="date_created",
16 | field=models.DateTimeField(db_index=True, verbose_name="date created"),
17 | ),
18 | migrations.AlterField(
19 | model_name="ngo",
20 | name="date_created",
21 | field=models.DateTimeField(db_index=True, verbose_name="date created"),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0010_alter_donor_date_created_alter_ngo_date_created.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-02-23 09:22
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0009_alter_donor_date_created_alter_ngo_date_created"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="donor",
15 | name="date_created",
16 | field=models.DateTimeField(auto_now_add=True, db_index=True, verbose_name="date created"),
17 | ),
18 | migrations.AlterField(
19 | model_name="ngo",
20 | name="date_created",
21 | field=models.DateTimeField(auto_now_add=True, db_index=True, verbose_name="date created"),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0011_alter_donor_first_name_alter_donor_last_name.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.13 on 2024-05-16 07:57
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 | dependencies = [
8 | ("donations", "0010_alter_donor_date_created_alter_ngo_date_created"),
9 | ]
10 |
11 | operations = [
12 | migrations.AlterField(
13 | model_name="donor",
14 | name="first_name",
15 | field=models.CharField(blank=True, default="", max_length=100, verbose_name="last name"),
16 | ),
17 | migrations.AlterField(
18 | model_name="donor",
19 | name="last_name",
20 | field=models.CharField(blank=True, default="", max_length=100, verbose_name="first name"),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0014_rename_first_name_donor_l_name.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.2 on 2024-10-31 10:44
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0013_rename_has_special_status_ngo_is_social_service_viable_and_more"),
10 | ]
11 |
12 | operations = [
13 | migrations.RenameField(
14 | model_name="donor",
15 | old_name="first_name",
16 | new_name="l_name",
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0015_rename_last_name_donor_f_name.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.2 on 2024-10-31 10:51
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0014_rename_first_name_donor_l_name"),
10 | ]
11 |
12 | operations = [
13 | migrations.RenameField(
14 | model_name="donor",
15 | old_name="last_name",
16 | new_name="f_name",
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0016_remove_donor_pdf_url.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.2 on 2024-10-31 14:40
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0015_rename_last_name_donor_f_name"),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name="donor",
15 | name="pdf_url",
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0017_donor_anaf_gdpr.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.4 on 2025-01-31 08:09
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0016_remove_donor_pdf_url"),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name="donor",
15 | name="anaf_gdpr",
16 | field=models.BooleanField(
17 | default=False,
18 | help_text="If the user agrees for ANAF to share their data with the NGO",
19 | verbose_name="ANAF GDPR",
20 | ),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0018_alter_ngo_is_accepting_forms.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.4 on 2025-01-31 08:38
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0017_donor_anaf_gdpr"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="ngo",
15 | name="is_accepting_forms",
16 | field=models.BooleanField(db_index=True, default=True, verbose_name="is accepting forms"),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0019_add_romanian_unaccent.py:
--------------------------------------------------------------------------------
1 | from django.contrib.postgres.operations import TrigramExtension, UnaccentExtension
2 | from django.db import migrations
3 |
4 |
5 | class Migration(migrations.Migration):
6 |
7 | dependencies = [
8 | ("donations", "0018_alter_ngo_is_accepting_forms"),
9 | ]
10 |
11 | operations = [
12 | TrigramExtension(),
13 | UnaccentExtension(),
14 | migrations.RunSQL(
15 | """
16 | CREATE TEXT SEARCH CONFIGURATION romanian_unaccent( COPY = romanian );
17 | ALTER TEXT SEARCH CONFIGURATION romanian_unaccent
18 | ALTER MAPPING FOR hword, hword_part, word
19 | WITH unaccent, romanian_stem;
20 | """
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0020_ngo_display_email_ngo_display_phone.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.4 on 2025-01-14 11:35
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0019_add_romanian_unaccent"),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name="ngo",
15 | name="display_email",
16 | field=models.BooleanField(db_index=True, default=False, verbose_name="display email"),
17 | ),
18 | migrations.AddField(
19 | model_name="ngo",
20 | name="display_phone",
21 | field=models.BooleanField(db_index=True, default=False, verbose_name="display phone"),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0021_ngo_locality.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.5 on 2025-01-28 14:34
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0020_ngo_display_email_ngo_display_phone"),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name="ngo",
15 | name="locality",
16 | field=models.CharField(blank=True, db_index=True, default="", max_length=100, verbose_name="locality"),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0024_alter_cause_prefilled_form.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.6 on 2025-03-04 09:22
2 |
3 | import donations.models.ngos
4 | import functools
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ("donations", "0023_auto_20250218_1306"),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name="cause",
17 | name="prefilled_form",
18 | field=models.FileField(
19 | blank=True,
20 | storage=donations.models.ngos.select_public_storage,
21 | upload_to=functools.partial(donations.models.ngos.year_cause_directory_path, *("ngo-forms",), **{}),
22 | verbose_name="form with prefilled cause",
23 | ),
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0025_alter_donor_f_name_alter_donor_l_name_and_more.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.6 on 2025-02-28 12:11
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0024_alter_cause_prefilled_form"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="donor",
15 | name="f_name",
16 | field=models.CharField(blank=True, db_index=True, default="", max_length=100, verbose_name="first name"),
17 | ),
18 | migrations.AlterField(
19 | model_name="donor",
20 | name="l_name",
21 | field=models.CharField(blank=True, db_index=True, default="", max_length=100, verbose_name="last name"),
22 | ),
23 | migrations.AlterField(
24 | model_name="donor",
25 | name="phone",
26 | field=models.CharField(blank=True, db_index=True, default="", max_length=30, verbose_name="telephone"),
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0026_job_number_of_donations.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.7 on 2025-03-13 12:54
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0025_alter_donor_f_name_alter_donor_l_name_and_more"),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name="job",
15 | name="number_of_donations",
16 | field=models.IntegerField(default=-1, verbose_name="donations count"),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0027_donor_is_available.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.7 on 2025-03-19 16:03
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0026_job_number_of_donations"),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name="donor",
15 | name="is_available",
16 | field=models.BooleanField(
17 | db_index=True,
18 | default=True,
19 | help_text="If the donation is available on the platform (not removed)",
20 | verbose_name="is available",
21 | ),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0028_cause_is_main_cause_ngo_main_cause_unique.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.7 on 2025-03-21 09:45
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0027_donor_is_available"),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name="cause",
15 | name="is_main",
16 | field=models.BooleanField(db_index=True, default=True, verbose_name="is main cause"),
17 | ),
18 | migrations.AddConstraint(
19 | model_name="cause",
20 | constraint=models.UniqueConstraint(
21 | condition=models.Q(("is_main", True)), fields=("ngo",), name="ngo_main_cause_unique"
22 | ),
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0029_cause_visibility.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.7 on 2025-03-28 13:38
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0028_cause_is_main_cause_ngo_main_cause_unique"),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name="cause",
15 | name="visibility",
16 | field=models.CharField(
17 | choices=[("pub", "public"), ("unl", "unlisted"), ("pri", "private")],
18 | db_index=True,
19 | default="pub",
20 | max_length=3,
21 | verbose_name="form visibility",
22 | ),
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0030_alter_cause_is_main.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.7 on 2025-04-02 13:45
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0029_cause_visibility"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="cause",
15 | name="is_main",
16 | field=models.BooleanField(db_index=True, default=False, verbose_name="is main cause"),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0031_cause_notification_email.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.7 on 2025-04-07 10:17
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0030_alter_cause_is_main"),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name="cause",
15 | name="notifications_email",
16 | field=models.EmailField(blank=True, default="", max_length=254, verbose_name="notifications email"),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0032_initialize_notifications_email.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.7 on 2025-04-04 13:07
2 |
3 | from django.db import migrations
4 |
5 |
6 | def initialize_notifications_email(apps, schema_editor):
7 | Cause = apps.get_model("donations", "Cause")
8 | batch = []
9 | for cause in Cause.objects.filter(ngo__is_accepting_forms=True, notifications_email="").select_related("ngo"):
10 | cause.notifications_email = cause.ngo.email
11 | batch.append(cause)
12 | Cause.objects.bulk_update(batch, ["notifications_email"])
13 |
14 |
15 | def go_backwards(apps, schema_editor):
16 | pass
17 |
18 |
19 | class Migration(migrations.Migration):
20 |
21 | dependencies = [
22 | ("donations", "0031_cause_notification_email"),
23 | ]
24 |
25 | operations = [
26 | migrations.RunPython(initialize_notifications_email, go_backwards),
27 | ]
28 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0033_auto_20250409_1226.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.7 on 2025-04-09 09:26
2 |
3 | from django.db import migrations
4 | from django.db.models.aggregates import Count
5 |
6 |
7 | def set_first_cause_to_main(apps, _):
8 | Cause = apps.get_model("donations", "Cause")
9 | Ngo = apps.get_model("donations", "Ngo")
10 |
11 | ngos_with_one_cause = Ngo.objects.annotate(num_causes=Count("causes")).filter(num_causes=1)
12 | Cause.objects.filter(ngo__in=ngos_with_one_cause).exclude(is_main=True).update(is_main=True)
13 |
14 |
15 | class Migration(migrations.Migration):
16 |
17 | dependencies = [
18 | ("donations", "0032_initialize_notifications_email"),
19 | ]
20 |
21 | operations = [
22 | migrations.RunPython(set_first_cause_to_main, reverse_code=migrations.RunPython.noop),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0034_cause_filename_cache.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.8 on 2025-04-09 12:59
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0033_auto_20250409_1226"),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name="cause",
15 | name="filename_cache",
16 | field=models.JSONField(default=dict, editable=False, verbose_name="Filename cache"),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/donations/migrations/0035_alter_ngo_bank_account_alter_ngo_description_and_more.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.8 on 2025-04-25 07:54
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0034_cause_filename_cache"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="ngo",
15 | name="bank_account",
16 | field=models.CharField(blank=True, default="", max_length=100, verbose_name="bank account"),
17 | ),
18 | migrations.AlterField(
19 | model_name="ngo",
20 | name="description",
21 | field=models.TextField(blank=True, default="", verbose_name="description"),
22 | ),
23 | migrations.AlterField(
24 | model_name="ngo",
25 | name="slug",
26 | field=models.SlugField(blank=True, default="", max_length=150, verbose_name="slug"),
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/backend/donations/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/donations/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/donations/models/__init__.py:
--------------------------------------------------------------------------------
1 | from .byof import OwnFormsUpload
2 | from .donors import Donor
3 | from .downloads import RedirectionsDownloadJob
4 | from .jobs import Job
5 | from .ngos import Cause, Ngo
6 |
7 | __all__ = [
8 | Cause,
9 | Donor,
10 | Job,
11 | Ngo,
12 | OwnFormsUpload,
13 | RedirectionsDownloadJob,
14 | ]
15 |
--------------------------------------------------------------------------------
/backend/donations/models/downloads.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.utils.translation import gettext_lazy as _
3 |
4 | from donations.models.common import AsyncJob
5 |
6 |
7 | class RedirectionsDownloadJob(AsyncJob):
8 | ngo = models.ForeignKey(
9 | "Ngo",
10 | verbose_name=_("NGO"),
11 | on_delete=models.CASCADE,
12 | db_index=True,
13 | related_name="download_jobs",
14 | )
15 |
16 | queryset = models.JSONField(verbose_name=_("queryset"), blank=False, null=False)
17 |
18 | output_rows = models.IntegerField(verbose_name=_("rows count"), default=-1)
19 | output_file = models.FileField(
20 | verbose_name=_("output file"),
21 | upload_to="donation-downloads/%Y/%m/%d/",
22 | blank=True,
23 | null=True,
24 | )
25 |
26 | class Meta:
27 | verbose_name = _("download job")
28 | verbose_name_plural = _("download jobs")
29 |
30 | ordering = ["-date_created"]
31 | get_latest_by = "date_created"
32 |
--------------------------------------------------------------------------------
/backend/donations/templatetags/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/donations/templatetags/__init__.py
--------------------------------------------------------------------------------
/backend/donations/templatetags/dictionary_management.py:
--------------------------------------------------------------------------------
1 | from django.template.defaulttags import register
2 |
3 |
4 | @register.filter
5 | def get_item(dictionary, key):
6 | return dictionary.get(key)
7 |
--------------------------------------------------------------------------------
/backend/donations/templatetags/elided_pagination.py:
--------------------------------------------------------------------------------
1 | from django import template
2 | from django.core.paginator import Paginator
3 |
4 | register = template.Library()
5 |
6 |
7 | @register.simple_tag
8 | def adjusted_elided_page_range(p, number, on_each_side=3, on_ends=2):
9 | paginator = Paginator(p.object_list, p.per_page)
10 | return paginator.get_elided_page_range(
11 | number=number,
12 | on_each_side=on_each_side,
13 | on_ends=on_ends,
14 | )
15 |
--------------------------------------------------------------------------------
/backend/donations/templatetags/redirection_action_menu.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Dict, List
2 |
3 | from django import template
4 | from django.urls import reverse
5 | from django.utils.translation import gettext_lazy as _
6 |
7 | register = template.Library()
8 |
9 |
10 | @register.filter
11 | def redirection_dropdown(redirection: Dict) -> List[Dict[str, Any]]:
12 | link = ""
13 | if is_active := redirection["has_signed"]:
14 | link = reverse("my-organization:redirection-download-link", args=[redirection["id"]])
15 |
16 | return [
17 | {
18 | "title": _("Download form"),
19 | "active": is_active,
20 | "link": link,
21 | },
22 | ]
23 |
24 |
25 | @register.filter
26 | def button_disabled(redirection: Dict) -> bool:
27 | return not redirection["has_signed"]
28 |
29 |
30 | @register.filter
31 | def button_title(redirection: Dict) -> str:
32 | if redirection["has_signed"]:
33 | return _("Form options")
34 | return _("Downloads are disabled for unsigned redirections")
35 |
--------------------------------------------------------------------------------
/backend/donations/templatetags/redirection_helpers.py:
--------------------------------------------------------------------------------
1 | from typing import Dict
2 |
3 | from django import template
4 | from django.utils import dateparse
5 | from donations.views.common.misc import archive_job_was_recent
6 |
7 | register = template.Library()
8 |
9 |
10 | @register.filter
11 | def job_was_recent(job: Dict) -> bool:
12 | """
13 | Check if the job was created recently.
14 | """
15 | if not job:
16 | return False
17 |
18 | job_created: str = job["date_created"]
19 | job_created_date = dateparse.parse_datetime(job_created)
20 |
21 | return archive_job_was_recent(job["status"], job_created_date)
22 |
--------------------------------------------------------------------------------
/backend/donations/templatetags/text_format.py:
--------------------------------------------------------------------------------
1 | from django import template
2 | from django.utils.translation import gettext_lazy as _
3 |
4 | register = template.Library()
5 |
6 |
7 | @register.filter
8 | def to_county(value):
9 | try:
10 | sector_value = int(value)
11 | return _("Sector %s") % sector_value
12 | except ValueError:
13 | return value
14 |
15 |
16 | @register.filter
17 | def iban(value: str) -> str:
18 | """
19 | Format IBAN number to human-readable format
20 | AAAABBBBCCCCDDDDEEEEFFFF -> AAAA BBBB CCCC DDDD EEEE FFFF
21 | """
22 |
23 | return " ".join([value[i : i + 4] for i in range(0, len(value), 4)])
24 |
--------------------------------------------------------------------------------
/backend/donations/templatetags/url_extras.py:
--------------------------------------------------------------------------------
1 | from django import template
2 | from django.conf import settings
3 | from django.urls import reverse
4 |
5 | register = template.Library()
6 |
7 |
8 | @register.simple_tag
9 | def apex_reverse(name: str):
10 | """
11 | Returns the URL with the Apex Domain included, like "//example.com/url-for-name/"
12 | """
13 | reversed_url = reverse(name)
14 | return f"//{settings.APEX_DOMAIN}{reversed_url}"
15 |
--------------------------------------------------------------------------------
/backend/donations/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/donations/tests/__init__.py
--------------------------------------------------------------------------------
/backend/donations/tests/test_donor_encryption.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from faker import Faker
3 |
4 | from donations.models.donors import Donor
5 |
6 |
7 | class DonorEncryptionTests(TestCase):
8 | def setUp(self):
9 | self.faker = Faker("ro_RO")
10 |
11 | def test_address_encryption(self):
12 | fake_address = {
13 | "street_name": self.faker.street_name(),
14 | "street_number": self.faker.building_number(),
15 | "street_bl": self.faker.building_number(),
16 | "street_sc": self.faker.building_number(),
17 | "street_et": self.faker.building_number(),
18 | "street_ap": self.faker.building_number(),
19 | }
20 |
21 | encrypted_address = Donor.encrypt_address(fake_address)
22 | decrypted_address = Donor.decrypt_address(encrypted_address)
23 |
24 | self.assertEqual(decrypted_address, fake_address)
25 |
26 | def test_cnp_encryption(self):
27 | fake_cnp: str = self.faker.unique.ssn()
28 |
29 | encrypted_cnp = Donor.encrypt_cnp(fake_cnp)
30 | decrypted_cnp = Donor.decrypt_cnp(encrypted_cnp)
31 |
32 | self.assertEqual(decrypted_cnp, fake_cnp)
33 |
--------------------------------------------------------------------------------
/backend/donations/views/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/donations/views/__init__.py
--------------------------------------------------------------------------------
/backend/donations/views/common/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/donations/views/common/__init__.py
--------------------------------------------------------------------------------
/backend/donations/views/dashboard/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/donations/views/dashboard/__init__.py
--------------------------------------------------------------------------------
/backend/donations/views/dashboard/dashboard.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth import get_user_model
2 | from django.urls import reverse
3 |
4 | from .admin_dashboard import callback as admin_callback
5 |
6 | UserModel = get_user_model()
7 |
8 |
9 | def callback(request, context):
10 | user: UserModel = request.user
11 |
12 | if not user or not user.is_authenticated:
13 | return context
14 |
15 | if user.is_admin:
16 | return admin_callback(request, context)
17 |
18 | return {"redirect": reverse("my-organization:dashboard")}
19 |
--------------------------------------------------------------------------------
/backend/donations/views/download_donations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/donations/views/download_donations/__init__.py
--------------------------------------------------------------------------------
/backend/donations/views/errors.py:
--------------------------------------------------------------------------------
1 | from django.http import HttpRequest, HttpResponse
2 | from django.shortcuts import render
3 |
4 |
5 | def create_error_view(error_code: int) -> callable:
6 | """
7 | Return a callable for handling different error codes
8 | """
9 |
10 | def custom_error_view(request: HttpRequest, exception: Exception = None) -> HttpResponse:
11 | if error_code in (400, 403, 404, 500):
12 | response = render(request, f"errors/{error_code}.html")
13 | response.status_code = error_code
14 | else:
15 | response = render(request, "errors/other.html")
16 | return response
17 |
18 | return custom_error_view
19 |
--------------------------------------------------------------------------------
/backend/donations/views/ngo_account/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/donations/views/ngo_account/__init__.py
--------------------------------------------------------------------------------
/backend/donations/workers/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/donations/workers/__init__.py
--------------------------------------------------------------------------------
/backend/frequent_questions/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/frequent_questions/__init__.py
--------------------------------------------------------------------------------
/backend/frequent_questions/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 | from django.utils.translation import gettext_lazy as _
3 |
4 |
5 | class FrequentQuestionsConfig(AppConfig):
6 | default_auto_field = "django.db.models.BigAutoField"
7 | name = "frequent_questions"
8 | verbose_name = _("Frequent Questions")
9 |
--------------------------------------------------------------------------------
/backend/frequent_questions/migrations/0002_alter_question_options_alter_section_options.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.4 on 2024-12-19 15:29
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("frequent_questions", "0001_initial"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterModelOptions(
14 | name="question",
15 | options={"ordering": ["order", "title"], "verbose_name": "Question", "verbose_name_plural": "Questions"},
16 | ),
17 | migrations.AlterModelOptions(
18 | name="section",
19 | options={"ordering": ["order", "title"], "verbose_name": "Section", "verbose_name_plural": "Sections"},
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/backend/frequent_questions/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/frequent_questions/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/frequent_questions/views.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.shortcuts import render
3 | from django.utils.translation import gettext as _
4 |
5 | from donations.views.base import BaseVisibleTemplateView
6 | from frequent_questions.models import Question
7 |
8 |
9 | class FAQHandler(BaseVisibleTemplateView):
10 | template_name = "public/faq.html"
11 | title = _("Frequently Asked Questions")
12 |
13 | def get(self, request, *args, **kwargs):
14 | questions = Question.get_all()
15 |
16 | context = {
17 | "title": _("Frequently Asked Questions"),
18 | "contact_email": settings.CONTACT_EMAIL_ADDRESS,
19 | "questions": questions,
20 | }
21 | return render(request, self.template_name, context)
22 |
--------------------------------------------------------------------------------
/backend/importer/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/importer/__init__.py
--------------------------------------------------------------------------------
/backend/importer/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 | from django.utils.translation import gettext_lazy as _
3 |
4 |
5 | class ImporterConfig(AppConfig):
6 | default_auto_field = "django.db.models.BigAutoField"
7 | name = "importer"
8 | verbose_name = _("Importer")
9 |
--------------------------------------------------------------------------------
/backend/importer/migrations/0002_alter_importjob_import_type.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-02-16 14:43
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("importer", "0001_initial"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="importjob",
15 | name="import_type",
16 | field=models.CharField(
17 | choices=[
18 | ("users.User", "User"),
19 | ("donations.Ngo", "Ngo"),
20 | ("donations.Donor", "Donor"),
21 | ("partners.Partner", "Partner"),
22 | ],
23 | max_length=32,
24 | verbose_name="Import type",
25 | ),
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/backend/importer/migrations/0003_alter_importjob_csv_file.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-02-21 10:40
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("importer", "0002_alter_importjob_import_type"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="importjob",
15 | name="csv_file",
16 | field=models.FileField(upload_to="imports/%Y/%m/%d/", verbose_name="File"),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/importer/migrations/0004_alter_importjob_options_remove_importjob_csv_file_and_more.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.2 on 2024-10-31 11:01
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("importer", "0003_alter_importjob_csv_file"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterModelOptions(
14 | name="importjob",
15 | options={},
16 | ),
17 | migrations.RemoveField(
18 | model_name="importjob",
19 | name="csv_file",
20 | ),
21 | migrations.RemoveField(
22 | model_name="importjob",
23 | name="has_header",
24 | ),
25 | migrations.RemoveField(
26 | model_name="importjob",
27 | name="import_type",
28 | ),
29 | migrations.RemoveField(
30 | model_name="importjob",
31 | name="status",
32 | ),
33 | migrations.RemoveField(
34 | model_name="importjob",
35 | name="uploaded_at",
36 | ),
37 | ]
38 |
--------------------------------------------------------------------------------
/backend/importer/migrations/0005_delete_importjob.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.2 on 2024-10-31 11:02
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("importer", "0004_alter_importjob_options_remove_importjob_csv_file_and_more"),
10 | ]
11 |
12 | operations = [
13 | migrations.DeleteModel(
14 | name="ImportJob",
15 | ),
16 | ]
17 |
--------------------------------------------------------------------------------
/backend/importer/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/importer/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/importer/tasks/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/importer/tasks/__init__.py
--------------------------------------------------------------------------------
/backend/importer/tasks/utils.py:
--------------------------------------------------------------------------------
1 | def batch(iterable, batch_size=1) -> iter:
2 | batch_length: int = len(iterable)
3 | for index in range(0, batch_length, batch_size):
4 | yield iterable[index : min(index + batch_size, batch_length)]
5 |
--------------------------------------------------------------------------------
/backend/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | """Run administrative tasks."""
9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "redirectioneaza.settings")
10 | try:
11 | from django.core.management import execute_from_command_line
12 | except ImportError as exc:
13 | raise ImportError(
14 | "Couldn't import Django. Are you sure it's installed and "
15 | "available on your PYTHONPATH environment variable? Did you "
16 | "forget to activate a virtual environment?"
17 | ) from exc
18 | execute_from_command_line(sys.argv)
19 |
20 |
21 | if __name__ == "__main__":
22 | main()
23 |
--------------------------------------------------------------------------------
/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "private": true,
4 | "scripts": {
5 | "dev": "vite",
6 | "build": "vite build"
7 | },
8 | "devDependencies": {
9 | "@alpinejs/collapse": "^3.14.8",
10 | "@alpinejs/focus": "^3.14.8",
11 | "@fontsource/inter": "^5.1.0",
12 | "@ryangjchandler/alpine-tooltip": "^2.0.1",
13 | "@tailwindcss/aspect-ratio": "^0.4.2",
14 | "@tailwindcss/container-queries": "^0.1.1",
15 | "@tailwindcss/forms": "^0.5.9",
16 | "@tailwindcss/typography": "^0.5.15",
17 | "alpinejs": "^3.14.7",
18 | "autoprefixer": "^10.4.20",
19 | "postcss": "^8.4.49",
20 | "signature_pad": "^5.0.4",
21 | "tailwindcss": "^3.4.16",
22 | "vite": "^6.3.4"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/backend/partners/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/partners/__init__.py
--------------------------------------------------------------------------------
/backend/partners/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 | from django.utils.translation import gettext_lazy as _
3 |
4 |
5 | class PartnersConfig(AppConfig):
6 | default_auto_field = "django.db.models.BigAutoField"
7 | name = "partners"
8 | verbose_name = _("Partners")
9 |
--------------------------------------------------------------------------------
/backend/partners/context_processors.py:
--------------------------------------------------------------------------------
1 | from django.http import HttpRequest
2 |
3 |
4 | def custom_subdomain(request: HttpRequest):
5 | return {
6 | "custom_subdomain": request.partner.subdomain if request.partner else "",
7 | }
8 |
--------------------------------------------------------------------------------
/backend/partners/management/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/partners/management/__init__.py
--------------------------------------------------------------------------------
/backend/partners/management/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/partners/management/commands/__init__.py
--------------------------------------------------------------------------------
/backend/partners/migrations/0002_alter_partner_ngos.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-02-16 16:11
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0002_initial"),
10 | ("partners", "0001_initial"),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name="partner",
16 | name="ngos",
17 | field=models.ManyToManyField(blank=True, related_name="partners", to="donations.ngo", verbose_name="NGOs"),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/backend/partners/migrations/0003_partner_display_ordering_alter_partner_subdomain.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.11 on 2024-03-25 09:34
2 |
3 | from django.db import migrations, models
4 | import redirectioneaza.common.validators
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("partners", "0002_alter_partner_ngos"),
11 | ]
12 |
13 | operations = [
14 | migrations.AddField(
15 | model_name="partner",
16 | name="display_ordering",
17 | field=models.CharField(
18 | choices=[("ABC", "Alphabetical"), ("AGE", "Newest"), ("RND", "Random")],
19 | default="RND",
20 | max_length=3,
21 | verbose_name="display ordering",
22 | ),
23 | ),
24 | migrations.AlterField(
25 | model_name="partner",
26 | name="subdomain",
27 | field=models.CharField(
28 | max_length=100,
29 | unique=True,
30 | validators=[redirectioneaza.common.validators.url_validator],
31 | verbose_name="subdomain",
32 | ),
33 | ),
34 | ]
35 |
--------------------------------------------------------------------------------
/backend/partners/migrations/0005_auto_20250211_1317.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.5 on 2025-02-11 11:17
2 |
3 | from django.db import migrations
4 |
5 |
6 | def create_through_relations(apps, _):
7 | Partner = apps.get_model("partners", "Partner")
8 | PartnerNgo = apps.get_model("partners", "PartnerNgo")
9 |
10 | for partner in Partner.objects.all():
11 | for ngo in partner.ngos.all():
12 | PartnerNgo(
13 | ngo=ngo,
14 | partner=partner,
15 | ).save()
16 |
17 |
18 | class Migration(migrations.Migration):
19 |
20 | dependencies = [
21 | ("partners", "0004_partnerngo"),
22 | ]
23 |
24 | operations = [
25 | migrations.RunPython(create_through_relations, reverse_code=migrations.RunPython.noop),
26 | ]
27 |
--------------------------------------------------------------------------------
/backend/partners/migrations/0006_remove_partner_ngos.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.5 on 2025-02-11 11:23
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("partners", "0005_auto_20250211_1317"),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name="partner",
15 | name="ngos",
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/backend/partners/migrations/0007_partner_ngos.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.5 on 2025-02-11 11:24
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("donations", "0021_ngo_locality"),
10 | ("partners", "0006_remove_partner_ngos"),
11 | ]
12 |
13 | operations = [
14 | migrations.AddField(
15 | model_name="partner",
16 | name="ngos",
17 | field=models.ManyToManyField(
18 | blank=True,
19 | related_name="partners",
20 | through="partners.PartnerNgo",
21 | to="donations.ngo",
22 | verbose_name="NGOs",
23 | ),
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/backend/partners/migrations/0008_alter_partner_display_ordering.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.5 on 2025-02-11 14:22
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("partners", "0007_partner_ngos"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="partner",
15 | name="display_ordering",
16 | field=models.CharField(
17 | choices=[
18 | ("CST", "Custom"),
19 | ("ABC", "Alphabetical"),
20 | ("CBA", "Alphabetical (reverse)"),
21 | ("AGE", "Newest"),
22 | ("OLD", "Oldest"),
23 | ("RND", "Random"),
24 | ],
25 | default="RND",
26 | max_length=3,
27 | verbose_name="display ordering",
28 | ),
29 | ),
30 | ]
31 |
--------------------------------------------------------------------------------
/backend/partners/migrations/0011_partner_custom_cta.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.8 on 2025-05-12 10:52
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("partners", "0010_auto_20250219_1900"),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name="partner",
15 | name="custom_cta",
16 | field=models.TextField(
17 | blank=True,
18 | default="",
19 | help_text="Custom call-to-action for the partner, if empty, the default call-to-action will be used.",
20 | max_length=50,
21 | verbose_name="custom call-to-action",
22 | ),
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/backend/partners/migrations/0012_auto_20250512_1408.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.8 on 2025-05-12 11:08
2 |
3 | from django.db import migrations
4 |
5 |
6 | def set_default_cta(apps, _):
7 | Partner = apps.get_model("partners", "Partner")
8 |
9 | partners_with_custom_note = Partner.objects.filter(has_custom_note=True)
10 | partners_with_custom_note.update(custom_cta="către un ONG la alegere")
11 |
12 |
13 | def set_custom_note(apps, _):
14 | Partner = apps.get_model("partners", "Partner")
15 |
16 | partners_with_custom_note = Partner.objects.exclude(custom_cta="")
17 | partners_with_custom_note.update(has_custom_note=True)
18 |
19 |
20 | class Migration(migrations.Migration):
21 |
22 | dependencies = [
23 | ("partners", "0011_partner_custom_cta"),
24 | ]
25 |
26 | operations = [
27 | migrations.RunPython(set_default_cta, reverse_code=set_custom_note),
28 | ]
29 |
--------------------------------------------------------------------------------
/backend/partners/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/partners/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/partners/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/partners/tests/__init__.py
--------------------------------------------------------------------------------
/backend/partners/tests/test_middleware.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | from partners.middleware import InvalidSubdomain, PartnerDomainMiddleware
4 |
5 |
6 | class PartnerMiddlewareTests(TestCase):
7 | def setUp(self):
8 | pass
9 |
10 | def test_partner_domain_middleware_extraction(self):
11 | apex = "example.com"
12 | self.assertEqual("test1", PartnerDomainMiddleware.extract_subdomain("test1.example.com", apex))
13 | self.assertEqual("test1", PartnerDomainMiddleware.extract_subdomain("test1.exaMpLe.com", apex))
14 | self.assertEqual("", PartnerDomainMiddleware.extract_subdomain("exAmple.com", apex))
15 |
16 | def test_invalid_subdomain(self):
17 | self.assertRaises(
18 | InvalidSubdomain, PartnerDomainMiddleware.extract_subdomain, "test1.example.ORG", "example.com"
19 | )
20 |
--------------------------------------------------------------------------------
/backend/partners/views.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 |
--------------------------------------------------------------------------------
/backend/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/backend/q_heartbeat/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/q_heartbeat/__init__.py
--------------------------------------------------------------------------------
/backend/q_heartbeat/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class QHeartbeatConfig(AppConfig):
5 | default_auto_field = "django.db.models.BigAutoField"
6 | name = "q_heartbeat"
7 |
--------------------------------------------------------------------------------
/backend/q_heartbeat/management/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/q_heartbeat/management/__init__.py
--------------------------------------------------------------------------------
/backend/q_heartbeat/management/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/q_heartbeat/management/commands/__init__.py
--------------------------------------------------------------------------------
/backend/q_heartbeat/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/q_heartbeat/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/redirectioneaza/__init__.py:
--------------------------------------------------------------------------------
1 | import pymysql
2 |
3 | pymysql.install_as_MySQLdb()
4 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for redirectioneaza project.
3 |
4 | It exposes the ASGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.asgi import get_asgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "redirectioneaza.settings")
15 |
16 | application = get_asgi_application()
17 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/callbacks.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 |
3 |
4 | def environment_callback(_):
5 | environment = f"{settings.ENVIRONMENT} | {settings.VERSION_SUFFIX}"
6 | log_level = settings.DJANGO_LOG_LEVEL
7 |
8 | return environment, log_level
9 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/common/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/redirectioneaza/common/__init__.py
--------------------------------------------------------------------------------
/backend/redirectioneaza/common/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django.utils.translation import gettext_lazy as _
3 |
4 |
5 | class HasNgoFilter(admin.SimpleListFilter):
6 | title = _("Has NGO")
7 | parameter_name = "has_ngo"
8 |
9 | def lookups(self, request, model_admin):
10 | return ("yes", "Yes"), ("no", "No")
11 |
12 | def queryset(self, request, queryset):
13 | if self.value() == "yes":
14 | return queryset.exclude(ngo=None)
15 | if self.value() == "no":
16 | return queryset.filter(ngo=None)
17 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/common/app_url.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.http import HttpRequest
3 |
4 |
5 | def build_uri(path: str, request: HttpRequest = None) -> str:
6 | if not request:
7 | return f"https://{settings.APEX_DOMAIN}{path}"
8 |
9 | return request.build_absolute_uri(path)
10 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/common/async_wrapper.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Callable
2 |
3 | from django.conf import settings
4 | from django_q.tasks import async_task
5 |
6 |
7 | def async_wrapper(
8 | func: Callable,
9 | *args: Any,
10 | **kwargs: Any,
11 | ) -> Callable:
12 | """
13 | A wrapper function to execute a function either synchronously or asynchronously
14 | """
15 |
16 | if settings.DEFAULT_RUN_METHOD == "async":
17 | return async_task(func, *args, **kwargs)
18 |
19 | return func(*args, **kwargs)
20 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/common/testing.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.test import Client
3 |
4 |
5 | class ApexClient(Client):
6 | def __init__(
7 | self,
8 | enforce_csrf_checks=False,
9 | raise_request_exception=True,
10 | *,
11 | headers=None,
12 | query_params=None,
13 | **defaults,
14 | ):
15 | # set a default SERVER_NAME and HTTP_HOST if no custom names are provided
16 | defaults["SERVER_NAME"] = defaults.get("SERVER_NAME", settings.APEX_DOMAIN.split(":")[0])
17 | defaults["HTTP_HOST"] = defaults.get("HTTP_HOST", settings.APEX_DOMAIN) # Docs recommend the headers parameter
18 |
19 | super().__init__(headers=headers, query_params=query_params, **defaults)
20 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/common/validators.py:
--------------------------------------------------------------------------------
1 | from django.core.exceptions import ValidationError
2 | from django.utils.translation import gettext_lazy as _
3 |
4 |
5 | def url_validator(value):
6 | if value in ("redirectioneaza", "www", "admin", "api", "ftp", "dns", "ns1", "ns2"):
7 | raise ValidationError(_("This subdomain is reserved."))
8 |
9 | if not value.islower():
10 | raise ValidationError(_("Subdomain must contain only lowercase characters."))
11 |
12 | if not "".join(value.split("-")).isalnum():
13 | raise ValidationError(_("Subdomain must contain only alphanumeric characters and '-' (hyphens)."))
14 |
15 | if not (3 <= len(value) <= 100):
16 | raise ValidationError(_("Subdomain must have between 3 and 100 characters."))
17 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/context_processors/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/redirectioneaza/context_processors/__init__.py
--------------------------------------------------------------------------------
/backend/redirectioneaza/context_processors/feature_flags.py:
--------------------------------------------------------------------------------
1 | from typing import Dict
2 |
3 | from django.conf import settings
4 | from django.http import HttpRequest
5 |
6 |
7 | def main(_: HttpRequest) -> Dict[str, bool]:
8 | return {
9 | "enable_multiple_forms": settings.ENABLE_MULTIPLE_FORMS,
10 | "enable_byof": settings.ENABLE_BYOF,
11 | "enable_csv_download": settings.ENABLE_CSV_DOWNLOAD,
12 | }
13 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/context_processors/variables.py:
--------------------------------------------------------------------------------
1 | from typing import Dict, List
2 |
3 | from django.conf import settings
4 | from django.http import HttpRequest
5 |
6 |
7 | def main(_: HttpRequest) -> Dict[str, Dict[str, List[Dict[str, str]]]]:
8 | return {
9 | "GOOGLE_ANALYTICS_ID": settings.GOOGLE_ANALYTICS_ID,
10 | }
11 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/settings/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for the redirectioneaza project.
3 |
4 | Initially generated by 'django-admin startproject' using Django 4.2.7.
5 | Broken down into multiple files for better organization and readability.
6 |
7 | For more information on this file, see
8 | https://docs.djangoproject.com/en/4.2/topics/settings/
9 |
10 | For the full list of settings and their values, see
11 | https://docs.djangoproject.com/en/4.2/ref/settings/
12 | """
13 |
14 | from .app_configs import *
15 | from .auth import *
16 | from .base import *
17 | from .cache import *
18 | from .captcha_analytics import *
19 | from .constants import *
20 | from .database import *
21 | from .django_q import *
22 | from .email import *
23 | from .environment import *
24 | from .feature_flags import *
25 | from .i18n import *
26 | from .logging import *
27 | from .storages import *
28 | from .templates import *
29 | from .unfold import *
30 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/settings/cache.py:
--------------------------------------------------------------------------------
1 | from .constants import HOUR, MINUTE
2 | from .environment import env
3 |
4 | # Cache settings
5 | TIMEOUT_CACHE_SHORT = 1 * MINUTE
6 | TIMEOUT_CACHE_NORMAL = 15 * MINUTE
7 | TIMEOUT_CACHE_LONG = 2 * HOUR
8 |
9 | ENABLE_CACHE = env.bool("ENABLE_CACHE")
10 | if ENABLE_CACHE:
11 | CACHES = {
12 | "default": {
13 | "BACKEND": "django.core.cache.backends.db.DatabaseCache",
14 | "LOCATION": "redirect_cache_default",
15 | "TIMEOUT": TIMEOUT_CACHE_NORMAL, # default cache timeout in seconds
16 | }
17 | }
18 | else:
19 | CACHES = {
20 | "default": {
21 | "BACKEND": "django.core.cache.backends.dummy.DummyCache",
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/settings/captcha_analytics.py:
--------------------------------------------------------------------------------
1 | from .base import DEBUG
2 | from .environment import env
3 |
4 | # reCAPTCHA & Analytics settings
5 |
6 | GOOGLE_ANALYTICS_ID = env.str("GOOGLE_ANALYTICS_ID")
7 |
8 | RECAPTCHA_PUBLIC_KEY = env.str("CAPTCHA_PUBLIC_KEY")
9 | RECAPTCHA_PRIVATE_KEY = env.str("CAPTCHA_PRIVATE_KEY")
10 | RECAPTCHA_REQUIRED_SCORE = env.float("CAPTCHA_REQUIRED_SCORE")
11 |
12 | RECAPTCHA_VERIFY_URL = env.str("CAPTCHA_VERIFY_URL")
13 | RECAPTCHA_POST_PARAM = env.str("CAPTCHA_POST_PARAM")
14 |
15 | RECAPTCHA_ENABLED = env.bool("RECAPTCHA_ENABLED", True if RECAPTCHA_PUBLIC_KEY else False)
16 |
17 | if DEBUG or not RECAPTCHA_ENABLED:
18 | SILENCED_SYSTEM_CHECKS = ["django_recaptcha.recaptcha_test_key_error"]
19 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/settings/constants.py:
--------------------------------------------------------------------------------
1 | import os
2 | from pathlib import Path
3 |
4 | # Constants for memory sizes
5 | KIBIBYTE = 1024
6 | MEBIBYTE = KIBIBYTE * 1024
7 | GIBIBYTE = MEBIBYTE * 1024
8 | TEBIBYTE = GIBIBYTE * 1024
9 |
10 | # Constants for time
11 | SECOND = 1
12 | MINUTE = 60 * SECOND
13 | HOUR = 60 * MINUTE
14 | DAY = 24 * HOUR
15 | WEEK = 7 * DAY
16 |
17 |
18 | # Build paths inside the project like this: BASE_DIR / 'subdir'.
19 | ROOT = Path(__file__).resolve().parent.parent.parent.parent
20 | BASE_DIR = os.path.abspath(os.path.join(ROOT, "backend"))
21 |
22 | ENV_FILE_NAME = os.environ.get("ENV_FILE_NAME", ".env.local")
23 | ENV_FILE_PATH = os.path.join(BASE_DIR, os.pardir, ENV_FILE_NAME)
24 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/settings/database.py:
--------------------------------------------------------------------------------
1 | from .environment import env
2 | import sys
3 |
4 | # Database
5 | # https://docs.djangoproject.com/en/4.2/ref/settings/#databases
6 | DATABASES = {
7 | "default": {
8 | "ENGINE": "django.db.backends.postgresql",
9 | "NAME": env.str("DATABASE_NAME"),
10 | "USER": env.str("DATABASE_USER"),
11 | "PASSWORD": env.str("DATABASE_PASSWORD"),
12 | "HOST": env.str("DATABASE_HOST"),
13 | "PORT": env.str("DATABASE_PORT"),
14 | }
15 | }
16 |
17 | # Default primary key field type
18 | # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
19 |
20 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
21 |
22 | # If we’re running tests, point at a dedicated test DB if specified
23 | if sys.argv[1:2] == ["test"]:
24 | DATABASES["default"].update(
25 | {
26 | "NAME": env.str("TEST_DATABASE_NAME", env.str("DATABASE_NAME")),
27 | "HOST": env.str("TEST_DATABASE_HOST", env.str("DATABASE_HOST")),
28 | "USER": env.str("TEST_DATABASE_USER", env.str("DATABASE_USER")),
29 | "PASSWORD": env.str("TEST_DATABASE_PASSWORD", env.str("DATABASE_PASSWORD")),
30 | }
31 | )
32 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/settings/feature_flags.py:
--------------------------------------------------------------------------------
1 | from .environment import env
2 |
3 | # Feature flags
4 | ENABLE_FLAG_CONTACT = env.bool("ENABLE_FLAG_CONTACT", False)
5 | ENABLE_MULTIPLE_FORMS = env.bool("ENABLE_MULTIPLE_FORMS", True)
6 | ENABLE_BYOF = env.bool("ENABLE_BYOF", False)
7 | ENABLE_CSV_DOWNLOAD = env.bool("ENABLE_CSV_DOWNLOAD", False)
8 |
9 | # Configurations for the NGO Hub integration
10 | UPDATE_ORGANIZATION_METHOD = env("UPDATE_ORGANIZATION_METHOD")
11 |
12 | # Legal data full validation settings
13 | ENABLE_FULL_VALIDATION_CUI = env.bool("ENABLE_FULL_VALIDATION_CUI")
14 | ENABLE_FULL_VALIDATION_IBAN = env.bool("ENABLE_FULL_VALIDATION_IBAN")
15 | ENABLE_FULL_VALIDATION_PHONE = env.bool("ENABLE_FULL_VALIDATION_PHONE")
16 | ENABLE_FULL_VALIDATION_CNP = env.bool("ENABLE_FULL_VALIDATION_CNP")
17 |
18 | # Form download settings
19 | ENABLE_FORMS_DOWNLOAD = env.bool("ENABLE_FORMS_DOWNLOAD", True)
20 | TIMEDELTA_FORMS_DOWNLOAD_MINUTES = env.int("TIMEDELTA_FORMS_DOWNLOAD_MINUTES")
21 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/settings/i18n.py:
--------------------------------------------------------------------------------
1 | # Internationalization
2 | # https://docs.djangoproject.com/en/4.2/topics/i18n/
3 | import os
4 |
5 | from .constants import BASE_DIR
6 | from .environment import env
7 |
8 |
9 | LANGUAGE_CODE = env.str("LANGUAGE_CODE")
10 |
11 | TIME_ZONE = "Europe/Bucharest"
12 |
13 | USE_I18N = True
14 |
15 | USE_TZ = True
16 |
17 | LOCALE_PATHS = [
18 | os.path.join(BASE_DIR, "locale"),
19 | os.path.join(BASE_DIR, "locale_localflavor"),
20 | ]
21 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/views.py:
--------------------------------------------------------------------------------
1 | from django.views.generic.base import TemplateView
2 |
3 |
4 | class StaticPageView(TemplateView):
5 | template_name = ""
6 |
7 | def get_context_data(self, **kwargs):
8 | context = super().get_context_data(**kwargs)
9 |
10 | return context
11 |
--------------------------------------------------------------------------------
/backend/redirectioneaza/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for redirectioneaza project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "redirectioneaza.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/backend/requirements-dev.in:
--------------------------------------------------------------------------------
1 | pip-tools~= 7.4.1
2 |
3 | # formatting & linting
4 | black~=25.1.0
5 | ruff~=0.11
6 |
7 | faker~=37.1
8 |
9 | # testing
10 | pytest~=8.3.5
11 |
12 | pytest-django~=4.11
13 | pytest-xdist~=3.6.1
14 | pytest-cov~=6.0.0
15 |
16 | # debugging
17 | debugpy~=1.8.14
18 |
19 | -r ./requirements.txt
20 |
--------------------------------------------------------------------------------
/backend/static_extras/files/sample.csv:
--------------------------------------------------------------------------------
1 | cnp,prenume,initiala,nume,adresa,telefon,email,anaf_gdpr,perioada
2 | 1880422412316,Ion,I,Popescu,"Bulevardul Adresei nr. 8C, Mun. Iași, Jud. Iași",0712345678,email@example.com,1,2
3 | 2960523201287,Ioana,E,Enescu,,,,,
4 |
--------------------------------------------------------------------------------
/backend/static_extras/font/opensans.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/font/opensans.ttf
--------------------------------------------------------------------------------
/backend/static_extras/images/code4romania-inversed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/code4romania-inversed.png
--------------------------------------------------------------------------------
/backend/static_extras/images/code4romania.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/code4romania.png
--------------------------------------------------------------------------------
/backend/static_extras/images/favicon/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/favicon/android-chrome-192x192.png
--------------------------------------------------------------------------------
/backend/static_extras/images/favicon/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/favicon/android-chrome-512x512.png
--------------------------------------------------------------------------------
/backend/static_extras/images/favicon/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/favicon/apple-touch-icon.png
--------------------------------------------------------------------------------
/backend/static_extras/images/favicon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/favicon/favicon-16x16.png
--------------------------------------------------------------------------------
/backend/static_extras/images/favicon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/favicon/favicon-32x32.png
--------------------------------------------------------------------------------
/backend/static_extras/images/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/favicon/favicon.ico
--------------------------------------------------------------------------------
/backend/static_extras/images/favicon/site.webmanifest:
--------------------------------------------------------------------------------
1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
--------------------------------------------------------------------------------
/backend/static_extras/images/first_page.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/first_page.jpg
--------------------------------------------------------------------------------
/backend/static_extras/images/footer-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/footer-logo.png
--------------------------------------------------------------------------------
/backend/static_extras/images/formular-2019.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/formular-2019.jpg
--------------------------------------------------------------------------------
/backend/static_extras/images/formular-2021.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/formular-2021.jpg
--------------------------------------------------------------------------------
/backend/static_extras/images/formular-2025.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/formular-2025.jpg
--------------------------------------------------------------------------------
/backend/static_extras/images/hub.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/hub.png
--------------------------------------------------------------------------------
/backend/static_extras/images/icons/envelope.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/backend/static_extras/images/icons/question-mark-circle.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/backend/static_extras/images/icons/search.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/backend/static_extras/images/icons/star.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/backend/static_extras/images/logo-homepage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/logo-homepage.png
--------------------------------------------------------------------------------
/backend/static_extras/images/logo-smaller.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/logo-smaller.png
--------------------------------------------------------------------------------
/backend/static_extras/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/logo.png
--------------------------------------------------------------------------------
/backend/static_extras/images/logo_bw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/logo_bw.png
--------------------------------------------------------------------------------
/backend/static_extras/images/logo_gray.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/logo_gray.png
--------------------------------------------------------------------------------
/backend/static_extras/images/ngohub-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/ngohub-screenshot.png
--------------------------------------------------------------------------------
/backend/static_extras/images/placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/placeholder.png
--------------------------------------------------------------------------------
/backend/static_extras/images/second_page.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/second_page.jpg
--------------------------------------------------------------------------------
/backend/static_extras/images/semnatura.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/semnatura.png
--------------------------------------------------------------------------------
/backend/static_extras/images/social-icons/facebook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/social-icons/facebook.png
--------------------------------------------------------------------------------
/backend/static_extras/images/social-icons/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/social-icons/github.png
--------------------------------------------------------------------------------
/backend/static_extras/images/social-icons/instagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/social-icons/instagram.png
--------------------------------------------------------------------------------
/backend/static_extras/images/thank_you.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/static_extras/images/thank_you.jpg
--------------------------------------------------------------------------------
/backend/tailwind.config.js:
--------------------------------------------------------------------------------
1 | import aspectRatio from '@tailwindcss/aspect-ratio';
2 | import forms from '@tailwindcss/forms';
3 | import typography from '@tailwindcss/typography';
4 |
5 | /** @type {import('tailwindcss').Config} */
6 | export default {
7 | content: [
8 | './templates/**/*.html',
9 | ],
10 | theme: {
11 | container: {
12 | center: true,
13 | padding: {
14 | DEFAULT: '1rem',
15 | sm: '1.5rem',
16 | lg: '2rem',
17 | },
18 | },
19 | fontFamily: {
20 | sans: [
21 | 'Inter, sans-serif',
22 | ],
23 | },
24 | extend: {},
25 | },
26 | plugins: [
27 | aspectRatio,
28 | forms,
29 | typography,
30 | ],
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/backend/templates/v2/account/components/email-input.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% include "components/input/input.html" with input_id=input_id input_title=input_title input_type="email" input_name=input_name value=value is_required=True max_length=280 %}
4 |
--------------------------------------------------------------------------------
/backend/templates/v2/account/components/password-input.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% if not field_label %}
4 | {% trans "Password" as field_label %}
5 | {% endif %}
6 |
7 |
8 |
9 | {% include "components/input/input.html" with input_id=input_id input_title=input_title input_type="password" input_name=input_name is_required=True max_length=max_length %}
10 |
11 |
--------------------------------------------------------------------------------
/backend/templates/v2/account/errors/base.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load static %}
3 | {% load i18n %}
4 |
5 | {% block content %}
6 |
7 |
8 |
9 | {% block title %}{% endblock %}
10 |
11 |
12 | {% block message %}{% endblock %}
13 |
14 |
15 | {% block extra_content %}{% endblock %}
16 |
17 |
18 | {% endblock %}
19 |
--------------------------------------------------------------------------------
/backend/templates/v2/account/errors/login/app_missing.html:
--------------------------------------------------------------------------------
1 | {% extends "account/errors/login/base_login.html" %}
2 | {% load i18n %}
3 |
4 | {% block title %}
5 | {% trans "Application not activated" %}
6 | {% endblock %}
7 |
8 | {% block message %}
9 |
10 | {% trans "The application is not activated for this user/organization." %}
11 |
12 |
13 |
14 | {% trans "If you are the owner of the organization, please activate it in NGO Hub." %}
15 |
16 |
17 |
18 | {% endblock %}
19 |
--------------------------------------------------------------------------------
/backend/templates/v2/account/errors/login/base_login.html:
--------------------------------------------------------------------------------
1 | {% extends 'account/errors/base.html' %}
2 | {% load i18n %}
3 |
4 | {% block extra_content %}
5 |
6 | {% trans "Return to login" %}
7 |
8 | {% endblock %}
9 |
--------------------------------------------------------------------------------
/backend/templates/v2/account/errors/login/multiple_ngos.html:
--------------------------------------------------------------------------------
1 | {% extends 'account/errors/login/base_login.html' %}
2 | {% load i18n %}
3 |
4 | {% block title %}
5 | {% trans 'Multiple NGOs for this CUI' %}
6 | {% endblock %}
7 |
8 | {% block message %}
9 | {% trans 'Multiple NGOs have been found during synchronization. Admins have been notified and will resolve the issue shortly.' %}
10 | {% endblock %}
11 |
--------------------------------------------------------------------------------
/backend/templates/v2/account/errors/login/unknown_error.html:
--------------------------------------------------------------------------------
1 | {% extends 'account/errors/login/base_login.html' %}
2 | {% load i18n %}
3 |
4 | {% block title %}
5 | {% trans 'Unknown role' %}
6 | {% endblock %}
7 |
8 | {% block message %}
9 | {% trans "An unknown error occurred during authentication. Please try again later. The problem has been reported to the team." %}
10 | {% endblock %}
11 |
--------------------------------------------------------------------------------
/backend/templates/v2/account/errors/login/unknown_role.html:
--------------------------------------------------------------------------------
1 | {% extends 'account/errors/login/base_login.html' %}
2 | {% load i18n %}
3 |
4 | {% block title %}
5 | {% trans 'Unknown role' %}
6 | {% endblock %}
7 |
8 | {% block message %}
9 | {% trans "Your current role isn't supported." %}
10 | {% endblock %}
11 |
--------------------------------------------------------------------------------
/backend/templates/v2/account/login.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% load i18n allauth account %}
3 |
4 | {% block content %}
5 |
6 |
7 | {% if SOCIALACCONUNT_ONLY %}
8 |
9 | {% include "account/snippets/third-party.html" with page_type="login" %}
10 |
11 | {% elif SOCIALACCOUNT_ENABLED %}
12 |
13 | {% include "account/snippets/third-party.html" with page_type="login" %}
14 |
15 |
16 |
17 | {% include "account/snippets/login-form.html" %}
18 |
19 | {% else %}
20 |
21 | {% include "account/snippets/login-form.html" %}
22 |
23 | {% endif %}
24 |
25 |
26 |
31 |
32 |
33 |
34 | {% include "account/snippets/cta-ngohub.html" %}
35 |
36 | {% endblock content %}
37 |
--------------------------------------------------------------------------------
/backend/templates/v2/account/signup-confirmation.html:
--------------------------------------------------------------------------------
1 | {% extends "layouts/content-and-image-page.html" %}
2 |
3 | {% load i18n %}
4 |
5 | {% block page_content %}
6 |
7 |
10 |
11 |
12 | {% blocktrans trimmed %}
13 | Your account has been succesfully created!
14 | You will receive an email message shortly.
15 | Click on the link in the email to finish your account validation.
16 | Unable to find the email? Be sure to also check the spam folder.
17 | {% endblocktrans %}
18 |
19 |
20 |
21 | {% trans "Authentication problems?" %}
22 |
23 |
24 |
25 | {% trans "Check the support page" %}
26 | {% trans "or contact us at" %}
27 | redirectioneaza@code4.ro
28 |
29 |
30 | {% endblock %}
31 |
--------------------------------------------------------------------------------
/backend/templates/v2/account/signup-verification.html:
--------------------------------------------------------------------------------
1 | {% extends "layouts/content-and-image-page.html" %}
2 |
3 | {% load i18n static %}
4 |
5 | {% block page_content %}
6 |
7 |
10 |
11 |
12 | {% blocktrans trimmed %}
13 | Congratulations! You have completed your redirectioneaza.ro account validation.
14 | Now you can return to the authentication page and log into your account.
15 | {% endblocktrans %}
16 |
17 |
18 | {% url "login" as button_url %}
19 | {% trans "Authenticate" as button_title %}
20 | {% include "components/buttons/link.html" with style="white" %}
21 |
22 | {% endblock %}
23 |
24 | {% block page_image %}
25 |
28 | {% endblock page_image %}
29 |
--------------------------------------------------------------------------------
/backend/templates/v2/account/snippets/errors.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 | ×
5 |
6 | {% trans "Error!" %}
7 |
8 | {{ errors }}
9 |
10 |
--------------------------------------------------------------------------------
/backend/templates/v2/allauth/layouts/base.html:
--------------------------------------------------------------------------------
1 | {% extends 'base.html' %}
2 | {% load i18n static %}
3 |
4 | {% block title %}{% block head_title %}{% endblock head_title %}{% endblock %}
5 |
6 | {% block content %}{% endblock content %}
7 |
--------------------------------------------------------------------------------
/backend/templates/v2/components/action-menu/item-action.html:
--------------------------------------------------------------------------------
1 |
29 |
--------------------------------------------------------------------------------
/backend/templates/v2/components/action-menu/item_href.html:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/backend/templates/v2/components/buttons/link.html:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/backend/templates/v2/components/copy-to-clipboard/base.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% trans "Copy the link" as title %}
4 |
5 |
9 |
10 | showConfirmation = false, 5000)"
13 | class="{{ button_classes }}">
14 |
15 | {% block clickable_content %}{{ title }}{% endblock %}
16 |
17 |
18 |
19 |
24 |
25 | {% trans "Copied the link" as message %}
26 | {% include "components/notifications/message.html" with message_text=message %}
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/backend/templates/v2/components/copy-to-clipboard/icon.html:
--------------------------------------------------------------------------------
1 | {% extends "components/copy-to-clipboard/base.html" %}
2 |
3 | {% load i18n %}
4 |
5 |
6 | {% block clickable_content %}
7 |
12 |
13 | {% if icon_path %}
14 |
15 | {% else %}
16 |
18 |
20 | {% endif %}
21 |
22 |
23 | {% endblock %}
24 |
--------------------------------------------------------------------------------
/backend/templates/v2/components/copy-to-clipboard/text.html:
--------------------------------------------------------------------------------
1 | {% extends "components/copy-to-clipboard/base.html" %}
2 |
3 | {% load i18n %}
4 |
5 |
6 | {% block clickable_content %}
7 | {% if button_text %}
8 | {{ button_text }}
9 | {% else %}
10 | {{ block.super }}
11 | {% endif %}
12 | {% endblock %}
13 |
--------------------------------------------------------------------------------
/backend/templates/v2/components/form/legend.html:
--------------------------------------------------------------------------------
1 |
2 | {{ legend_title }}
3 |
4 | {% if info_tooltip %}
5 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | {% endif %}
16 |
17 |
18 |
--------------------------------------------------------------------------------
/backend/templates/v2/components/input/base.html:
--------------------------------------------------------------------------------
1 |
32 |
--------------------------------------------------------------------------------
/backend/templates/v2/components/input/filter.html:
--------------------------------------------------------------------------------
1 | {% if filter.options %}
2 |
3 | {% with input_id=filter.id %}
4 | {% with input_title=filter.title %}
5 | {% with input_name=filter.key %}
6 | {% with options=filter.options %}
7 | {% with current_value=filters_active|get_item:filter.key %}
8 |
9 | {% if filter.type == "combobox" %}
10 | {% include "components/input/combobox.html" %}
11 | {% elif filter.type == "select" %}
12 | {% include "components/input/select.html" with include_default_option=True on_change="handleChange" %}
13 | {% endif %}
14 |
15 | {% endwith %}
16 | {% endwith %}
17 | {% endwith %}
18 | {% endwith %}
19 | {% endwith %}
20 |
21 | {% endif %}
22 |
--------------------------------------------------------------------------------
/backend/templates/v2/components/input/help-text.html:
--------------------------------------------------------------------------------
1 | {% if help_text %}
2 |
3 | {{ help_text }}
4 |
5 | {% endif %}
6 |
--------------------------------------------------------------------------------
/backend/templates/v2/components/input/textarea.html:
--------------------------------------------------------------------------------
1 | {% extends 'components/input/base.html' %}
2 |
3 | {% block input %}
4 |
5 |
14 |
15 | {% endblock %}
16 |
--------------------------------------------------------------------------------
/backend/templates/v2/components/logo-or-default.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 |
3 |
13 |
--------------------------------------------------------------------------------
/backend/templates/v2/components/notifications/messages.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% for message in messages %}
5 |
17 |
18 | {% include "components/notifications/message.html" with message_text=message message_tags=message.tags %}
19 |
20 |
21 | {% endfor %}
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/account/activate-account/main.html:
--------------------------------------------------------------------------------
1 | {% extends "emails/base.html" %}
2 |
3 | {% load i18n %}
4 |
5 | {% block heading %}
6 | {% trans "Hello," %}
7 | {{ first_name }},
8 | {% endblock %}
9 |
10 | {% block content %}
11 |
12 |
13 | {% blocktrans trimmed %}
14 | Welcome to redirectioneaza.ro!
15 | {% endblocktrans %}
16 |
17 |
18 |
19 | {% blocktrans trimmed %}
20 | We're happy to have you on the platform!
21 | To activate your account, you have to confirm your email address by clicking the button below.
22 | {% endblocktrans %}
23 |
24 |
25 | {% trans "Activate your account" as action_title %}
26 | {% include "emails/components/action.html" with title=action_title url=action_url %}
27 |
28 | {% blocktrans trimmed %}
29 | Thank you for being part of this community!
30 | {% endblocktrans %}
31 |
32 | {% endblock %}
33 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/account/activate-account/main.txt:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% trans "Hello," %} {{ first_name }},
4 |
5 | {% blocktrans trimmed %}
6 | Welcome to redirectioneaza.ro!
7 | {% endblocktrans %}
8 |
9 | {% blocktrans trimmed %}
10 | We're happy to have you on the platform!
11 | To activate your account, you have to confirm your email address by clicking the button below.
12 | {% endblocktrans %}
13 |
14 | {{ action_url }}
15 |
16 | {% blocktrans trimmed %}
17 | Thank you for being part of this community!
18 | {% endblocktrans %}
19 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/account/ignore-email.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 | {% blocktrans trimmed %}
5 | If you didn't request a password reset,
6 | you can ignore this email.
7 | For any other questions or concerns, feel free to write to us at
8 | {{ contact_email }}
9 | or
10 | check out the support page .
11 | {% endblocktrans %}
12 |
13 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/account/ignore-email.txt:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% blocktrans trimmed %}
4 | If you didn't request a password reset,
5 | you can ignore this email.
6 | For any other questions or concerns, feel free to write to us at
7 | {{ contact_email }}
8 | or
9 | check out the support page.
10 | {% endblocktrans %}
11 |
12 | https://www.ngohub.ro/ro/suport-tehnic-redirectioneaza
13 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/account/invite-partner/main.html:
--------------------------------------------------------------------------------
1 | {% extends "emails/base.html" %}
2 |
3 | {% load i18n %}
4 |
5 | {% block heading %}
6 | {% trans "Hello," %}
7 | {{ first_name }},
8 | {% endblock %}
9 |
10 | {% block content %}
11 |
12 |
13 | {% blocktrans trimmed %}
14 | You have been invited to join redirectioneaza.ro
15 | and manage the partner account for {{ partner_name }}.
16 | To accept the invitation and set up your account, please click the button below.
17 | {% endblocktrans %}
18 |
19 |
20 | {% trans "Confirm invite" as action_title %}
21 | {% include "emails/components/action.html" with title=action_title url=action_url %}
22 |
23 | {% include "emails/components/misdelivered.html" %}
24 |
25 | {% endblock %}
26 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/account/invite-partner/main.txt:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% trans "Hello," %} {{ first_name }}
4 |
5 | {% blocktrans trimmed %}
6 | You have been invited to join redirectioneaza.ro
7 | and manage the partner account for {{ partner_name }}.
8 | To accept the invitation and set up your account, please click the button below.
9 | {% endblocktrans %}
10 |
11 | {{ action_url }}
12 |
13 | {% include "emails/components/misdelivered.html" %}
14 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/account/ngohub-notification/main.html:
--------------------------------------------------------------------------------
1 | {% extends "emails/base.html" %}
2 |
3 | {% load i18n %}
4 |
5 | {% block heading %}
6 | {% trans "Hello," %}
7 | {{ first_name }},
8 | {% endblock %}
9 |
10 | {% block content %}
11 |
12 |
13 | {% blocktrans trimmed %}
14 | We received a request to reset your password on redirectioneaza.ro.
15 | Your account is managed through the NGO Hub platform.
16 | To log in or manage your password,
17 | please use your NGO Hub account credentials
18 | and log in to redirectioneaza.ro through NGO Hub.
19 | {% endblocktrans %}
20 |
21 |
22 | {% trans "Continue with NGO Hub" as action_title %}
23 | {% include "emails/components/action.html" with title=action_title url=action_url %}
24 |
25 | {% include "emails/account/ignore-email.html" %}
26 |
27 | {% endblock %}
28 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/account/ngohub-notification/main.txt:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% trans "Hello," %} {{ first_name }},
4 |
5 | {% blocktrans trimmed %}
6 | We received a request to reset your password on redirectioneaza.ro.
7 | Your account is managed through the NGO Hub platform.
8 | To log in or manage your password,
9 | please use your NGO Hub account credentials
10 | and log in to redirectioneaza.ro through NGO Hub.
11 | {% endblocktrans %}
12 |
13 | {{ action_url }}
14 |
15 | {{ ngohub_site }}
16 |
17 | {% include "emails/account/ignore-email.html" %}
18 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/account/reset-password/main.html:
--------------------------------------------------------------------------------
1 | {% extends "emails/base.html" %}
2 |
3 | {% load i18n %}
4 |
5 | {% block heading %}
6 | {% trans "Hello," %}
7 | {{ first_name }},
8 | {% endblock %}
9 |
10 | {% block content %}
11 |
12 |
13 | {% blocktrans trimmed %}
14 | We received a request to reset your password on redirectioneaza.ro.
15 | You can reset your password by clicking the button below.
16 | {% endblocktrans %}
17 |
18 |
19 | {% trans "Reset password" as action_title %}
20 | {% include "emails/components/action.html" with title=action_title url=action_url %}
21 |
22 | {% include "emails/account/ignore-email.html" %}
23 |
24 | {% endblock %}
25 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/account/reset-password/main.txt:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% trans "Hello," %} {{ first_name }},
4 |
5 | {% blocktrans trimmed %}
6 | We received a request to reset your password on redirectioneaza.ro.
7 | You can reset your password by clicking the button below.
8 | {% endblocktrans %}
9 |
10 | {{ action_url }}
11 |
12 | {% include "emails/account/ignore-email.html" %}
13 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/admin/new-ngo/main.html:
--------------------------------------------------------------------------------
1 | {% extends "emails/base.html" %}
2 |
3 | {% load i18n %}
4 |
5 | {% block heading %}
6 | {% trans "A new NGO has registered on redirectioneaza.ro" %}
7 | {% endblock %}
8 |
9 | {% block content %}
10 |
11 |
12 |
13 |
14 | {% trans "NGO name" %}:
15 |
16 |
17 | {{ ngo_name }}
18 |
19 |
20 |
21 |
22 | {% trans "NGO email" %}:
23 |
24 |
25 | {{ ngo_email }}
26 |
27 |
28 |
29 |
30 | {% trans "NGO ID" %}:
31 |
32 |
33 | {{ ngo_id }}
34 |
35 |
36 |
37 | {% endblock %}
38 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/admin/new-ngo/main.txt:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% trans "A new NGO has registered on redirectioneaza.ro" %}
4 |
5 | - {% trans "NGO name" %}: {{ ngo.name }}
6 | - {% trans "NGO email" %}: {{ ngo.email }}
7 | - {% trans "NGO ID" %}: {{ ngo.id }}
8 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/base-header.html:
--------------------------------------------------------------------------------
1 | {% load i18n static %}
2 |
3 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/components/action.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/components/extra-hand.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 | {% trans "Do you want to give an extra helping hand?" %}
5 |
6 |
7 |
8 | {% blocktrans trimmed %}
9 | If you want to be a true civic superhero,
10 | you can help the organization collect more forms!
11 | Here's how:
12 | {% endblocktrans %}
13 |
14 |
15 |
16 |
17 | {% blocktranslate trimmed %}
18 | Share the
19 | organization's page
20 | on social networks
21 | and encourage your friends to complete the form online for the organization you believe in,
22 | simply and safely,
23 | on redirectioneaza.ro.
24 | {% endblocktranslate %}
25 |
26 |
27 | {% blocktranslate trimmed %}
28 | Download the form 230 template for
29 | {{ ngo_name }} ,
30 | print multiple copies and convince your colleagues,
31 | friends or family to complete it.
32 | {% endblocktranslate %}
33 |
34 |
35 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/components/extra-hand.txt:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% trans "Do you want to give an extra helping hand?" %}
4 |
5 | {% blocktrans trimmed %}
6 | If you want to be a true civic superhero, you can help the organization collect more forms!
7 | Here's how:
8 | {% endblocktrans %}
9 |
10 | {% blocktranslate trimmed %}
11 | Share the
12 | organization's page
13 | on social networks
14 | and encourage your friends to complete the form online for the organization you believe in,
15 | simply and safely,
16 | on redirectioneaza.ro.
17 | {% endblocktranslate %}
18 |
19 | {% blocktranslate trimmed %}
20 | Download the form 230 template for
21 | {{ ngo_name }},
22 | print multiple copies and convince your colleagues,
23 | friends or family to complete it.
24 | {% endblocktranslate %}
25 |
26 | {{ cause_url }}
27 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/components/misdelivered.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 | {% blocktranslate trimmed %}
5 | Did you receive this email by mistake?
6 | Please let us know at {{ contact_email }} .
7 | {% endblocktranslate %}
8 |
9 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/components/misdelivered.txt:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% blocktranslate trimmed %}
4 | Did you receive this email by mistake?
5 | Please let us know at
6 | {{ contact_email }}.
7 | {% endblocktranslate %}
8 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/components/panel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ content }}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/components/subcopy.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 |
5 |
6 | {% blocktrans trimmed %}
7 |
8 | If you're having trouble clicking the {{ title }} button, copy and paste the URL below into your web browser:
9 | {{ url }}
10 |
11 | {% endblocktrans %}
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/donor/redirection-with-signature/main.html:
--------------------------------------------------------------------------------
1 | {% extends "emails/base.html" %}
2 |
3 | {% load i18n %}
4 |
5 | {% block heading %}
6 | {% trans "Good news! You have just redirected a part of your income tax!" %}
7 | {% endblock %}
8 |
9 | {% block content %}
10 |
11 |
12 | {% blocktrans trimmed %}
13 | Thank you for choosing to support {{ ngo_name }}!
14 | Your form has reached the organization,
15 | so you don't have to worry about anything else.
16 | You can see the form you have filled in by clicking the button below.
17 | {% endblocktrans %}
18 |
19 |
20 | {% trans "Download form" as action_title %}
21 | {% include "emails/components/action.html" with title=action_title url=action_url %}
22 |
23 | {% include "emails/components/extra-hand.html" %}
24 |
25 |
26 | {% trans "Thank you for your involvement!" %}
27 |
28 |
29 | {% include "emails/components/misdelivered.html" %}
30 |
31 | {% endblock %}
32 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/donor/redirection-with-signature/main.txt:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% trans "Good news! You have just redirected a part of your income tax!" %}
4 |
5 | {% blocktrans trimmed %}
6 | Thank you for choosing to support {{ ngo_name }}!
7 | Your form has reached the organization,
8 | so you don't have to worry about anything else.
9 | You can see the form you have filled in by clicking the button below.
10 | {% endblocktrans %}
11 |
12 | {{ action_url }}
13 |
14 | {% include "emails/components/extra-hand.txt" %}
15 |
16 | {% trans "Thank you for your involvement!" %}
17 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/donor/removed-redirection/main.html:
--------------------------------------------------------------------------------
1 | {% extends "emails/base.html" %}
2 |
3 | {% load i18n %}
4 |
5 | {% block heading %}
6 | {% trans "Your redirection form was removed from redirectioneaza.ro" %}
7 | {% endblock %}
8 |
9 | {% block content %}
10 |
11 |
12 | {% blocktrans trimmed %}
13 | Thank you for choosing to support {{ cause_name }}!
14 | The administrators of the platform have unfortunately removed the redirection
15 | because of an error or a request from the NGO.
16 | If you wish to redirect again to the same cause, please go to the following link:
17 | {% endblocktrans %}
18 |
19 |
20 | {% trans "Redirect" as action_title %}
21 | {% include "emails/components/action.html" with title=action_title url=action_url %}
22 |
23 |
24 | {% trans "Thank you for your involvement!" %}
25 |
26 |
27 | {% include "emails/components/misdelivered.html" %}
28 |
29 | {% endblock %}
30 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/donor/removed-redirection/main.txt:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% trans "Your redirection form was removed from redirectioneaza.ro" %}
4 |
5 | {% blocktrans trimmed %}
6 | Thank you for choosing to support {{ cause_name }}!
7 | The administrators of the platform have unfortunately removed the redirection
8 | because of an error or a request from the NGO.
9 | If you wish to redirect again to the same cause, please go to the following link:
10 | {% endblocktrans %}
11 |
12 | {{ action_url }}
13 |
14 | {% include "emails/components/extra-hand.txt" %}
15 |
16 | {% trans "Thank you for your involvement!" %}
17 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/ngo/download-archive/main.html:
--------------------------------------------------------------------------------
1 | {% extends "emails/base.html" %}
2 |
3 | {% load i18n %}
4 |
5 | {% block heading %}
6 | {% trans "Hello," %}
7 | {% endblock %}
8 |
9 | {% block content %}
10 |
11 |
12 | {% blocktrans trimmed %}
13 | We have received your request and prepared the archive with your organization's forms.
14 | You can access the documents by clicking the button below.
15 | Remember that the deadline for submitting the forms to ANAF is
16 | {% endblocktrans %}
17 | {{ donation_limit_day }} {{ donation_limit_month_name }} {{ donation_limit_year }} .
18 |
19 |
20 |
21 | {% trans "Download archive" as action_title %}
22 | {% include "emails/components/action.html" with title=action_title url=action_url %}
23 |
24 | {% blocktrans trimmed %}
25 | Thank you for being part of this community!
26 | {% endblocktrans %}
27 |
28 | {% endblock %}
29 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/ngo/download-archive/main.txt:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% trans "Hello," %}
4 |
5 | {% blocktrans trimmed %}
6 | We have received your request and prepared the archive with your organization's forms.
7 | You can access the documents by clicking the button below.
8 | Remember that the deadline for submitting the forms to ANAF is
9 | {% endblocktrans %}
10 |
11 | {{ donation_limit_day }} {{ donation_limit_month_name }} {{ donation_limit_year }}
12 |
13 | {{ action_url }}
14 |
15 | {% blocktrans trimmed %}
16 | Thank you for being part of this community!
17 | {% endblocktrans %}
18 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/ngo/download-csv/main.txt:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% trans "Hello," %}
4 |
5 | {% blocktrans trimmed %}
6 | We have received your request and prepared the CSV with the requested forms.
7 | You can access the documents by clicking the button below.
8 | Remember that the deadline for submitting the forms to ANAF is
9 | {% endblocktrans %}
10 |
11 | {{ donation_limit_day }} {{ donation_limit_month_name }} {{ donation_limit_year }}.
12 |
13 | {{ action_url }}
14 |
15 |
16 | {% blocktrans trimmed %}
17 | The CSV was generated with the following filters:
18 | {% endblocktrans %}
19 | {% for filter_key, filter_value in filters.items %}
20 | - {{ filter_key }}: {{ filter_value }}
21 | {% endfor %}
22 |
23 | {% blocktrans trimmed %}
24 | Thank you for being part of this community!
25 | {% endblocktrans %}
26 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/ngo/new-form-received/main.html:
--------------------------------------------------------------------------------
1 | {% extends "emails/base.html" %}
2 |
3 | {% load i18n %}
4 |
5 | {% block heading %}
6 | {% trans "Good news! You have a new redirection form" %}
7 | {% endblock %}
8 |
9 | {% block content %}
10 |
11 |
12 | {% blocktrans trimmed %}
13 | A new superhero has decided to support your organization by redirecting income tax.
14 | You can access the completed form by clicking the button below.
15 | Remember to submit it to ANAF to complete the process!
16 | {% endblocktrans %}
17 |
18 |
19 | {% trans "Download form" as action_title %}
20 | {% include "emails/components/action.html" with title=action_title url=action_url %}
21 |
22 | {% endblock %}
23 |
--------------------------------------------------------------------------------
/backend/templates/v2/emails/ngo/new-form-received/main.txt:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% trans "Good news! You have a new redirection form" %}
4 |
5 | {% blocktrans trimmed %}
6 | A new superhero has decided to support your organization by redirecting income tax.
7 | You can access the completed form by clicking the button below.
8 | Remember to submit it to ANAF to complete the process!
9 | {% endblocktrans %}
10 |
11 | {{ action_url }}
12 |
--------------------------------------------------------------------------------
/backend/templates/v2/errors/400.html:
--------------------------------------------------------------------------------
1 | {% extends "errors/base.html" %}
2 |
3 | {% block html_title %}Eroare 400{% endblock %}
4 |
5 | {% block error_heading %}Eroare 400{% endblock %}
6 |
7 | {% block error_details %}Bad request.{% endblock %}
8 |
--------------------------------------------------------------------------------
/backend/templates/v2/errors/403.html:
--------------------------------------------------------------------------------
1 | {% extends "errors/base.html" %}
2 |
3 | {% block html_title %}Eroare 403{% endblock %}
4 |
5 | {% block error_heading %}Eroare 403{% endblock %}
6 |
7 | {% block error_details %}Nu aveți permisiunea pentru această acțiune.{% endblock %}
8 |
--------------------------------------------------------------------------------
/backend/templates/v2/errors/404.html:
--------------------------------------------------------------------------------
1 | {% extends "errors/base.html" %}
2 |
3 | {% block html_title %}Eroare 404{% endblock %}
4 |
5 | {% block error_heading %}Eroare 404{% endblock %}
6 |
7 | {% block error_details %}Pagina căutată nu a fost găsită.{% endblock %}
8 |
--------------------------------------------------------------------------------
/backend/templates/v2/errors/500.html:
--------------------------------------------------------------------------------
1 | {% extends "errors/base.html" %}
2 |
3 | {% block html_title %}Eroare 500{% endblock %}
4 |
5 | {% block error_heading %}Eroare 500{% endblock %}
6 |
7 | {% block error_details %}Serverul are o eroare.{% endblock %}
8 |
--------------------------------------------------------------------------------
/backend/templates/v2/errors/base.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% load static %}
3 |
4 | {% block content %}
5 |
6 |
7 |
8 |
9 |
12 |
13 | {% block error_details %}{% endblock %}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | {% endblock %}
24 |
--------------------------------------------------------------------------------
/backend/templates/v2/errors/other.html:
--------------------------------------------------------------------------------
1 | {% extends "errors/base.html" %}
2 |
3 | {% block html_title %}Eroare{% endblock %}
4 |
5 | {% block error_heading %}Eroare{% endblock %}
6 |
7 | {% block error_details %}Din păcate nu am alte detalii.{% endblock %}
8 |
--------------------------------------------------------------------------------
/backend/templates/v2/form/header/other-form.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{ cause.name }}
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 | {{ cause.description|linebreaks }}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | {% with ngo_logo_class="aspect-square max-h-64 object-cover" %}
30 | {% include "components/logo-or-default.html" with logo=cause.display_image logo_class=ngo_logo_class %}
31 | {% endwith %}
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/backend/templates/v2/form/redirection-closed.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 |
5 |
6 | {% trans "Notice!" %}
7 |
8 |
9 | {% blocktranslate trimmed %}
10 | The donation form can only be filled out between
11 | January 1st
12 | and
13 | {{ day_limit }} {{ month_limit }}
14 | of the current year.
15 | {% endblocktranslate %}
16 |
17 |
18 |
--------------------------------------------------------------------------------
/backend/templates/v2/form/success/main.html:
--------------------------------------------------------------------------------
1 | {% extends "layouts/content-and-image-page.html" %}
2 |
3 | {% load i18n static %}
4 |
5 | {% block page_content %}
6 |
7 | {% if donor.has_signed %}
8 |
9 | {% include "form/success/signed.html" %}
10 |
11 | {% else %}
12 |
13 | {% include "form/success/unsigned.html" %}
14 |
15 | {% endif %}
16 |
17 | {% endblock %}
18 |
19 | {% block extra_content %}
20 | {% include "components/cta-redirection.html" with cause=donor.cause %}
21 | {% endblock %}
22 |
--------------------------------------------------------------------------------
/backend/templates/v2/layouts/content-and-image-page.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% load i18n static %}
4 |
5 | {% block content %}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | {% block page_content %}{% endblock %}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
22 | {% block page_image %}
23 |
26 | {% endblock page_image %}
27 |
28 |
29 |
30 |
31 |
32 |
33 | {% block extra_content %}{% endblock %}
34 |
35 | {% endblock %}
36 |
--------------------------------------------------------------------------------
/backend/templates/v2/ngo-account/archives/listing.html:
--------------------------------------------------------------------------------
1 | {% extends "ngo-account/base-listing.html" %}
2 |
3 | {% load i18n %}
4 |
5 | {% block table_header %}
6 | {% include "ngo-account/archives/list-header.html" %}
7 | {% endblock %}
8 |
9 | {% block table_body %}
10 | {% include "ngo-account/archives/list-items.html" %}
11 | {% endblock %}
12 |
13 | {% block pagination %}
14 | {% trans "exports" as paginated_type %}
15 | {% include "public/components/pagination.html" with page_obj=page_obj paginated_type=paginated_type %}
16 | {% endblock %}
17 |
--------------------------------------------------------------------------------
/backend/templates/v2/ngo-account/archives/main.html:
--------------------------------------------------------------------------------
1 | {% extends "ngo-account/redirections/base.html" %}
2 |
3 | {% load i18n static %}
4 |
5 | {% block page_title %}
6 | {% trans "Archives history" %}
7 | {% endblock %}
8 |
9 | {% block page_subtitle %}
10 | {% blocktrans trimmed %}
11 | Here you can find the archives of forms that have been exported.
12 | If are collecting signed forms,
13 | the archive containing those forms will also contain the documents for the ANAF dynamic PDF.
14 | {% endblocktrans %}
15 | {% endblock %}
16 |
17 |
18 | {% block content %}
19 |
20 | {% include "ngo-account/archives/listing.html" %}
21 |
22 | {% endblock %}
23 |
--------------------------------------------------------------------------------
/backend/templates/v2/ngo-account/banners/form-ngohub.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% blocktranslate trimmed asvar banner_message %}
4 |
5 | The information below is public for redirectioneaza.ro users.
6 |
7 |
8 | Some fields are disabled because they are automatically taken
9 | from your organization's account in NGO Hub.
10 | You can modify them at any time directly in NGO Hub,
11 | then update them on redirectioneaza.ro
12 | by pressing the "Fetch from NGO Hub" button.
13 |
14 | {% endblocktranslate %}
15 |
16 | {% trans "Go to NGO Hub" as banner_cta_message %}
17 |
18 | {% include "components/info-banner.html" with style="info" banner_cta_url=ngohub_url %}
19 |
--------------------------------------------------------------------------------
/backend/templates/v2/ngo-account/banners/form-notifications-banner.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 | {% trans "Your organization is not receiving notifications for signed forms." as banner_title %}
5 |
6 | {% trans "Receive notifications for each signed form" as button_input_title %}
7 | {% blocktranslate trimmed asvar banner_message %}
8 |
9 | Users can sign the form, but your organization will not receive any notifications.
10 | If you want to receive notifications for each signed form, please enable the
11 | "{{ button_input_title }}"
12 | option and fill in the email address where you want to receive the notifications.
13 |
14 | {% endblocktranslate %}
15 |
16 | {% if active_tab == "presentation" %}
17 | {% url "my-organization:form" as banner_cta_url %}
18 | {% trans "Go to the form page" as banner_cta_message %}
19 | {% endif %}
20 |
21 | {% include "components/info-banner.html" with style="warning" %}
22 |
--------------------------------------------------------------------------------
/backend/templates/v2/ngo-account/base-listing.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {% block table_header %}{% endblock %}
9 |
10 |
11 |
12 |
13 | {% block table_body %}{% endblock %}
14 |
15 |
16 |
17 |
18 |
19 | {% block pagination %}{% endblock %}
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/backend/templates/v2/ngo-account/byof/list-header.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 | {% trans "#" %}
5 |
6 |
7 | {% trans "Date" %}
8 |
9 |
10 | {% trans "IBAN" %}
11 |
12 |
13 | {% trans "Status" %}
14 |
15 |
16 | {% trans "Original file" %}
17 |
18 |
19 | {% trans "No. of Forms" %}
20 |
21 |
22 | {% trans "Download link" %}
23 |
24 |
--------------------------------------------------------------------------------
/backend/templates/v2/ngo-account/byof/listing.html:
--------------------------------------------------------------------------------
1 | {% extends "ngo-account/base-listing.html" %}
2 |
3 | {% load i18n %}
4 |
5 | {% block table_header %}
6 | {% include "ngo-account/byof/list-header.html" %}
7 | {% endblock %}
8 |
9 | {% block table_body %}
10 | {% include "ngo-account/byof/list-items.html" %}
11 | {% endblock %}
12 |
13 | {% block pagination %}
14 | {% trans "exports" as paginated_type %}
15 | {% include "public/components/pagination.html" with page_obj=page_obj paginated_type=paginated_type %}
16 | {% endblock %}
17 |
--------------------------------------------------------------------------------
/backend/templates/v2/ngo-account/byof/main.html:
--------------------------------------------------------------------------------
1 | {% extends "ngo-account/redirections/base.html" %}
2 |
3 | {% load i18n static %}
4 |
5 | {% block page_title %}
6 | {% trans "Generate the XML for the ANAF smart PDF from external data" %}
7 | {% endblock %}
8 |
9 | {% block page_subtitle %}
10 | {% blocktrans trimmed %}
11 | Here you can upload your own table of data in CSV format,
12 | and we will generate the XML for the ANAF smart PDF.
13 | {% endblocktrans %}
14 | {% endblock %}
15 |
16 |
17 | {% block content %}
18 | {% include "ngo-account/byof/upload.html" %}
19 |
20 | {% include "ngo-account/byof/listing.html" %}
21 | {% endblock %}
22 |
--------------------------------------------------------------------------------
/backend/templates/v2/ngo-account/causes/list-header.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 | {% trans "Cause" %}
5 |
6 |
7 | {% trans "IBAN" %}
8 |
9 |
10 | {% trans "Form URL" %}
11 |
12 |
13 | {% trans "Online Signing" %}
14 |
15 |
16 | {% trans "Visibility" %}
17 |
18 |
19 | {% trans "Edited at" %}
20 |
21 |
22 | {% trans "Contributions" %}
23 |
24 |
25 |
26 | {% trans "Actions" %}
27 |
28 |
29 |
--------------------------------------------------------------------------------
/backend/templates/v2/ngo-account/causes/listing.html:
--------------------------------------------------------------------------------
1 | {% extends "ngo-account/base-listing.html" %}
2 |
3 | {% load i18n %}
4 |
5 | {% block table_header %}
6 | {% include "ngo-account/causes/list-header.html" %}
7 | {% endblock %}
8 |
9 | {% block table_body %}
10 | {% include "ngo-account/causes/list-items.html" %}
11 | {% endblock %}
12 |
13 | {% block pagination %}
14 | {% trans "causes" as paginated_type %}
15 | {% include "public/components/pagination.html" with page_obj=page_obj url_params=url_search_query paginated_type=paginated_type %}
16 | {% endblock %}
17 |
--------------------------------------------------------------------------------
/backend/templates/v2/ngo-account/components/tab.html:
--------------------------------------------------------------------------------
1 | {% if is_disabled %}
2 |
3 | {{ tab_title }}
4 |
5 | {% else %}
6 |
15 | {{ tab_title }}
16 |
17 | {% endif %}
18 |
--------------------------------------------------------------------------------
/backend/templates/v2/ngo-account/redirections-downloads/list-header.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 | {% trans "#" %}
5 |
6 |
7 | {% trans "Date generated" %}
8 |
9 |
10 | {% trans "Status" %}
11 |
12 |
13 | {% trans "Filters" %}
14 |
15 |
16 | {% trans "No. of Forms" %}
17 |
18 |
19 | {% trans "Download link" %}
20 |
21 |
--------------------------------------------------------------------------------
/backend/templates/v2/ngo-account/redirections-downloads/listing.html:
--------------------------------------------------------------------------------
1 | {% extends "ngo-account/base-listing.html" %}
2 |
3 | {% load i18n %}
4 |
5 | {% block table_header %}
6 | {% include "ngo-account/redirections-downloads/list-header.html" %}
7 | {% endblock %}
8 |
9 | {% block table_body %}
10 | {% include "ngo-account/redirections-downloads/list-items.html" %}
11 | {% endblock %}
12 |
13 | {% block pagination %}
14 | {% trans "exports" as paginated_type %}
15 | {% include "public/components/pagination.html" with page_obj=page_obj paginated_type=paginated_type %}
16 | {% endblock %}
17 |
--------------------------------------------------------------------------------
/backend/templates/v2/ngo-account/redirections-downloads/main.html:
--------------------------------------------------------------------------------
1 | {% extends "ngo-account/redirections/base.html" %}
2 |
3 | {% load i18n static %}
4 |
5 | {% block page_title %}
6 | {% trans "CSV Downloads history" %}
7 | {% endblock %}
8 |
9 | {% block page_subtitle %}
10 | {% blocktrans trimmed %}
11 | Here you can find the CSVs with the downloaded redirections.
12 | {% endblocktrans %}
13 | {% endblock %}
14 |
15 | {% block content %}
16 |
17 | {% include "ngo-account/redirections-downloads/listing.html" %}
18 |
19 | {% endblock %}
20 |
--------------------------------------------------------------------------------
/backend/templates/v2/ngo-account/redirections/list-header.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 | {% trans "#" %}
5 |
6 |
7 | {% trans "Date filled" %}
8 |
9 |
10 | {% trans "Name" %}
11 |
12 |
13 | {% trans "Locality, County" %}
14 |
15 |
16 | {% trans "E-mail" %}
17 |
18 |
19 | {% trans "Phone number" %}
20 |
21 |
22 | {% trans "2 years" %}
23 |
24 |
25 | {% trans "Status" %}
26 |
27 |
28 |
29 | {% trans "Actions" %}
30 |
31 |
32 |
--------------------------------------------------------------------------------
/backend/templates/v2/ngo-account/redirections/listing.html:
--------------------------------------------------------------------------------
1 | {% extends "ngo-account/base-listing.html" %}
2 |
3 | {% load i18n %}
4 |
5 | {% block table_header %}
6 | {% include "ngo-account/redirections/list-header.html" %}
7 | {% endblock %}
8 |
9 | {% block table_body %}
10 | {% include "ngo-account/redirections/list-items.html" %}
11 | {% endblock %}
12 |
13 | {% block pagination %}
14 | {% trans "donations" as paginated_type %}
15 | {% include "public/components/pagination.html" with page_obj=page_obj url_params=url_search_query paginated_type=paginated_type %}
16 | {% endblock %}
17 |
--------------------------------------------------------------------------------
/backend/templates/v2/public/articles/base.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 |
5 |
6 |
7 | {% block article_content %}{% endblock %}
8 |
9 |
10 |
11 |
12 | {% block article_content_extra %}{% endblock %}
13 |
14 | {% endblock %}
15 |
--------------------------------------------------------------------------------
/backend/templates/v2/public/components/faq-question.html:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
12 |
13 |
14 | {{ question }}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | {{ answer|safe }}
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/backend/templates/v2/public/components/grid-card.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% include "components/logo-or-default.html" with logo=card_logo %}
5 |
6 |
7 |
8 |
9 |
15 |
16 |
{{ card_description|truncatechars:200 }}
17 |
18 |
19 |
--------------------------------------------------------------------------------
/backend/templates/v2/public/components/home/explore.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 |
16 |
17 |
18 | {% for cause in causes %}
19 | {% include "public/components/grid-card.html" with card_logo=cause.display_image card_slug=cause.slug card_title=cause.name card_description=cause.description %}
20 | {% endfor %}
21 |
22 |
23 | {% if not custom_subdomain %}
24 | {% url "organizations" as button_url %}
25 | {% trans "See all organizations" as button_title %}
26 | {% include "components/buttons/link.html" %}
27 | {% endif %}
28 |
29 |
30 |
--------------------------------------------------------------------------------
/backend/templates/v2/public/components/home/hero-stats.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {% for stat in stats %}
5 |
6 |
7 | {{ stat.title }}
8 |
9 |
10 | {{ stat.value }}
11 |
12 |
13 | {% endfor %}
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/backend/templates/v2/public/home.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block content %}
4 |
5 | {% include "public/components/home/hero.html" %}
6 |
7 | {% include "public/components/home/explore.html" %}
8 |
9 | {% if not custom_subdomain %}
10 | {% include "account/snippets/cta-ngohub.html" %}
11 | {% endif %}
12 |
13 | {% endblock %}
14 |
--------------------------------------------------------------------------------
/backend/templates/v2/redirect/header/desktop-item.html:
--------------------------------------------------------------------------------
1 | {% load i18n static %}
2 |
3 | {% if item.url %}
4 |
5 |
12 |
13 | {{ item.title }}
14 |
15 |
16 |
17 | {% elif item.content %}
18 |
19 | {% include "redirect/header/dropdown-item.html" with item=item %}
20 |
21 | {% endif %}
22 |
--------------------------------------------------------------------------------
/backend/templates/v2/redirect/header/main.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 |
3 | {% if header_content %}
4 |
8 |
9 | {% block header_desktop %}
10 | {% include "redirect/header/desktop.html" %}
11 | {% endblock %}
12 |
13 |
14 | {% block header_mobile %}
15 | {% include "redirect/header/mobile.html" %}
16 | {% endblock %}
17 |
18 |
19 | {% endif %}
20 |
--------------------------------------------------------------------------------
/backend/templates/v2/redirect/header/mobile.html:
--------------------------------------------------------------------------------
1 | {% load i18n static %}
2 |
3 |
9 |
10 |
11 |
12 |
13 | {% if header_content.main_menu %}
14 |
15 |
16 |
17 | {% for main_menu_item in header_content.main_menu %}
18 |
19 | {% include "redirect/header/mobile-item.html" with menu_item=main_menu_item %}
20 |
21 | {% endfor %}
22 |
23 |
24 |
25 | {% endif %}
26 |
27 | {% if header_content.auth_menu %}
28 |
29 |
30 |
31 | {% for auth_menu_item in header_content.auth_menu %}
32 |
33 | {% include "redirect/header/mobile-item.html" with menu_item=auth_menu_item %}
34 |
35 | {% endfor %}
36 |
37 |
38 |
39 | {% endif %}
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/backend/templates/v3/admin/announcements/base.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 |
4 |
5 |
6 | {% block announcement_title %}{% endblock %}
7 |
8 |
9 |
10 | {% block announcement_description %}{% endblock %}
11 |
12 |
13 |
14 | {% block announcement_cta %}
15 |
23 | {% endblock %}
24 |
25 |
--------------------------------------------------------------------------------
/backend/templates/v3/admin/announcements/work_in_progress.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/announcements/base.html" %}
2 |
3 | {% load i18n %}
4 |
5 | {% block announcement_title %}
6 | {% trans "Work in progress" %}
7 | {% endblock %}
8 |
9 | {% block announcement_description %}
10 | {% trans "The site is still a work in progress. If you have any feedback, please let us know" %}
11 | {% endblock %}
12 |
13 | {% block announcement_cta_url %}mailto:{{ contact_email }}{% endblock %}
14 |
15 | {% block announcement_cta_text %}{% trans "Contact us" %}{% endblock %}
16 |
--------------------------------------------------------------------------------
/backend/templates/v3/admin/base_admin.html:
--------------------------------------------------------------------------------
1 | {% extends 'admin/base.html' %}
2 |
3 | {% load cache humanize i18n %}
4 |
5 | {% block breadcrumbs %}{{ block.super }}{% endblock %}
6 |
7 | {% block title %}
8 | {% if subtitle %}
9 | {{ subtitle }} |
10 | {% endif %}
11 |
12 | {{ title }} | {{ site_title|default:_('Django site admin') }}
13 | {% endblock %}
14 |
15 | {% block branding %}
16 |
21 | {% endblock %}
22 |
23 | {% block content %}
24 |
25 |
26 |
27 | {% if redirect %}
28 |
29 | {% endif %}
30 |
31 | {% block body %}{% endblock body %}
32 |
33 |
34 |
35 | {% endblock content %}
36 |
--------------------------------------------------------------------------------
/backend/templates/v3/admin/dashboard_components/header_stats.html:
--------------------------------------------------------------------------------
1 | {% load unfold %}
2 |
3 | {% for stat_row in header_stats %}
4 | {% component "unfold/components/flex.html" with class="gap-8 mb-8 flex-col lg:flex-row" %}
5 | {% for stat in stat_row %}
6 | {% component "unfold/components/card.html" with class="lg:w-1/3" label=stat.label footer=stat.footer footer_class=stat.footer_class %}
7 | {% component "unfold/components/title.html" %}
8 | {{ stat.metric }}
9 | {% endcomponent %}
10 |
11 | {% component "unfold/components/text.html" %}
12 | {{ stat.title }}
13 | {% endcomponent %}
14 | {% endcomponent %}
15 | {% endfor %}
16 | {% endcomponent %}
17 | {% endfor %}
18 |
--------------------------------------------------------------------------------
/backend/templates/v3/admin/dashboard_components/monthly_forms_chart.html:
--------------------------------------------------------------------------------
1 | {% load unfold %}
2 |
3 | {% component "unfold/components/card.html" with class="mb-8" title=forms_per_month_chart.title %}
4 | {% component "unfold/components/chart/line.html" with data=forms_per_month_chart.data height=320 %}{% endcomponent %}
5 | {% endcomponent %}
6 |
--------------------------------------------------------------------------------
/backend/templates/v3/admin/dashboard_components/table_stats.html:
--------------------------------------------------------------------------------
1 | {% load unfold %}
2 |
3 | {% component "unfold/components/card.html" with title=table_stats.title %}
4 |
5 | {% component "unfold/components/table.html" with table=table_stats.data card_included=1 striped=1 %}
6 |
7 | {% endcomponent %}
8 | {% endcomponent %}
9 |
--------------------------------------------------------------------------------
/backend/templates/v3/admin/dashboard_components/yearly_stats.html:
--------------------------------------------------------------------------------
1 | {% load unfold %}
2 |
3 | {% for yearly_stat in yearly_stats %}
4 | {% component "unfold/components/title.html" %}
5 | {{ yearly_stat.year }}
6 | {% endcomponent %}
7 |
8 | {% component "unfold/components/flex.html" with class="gap-8 mb-8 flex-col lg:flex-row" %}
9 | {% for stat_card in yearly_stat.stats %}
10 |
11 | {% component "unfold/components/card.html" with class="lg:w-1/3" label=stat_card.label %}
12 | {% component "unfold/components/title.html" %}
13 | {{ stat_card.metric }}
14 | {% endcomponent %}
15 |
16 | {% component "unfold/components/text.html" %}
17 | {{ stat_card.title }}
18 | {% endcomponent %}
19 | {% endcomponent %}
20 | {% endfor %}
21 | {% endcomponent %}
22 | {% endfor %}
23 |
24 |
--------------------------------------------------------------------------------
/backend/templates/v3/admin/forms/action.html:
--------------------------------------------------------------------------------
1 | {% extends "admin/base_site.html" %}
2 |
3 | {% load i18n unfold %}
4 |
5 | {% block breadcrumbs %}{% endblock %}
6 |
7 | {% block extrahead %}
8 | {{ block.super }}
9 |
10 | {{ form.media }}
11 | {% endblock %}
12 |
13 | {% block content %}
14 |
29 | {% endblock %}
30 |
--------------------------------------------------------------------------------
/backend/templates/v3/admin/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'admin/base_admin.html' %}
2 |
3 | {% load unfold i18n %}
4 |
5 | {% block body %}
6 | {% component "unfold/components/container.html" %}
7 |
8 | {% if header_stats %}
9 | {% include "admin/dashboard_components/header_stats.html" with header_stats=header_stats %}
10 | {% endif %}
11 |
12 |
13 | {% if forms_per_month_chart %}
14 | {% include "admin/dashboard_components/monthly_forms_chart.html" with forms_per_month_chart=forms_per_month_chart %}
15 | {% endif %}
16 |
17 |
18 | {% if yearly_stats %}
19 | {% include "admin/dashboard_components/yearly_stats.html" with yearly_stats=yearly_stats %}
20 | {% endif %}
21 |
22 |
23 | {% if table_stats %}
24 | {% include "admin/dashboard_components/table_stats.html" with table_stats=table_stats %}
25 | {% endif %}
26 |
27 | {% endcomponent %}
28 |
29 | {% endblock %}
30 |
--------------------------------------------------------------------------------
/backend/templates/v3/redirect/components/card.html:
--------------------------------------------------------------------------------
1 |
2 | {% if title %}
3 |
4 | {{ title }}
5 |
6 | {% endif %}
7 |
8 |
9 | {{ children }}
10 |
11 | {% if label %}
12 |
13 | {% include "unfold/helpers/label.html" with text=label type="primary" %}
14 |
15 | {% endif %}
16 |
17 | {% if icon %}
18 |
{{ icon }}
19 | {% endif %}
20 |
21 |
22 | {% if footer %}
23 |
26 | {% endif %}
27 |
28 |
--------------------------------------------------------------------------------
/backend/templates/v3/redirect/components/dropdown_navigation.html:
--------------------------------------------------------------------------------
1 | {% if items %}
2 |
8 |
9 | {% for item in items %}
10 |
14 | {{ item.title }}
15 |
16 | {% endfor %}
17 |
18 |
19 |
20 | {% endif %}
21 |
--------------------------------------------------------------------------------
/backend/templates/v3/robots.txt:
--------------------------------------------------------------------------------
1 | User-Agent: *
2 | Disallow: /admin/
3 | Disallow: /cron/
4 | Disallow: /allauth/
5 | Disallow: /api/
6 | Disallow: /contul-meu/
7 | Disallow: /organizatia-mea/
8 | Disallow: /ngo-forms/
9 | Disallow: /donation-forms/
10 |
11 | User-agent: GPTBot
12 | Disallow: /
13 |
--------------------------------------------------------------------------------
/backend/users/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/users/__init__.py
--------------------------------------------------------------------------------
/backend/users/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 | from django.utils.translation import gettext_lazy as _
3 |
4 |
5 | class UsersConfig(AppConfig):
6 | default_auto_field = "django.db.models.BigAutoField"
7 | name = "users"
8 | verbose_name = _("Users")
9 |
--------------------------------------------------------------------------------
/backend/users/context_processors.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.http import HttpRequest
3 |
4 |
5 | def get_admin_properties(request: HttpRequest):
6 | user = request.user
7 | return {
8 | "is_admin": user.has_perm("users.can_view_old_dashboard"),
9 | "is_staff": user.is_staff,
10 | "SOCIALACCOUNT_ENABLED": settings.SOCIALACCOUNT_ENABLED,
11 | "SOCIALACCOUNT_ONLY": settings.SOCIALACCOUNT_ONLY,
12 | }
13 |
--------------------------------------------------------------------------------
/backend/users/management/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/users/management/__init__.py
--------------------------------------------------------------------------------
/backend/users/management/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/users/management/commands/__init__.py
--------------------------------------------------------------------------------
/backend/users/management/commands/_private/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/users/management/commands/_private/__init__.py
--------------------------------------------------------------------------------
/backend/users/management/commands/seed_superuser.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from django.conf import settings
4 | from django.contrib.auth.models import Group
5 |
6 | from users.groups_management import MAIN_ADMIN
7 | from ._private.seed_user import CommonCreateUserCommand
8 |
9 | logger = logging.getLogger(__name__)
10 |
11 |
12 | class Command(CommonCreateUserCommand):
13 | help = "Command to create a superuser"
14 |
15 | def handle(self, *args, **kwargs):
16 | kwargs["last_name"] = "Super"
17 | kwargs["first_name"] = "User"
18 |
19 | super_admin = self._get_or_create_user(
20 | new_email=settings.DJANGO_ADMIN_EMAIL,
21 | password=settings.DJANGO_ADMIN_PASSWORD,
22 | is_superuser=True,
23 | is_staff=True,
24 | first_name=kwargs.get("first_name", ""),
25 | last_name=kwargs.get("last_name", ""),
26 | )
27 | logger.info("Super admin created successfully")
28 |
29 | admin_group = Group.objects.get(name=MAIN_ADMIN)
30 | super_admin.groups.add(admin_group)
31 |
32 | return 0
33 |
--------------------------------------------------------------------------------
/backend/users/migrations/0002_user_date_created_user_date_updated_and_more.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-02-15 20:39
2 |
3 | from django.db import migrations, models
4 | import django.utils.timezone
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("users", "0001_initial"),
11 | ]
12 |
13 | operations = [
14 | migrations.AddField(
15 | model_name="user",
16 | name="date_created",
17 | field=models.DateTimeField(
18 | auto_now_add=True, db_index=True, default=django.utils.timezone.now, verbose_name="date created"
19 | ),
20 | preserve_default=False,
21 | ),
22 | migrations.AddField(
23 | model_name="user",
24 | name="date_updated",
25 | field=models.DateTimeField(auto_now=True, db_index=True, verbose_name="date updated"),
26 | ),
27 | migrations.AddField(
28 | model_name="user",
29 | name="old_password",
30 | field=models.CharField(blank=True, max_length=128, null=True, verbose_name="old password"),
31 | ),
32 | ]
33 |
--------------------------------------------------------------------------------
/backend/users/migrations/0003_alter_user_date_created.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-02-20 10:26
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("users", "0002_user_date_created_user_date_updated_and_more"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="user",
15 | name="date_created",
16 | field=models.DateTimeField(verbose_name="date created"),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/users/migrations/0004_alter_user_date_created.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-02-21 14:58
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("users", "0003_alter_user_date_created"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="user",
15 | name="date_created",
16 | field=models.DateTimeField(auto_now_add=True, db_index=True, verbose_name="date created"),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/users/migrations/0005_alter_user_date_created.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-02-23 09:21
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("users", "0004_alter_user_date_created"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="user",
15 | name="date_created",
16 | field=models.DateTimeField(db_index=True, verbose_name="date created"),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/users/migrations/0006_alter_user_date_created.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-02-23 09:22
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("users", "0005_alter_user_date_created"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="user",
15 | name="date_created",
16 | field=models.DateTimeField(auto_now_add=True, db_index=True, verbose_name="date created"),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/users/migrations/0007_alter_user_options.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.10 on 2024-02-28 09:49
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("users", "0006_alter_user_date_created"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterModelOptions(
14 | name="user",
15 | options={"permissions": (("can_view_old_dashboard", "Can view the old dashboard"),)},
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/backend/users/migrations/0008_groupproxy.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.1 on 2024-09-12 10:08
2 |
3 | import django.contrib.auth.models
4 | from django.db import migrations
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("auth", "0012_alter_user_first_name_max_length"),
11 | ("users", "0007_alter_user_options"),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name="GroupProxy",
17 | fields=[],
18 | options={
19 | "verbose_name": "Group",
20 | "verbose_name_plural": "Groups",
21 | "proxy": True,
22 | "indexes": [],
23 | "constraints": [],
24 | },
25 | bases=("auth.group",),
26 | managers=[
27 | ("objects", django.contrib.auth.models.GroupManager()),
28 | ],
29 | ),
30 | ]
31 |
--------------------------------------------------------------------------------
/backend/users/migrations/0009_user_is_ngohub_user.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.2.13 on 2024-08-09 09:58
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("users", "0008_groupproxy"),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name="user",
15 | name="is_ngohub_user",
16 | field=models.BooleanField(default=False),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/users/migrations/0010_user_partner.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.5 on 2025-02-11 13:59
2 |
3 | import django.db.models.deletion
4 | from django.db import migrations, models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("partners", "0008_alter_partner_display_ordering"),
11 | ("users", "0009_user_is_ngohub_user"),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name="user",
17 | name="partner",
18 | field=models.ForeignKey(
19 | blank=True,
20 | null=True,
21 | on_delete=django.db.models.deletion.SET_NULL,
22 | related_name="users",
23 | to="partners.partner",
24 | verbose_name="Partner",
25 | ),
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/backend/users/migrations/0011_alter_user_is_ngohub_user.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 5.1.6 on 2025-02-19 08:52
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ("users", "0010_user_partner"),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name="user",
15 | name="is_ngohub_user",
16 | field=models.BooleanField(db_index=True, default=False, verbose_name="is ngohub user"),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/users/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/backend/users/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/users/views.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 |
--------------------------------------------------------------------------------
/docker-compose.dbless.yml:
--------------------------------------------------------------------------------
1 | name: redirect_dev
2 |
3 | services:
4 |
5 | webapp_local_psql:
6 | extends:
7 | file: docker-compose.base.yml
8 | service: redirect_dev_base
9 | environment:
10 | - "DATABASE_HOST=127.0.0.1"
11 | - "DATABASE_PORT=5432"
12 |
--------------------------------------------------------------------------------
/docker-compose.prod.yml:
--------------------------------------------------------------------------------
1 | name: redirect_prod
2 |
3 | services:
4 |
5 | webapp:
6 | extends:
7 | file: docker-compose.base.yml
8 | service: redirect_base
9 | container_name: redirect_prod
10 | build:
11 | context: .
12 | dockerfile: ./docker/dockerfiles/Dockerfile
13 | volumes:
14 | - ./backend/media:/var/www/redirect/backend/media
15 | environment:
16 | - "ENVIRONMENT=production"
17 | ports:
18 | - "${WEBAPP_PORT:-8080}:80"
19 | depends_on:
20 | - db
21 |
22 | db:
23 | extends:
24 | file: docker-compose.base.yml
25 | service: db_base_psql
26 | container_name: redirect_psql
27 |
28 | volumes:
29 | redirect_psql:
30 | node_modules:
31 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | name: redirect_dev
2 |
3 | services:
4 |
5 | webapp_psql:
6 | extends:
7 | file: docker-compose.base.yml
8 | service: redirect_dev_base
9 | environment:
10 | - "DATABASE_HOST=redirect_db_dev"
11 | - "DATABASE_PORT=5432"
12 | depends_on:
13 | - db_psql_dev
14 |
15 | db_psql_dev:
16 | extends:
17 | file: docker-compose.base.yml
18 | service: db_base_psql
19 | volumes:
20 | - ./docker/init-psql:/docker-entrypoint-initdb.d
21 |
22 | volumes:
23 | redirect_psql:
24 | node_modules:
25 |
--------------------------------------------------------------------------------
/docker/nginx/nginx.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen [::]:80 default_server;
3 | listen 80 default_server;
4 | server_name _;
5 |
6 | client_max_body_size 200M;
7 |
8 | location / {
9 | # proxy_set_header X-Forwarded-Proto https;
10 | proxy_set_header X-Url-Scheme $scheme;
11 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
12 | proxy_set_header Host $http_host;
13 | proxy_redirect off;
14 | proxy_pass http://unix:/run/gunicorn.sock;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/docker/s6-rc.d/backend/dependencies:
--------------------------------------------------------------------------------
1 | init
2 |
--------------------------------------------------------------------------------
/docker/s6-rc.d/backend/type:
--------------------------------------------------------------------------------
1 | longrun
2 |
--------------------------------------------------------------------------------
/docker/s6-rc.d/cron/dependencies:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/docker/s6-rc.d/cron/dependencies
--------------------------------------------------------------------------------
/docker/s6-rc.d/cron/run:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | touch /etc/crontab /etc/cron.*/*
4 |
5 |
6 | # Check if we have cron (Debian) or crond (Alpine)
7 |
8 | if ! type "cron" > /dev/null; then
9 | crond -f
10 | else
11 | cron -f
12 | fi
13 |
--------------------------------------------------------------------------------
/docker/s6-rc.d/cron/type:
--------------------------------------------------------------------------------
1 | longrun
2 |
--------------------------------------------------------------------------------
/docker/s6-rc.d/frontend_dev/dependencies:
--------------------------------------------------------------------------------
1 | init
2 |
--------------------------------------------------------------------------------
/docker/s6-rc.d/frontend_dev/run:
--------------------------------------------------------------------------------
1 | #!/command/with-contenv sh
2 | set -e
3 |
4 | cd /var/www/redirect/backend/ || exit 1
5 |
6 | [ "${ENVIRONMENT}" = "development" ] && npm run dev
7 |
--------------------------------------------------------------------------------
/docker/s6-rc.d/frontend_dev/type:
--------------------------------------------------------------------------------
1 | longrun
2 |
--------------------------------------------------------------------------------
/docker/s6-rc.d/init/type:
--------------------------------------------------------------------------------
1 | oneshot
2 |
--------------------------------------------------------------------------------
/docker/s6-rc.d/init/up:
--------------------------------------------------------------------------------
1 | /etc/s6-overlay/s6-rc.d/init/init.sh
2 |
--------------------------------------------------------------------------------
/docker/s6-rc.d/nginx/dependencies:
--------------------------------------------------------------------------------
1 | backend
2 |
--------------------------------------------------------------------------------
/docker/s6-rc.d/nginx/run:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | set -e
4 |
5 | nginx -g "daemon off;"
6 |
--------------------------------------------------------------------------------
/docker/s6-rc.d/nginx/type:
--------------------------------------------------------------------------------
1 | longrun
2 |
--------------------------------------------------------------------------------
/docker/s6-rc.d/qcluster/dependencies:
--------------------------------------------------------------------------------
1 | init
2 |
--------------------------------------------------------------------------------
/docker/s6-rc.d/qcluster/run:
--------------------------------------------------------------------------------
1 | #!/command/with-contenv sh
2 |
3 | set -e
4 |
5 | cd /var/www/redirect/backend
6 |
7 | if [ "${ENVIRONMENT}" = "debug" ]; then
8 | echo "*********************************************"
9 | echo "*** Starting the qcluster in DEBUG mode ***"
10 | echo "*********************************************"
11 | python3 -Xfrozen_modules=off -m debugpy --listen 0.0.0.0:5677 manage.py qcluster
12 | elif [ "${ENVIRONMENT}" = "debugwait" ]; then
13 | echo "*********************************************"
14 | echo "*** Starting the qcluster in DEBUG mode ***"
15 | echo "*** Waiting for debugger connection... ***"
16 | echo "*********************************************"
17 | python3 -Xfrozen_modules=off -m debugpy --wait-for-client --listen 0.0.0.0:5677 manage.py qcluster
18 | else
19 | echo "Starting the qcluster in production mode"
20 |
21 | python3 manage.py qcluster
22 | fi
23 |
24 |
--------------------------------------------------------------------------------
/docker/s6-rc.d/qcluster/type:
--------------------------------------------------------------------------------
1 | longrun
2 |
--------------------------------------------------------------------------------
/docker/s6-rc.d/user/contents.d/backend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/docker/s6-rc.d/user/contents.d/backend
--------------------------------------------------------------------------------
/docker/s6-rc.d/user/contents.d/frontend_dev:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/docker/s6-rc.d/user/contents.d/frontend_dev
--------------------------------------------------------------------------------
/docker/s6-rc.d/user/contents.d/init:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/docker/s6-rc.d/user/contents.d/init
--------------------------------------------------------------------------------
/docker/s6-rc.d/user/contents.d/nginx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/docker/s6-rc.d/user/contents.d/nginx
--------------------------------------------------------------------------------
/docker/s6-rc.d/user/contents.d/qcluster:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/code4romania/redirectioneaza/a917780e65f4923798b75a12abc1b1d93bdda82e/docker/s6-rc.d/user/contents.d/qcluster
--------------------------------------------------------------------------------
/terraform/.gitignore:
--------------------------------------------------------------------------------
1 | # Local .terraform directories
2 | **/.terraform/*
3 |
4 | # .tfstate files
5 | *.tfstate
6 | *.tfstate.*
7 |
8 | # Crash log files
9 | crash.log
10 | crash.*.log
11 |
12 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most
13 | # .tfvars files are managed as part of configuration and so should be included in
14 | # version control.
15 | *.tfvars
16 | *.tfvars.json
17 |
18 | # Ignore override files as they are usually used to override resources locally and so
19 | # are not checked in
20 | override.tf
21 | override.tf.json
22 | *_override.tf
23 | *_override.tf.json
24 |
25 | # Include override files you do wish to add to version control using negated pattern
26 | #
27 | # !example_override.tf
28 |
29 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
30 | *tfplan*
31 |
32 | # Ignore CLI configuration files
33 | .terraformrc
34 | terraform.rc
35 |
36 | .infracost
37 |
--------------------------------------------------------------------------------
/terraform/README.md:
--------------------------------------------------------------------------------
1 | # terraform instructions
2 |
3 | 1. Go to [ECS Account settings](https://eu-central-1.console.aws.amazon.com/ecs/v2/account-settings?region=eu-central-1) for the region you're deploying in and make sure AWSVPC Trunking is turned on.
4 |
5 | 2. Replace the backend configuration in `providers.tf` with your own bucket name, key and region.
6 |
7 | 3. Configure the required variables from `variables.tf`. If you've ever created a cluster in this AWS account, make sure to set the `create_iam_service_linked_role` to false.
8 |
9 | Example configuration:
10 | ```
11 | route_53_zone_id = "Z1Q2W3E4R5T6Y7"
12 | domain_name = "test.example.com"
13 | bastion_public_key = "ssh-ed25519 AAAAC3NzaC1lZD..."
14 | seed_admin_email = "test@example.com"
15 | seed_admin_password = "super_secure_random_password"
16 | create_iam_service_linked_role = false
17 | ```
18 |
19 | 4. Run `terraform apply` and wait for the cluster to be created.
20 |
21 | 5. Once everything is done, you should be able to login at `https://[domain_name]/staff/users/login/` using the `seed_admin_email` and `seed_admin_password` you've configured.
22 |
--------------------------------------------------------------------------------
/terraform/acm.tf:
--------------------------------------------------------------------------------
1 | resource "aws_acm_certificate" "main" {
2 | provider = aws.acm
3 | validation_method = "DNS"
4 | domain_name = var.domain_name
5 | subject_alternative_names = [
6 | "*.${var.domain_name}",
7 | ]
8 |
9 | lifecycle {
10 | create_before_destroy = true
11 | }
12 | }
13 |
14 | resource "aws_route53_record" "acm_validation" {
15 | for_each = {
16 | for dvo in aws_acm_certificate.main.domain_validation_options : dvo.domain_name => {
17 | name = dvo.resource_record_name
18 | record = dvo.resource_record_value
19 | type = dvo.resource_record_type
20 | }
21 | }
22 |
23 | allow_overwrite = true
24 | name = each.value.name
25 | records = [each.value.record]
26 | ttl = 60
27 | type = each.value.type
28 | zone_id = data.aws_route53_zone.main.zone_id
29 | }
30 |
--------------------------------------------------------------------------------
/terraform/data.tf:
--------------------------------------------------------------------------------
1 | data "aws_availability_zones" "current" {}
2 |
3 | data "aws_region" "current" {}
4 |
5 | data "aws_route53_zone" "main" {
6 | zone_id = var.route_53_zone_id
7 | }
8 |
9 | data "aws_ecr_repository" "this" {
10 | name = "redirectioneaza"
11 | }
12 |
--------------------------------------------------------------------------------
/terraform/functions/www-redirect.js:
--------------------------------------------------------------------------------
1 | function handler(event) {
2 | if (!event.request.headers.hasOwnProperty('host')) {
3 | return {
4 | statusCode: 404,
5 | statusDescription: 'Not Found',
6 | }
7 | }
8 |
9 | if (event.request.headers.host.value.startsWith('www.')) {
10 | return {
11 | statusCode: 301,
12 | statusDescription: 'Moved Permanently',
13 | headers: {
14 | location: {
15 | value: `https://${event.request.headers.host.value.replace('www.', '')}${event.request.uri}`,
16 | },
17 | },
18 | }
19 | }
20 |
21 | return event.request
22 | }
23 |
--------------------------------------------------------------------------------
/terraform/locals.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | namespace = "redirectioneaza-${var.env}"
3 | image_repo = "code4romania/redirectioneaza"
4 | image_tag = "3.4.12"
5 |
6 | availability_zone = data.aws_availability_zones.current.names[0]
7 |
8 | domains = [
9 | var.domain_name,
10 | "*.${var.domain_name}",
11 | ]
12 |
13 | ecs = {
14 | instance_types = {
15 | "m5.large" = ""
16 | "m5a.large" = ""
17 | }
18 | }
19 |
20 | db = {
21 | name = "redirectioneaza"
22 | instance_class = var.env == "production" ? "db.t4g.medium" : "db.t4g.micro"
23 | }
24 |
25 | networking = {
26 | cidr_block = "10.0.0.0/16"
27 |
28 | public_subnets = [
29 | "10.0.1.0/24",
30 | "10.0.2.0/24",
31 | "10.0.3.0/24"
32 | ]
33 |
34 | private_subnets = [
35 | "10.0.4.0/24",
36 | "10.0.5.0/24",
37 | "10.0.6.0/24"
38 | ]
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/terraform/modules/ecs-cluster/cloudwatch.tf:
--------------------------------------------------------------------------------
1 | ### CloudWatch Log group for container logs
2 | resource "aws_cloudwatch_log_group" "ecs" {
3 | name = "${var.name}-container-logs"
4 | retention_in_days = var.ecs_cloudwatch_log_retention
5 |
6 | tags = var.tags
7 | }
8 |
9 | resource "aws_cloudwatch_log_group" "userdata" {
10 | name = "${var.name}-ecs-nodes-userdata"
11 | retention_in_days = var.userdata_cloudwatch_log_retention
12 |
13 | tags = var.tags
14 | }
15 |
--------------------------------------------------------------------------------
/terraform/modules/ecs-cluster/data.tf:
--------------------------------------------------------------------------------
1 | ### Latest Amazon Linux ECS Optimized AMI
2 | data "aws_ami" "this" {
3 | most_recent = true
4 |
5 | owners = ["amazon"]
6 |
7 | filter {
8 | name = "name"
9 | values = ["amzn2-ami-ecs-kernel-5.10-hvm-*-ebs"]
10 | }
11 |
12 | filter {
13 | name = "owner-alias"
14 | values = ["amazon"]
15 | }
16 |
17 | filter {
18 | name = "architecture"
19 | values = ["x86_64"]
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/terraform/modules/ecs-cluster/iam.tf:
--------------------------------------------------------------------------------
1 | ### IAM Resources
2 | data "aws_iam_policy_document" "ecs" {
3 | statement {
4 | actions = ["sts:AssumeRole"]
5 |
6 | principals {
7 | type = "Service"
8 | identifiers = ["ec2.amazonaws.com"]
9 | }
10 | }
11 | }
12 |
13 | resource "aws_iam_role" "ecs" {
14 | name = "${var.name}-ecs-instance"
15 | path = var.iam_path
16 | assume_role_policy = data.aws_iam_policy_document.ecs.json
17 | managed_policy_arns = [
18 | "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role",
19 | "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore",
20 | "arn:aws:iam::aws:policy/CloudWatchAgentAdminPolicy"
21 | ]
22 |
23 | tags = var.tags
24 | }
25 |
26 | resource "aws_iam_instance_profile" "ecs" {
27 | name = "${var.name}-ecs-instance"
28 | role = aws_iam_role.ecs.name
29 | }
30 |
--------------------------------------------------------------------------------
/terraform/modules/ecs-cluster/locals.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | tags = concat(
3 | [
4 | {
5 | key = "Name"
6 | value = "${var.name}-ecs-node"
7 | propagate_at_launch = true
8 | },
9 | {
10 | key = "AmazonECSManaged"
11 | value = "true"
12 | propagate_at_launch = true
13 | }
14 | ],
15 | var.asg_tags,
16 | local.tags_asg_format,
17 | )
18 |
19 | tags_asg_format = null_resource.tags_as_list_of_maps.*.triggers
20 | }
21 |
22 | resource "null_resource" "tags_as_list_of_maps" {
23 | count = length(keys(var.tags))
24 |
25 | triggers = {
26 | "key" = keys(var.tags)[count.index]
27 | "value" = values(var.tags)[count.index]
28 | "propagate_at_launch" = "true"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/terraform/modules/ecs-cluster/outputs.tf:
--------------------------------------------------------------------------------
1 | output "cluster_id" {
2 | value = aws_ecs_cluster.ecs.id
3 | }
4 |
5 | output "cluster_name" {
6 | value = aws_ecs_cluster.ecs.name
7 | }
8 |
9 | output "log_group_name" {
10 | value = aws_cloudwatch_log_group.ecs.name
11 | }
12 |
13 | output "asg_arn" {
14 | value = aws_autoscaling_group.ecs.arn
15 | }
16 |
17 | output "asg_name" {
18 | value = aws_autoscaling_group.ecs.name
19 | }
20 |
21 | output "service_discovery_namespace_id" {
22 | value = aws_service_discovery_private_dns_namespace.ecs.id
23 | }
24 |
--------------------------------------------------------------------------------
/terraform/modules/ecs-cluster/service_discovery.tf:
--------------------------------------------------------------------------------
1 | resource "aws_service_discovery_private_dns_namespace" "ecs" {
2 | name = var.service_discovery_domain
3 | vpc = var.vpc_id
4 | }
5 |
--------------------------------------------------------------------------------
/terraform/modules/ecs-service/autoscaling.tf:
--------------------------------------------------------------------------------
1 | resource "aws_appautoscaling_target" "this" {
2 | count = local.fixed_capacity ? 0 : 1
3 |
4 | service_namespace = "ecs"
5 | resource_id = "service/${data.aws_ecs_cluster.this.cluster_name}/${aws_ecs_service.this.name}"
6 | scalable_dimension = "ecs:service:DesiredCount"
7 | min_capacity = var.min_capacity
8 | max_capacity = var.max_capacity
9 | }
10 |
11 | resource "aws_appautoscaling_policy" "this" {
12 | count = local.fixed_capacity ? 0 : 1
13 |
14 | name = "${var.name}-target-scaling"
15 | resource_id = aws_appautoscaling_target.this.0.resource_id
16 | scalable_dimension = aws_appautoscaling_target.this.0.scalable_dimension
17 | service_namespace = aws_appautoscaling_target.this.0.service_namespace
18 | policy_type = "TargetTrackingScaling"
19 |
20 | target_tracking_scaling_policy_configuration {
21 | target_value = var.target_value
22 | scale_in_cooldown = var.scale_in_cooldown
23 | scale_out_cooldown = var.scale_out_cooldown
24 |
25 | predefined_metric_specification {
26 | predefined_metric_type = var.predefined_metric_type
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/terraform/modules/ecs-service/data.tf:
--------------------------------------------------------------------------------
1 | data "aws_region" "current" {}
2 |
3 | data "aws_ecs_cluster" "this" {
4 | cluster_name = var.cluster_name
5 | }
6 |
--------------------------------------------------------------------------------
/terraform/modules/ecs-service/lb.tf:
--------------------------------------------------------------------------------
1 | resource "aws_lb_target_group" "this" {
2 | count = var.use_load_balancer ? 1 : 0
3 |
4 | name = var.name
5 | port = var.container_port
6 | protocol = "HTTP"
7 | vpc_id = var.lb_vpc_id
8 | target_type = "ip"
9 |
10 | health_check {
11 | enabled = var.lb_health_check_enabled
12 | healthy_threshold = var.lb_healthy_threshold
13 | interval = var.lb_interval
14 | protocol = var.lb_protocol
15 | matcher = var.lb_matcher
16 | timeout = var.lb_timeout
17 | path = var.lb_path
18 | unhealthy_threshold = var.lb_unhealthy_threshold
19 | }
20 | }
21 |
22 | resource "aws_lb_listener_rule" "routing" {
23 | count = var.use_load_balancer ? 1 : 0
24 |
25 | listener_arn = var.lb_listener_arn
26 |
27 | action {
28 | type = "forward"
29 | target_group_arn = aws_lb_target_group.this.0.arn
30 | }
31 |
32 | condition {
33 | host_header {
34 | values = var.lb_hosts
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/terraform/modules/ecs-service/outputs.tf:
--------------------------------------------------------------------------------
1 | output "task_arn" {
2 | value = aws_ecs_task_definition.this.arn
3 | }
4 |
5 | output "service_name" {
6 | value = aws_ecs_service.this.name
7 | }
8 |
9 | output "service_arn" {
10 | value = aws_ecs_service.this.id
11 | }
12 |
--------------------------------------------------------------------------------
/terraform/modules/ecs-service/route53.tf:
--------------------------------------------------------------------------------
1 | # # A record
2 | # resource "aws_route53_record" "ipv4" {
3 | # count = length(var.lb_hosts)
4 |
5 | # zone_id = var.lb_domain_zone_id
6 | # name = var.lb_hosts[count.index]
7 | # type = "A"
8 |
9 | # alias {
10 | # name = var.lb_dns_name
11 | # zone_id = var.lb_zone_id
12 | # evaluate_target_health = true
13 | # }
14 | # }
15 |
16 | # # AAAA record
17 | # resource "aws_route53_record" "ipv6" {
18 | # count = length(var.lb_hosts)
19 |
20 | # zone_id = var.lb_domain_zone_id
21 | # name = var.lb_hosts[count.index]
22 | # type = "AAAA"
23 |
24 | # alias {
25 | # name = var.lb_dns_name
26 | # zone_id = var.lb_zone_id
27 | # evaluate_target_health = true
28 | # }
29 | # }
30 |
--------------------------------------------------------------------------------
/terraform/modules/ecs-service/service_discovery.tf:
--------------------------------------------------------------------------------
1 | resource "aws_service_discovery_service" "this" {
2 | name = var.name
3 |
4 | dynamic "dns_config" {
5 | for_each = var.service_discovery_namespace_id == null ? [] : [1]
6 |
7 | content {
8 | namespace_id = var.service_discovery_namespace_id
9 | routing_policy = "MULTIVALUE"
10 |
11 | dns_records {
12 | ttl = 10
13 | type = "A"
14 | }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/terraform/modules/s3/outputs.tf:
--------------------------------------------------------------------------------
1 | output "id" {
2 | value = aws_s3_bucket.this.id
3 | }
4 |
5 | output "arn" {
6 | value = aws_s3_bucket.this.arn
7 | }
8 |
9 | output "bucket" {
10 | value = aws_s3_bucket.this.bucket
11 | }
12 |
13 | output "bucket_regional_domain_name" {
14 | value = aws_s3_bucket.this.bucket_regional_domain_name
15 | }
16 |
--------------------------------------------------------------------------------
/terraform/modules/s3/random.tf:
--------------------------------------------------------------------------------
1 | resource "random_string" "suffix" {
2 | length = 4
3 | special = false
4 | upper = false
5 | numeric = false
6 | }
7 |
--------------------------------------------------------------------------------
/terraform/modules/s3/variables.tf:
--------------------------------------------------------------------------------
1 | variable "name" {
2 | description = "Name to be used throughout the resources"
3 | type = string
4 | }
5 |
6 | variable "enable_versioning" {
7 | description = "Whether Amazon S3 should enable versioning for this bucket."
8 | type = bool
9 | default = false
10 | }
11 |
12 | variable "block_public_acls" {
13 | description = "Whether Amazon S3 should block public ACLs for this bucket."
14 | type = bool
15 | default = true
16 | }
17 |
18 | variable "block_public_policy" {
19 | description = "Whether Amazon S3 should block public bucket policies for this bucket."
20 | type = bool
21 | default = true
22 | }
23 |
24 | variable "ignore_public_acls" {
25 | description = "Whether Amazon S3 should ignore public ACLs for this bucket."
26 | type = bool
27 | default = true
28 | }
29 |
30 | variable "restrict_public_buckets" {
31 | description = "Whether Amazon S3 should restrict public bucket policies for this bucket."
32 | type = bool
33 | default = true
34 | }
35 |
36 | variable "policy" {
37 | description = "(Optional) A valid bucket policy JSON document."
38 | type = string
39 | }
40 |
--------------------------------------------------------------------------------
/terraform/networking_eips.tf:
--------------------------------------------------------------------------------
1 | resource "aws_eip" "nat_gateway" {
2 | domain = "vpc"
3 | tags = {
4 | Name = "${local.namespace}-nat-gateway"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/terraform/networking_gateways.tf:
--------------------------------------------------------------------------------
1 | resource "aws_internet_gateway" "main" {
2 | vpc_id = aws_vpc.main.id
3 |
4 | tags = {
5 | Name = "${local.namespace}-internet-gateway"
6 | }
7 | }
8 |
9 | resource "aws_nat_gateway" "nat_gateway" {
10 | allocation_id = aws_eip.nat_gateway.id
11 | subnet_id = aws_subnet.public.0.id
12 |
13 | tags = {
14 | Name = "${local.namespace}-nat-gateway"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/terraform/networking_routing.tf:
--------------------------------------------------------------------------------
1 | # Public
2 | resource "aws_route_table" "public" {
3 | vpc_id = aws_vpc.main.id
4 |
5 | route {
6 | cidr_block = "0.0.0.0/0"
7 | gateway_id = aws_internet_gateway.main.id
8 | }
9 |
10 | tags = {
11 | Name = "${local.namespace}-public"
12 | }
13 | }
14 |
15 | resource "aws_route_table_association" "public" {
16 | count = length(data.aws_availability_zones.current.names)
17 | subnet_id = element(aws_subnet.public.*.id, count.index)
18 | route_table_id = aws_route_table.public.id
19 | }
20 |
21 | # Private
22 | resource "aws_route_table" "private" {
23 | vpc_id = aws_vpc.main.id
24 |
25 | route {
26 | cidr_block = "0.0.0.0/0"
27 | nat_gateway_id = aws_nat_gateway.nat_gateway.id
28 | }
29 |
30 | tags = {
31 | Name = "${local.namespace}-private"
32 | }
33 | }
34 |
35 | resource "aws_route_table_association" "private" {
36 | count = length(data.aws_availability_zones.current.names)
37 | subnet_id = element(aws_subnet.private.*.id, count.index)
38 | route_table_id = aws_route_table.private.id
39 | }
40 |
--------------------------------------------------------------------------------
/terraform/networking_vpc.tf:
--------------------------------------------------------------------------------
1 | resource "aws_vpc" "main" {
2 | cidr_block = local.networking.cidr_block
3 | enable_dns_hostnames = true
4 | enable_dns_support = true
5 |
6 | tags = {
7 | Name = "${local.namespace}-vpc"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/terraform/outputs.tf:
--------------------------------------------------------------------------------
1 | output "bastion_ip" {
2 | value = aws_instance.bastion.public_ip
3 | }
4 |
--------------------------------------------------------------------------------
/terraform/providers.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = "~> 1.5"
3 |
4 | required_providers {
5 | aws = {
6 | source = "hashicorp/aws"
7 | version = "~> 5.16"
8 | }
9 | }
10 |
11 | cloud {
12 | organization = "code4romania"
13 |
14 | workspaces {
15 | name = "redirectioneaza-production"
16 | project = "redirectioneaza"
17 | }
18 | }
19 | }
20 |
21 | provider "aws" {
22 | region = var.region
23 |
24 | default_tags {
25 | tags = {
26 | app = "redirectioneaza"
27 | env = var.env
28 | }
29 | }
30 | }
31 |
32 | provider "aws" {
33 | alias = "acm"
34 | region = "us-east-1"
35 |
36 | default_tags {
37 | tags = {
38 | app = "redirectioneaza"
39 | env = var.env
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/terraform/random.tf:
--------------------------------------------------------------------------------
1 | resource "random_string" "secrets_suffix" {
2 | length = 8
3 | special = false
4 | upper = false
5 | numeric = false
6 |
7 | lifecycle {
8 | ignore_changes = [
9 | length,
10 | special,
11 | upper,
12 | numeric,
13 | ]
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/terraform/route53.tf:
--------------------------------------------------------------------------------
1 | # A record
2 | resource "aws_route53_record" "ipv4" {
3 | count = length(local.domains)
4 |
5 | zone_id = data.aws_route53_zone.main.zone_id
6 | name = local.domains[count.index]
7 | type = "A"
8 |
9 | alias {
10 | name = aws_cloudfront_distribution.main.domain_name
11 | zone_id = aws_cloudfront_distribution.main.hosted_zone_id
12 | evaluate_target_health = true
13 | }
14 | }
15 |
16 | # AAAA record
17 | resource "aws_route53_record" "ipv6" {
18 | count = length(local.domains)
19 |
20 | zone_id = data.aws_route53_zone.main.zone_id
21 | name = local.domains[count.index]
22 | type = "AAAA"
23 |
24 | alias {
25 | name = aws_cloudfront_distribution.main.domain_name
26 | zone_id = aws_cloudfront_distribution.main.hosted_zone_id
27 | evaluate_target_health = true
28 | }
29 | }
30 |
--------------------------------------------------------------------------------