├── .circleci └── config.yml ├── .coveragerc ├── .dockerignore ├── .env ├── .github └── ISSUE_TEMPLATE │ ├── ---bug-report.md │ └── ---feature-request.md ├── .gitignore ├── .isort.cfg ├── Dockerfile ├── Dockerfile.test ├── Makefile ├── README.md ├── apps ├── __init__.py ├── admins │ ├── __init__.py │ ├── exceptions.py │ ├── fixtures │ │ ├── __init__.py │ │ └── core_data.json │ ├── helpers.py │ ├── managers.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_created_at.py │ │ ├── 0003_merge.py │ │ ├── 0004_admin_customer_id.py │ │ ├── 0005_auto_20150318_1059.py │ │ ├── 0006_remove_admin_customer_id.py │ │ ├── 0007_auto_20150409_1234.py │ │ ├── 0008_auto_20150429_1512.py │ │ ├── 0009_role.py │ │ ├── 0010_role_as_fk.py │ │ ├── 0011_length.py │ │ ├── 0012_last_login.py │ │ ├── 0013_remove_adminsocialprofile_email.py │ │ ├── 0014_auto_20151110_1458.py │ │ ├── 0015_auto_20151116_1124.py │ │ ├── 0016_email_trgm_index.py │ │ ├── 0017_add_last_access.py │ │ ├── 0018_admin_metadata.py │ │ ├── 0019_admin_is_trusted.py │ │ └── __init__.py │ ├── mixins.py │ ├── models.py │ ├── permissions.py │ ├── serializers.py │ ├── sql │ │ ├── __init__.py │ │ └── admin.sql │ ├── tasks.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_admin_api.py │ │ ├── test_remove_unused.py │ │ ├── test_social_login.py │ │ └── test_tasks.py │ ├── v1 │ │ ├── __init__.py │ │ └── urls.py │ └── views.py ├── analytics │ ├── __init__.py │ ├── appconfig.py │ ├── client.py │ ├── mixins.py │ ├── models.py │ ├── signal_handlers.py │ ├── tasks.py │ └── tests │ │ ├── __init__.py │ │ ├── test_admin.py │ │ ├── test_billing.py │ │ ├── test_invitations.py │ │ └── test_summary.py ├── apikeys │ ├── __init__.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_apikey_instance.py │ │ ├── 0003_created_at.py │ │ ├── 0004_remove_apikey_permissions_updated.py │ │ ├── 0005_description.py │ │ └── __init__.py │ ├── models.py │ ├── permissions.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_apikey_api.py │ │ └── test_apikey_api_v2.py │ ├── v1 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ └── v2 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py ├── async_tasks │ ├── __init__.py │ ├── clients.py │ ├── exceptions.py │ ├── handlers.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_clients.py │ │ └── test_handlers.py │ └── wsgi.py ├── backups │ ├── __init__.py │ ├── appconfig.py │ ├── exceptions.py │ ├── executor.py │ ├── fields.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_backup_query_args.py │ │ ├── 0003_auto_20160308_1121.py │ │ ├── 0004_backup_status_info.py │ │ ├── 0005_restore_status_info.py │ │ ├── 0006_backup_details.py │ │ ├── 0007_backup_size_to_int64.py │ │ ├── 0008_backup_location.py │ │ └── __init__.py │ ├── models.py │ ├── options.py │ ├── permissions.py │ ├── serializers.py │ ├── signal_handlers.py │ ├── site.py │ ├── storage.py │ ├── tasks.py │ ├── tests │ │ ├── __init__.py │ │ ├── helpers.py │ │ ├── old_instance.py │ │ ├── test_backup.py │ │ ├── test_backup_api.py │ │ └── test_restore.py │ ├── v1 │ │ ├── __init__.py │ │ ├── urls.py │ │ └── urls_toplevel.py │ └── views.py ├── batch │ ├── __init__.py │ ├── decorators.py │ ├── exceptions.py │ ├── parsers.py │ ├── serializers.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_batch_api.py │ │ └── test_preflight.py │ ├── utils.py │ ├── v1 │ │ ├── __init__.py │ │ └── urls.py │ ├── validators.py │ └── views.py ├── billing │ ├── __init__.py │ ├── appconfig.py │ ├── exceptions.py │ ├── fixtures │ │ ├── __init__.py │ │ └── core_data.json │ ├── generics.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20150128_1538.py │ │ ├── 0003_auto_20150306_0820.py │ │ ├── 0004_auto_20150311_1654.py │ │ ├── 0005_event.py │ │ ├── 0006_profile.py │ │ ├── 0007_auto_20150403_1434.py │ │ ├── 0008_auto_20150408_1235.py │ │ ├── 0009_invoice_invoiceitem_transaction.py │ │ ├── 0010_auto_20150410_1255.py │ │ ├── 0011_auto_20150413_1236.py │ │ ├── 0012_auto_20150416_1034.py │ │ ├── 0013_auto_20150416_0932.py │ │ ├── 0014_remove_profile_balance.py │ │ ├── 0015_auto_20150420_0758.py │ │ ├── 0016_auto_20150421_1355.py │ │ ├── 0017_profile_soft_limit_reached.py │ │ ├── 0018_auto_20150421_1209.py │ │ ├── 0019_auto_20150422_1322.py │ │ ├── 0020_profile_hard_limit_reached.py │ │ ├── 0021_auto_20150424_1036.py │ │ ├── 0022_auto_20150427_1501.py │ │ ├── 0023_auto_20150428_1348.py │ │ ├── 0024_length.py │ │ ├── 0025_auto_20150512_1153.py │ │ ├── 0026_instance_id.py │ │ ├── 0027_cleanup.py │ │ ├── 0028_plans.py │ │ ├── 0029_pricing.py │ │ ├── 0030_free_plan.py │ │ ├── 0031_invoices.py │ │ ├── 0032_cleanup.py │ │ ├── 0033_strippedslug.py │ │ ├── 0034_invoice_is_prorated.py │ │ ├── 0035_price_digits.py │ │ ├── 0036_adminlimit.py │ │ ├── 0037_adminlimit_data.py │ │ ├── 0038_cleanup_nulls_data.py │ │ ├── 0039_cleanup_nulls.py │ │ ├── 0040_invoice_status_sent.py │ │ ├── 0041_subscription_charged_until.py │ │ ├── 0042_charged_until_data.py │ │ ├── 0043_auto_20160210_1504.py │ │ ├── 0044_codebox_time.py │ │ ├── 0045_add_range_field_with_default.py │ │ ├── 0046_remove_default_and_update_range_field.py │ │ ├── 0047_range_index.py │ │ └── __init__.py │ ├── mixins.py │ ├── models.py │ ├── permissions.py │ ├── querysets.py │ ├── serializers.py │ ├── signal_handlers.py │ ├── signals.py │ ├── sql │ │ ├── __init__.py │ │ └── subscription.sql │ ├── tasks.py │ ├── templates │ │ └── billing │ │ │ └── invoice_pdf.html │ ├── tests │ │ ├── __init__.py │ │ ├── test_billing_api.py │ │ ├── test_blocking.py │ │ ├── test_charging.py │ │ ├── test_customer_tasks.py │ │ ├── test_discounts.py │ │ ├── test_invoices.py │ │ ├── test_limits.py │ │ ├── test_plans.py │ │ ├── test_shared_access.py │ │ ├── test_stripe_integration.py │ │ └── test_transactions.py │ ├── v1 │ │ ├── __init__.py │ │ └── urls.py │ ├── views.py │ └── viewsets.py ├── channels │ ├── __init__.py │ ├── appconfig.py │ ├── backup.py │ ├── exceptions.py │ ├── handlers.py │ ├── helpers.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_length.py │ │ ├── 0003_channel_description.py │ │ ├── 0004_like_index.py │ │ ├── 0005_description.py │ │ ├── 0006_acl.py │ │ ├── 0007_default_channel.py │ │ ├── 0008_longer_room.py │ │ ├── 0009_eventlog_channel.py │ │ ├── 0010_remove_change.py │ │ └── __init__.py │ ├── models.py │ ├── permissions.py │ ├── signal_handlers.py │ ├── sql │ │ ├── __init__.py │ │ └── channel.sql │ ├── tests │ │ ├── __init__.py │ │ ├── test_changes.py │ │ ├── test_changes_api.py │ │ ├── test_changes_v2.py │ │ ├── test_channels_api.py │ │ ├── test_handlers.py │ │ ├── test_throttle.py │ │ └── test_v2_acl.py │ ├── throttling.py │ ├── v1 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ └── v2 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py ├── codeboxes │ ├── __init__.py │ ├── appconfig.py │ ├── backup.py │ ├── container_manager.py │ ├── exceptions.py │ ├── field_serializers.py │ ├── helpers.py │ ├── managers.py │ ├── migrations │ │ ├── 0001_squashed_0032.py │ │ ├── 0032_remove_codeboxschedule__is_live.py │ │ ├── 0033_runtimes.py │ │ ├── 0034_auto_add_encoding.py │ │ ├── 0035_codebox_socket.py │ │ ├── 0036_codeboxschedule_socket.py │ │ ├── 0037_add_checksum.py │ │ ├── 0038_add_path.py │ │ ├── 0039_path_unique_with_socket.py │ │ ├── 0040_codeboxschedule_event_handler.py │ │ └── __init__.py │ ├── models.py │ ├── permissions.py │ ├── runner.py │ ├── runtimes.py │ ├── signal_handlers.py │ ├── signals.py │ ├── sql │ │ ├── __init__.py │ │ └── codebox.sql │ ├── tasks.py │ ├── tests │ │ ├── __init__.py │ │ ├── mixins.py │ │ ├── test_api_v1_1.py │ │ ├── test_api_v2.py │ │ ├── test_blocking.py │ │ ├── test_codebox.py │ │ ├── test_codebox_api.py │ │ ├── test_pagination.py │ │ ├── test_periodic.py │ │ └── test_tasks.py │ ├── v1 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ ├── v1_1 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ └── v2 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py ├── controlpanel │ ├── __init__.py │ ├── filters.py │ ├── migrations │ │ └── __init__.py │ ├── permissions.py │ ├── serializer.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_controlpanel_api.py │ │ └── test_staffkey.py │ ├── v1 │ │ ├── __init__.py │ │ └── urls.py │ └── views.py ├── core │ ├── __init__.py │ ├── abstract_models.py │ ├── appconfig.py │ ├── atomic_signals │ │ ├── __init__.py │ │ └── signals.py │ ├── authentication.py │ ├── backends │ │ ├── __init__.py │ │ └── storage.py │ ├── contextmanagers.py │ ├── coverage_runner.py │ ├── decorators.py │ ├── exceptions.py │ ├── field_serializers.py │ ├── fields.py │ ├── filter_fields.py │ ├── fixtures │ │ ├── __init__.py │ │ ├── development.json │ │ └── production.json │ ├── helpers.py │ ├── management │ │ ├── __init__.py │ │ └── commands │ │ │ ├── __init__.py │ │ │ ├── delete_dead_objects.py │ │ │ ├── helpers │ │ │ ├── __init__.py │ │ │ └── migrate.py │ │ │ └── migrate.py │ ├── managers.py │ ├── metadata.py │ ├── middleware.py │ ├── mixins │ │ ├── __init__.py │ │ ├── querysets.py │ │ ├── serializers.py │ │ └── views.py │ ├── models.py │ ├── pagination.py │ ├── parsers.py │ ├── permissions.py │ ├── postgresql_backend │ │ ├── __init__.py │ │ └── base.py │ ├── querysets.py │ ├── renderers.py │ ├── response.py │ ├── routers.py │ ├── serializers.py │ ├── signal_handlers.py │ ├── signals.py │ ├── social_helpers.py │ ├── social_proxies.py │ ├── tasks.py │ ├── tests │ │ ├── __init__.py │ │ ├── mixins.py │ │ ├── test_cacheable_model.py │ │ ├── test_commands.py │ │ ├── test_config_validator.py │ │ ├── test_decorators.py │ │ ├── test_deleteliveobject_task.py │ │ ├── test_deletion_chunk.py │ │ ├── test_helpers.py │ │ ├── test_hyperlinked_mixin.py │ │ ├── test_live_model.py │ │ ├── test_metadata_validator.py │ │ ├── test_social_proxies.py │ │ ├── test_trackchanges_model.py │ │ ├── test_uniquekey_model.py │ │ ├── test_views.py │ │ └── testcases.py │ ├── throttling.py │ ├── tokens.py │ ├── urls.py │ ├── validators.py │ ├── versioning.py │ └── views.py ├── data │ ├── __init__.py │ ├── adapters.py │ ├── appconfig.py │ ├── backup.py │ ├── contextmanagers.py │ ├── decorators.py │ ├── exceptions.py │ ├── field_serializers.py │ ├── fields.py │ ├── filters │ │ ├── __init__.py │ │ ├── lookups.py │ │ └── mixins.py │ ├── helpers.py │ ├── lookups.py │ ├── migrations │ │ ├── 0001_squashed_0016.py │ │ ├── 0016_estimate_fix.py │ │ ├── 0017_to_intarray.py │ │ ├── 0018_acl.py │ │ ├── 0019_data_endpoint_acl.py │ │ ├── 0020_default_acl_for_users.py │ │ ├── 0021_klass_acl.py │ │ ├── 0022_auto_20160606_0837.py │ │ ├── 0023_klass_acl_indexes.py │ │ ├── 0024_klass_visible.py │ │ ├── 0025_klass_refs.py │ │ └── __init__.py │ ├── mixins.py │ ├── models.py │ ├── permissions.py │ ├── querysets.py │ ├── signal_handlers.py │ ├── sql │ │ ├── __init__.py │ │ ├── class.sql │ │ └── dataobject.sql │ ├── tasks.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_builtin_filtering.py │ │ ├── test_classes_api.py │ │ ├── test_classes_index_task.py │ │ ├── test_filtering.py │ │ ├── test_objects_api.py │ │ ├── test_pagination.py │ │ ├── test_v2_acl.py │ │ ├── test_validators.py │ │ └── testcases.py │ ├── v1 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ ├── v2 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ └── validators.py ├── endpoints │ ├── __init__.py │ ├── tests │ │ ├── __init__.py │ │ └── test_api.py │ ├── v1_1 │ │ ├── __init__.py │ │ ├── urls.py │ │ └── views.py │ └── v2 │ │ ├── __init__.py │ │ ├── urls.py │ │ └── views.py ├── high_level │ ├── __init__.py │ ├── backup.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_dataobjecthighlevelapi_query.py │ │ ├── 0003_description.py │ │ └── __init__.py │ ├── models.py │ ├── permissions.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_objects_api.py │ │ └── test_objects_api_v2.py │ ├── v1 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ ├── v1_1 │ │ ├── __init__.py │ │ └── urls.py │ ├── v2 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ └── validators.py ├── hosting │ ├── __init__.py │ ├── appconfig.py │ ├── backup.py │ ├── exceptions.py │ ├── helpers.py │ ├── middleware.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_hosting_domains.py │ │ ├── 0003_hosting_label_rename_name.py │ │ ├── 0004_hosting_is_active.py │ │ ├── 0005_hosting_default.py │ │ ├── 0006_hosting_ssl_status.py │ │ ├── 0007_hostingfile_checksum.py │ │ ├── 0008_hosting_socket.py │ │ ├── 0009_hosting_ssl_status_info.py │ │ ├── 0010_removed_ssl_status_info.py │ │ ├── 0011_hosting_auth.py │ │ ├── 0012_hosting_config.py │ │ └── __init__.py │ ├── models.py │ ├── negotiations.py │ ├── permissions.py │ ├── signal_handlers.py │ ├── sql │ │ └── hosting.sql │ ├── tasks.py │ ├── tests │ │ ├── __init__.py │ │ ├── base.py │ │ ├── test_api.py │ │ ├── test_api_v2.py │ │ ├── test_fileview.py │ │ └── test_tasks.py │ ├── v1_1 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ ├── v2 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ └── validators.py ├── instances │ ├── __init__.py │ ├── appconfig.py │ ├── contextmanagers.py │ ├── exceptions.py │ ├── fixtures │ │ ├── __init__.py │ │ └── init_instances.json │ ├── helpers.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20150127_1552.py │ │ ├── 0003_auto_20150127_1712.py │ │ ├── 0004_instance_metadata.py │ │ ├── 0005_shorter_name.py │ │ ├── 0006_length.py │ │ ├── 0007_add_instance_indicator__model.py │ │ ├── 0008_like_index.py │ │ ├── 0009_validators.py │ │ ├── 0010_remove_instance_old_type.py │ │ ├── 0011_instance_last_access.py │ │ ├── 0012_instance_noticed_at.py │ │ ├── 0013_instance_database.py │ │ ├── 0014_instance_storage_prefix.py │ │ ├── 0015_auto_20160407_1144.py │ │ ├── 0016_instance_config.py │ │ ├── 0017_instance_klasses_acl.py │ │ ├── 0018_instance_channels_acl.py │ │ ├── 0019_instance_script_endpoints_acl.py │ │ ├── 0020_instance_groups_acl.py │ │ ├── 0021_remove_last_access_fields.py │ │ ├── 0022_instance_domains.py │ │ ├── 0023_instance_version.py │ │ ├── 0024_instance_location.py │ │ └── __init__.py │ ├── mixins.py │ ├── models.py │ ├── permissions.py │ ├── postgresql_backend │ │ ├── __init__.py │ │ ├── base.py │ │ └── introspection.py │ ├── routers.py │ ├── signal_handlers.py │ ├── sql │ │ ├── __init__.py │ │ └── instance.sql │ ├── tests │ │ ├── __init__.py │ │ ├── test_indicators.py │ │ ├── test_instances_api.py │ │ ├── test_last_access.py │ │ └── test_throttle.py │ ├── throttling.py │ ├── v1 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ ├── v1_1 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ └── v2 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py ├── invitations │ ├── __init__.py │ ├── exceptions.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20150130_1243.py │ │ ├── 0003_invitation_inviter.py │ │ ├── 0004_role_as_integer.py │ │ ├── 0005_role_as_fk.py │ │ ├── 0006_invitation_admin.py │ │ ├── 0007_admin_data.py │ │ ├── 0008_cleanup_indexes.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_invitation_api.py │ │ └── test_invitations.py │ ├── v1 │ │ ├── __init__.py │ │ └── urls.py │ └── views.py ├── metrics │ ├── __init__.py │ ├── abstract_models.py │ ├── abstract_tasks.py │ ├── appconfig.py │ ├── exceptions.py │ ├── helpers.py │ ├── migrations │ │ ├── 0012_cleanup.py │ │ ├── 0013_choices.py │ │ ├── 0014_source_to_char.py │ │ ├── 0015_after_trigger_source_rename.py │ │ ├── 0016_ordering.py │ │ ├── 0017_cleanup_nulls_data.py │ │ ├── 0018_cleanup_nulls.py │ │ ├── 0019_codebox_time.py │ │ ├── 0020_worklogentry_location.py │ │ ├── 0021_worklog_unique_index.py │ │ └── __init__.py │ ├── models.py │ ├── serializers.py │ ├── signal_handlers.py │ ├── signals.py │ ├── tasks.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_aggregate.py │ │ └── test_metrics_api.py │ ├── v1 │ │ ├── __init__.py │ │ └── urls.py │ └── views.py ├── push_notifications │ ├── __init__.py │ ├── apns │ │ ├── __init__.py │ │ ├── exceptions.py │ │ ├── message.py │ │ └── sockets.py │ ├── appconfig.py │ ├── backup.py │ ├── fields.py │ ├── filters.py │ ├── forms.py │ ├── migrations │ │ ├── 0001_squashed_0011.py │ │ ├── 0011_auto_20160303_1509.py │ │ ├── 0013_auto_20160519_0847.py │ │ ├── 0014_indicator_fix.py │ │ └── __init__.py │ ├── mixins.py │ ├── models.py │ ├── permissions.py │ ├── serializers.py │ ├── signal_handlers.py │ ├── tasks.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_config_api.py │ │ ├── test_devices_api.py │ │ ├── test_messages_api.py │ │ ├── test_tasks.py │ │ └── test_validators.py │ ├── v1 │ │ ├── __init__.py │ │ └── urls.py │ ├── validators.py │ └── views.py ├── redis_storage │ ├── __init__.py │ ├── fields.py │ ├── models.py │ ├── pagination.py │ ├── tests │ │ ├── __init__.py │ │ └── test_models.py │ └── views.py ├── response_templates │ ├── __init__.py │ ├── appconfig.py │ ├── backup.py │ ├── exceptions.py │ ├── jinja2_environments.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_auto_20151218_1535.py │ │ ├── 0003_add_default_reponse_templates.py │ │ ├── 0004_responsetemplate_description.py │ │ └── __init__.py │ ├── models.py │ ├── negotiations.py │ ├── permissions.py │ ├── predefined_templates.py │ ├── renderers.py │ ├── serializers.py │ ├── signal_handlers.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_renderer.py │ │ ├── test_response_templates_api.py │ │ └── test_validators.py │ ├── utils.py │ ├── v1 │ │ ├── __init__.py │ │ └── urls.py │ ├── validators.py │ └── views.py ├── snippets │ ├── __init__.py │ ├── serializers.py │ ├── tests │ │ ├── __init__.py │ │ └── test_api.py │ ├── v1_1 │ │ ├── __init__.py │ │ └── urls.py │ └── views.py ├── sockets │ ├── __init__.py │ ├── appconfig.py │ ├── backup.py │ ├── download_utils.py │ ├── exceptions.py │ ├── handlers.py │ ├── helpers.py │ ├── importer.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_socket_description.py │ │ ├── 0003_socket_url.py │ │ ├── 0004_socketendpoint_metadata.py │ │ ├── 0005_socket_config.py │ │ ├── 0006_socket_installed_deps.py │ │ ├── 0007_socketendpoint_acl.py │ │ ├── 0008_socket_zip_file.py │ │ ├── 0009_remove_obsolete_fields.py │ │ ├── 0010_socket_version.py │ │ ├── 0011_socket_handler.py │ │ ├── 0012_add_installed.py │ │ ├── 0013_alter_status_info.py │ │ ├── 0014_socket_environment.py │ │ ├── 0015_socket_zip_file_list.py │ │ ├── 0016_socketenvironment_checksum.py │ │ ├── 0017_socketenvironment_name_unique.py │ │ ├── 0018_socket_checksum.py │ │ ├── 0019_socket_install_config.py │ │ └── __init__.py │ ├── models.py │ ├── permissions.py │ ├── processor.py │ ├── signal_handlers.py │ ├── tasks.py │ ├── tests │ │ ├── __init__.py │ │ ├── data_test.py │ │ ├── test_acl.py │ │ ├── test_api_v2.py │ │ ├── test_endpoints.py │ │ ├── test_environment.py │ │ ├── test_event_handlers.py │ │ ├── test_handler.py │ │ ├── test_importer.py │ │ ├── test_loader.py │ │ └── test_processor.py │ ├── v2 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ └── validators.py ├── triggers │ ├── __init__.py │ ├── appconfig.py │ ├── backup.py │ ├── events.py │ ├── helpers.py │ ├── migrations │ │ ├── 0001_squashed_0006.py │ │ ├── 0006_description.py │ │ ├── 0007_event_and_signals.py │ │ ├── 0008_event_data.py │ │ ├── 0009_cleanup.py │ │ ├── 0010_trigger_socket.py │ │ ├── 0011_trigger_signals.py │ │ └── __init__.py │ ├── models.py │ ├── permissions.py │ ├── querysets.py │ ├── signal_handlers.py │ ├── sql │ │ ├── __init__.py │ │ └── trigger.sql │ ├── tasks.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_api.py │ │ ├── test_api_v1_1.py │ │ ├── test_api_v2.py │ │ └── test_triggers.py │ ├── v1 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ ├── v1_1 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ ├── v2 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ └── validators.py ├── users │ ├── __init__.py │ ├── appconfig.py │ ├── backup.py │ ├── exceptions.py │ ├── migrations │ │ ├── 0001_squashed_0011.py │ │ ├── 0011_auto_20151116_1124.py │ │ ├── 0012_acl.py │ │ ├── 0013_group_name.py │ │ ├── 0014_index.py │ │ └── __init__.py │ ├── mixins.py │ ├── models.py │ ├── permissions.py │ ├── signal_handlers.py │ ├── signals.py │ ├── sql │ │ ├── __init__.py │ │ └── user.sql │ ├── tests │ │ ├── __init__.py │ │ ├── test_filtering.py │ │ ├── test_group_api.py │ │ ├── test_groupmembership_api.py │ │ ├── test_groupmembership_api_v2.py │ │ ├── test_social_login.py │ │ ├── test_user_api.py │ │ ├── test_user_api_v2.py │ │ ├── test_usermembership_api.py │ │ ├── test_usermembership_api_v2.py │ │ └── test_v2_acl.py │ ├── v1 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py │ └── v2 │ │ ├── __init__.py │ │ ├── serializers.py │ │ ├── urls.py │ │ └── views.py └── webhooks │ ├── __init__.py │ ├── backup.py │ ├── exceptions.py │ ├── handlers.py │ ├── helpers.py │ ├── migrations │ ├── 0001_squashed_0012.py │ ├── 0012_auto_20160225_1642.py │ ├── 0013_acl.py │ ├── 0014_webhook_socket.py │ └── __init__.py │ ├── mixins.py │ ├── models.py │ ├── permissions.py │ ├── sql │ ├── __init__.py │ └── webhook.sql │ ├── tasks.py │ ├── tests │ ├── __init__.py │ ├── test_api.py │ ├── test_handler.py │ ├── test_integration.py │ ├── test_v1_1_api.py │ ├── test_v2_acl.py │ └── test_v2_api.py │ ├── v1 │ ├── __init__.py │ ├── serializers.py │ ├── urls.py │ └── views.py │ ├── v1_1 │ ├── __init__.py │ ├── serializers.py │ ├── urls.py │ └── views.py │ └── v2 │ ├── __init__.py │ ├── serializers.py │ ├── urls.py │ └── views.py ├── collect_sql_migrations.sh ├── conf ├── __init__.py ├── haproxy_test.cfg ├── nginx │ ├── conf.d │ │ ├── platform.conf │ │ └── uwsgi.conf │ ├── nginx.conf │ └── uwsgi_params ├── supervisor │ └── conf.d │ │ ├── celery.conf │ │ ├── codebox.conf │ │ └── uwsgi.conf ├── uwsgi │ ├── uwsgi.ini │ └── uwsgi_offload.ini └── wsgi.py ├── db ├── 00alter_settings.sh ├── 01extensions.sql ├── 02template_extensions.sh └── 03createdb.sh ├── deploy.sh ├── deploy ├── dind-run.sh ├── env │ ├── eu1.env │ ├── eu1.secrets.gpg │ ├── staging.env │ └── staging.secrets.gpg └── yaml │ ├── codebox-daemonset.yml.j2 │ ├── codebox-deployment.yml.j2 │ ├── codebox-hpa.yml.j2 │ ├── migration-job.yml.j2 │ ├── web-deployment.yml.j2 │ ├── web-hpa.yml.j2 │ ├── web-service.yml.j2 │ ├── worker-deployment.yml.j2 │ └── worker-hpa.yml.j2 ├── docker-compose.yml ├── manage.py ├── modules ├── __init__.py └── serializer │ ├── python │ ├── py_defines.h │ ├── serializer.c │ └── version.h │ └── setup.py ├── requirements.txt ├── requirements_development.txt ├── run.sh ├── run_care.sh ├── run_celery.sh ├── scripts └── certs │ ├── issue.sh │ ├── remove.sh │ └── renew.sh ├── settings ├── __init__.py ├── celeryconf.py ├── celeryconf_debug.py ├── common.py ├── development.py ├── production.py └── tests.py ├── setup.cfg ├── supervisord.conf ├── test.sh ├── tools ├── dump_instance.sh ├── restore_instance.sh └── setup_codebox.py └── urls ├── __init__.py ├── api ├── __init__.py ├── v1.py ├── v1_1.py └── v2.py └── backend.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = apps 3 | concurrency = multiprocessing 4 | parallel = True 5 | omit = 6 | **/migrations/* 7 | **/tests/* 8 | **/management/commands/* 9 | **/proto/* 10 | 11 | [report] 12 | exclude_lines = 13 | # Have to re-enable the standard pragma 14 | pragma: no cover 15 | 16 | # Don't complain if tests don't hit defensive assertion code: 17 | raise AssertionError 18 | raise NotImplementedError 19 | 20 | # Don't complain if non-runnable code isn't run: 21 | if 0: 22 | if __name__ == .__main__.: 23 | 24 | ignore_errors = True 25 | skip_covered = True 26 | 27 | [html] 28 | directory = coverage_report 29 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .* 2 | coverage_report 3 | db 4 | deploy 5 | dev 6 | media/** 7 | static/** 8 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | API_HOST=api.syncano.test:8000 2 | COLORS_ENABLED=true 3 | DB_CONN_MAX_AGE=300 4 | DJANGO_DEBUG=true 5 | DJANGO_SETTINGS_MODULE=settings.development 6 | LEGACY_CODEBOX_ENABLED=true 7 | PYTHONHASHSEED=0 8 | TRACING_SAMPLING=0 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug report" 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Feature request" 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .envrc 2 | .coverage* 3 | coverage*.out 4 | dev 5 | *.swagger.json 6 | *.unenc 7 | build/ 8 | coverage_report/ 9 | dist/ 10 | media/ 11 | static/ 12 | _storage/ 13 | *.egg-info/ 14 | *.pyc 15 | *.pyo 16 | *.pyd 17 | *.pid 18 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/.isort.cfg -------------------------------------------------------------------------------- /Dockerfile.test: -------------------------------------------------------------------------------- 1 | FROM syncano/platform 2 | 3 | USER root 4 | COPY --chown=syncano ./requirements_development.txt /home/syncano/app/ 5 | RUN pip install --no-cache-dir -r requirements_development.txt 6 | USER syncano 7 | -------------------------------------------------------------------------------- /apps/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/__init__.py -------------------------------------------------------------------------------- /apps/admins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/admins/__init__.py -------------------------------------------------------------------------------- /apps/admins/exceptions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework import status 3 | 4 | from apps.core.exceptions import SyncanoException 5 | 6 | 7 | class InvitationNotFound(SyncanoException): 8 | status_code = status.HTTP_400_BAD_REQUEST 9 | field = 'invitation_key' 10 | default_detail = 'Invitation not found.' 11 | 12 | 13 | class AdminAlreadyActivated(SyncanoException): 14 | status_code = status.HTTP_400_BAD_REQUEST 15 | default_detail = 'Account has already been activated.' 16 | 17 | 18 | class RegistrationDisabled(SyncanoException): 19 | status_code = status.HTTP_400_BAD_REQUEST 20 | default_detail = 'Registration disabled.' 21 | -------------------------------------------------------------------------------- /apps/admins/fixtures/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/admins/fixtures/__init__.py -------------------------------------------------------------------------------- /apps/admins/fixtures/core_data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pk":1, 4 | "model":"admins.role", 5 | "fields":{ 6 | "name": "full" 7 | } 8 | }, 9 | { 10 | "pk":2, 11 | "model":"admins.role", 12 | "fields":{ 13 | "name": "write" 14 | } 15 | }, 16 | { 17 | "pk":3, 18 | "model":"admins.role", 19 | "fields":{ 20 | "name": "read" 21 | } 22 | } 23 | ] 24 | -------------------------------------------------------------------------------- /apps/admins/helpers.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | import urllib.parse 3 | 4 | import rapidjson as json 5 | 6 | 7 | def get_distinct_id(request_data): 8 | for k, v in request_data.items(): 9 | if 'mixpanel' not in k: 10 | continue 11 | 12 | try: 13 | unquoted = urllib.parse.unquote(v) 14 | return json.loads(unquoted).get('distinct_id') 15 | except ValueError: 16 | return None 17 | -------------------------------------------------------------------------------- /apps/admins/migrations/0002_created_at.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | dependencies = [ 7 | ('admins', '0001_initial'), 8 | ] 9 | 10 | operations = [ 11 | migrations.RenameField( 12 | model_name='admin', 13 | old_name='date_joined', 14 | new_name='created_at' 15 | ), 16 | migrations.AlterField( 17 | model_name='admin', 18 | name='created_at', 19 | field=models.DateTimeField(auto_now_add=True), 20 | preserve_default=True, 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /apps/admins/migrations/0004_admin_customer_id.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('admins', '0003_merge'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name='admin', 14 | name='customer_id', 15 | field=models.CharField(help_text='Designates relation between our user and entity in external payment service (Stripe).', max_length=18, verbose_name='customer id', db_index=True, blank=True), 16 | preserve_default=True, 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/admins/migrations/0005_auto_20150318_1059.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | from apps.billing.tasks import create_stripe_customer 5 | 6 | 7 | def create_stripe_customers(apps, schema_editor): 8 | Admin = apps.get_model('admins.Admin') 9 | for pk, email in Admin.objects.filter(customer_id='').values_list('pk', 'email'): 10 | create_stripe_customer(pk, email=email) 11 | 12 | 13 | class Migration(migrations.Migration): 14 | 15 | dependencies = [ 16 | ('admins', '0004_admin_customer_id'), 17 | ] 18 | 19 | operations = [ 20 | migrations.RunPython(create_stripe_customers), 21 | ] 22 | -------------------------------------------------------------------------------- /apps/admins/migrations/0006_remove_admin_customer_id.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('admins', '0005_auto_20150318_1059'), 9 | ('billing', '0007_auto_20150403_1434'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='admin', 15 | name='customer_id', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/admins/migrations/0007_auto_20150409_1234.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('admins', '0006_remove_admin_customer_id'), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveField( 13 | model_name='adminactivationkey', 14 | name='admin', 15 | ), 16 | migrations.DeleteModel( 17 | name='AdminActivationKey', 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/admins/migrations/0010_role_as_fk.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('admins', '0009_role'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='admininstancerole', 14 | name='role', 15 | field=models.ForeignKey(related_name='instance_admins', to='admins.Role', on_delete=models.CASCADE), 16 | preserve_default=True, 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/admins/migrations/0011_length.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('admins', '0010_role_as_fk'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='admin', 14 | name='first_name', 15 | field=models.CharField(max_length=64, verbose_name='first name', blank=True), 16 | preserve_default=True, 17 | ), 18 | migrations.AlterField( 19 | model_name='admin', 20 | name='last_name', 21 | field=models.CharField(max_length=64, verbose_name='last name', blank=True), 22 | preserve_default=True, 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /apps/admins/migrations/0012_last_login.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('admins', '0011_length'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='admin', 14 | name='last_login', 15 | field=models.DateTimeField(null=True, verbose_name='last login', blank=True), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/admins/migrations/0013_remove_adminsocialprofile_email.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('admins', '0012_last_login'), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveField( 13 | model_name='adminsocialprofile', 14 | name='email', 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /apps/admins/migrations/0014_auto_20151110_1458.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('admins', '0013_remove_adminsocialprofile_email'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='adminsocialprofile', 14 | name='backend', 15 | field=models.SmallIntegerField(choices=[(0, 'facebook'), (1, 'google-oauth2'), (2, 'github'), (3, 'linkedin')]), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/admins/migrations/0015_auto_20151116_1124.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('admins', '0014_auto_20151110_1458'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='adminsocialprofile', 14 | name='backend', 15 | field=models.SmallIntegerField(choices=[(0, 'facebook'), (1, 'google-oauth2'), (2, 'github'), (3, 'linkedin'), (4, 'twitter')]), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/admins/migrations/0016_email_trgm_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-04 13:14 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('admins', '0015_auto_20151116_1124'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RunSQL( 14 | """ 15 | CREATE INDEX admins_admin_trgm_email ON admins_admin 16 | USING GIN (email gin_trgm_ops); 17 | """ 18 | ) 19 | ] 20 | -------------------------------------------------------------------------------- /apps/admins/migrations/0018_admin_metadata.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2016-09-21 12:09 3 | import jsonfield.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('admins', '0017_add_last_access'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RunSQL( 15 | 'ALTER TABLE "admins_admin" ADD COLUMN "metadata" TEXT DEFAULT \'{}\' NOT NULL', 16 | 'ALTER TABLE "admins_admin" DROP COLUMN "metadata" CASCADE', 17 | [migrations.AddField( 18 | model_name='admin', 19 | name='metadata', 20 | field=jsonfield.fields.JSONField(blank=True, default={}), 21 | )]), 22 | ] 23 | -------------------------------------------------------------------------------- /apps/admins/migrations/0019_admin_is_trusted.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('admins', '0018_admin_metadata'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name='admin', 13 | name='is_trusted', 14 | field=models.BooleanField(default=False, verbose_name='trusted status'), 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /apps/admins/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/admins/migrations/__init__.py -------------------------------------------------------------------------------- /apps/admins/mixins.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.contrib.auth.hashers import make_password 3 | 4 | 5 | class PasswordSerializerMixin: 6 | def update(self, instance, validated_data): 7 | if 'password' in validated_data: 8 | validated_data['password'] = make_password(validated_data['password']) 9 | return super().update(instance, validated_data) 10 | 11 | def create(self, validated_data): 12 | if 'password' in validated_data: 13 | validated_data['password'] = make_password(validated_data['password']) 14 | return super().create(validated_data) 15 | -------------------------------------------------------------------------------- /apps/admins/sql/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/admins/sql/__init__.py -------------------------------------------------------------------------------- /apps/admins/sql/admin.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX admins_admin_trgm_email ON admins_admin 2 | USING GIN (email gin_trgm_ops); 3 | 4 | CREATE INDEX admins_admin_noticed_at ON admins_admin 5 | USING BTREE (noticed_at) 6 | WHERE noticed_at IS NOT NULL; 7 | -------------------------------------------------------------------------------- /apps/admins/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/admins/tests/__init__.py -------------------------------------------------------------------------------- /apps/admins/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/analytics/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.analytics.appconfig.AppConfig' 2 | -------------------------------------------------------------------------------- /apps/analytics/appconfig.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.apps import AppConfig as _AppConfig 3 | 4 | 5 | class AppConfig(_AppConfig): 6 | name = 'apps.analytics' 7 | 8 | def ready(self): 9 | from . import signal_handlers # noqa 10 | -------------------------------------------------------------------------------- /apps/analytics/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/analytics/models.py -------------------------------------------------------------------------------- /apps/analytics/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/analytics/tests/__init__.py -------------------------------------------------------------------------------- /apps/apikeys/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/apikeys/__init__.py -------------------------------------------------------------------------------- /apps/apikeys/migrations/0002_apikey_instance.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('instances', '0004_instance_metadata'), 9 | ('apikeys', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='apikey', 15 | name='instance', 16 | field=models.ForeignKey(default=1, to='instances.Instance', on_delete=models.CASCADE), 17 | preserve_default=False, 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/apikeys/migrations/0003_created_at.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | dependencies = [ 7 | ('apikeys', '0002_apikey_instance'), 8 | ] 9 | 10 | operations = [ 11 | migrations.RenameField( 12 | model_name='apikey', 13 | old_name='date_joined', 14 | new_name='created_at' 15 | ), 16 | migrations.AlterField( 17 | model_name='apikey', 18 | name='created_at', 19 | field=models.DateTimeField(auto_now_add=True), 20 | preserve_default=True, 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /apps/apikeys/migrations/0004_remove_apikey_permissions_updated.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('apikeys', '0003_created_at'), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveField( 13 | model_name='apikey', 14 | name='permissions_updated', 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /apps/apikeys/migrations/0005_description.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('apikeys', '0004_remove_apikey_permissions_updated'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='apikey', 14 | name='description', 15 | field=models.TextField(max_length=256, blank=True), 16 | preserve_default=True, 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/apikeys/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/apikeys/migrations/__init__.py -------------------------------------------------------------------------------- /apps/apikeys/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/apikeys/tests/__init__.py -------------------------------------------------------------------------------- /apps/apikeys/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/apikeys/v1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework.routers import SimpleRouter 3 | 4 | from apps.apikeys.v1 import views 5 | 6 | router = SimpleRouter() 7 | router.register('api_keys', views.ApiKeyViewSet) 8 | 9 | urlpatterns = router.urls 10 | -------------------------------------------------------------------------------- /apps/apikeys/v2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/apikeys/v2/__init__.py -------------------------------------------------------------------------------- /apps/apikeys/v2/serializers.py: -------------------------------------------------------------------------------- 1 | from apps.apikeys.v1 import serializers as v1_serializers 2 | 3 | 4 | class ApiKeySerializer(v1_serializers.ApiKeySerializer): 5 | class Meta(v1_serializers.ApiKeySerializer.Meta): 6 | fields = ('id', 'description', 'api_key', 'ignore_acl', 'created_at') 7 | -------------------------------------------------------------------------------- /apps/apikeys/v2/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework.routers import SimpleRouter 3 | 4 | from apps.apikeys.v2 import views as v2_views 5 | 6 | router = SimpleRouter() 7 | router.register('api_keys', v2_views.ApiKeyViewSet) 8 | 9 | urlpatterns = router.urls 10 | -------------------------------------------------------------------------------- /apps/apikeys/v2/views.py: -------------------------------------------------------------------------------- 1 | from apps.apikeys.v1 import views as v1_views 2 | from apps.apikeys.v2.serializers import ApiKeySerializer 3 | 4 | 5 | class ApiKeyViewSet(v1_views.ApiKeyViewSet): 6 | serializer_class = ApiKeySerializer 7 | -------------------------------------------------------------------------------- /apps/async_tasks/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/async_tasks/clients.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | import uuid 3 | 4 | 5 | class WebSocketClient: 6 | def __init__(self, request, fd, send_event, send_queue, recv_event, 7 | recv_queue, timeout=5): 8 | self.request = request 9 | self.fd = fd 10 | self.send_event = send_event 11 | self.send_queue = send_queue 12 | self.recv_event = recv_event 13 | self.recv_queue = recv_queue 14 | self.timeout = timeout 15 | self.id = str(uuid.uuid1()) 16 | self.connected = True 17 | 18 | def send(self, msg): 19 | self.send_queue.put(msg) 20 | self.send_event.set() 21 | 22 | def receive(self): 23 | return self.recv_queue.get() 24 | 25 | def close(self): 26 | self.connected = False 27 | -------------------------------------------------------------------------------- /apps/async_tasks/exceptions.py: -------------------------------------------------------------------------------- 1 | from rest_framework import status 2 | 3 | from apps.core.exceptions import SyncanoException 4 | 5 | 6 | class UwsgiValueError(SyncanoException): 7 | status_code = status.HTTP_400_BAD_REQUEST 8 | default_detail = 'Too big request passed.' 9 | -------------------------------------------------------------------------------- /apps/async_tasks/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/async_tasks/tests/__init__.py -------------------------------------------------------------------------------- /apps/backups/__init__.py: -------------------------------------------------------------------------------- 1 | from django.utils.module_loading import autodiscover_modules 2 | 3 | from apps.backups.site import default_site 4 | 5 | default_app_config = 'apps.backups.appconfig.AppConfig' 6 | 7 | 8 | def autodiscover(): 9 | """Import backup.py module for each of INSTALLED_APPS""" 10 | autodiscover_modules('backup', register_to=default_site) 11 | -------------------------------------------------------------------------------- /apps/backups/appconfig.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig as _AppConfig 2 | 3 | 4 | class AppConfig(_AppConfig): 5 | name = 'apps.backups' 6 | 7 | def ready(self): 8 | from . import signal_handlers # noqa 9 | self.module.autodiscover() 10 | -------------------------------------------------------------------------------- /apps/backups/fields.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | import base64 3 | 4 | from django.utils.encoding import force_bytes 5 | from rest_framework.serializers import CharField 6 | 7 | 8 | class BinaryField(CharField): 9 | def to_representation(self, value): 10 | return base64.b64encode(force_bytes(value)).decode('ascii') 11 | 12 | def to_internal_value(self, value): 13 | return memoryview(base64.b64decode(force_bytes(value))) 14 | -------------------------------------------------------------------------------- /apps/backups/migrations/0002_backup_query_args.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.2 on 2016-03-07 09:48 3 | import jsonfield.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('backups', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='backup', 16 | name='query_args', 17 | field=jsonfield.fields.JSONField(default={}), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/backups/migrations/0004_backup_status_info.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.4 on 2016-03-22 08:47 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('backups', '0003_auto_20160308_1121'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='backup', 15 | name='status_info', 16 | field=models.TextField(blank=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/backups/migrations/0005_restore_status_info.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.4 on 2016-03-30 06:43 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('backups', '0004_backup_status_info'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='restore', 15 | name='status_info', 16 | field=models.TextField(blank=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/backups/migrations/0006_backup_details.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2016-06-03 10:40 3 | import jsonfield.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('backups', '0005_restore_status_info'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='backup', 16 | name='details', 17 | field=jsonfield.fields.JSONField(default={}), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/backups/migrations/0007_backup_size_to_int64.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2017-01-19 14:48 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('backups', '0006_backup_details'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='backup', 15 | name='size', 16 | field=models.BigIntegerField(null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/backups/migrations/0008_backup_location.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('backups', '0007_backup_size_to_int64'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name='backup', 14 | name='location', 15 | field=models.TextField(db_index=True, default=settings.LOCATION), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/backups/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/backups/migrations/__init__.py -------------------------------------------------------------------------------- /apps/backups/permissions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework.permissions import BasePermission 3 | 4 | 5 | class UserHasFullPermissions(BasePermission): 6 | def has_permission(self, request, view): 7 | return request.user.is_authenticated and request.user.is_role_satisfied(request.instance, "full") 8 | -------------------------------------------------------------------------------- /apps/backups/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/backups/tests/__init__.py -------------------------------------------------------------------------------- /apps/backups/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/backups/v1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.urls import path 3 | from rest_framework.routers import SimpleRouter 4 | 5 | from apps.backups.views import FullBackupViewSet, PartialBackupViewSet, RestoreViewSet, TopBackupsInstanceLinkView 6 | 7 | router = SimpleRouter() 8 | router.register('restores', RestoreViewSet, base_name="restores") 9 | router.register('backups/full', FullBackupViewSet, base_name="full_backups") 10 | router.register('backups/partial', PartialBackupViewSet, base_name="partial_backups") 11 | 12 | urlpatterns = [path('backups/', TopBackupsInstanceLinkView.as_view(), name="instance_backups")] 13 | 14 | urlpatterns += router.urls 15 | -------------------------------------------------------------------------------- /apps/backups/v1/urls_toplevel.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.urls import path 3 | from rest_framework.routers import SimpleRouter 4 | 5 | from apps.backups.views import FullBackupsTopLevelViewSet, PartialBackupsTopLevelViewSet, TopBackupsLinkView 6 | 7 | backup_router = SimpleRouter() 8 | backup_router.register('full', FullBackupsTopLevelViewSet, base_name='full_backups-toplevel') 9 | backup_router.register('partial', PartialBackupsTopLevelViewSet, base_name='partial_backups-toplevel') 10 | 11 | urlpatterns = [path('', TopBackupsLinkView.as_view(), name='backups')] 12 | 13 | urlpatterns += backup_router.urls 14 | -------------------------------------------------------------------------------- /apps/batch/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/batch/__init__.py -------------------------------------------------------------------------------- /apps/batch/decorators.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | import functools 3 | 4 | from apps.batch.exceptions import BatchingNotAllowed 5 | 6 | 7 | def disallow_batching(f): 8 | """ 9 | Used to disallow including that endpoint in batching requests. 10 | """ 11 | 12 | @functools.wraps(f) 13 | def outer(self, request, *args, **kwargs): 14 | if request.META.get('X_BATCHING', '') == '1': 15 | raise BatchingNotAllowed() 16 | return f(self, request, *args, **kwargs) 17 | 18 | return outer 19 | -------------------------------------------------------------------------------- /apps/batch/exceptions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework import status 3 | 4 | from apps.core.exceptions import SyncanoException 5 | 6 | 7 | class BatchLimitExceeded(SyncanoException): 8 | status_code = status.HTTP_400_BAD_REQUEST 9 | default_detail_fmt = 'Max requests per batch exceeded (%d).' 10 | 11 | def __init__(self, limit): 12 | detail = self.default_detail_fmt % limit 13 | super().__init__(detail) 14 | 15 | 16 | class BatchingNotAllowed(SyncanoException): 17 | status_code = status.HTTP_405_METHOD_NOT_ALLOWED 18 | default_detail = 'Batching not allowed.' 19 | -------------------------------------------------------------------------------- /apps/batch/serializers.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework import serializers 3 | 4 | from apps.batch.validators import PathValidator 5 | from apps.core.field_serializers import JSONField, LowercaseCharField 6 | from apps.core.validators import validate_batch_body 7 | 8 | 9 | class BatchRequestSerializer(serializers.Serializer): 10 | method = serializers.ChoiceField(choices=('GET', 'POST', 'PUT', 'PATCH', 'DELETE')) 11 | path = LowercaseCharField(max_length=8192, validators=[PathValidator()]) 12 | body = JSONField(default={}, validators=[validate_batch_body]) 13 | 14 | 15 | class BatchSerializer(serializers.Serializer): 16 | requests = BatchRequestSerializer(many=True) 17 | -------------------------------------------------------------------------------- /apps/batch/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/batch/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/batch/v1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.urls import path 3 | 4 | from apps.batch import views 5 | 6 | urlpatterns = [ 7 | path('batch/', views.BatchView.as_view(), name='batch'), 8 | ] 9 | -------------------------------------------------------------------------------- /apps/batch/validators.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | import re 3 | 4 | from rest_framework import serializers 5 | 6 | VALID_URL_REGEX = re.compile(r'^(?:/[a-z0-9-_\.]+)+/(\?[^\?]+){0,1}$') 7 | 8 | 9 | class PathValidator: 10 | def __call__(self, value): 11 | if not value.startswith('/{}/instances/{}/'.format(self.request.version, self.request.instance.name)): 12 | raise serializers.ValidationError( 13 | 'Path needs to point to an endpoint within current instance and API version.' 14 | ) 15 | 16 | if not VALID_URL_REGEX.match(value): 17 | raise serializers.ValidationError('Invalid path specified.') 18 | 19 | def set_context(self, serializer_field): 20 | self.request = serializer_field.parent.context['request'] 21 | -------------------------------------------------------------------------------- /apps/billing/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.billing.appconfig.AppConfig' 2 | -------------------------------------------------------------------------------- /apps/billing/appconfig.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.apps import AppConfig as _AppConfig 3 | 4 | 5 | class AppConfig(_AppConfig): 6 | name = 'apps.billing' 7 | 8 | def ready(self): 9 | from . import signal_handlers # noqa 10 | -------------------------------------------------------------------------------- /apps/billing/fixtures/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/billing/fixtures/__init__.py -------------------------------------------------------------------------------- /apps/billing/migrations/0002_auto_20150128_1538.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | import apps.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='coupon', 16 | name='currency', 17 | field=apps.core.fields.LowercaseCharField(default='usd', max_length=3, choices=[('usd', 'USD')]), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/billing/migrations/0007_auto_20150403_1434.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | 5 | def create_profiles(apps, schema_editor): 6 | Admin = apps.get_model('admins.Admin') 7 | Profile = apps.get_model('billing.Profile') 8 | for pk, customer_id in Admin.objects.values_list('pk', 'customer_id'): 9 | Profile.objects.create(admin_id=pk, customer_id=customer_id) 10 | 11 | 12 | class Migration(migrations.Migration): 13 | 14 | dependencies = [ 15 | ('billing', '0006_profile'), 16 | ] 17 | 18 | operations = [ 19 | migrations.RunPython(create_profiles), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/billing/migrations/0008_auto_20150408_1235.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.conf import settings 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('billing', '0007_auto_20150403_1434'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='profile', 15 | name='admin', 16 | field=models.OneToOneField(related_name='billing_profile', primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE), 17 | preserve_default=True, 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/billing/migrations/0010_auto_20150410_1255.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('billing', '0009_invoice_invoiceitem_transaction'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='transaction', 14 | name='amount', 15 | field=models.DecimalField(max_digits=12, decimal_places=5), 16 | preserve_default=True, 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/billing/migrations/0014_remove_profile_balance.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('billing', '0013_auto_20150416_0932'), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveField( 13 | model_name='profile', 14 | name='balance', 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /apps/billing/migrations/0015_auto_20150420_0758.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('billing', '0014_remove_profile_balance'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='invoiceitem', 14 | name='source', 15 | field=models.SmallIntegerField(choices=[(0, b'API requests'), (1, 'Storage')]), 16 | preserve_default=True, 17 | ), 18 | migrations.AlterField( 19 | model_name='transaction', 20 | name='source', 21 | field=models.SmallIntegerField(choices=[(0, b'API requests'), (1, 'Storage')]), 22 | preserve_default=True, 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /apps/billing/migrations/0016_auto_20150421_1355.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('billing', '0015_auto_20150420_0758'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterModelOptions( 13 | name='invoice', 14 | options={'ordering': ('id',)}, 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /apps/billing/migrations/0017_profile_soft_limit_reached.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('billing', '0016_auto_20150421_1355'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name='profile', 14 | name='soft_limit_reached', 15 | field=models.DateField(null=True, blank=True), 16 | preserve_default=True, 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/billing/migrations/0018_auto_20150421_1209.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | def fill_soft_limit_reached(apps, schema_editor): 6 | Profile = apps.get_model('billing.Profile') 7 | Profile.objects.filter(soft_limit_reached__isnull=True).update(soft_limit_reached='1970-01-01') 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('billing', '0017_profile_soft_limit_reached'), 14 | ] 15 | 16 | operations = [ 17 | migrations.RunPython(fill_soft_limit_reached), 18 | migrations.AlterField( 19 | model_name='profile', 20 | name='soft_limit_reached', 21 | field=models.DateField(), 22 | preserve_default=True, 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /apps/billing/migrations/0019_auto_20150422_1322.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('billing', '0018_auto_20150421_1209'), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveField( 13 | model_name='invoiceitem', 14 | name='external_id', 15 | ), 16 | migrations.RemoveField( 17 | model_name='invoiceitem', 18 | name='reference', 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/billing/migrations/0020_profile_hard_limit_reached.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import datetime 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0019_auto_20150422_1322'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='profile', 16 | name='hard_limit_reached', 17 | field=models.DateField(default=datetime.date(1970, 1, 1)), 18 | preserve_default=False, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/billing/migrations/0021_auto_20150424_1036.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import datetime 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0020_profile_hard_limit_reached'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='profile', 16 | name='hard_limit_reached', 17 | field=models.DateField(default=datetime.date(1970, 1, 1)), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/billing/migrations/0022_auto_20150427_1501.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import datetime 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0021_auto_20150424_1036'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='profile', 16 | name='soft_limit_reached', 17 | field=models.DateField(default=datetime.date(1970, 1, 1)), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/billing/migrations/0023_auto_20150428_1348.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | 5 | def change_plan_to_linear(apps, schema_editor): 6 | PricingPlan = apps.get_model('billing', "PricingPlan") 7 | Subscription = apps.get_model('billing', "Subscription") 8 | try: 9 | linear_pricing_plan = PricingPlan.objects.get(code_name='LINEAR') 10 | Subscription.objects.update(pricing_plan=linear_pricing_plan) 11 | except PricingPlan.DoesNotExist: 12 | pass 13 | 14 | 15 | class Migration(migrations.Migration): 16 | 17 | dependencies = [ 18 | ('billing', '0022_auto_20150427_1501'), 19 | ] 20 | 21 | operations = [ 22 | migrations.RunPython(change_plan_to_linear), 23 | ] 24 | -------------------------------------------------------------------------------- /apps/billing/migrations/0024_length.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('billing', '0023_auto_20150428_1348'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='invoiceitem', 14 | name='instance_name', 15 | field=models.CharField(max_length=64), 16 | preserve_default=True, 17 | ), 18 | migrations.AlterField( 19 | model_name='transaction', 20 | name='instance_name', 21 | field=models.CharField(max_length=64), 22 | preserve_default=True, 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /apps/billing/migrations/0025_auto_20150512_1153.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from datetime import date 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | def fix_transaction_period(apps, schema_editor): 8 | Transaction = apps.get_model('billing.Transaction') 9 | transactions = Transaction.objects.exclude(period__day=1).values('period').annotate(total=models.Count('pk')) 10 | for transaction in transactions: 11 | period = (transaction['period'] or date.today()).replace(day=1) 12 | Transaction.objects.filter(period=transaction['period']).update(period=period) 13 | 14 | 15 | class Migration(migrations.Migration): 16 | 17 | dependencies = [ 18 | ('billing', '0024_length'), 19 | ] 20 | 21 | operations = [ 22 | migrations.RunPython(fix_transaction_period), 23 | ] 24 | -------------------------------------------------------------------------------- /apps/billing/migrations/0027_cleanup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('billing', '0026_instance_id'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterModelOptions( 13 | name='subscription', 14 | options={'ordering': ('id',)}, 15 | ), 16 | migrations.RemoveField( 17 | model_name='pricingplan', 18 | name='available', 19 | ), 20 | migrations.AlterUniqueTogether( 21 | name='subscription', 22 | unique_together=set([]), 23 | ), 24 | migrations.RemoveField( 25 | model_name='subscription', 26 | name='charged_until', 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /apps/billing/migrations/0033_strippedslug.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | import apps.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0032_cleanup'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='pricingplan', 16 | name='name', 17 | field=apps.core.fields.StrippedSlugField(unique=True, max_length=64), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/billing/migrations/0034_invoice_is_prorated.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('billing', '0033_strippedslug'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name='invoice', 14 | name='is_prorated', 15 | field=models.BooleanField(default=False), 16 | preserve_default=True, 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/billing/migrations/0035_price_digits.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from decimal import Decimal 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0034_invoice_is_prorated'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='invoiceitem', 16 | name='price', 17 | field=models.DecimalField(default=Decimal('0'), max_digits=12, decimal_places=7), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/billing/migrations/0037_adminlimit_data.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | 5 | def create_limits(apps, schema_editor): 6 | Admin = apps.get_model('admins.Admin') 7 | AdminLimit = apps.get_model('billing.AdminLimit') 8 | for pk in Admin.objects.values_list('pk', flat=True): 9 | AdminLimit.objects.create(admin_id=pk) 10 | 11 | 12 | class Migration(migrations.Migration): 13 | 14 | dependencies = [ 15 | ('billing', '0036_adminlimit'), 16 | ] 17 | 18 | operations = [ 19 | migrations.RunPython(create_limits), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/billing/migrations/0038_cleanup_nulls_data.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | def cleanup_nulls(apps, schema_editor): 6 | Invoice = apps.get_model('billing', 'Invoice') 7 | Transaction = apps.get_model('billing', 'Transaction') 8 | models = (Invoice, Transaction) 9 | 10 | for model in models: 11 | model.objects.filter(admin__isnull=True).delete() 12 | 13 | 14 | class Migration(migrations.Migration): 15 | dependencies = [ 16 | ('billing', '0037_adminlimit_data'), 17 | ] 18 | 19 | operations = [ 20 | migrations.RunPython(cleanup_nulls), 21 | ] 22 | -------------------------------------------------------------------------------- /apps/billing/migrations/0039_cleanup_nulls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | dependencies = [ 7 | ('billing', '0038_cleanup_nulls_data'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name='invoice', 13 | name='admin', 14 | field=models.ForeignKey(related_name='invoices', to='admins.Admin', on_delete=models.CASCADE), 15 | ), 16 | migrations.AlterField( 17 | model_name='transaction', 18 | name='admin', 19 | field=models.ForeignKey(related_name='transactions', to='admins.Admin', on_delete=models.CASCADE), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /apps/billing/migrations/0043_auto_20160210_1504.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.2 on 2016-02-10 15:04 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('billing', '0042_charged_until_data'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterIndexTogether( 14 | name='profile', 15 | index_together=set([('admin', 'hard_limit_reached'), ('admin', 'soft_limit_reached')]), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/billing/migrations/0045_add_range_field_with_default.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.2 on 2016-02-24 13:41 3 | import django.contrib.postgres.fields.ranges 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0044_codebox_time'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='subscription', 16 | name='range', 17 | field=django.contrib.postgres.fields.ranges.DateRangeField(default=(None, None), db_index=True), 18 | preserve_default=False, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/billing/migrations/0047_range_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.4 on 2016-05-04 11:07 3 | import django.contrib.postgres.fields.ranges 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('billing', '0046_remove_default_and_update_range_field'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='subscription', 16 | name='range', 17 | field=django.contrib.postgres.fields.ranges.DateRangeField(), 18 | ), 19 | migrations.RunSQL(""" 20 | CREATE INDEX subscription_range ON billing_subscription 21 | USING GIST (range); 22 | """ 23 | ) 24 | ] 25 | -------------------------------------------------------------------------------- /apps/billing/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/billing/migrations/__init__.py -------------------------------------------------------------------------------- /apps/billing/sql/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/billing/sql/__init__.py -------------------------------------------------------------------------------- /apps/billing/sql/subscription.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX subscription_range ON billing_subscription 2 | USING GIST (range); 3 | -------------------------------------------------------------------------------- /apps/billing/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/billing/tests/__init__.py -------------------------------------------------------------------------------- /apps/billing/tests/test_charging.py: -------------------------------------------------------------------------------- 1 | from datetime import timedelta 2 | from unittest import mock 3 | 4 | from django.test import TestCase 5 | from django.utils.timezone import now 6 | from django_dynamic_fixture import G 7 | 8 | from apps.metrics.helpers import floor_to_base 9 | from apps.metrics.models import WorkLogEntry 10 | from apps.metrics.tasks import AggregateHourTask 11 | 12 | last_full_hour = floor_to_base(now(), base=timedelta(hours=1)) 13 | 14 | 15 | class ChargingTestCase(TestCase): 16 | @mock.patch('apps.billing.tasks.ChargeOneHour.run') 17 | def test_one_hour_is_charged_when_metrics_finish_calculating(self, mock_func): 18 | worklog = G(WorkLogEntry) 19 | AggregateHourTask.delay(worklog.id, (last_full_hour + timedelta(hours=1)).isoformat()) 20 | mock_func.assert_called_with(last_full_hour.isoformat()) 21 | -------------------------------------------------------------------------------- /apps/billing/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/billing/viewsets.py: -------------------------------------------------------------------------------- 1 | from rest_framework.viewsets import ViewSetMixin 2 | 3 | from apps.billing.generics import GenericStripeAPIView 4 | 5 | 6 | class GenericStripeViewSet(ViewSetMixin, GenericStripeAPIView): 7 | """ 8 | The GenericStripeViewSet class does not provide any actions by default, 9 | but does include the base set of generic view behavior, such as 10 | the `get_resource` and `retrieve_resource` methods. 11 | """ 12 | pass 13 | -------------------------------------------------------------------------------- /apps/channels/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.channels.appconfig.AppConfig' 2 | -------------------------------------------------------------------------------- /apps/channels/appconfig.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.apps import AppConfig as _AppConfig 3 | 4 | 5 | class AppConfig(_AppConfig): 6 | name = 'apps.channels' 7 | 8 | def ready(self): 9 | from . import signal_handlers # noqa 10 | -------------------------------------------------------------------------------- /apps/channels/backup.py: -------------------------------------------------------------------------------- 1 | from apps.backups import site 2 | from apps.backups.options import ModelBackupByName 3 | 4 | from .models import Channel 5 | 6 | site.register(Channel, ModelBackupByName) 7 | -------------------------------------------------------------------------------- /apps/channels/exceptions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework import status 3 | 4 | from apps.core.exceptions import SyncanoException 5 | 6 | 7 | class RoomRequired(SyncanoException): 8 | status_code = status.HTTP_400_BAD_REQUEST 9 | field = 'room' 10 | default_detail = 'This field is required for channels with separate rooms.' 11 | 12 | 13 | class CustomPublishNotAllowed(SyncanoException): 14 | status_code = status.HTTP_403_FORBIDDEN 15 | default_detail = 'Custom publish not allowed on this channel.' 16 | -------------------------------------------------------------------------------- /apps/channels/helpers.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | 3 | 4 | def create_author_dict(request): 5 | author = {} 6 | if request.user.is_authenticated: 7 | author['admin'] = request.user.id 8 | else: 9 | author['api_key'] = request.auth.id 10 | if request.auth_user: 11 | author['user'] = request.auth_user.id 12 | return author 13 | -------------------------------------------------------------------------------- /apps/channels/migrations/0002_length.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | import apps.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('channels', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='change', 16 | name='room', 17 | field=apps.core.fields.LowercaseCharField(max_length=64, null=True), 18 | preserve_default=True, 19 | ), 20 | migrations.AlterField( 21 | model_name='channel', 22 | name='name', 23 | field=apps.core.fields.StrippedSlugField(max_length=64), 24 | preserve_default=True, 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /apps/channels/migrations/0003_channel_description.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import django.core.validators 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('channels', '0002_length'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='channel', 15 | name='description', 16 | field=models.TextField(blank=True, validators=[django.core.validators.MaxLengthValidator(256)]), 17 | preserve_default=True, 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/channels/migrations/0004_like_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('channels', '0003_channel_description'), 9 | ] 10 | 11 | operations = [ 12 | migrations.RunSQL(""" 13 | CREATE INDEX channel_name_like ON channels_channel 14 | USING BTREE (name varchar_pattern_ops); 15 | """) 16 | ] 17 | -------------------------------------------------------------------------------- /apps/channels/migrations/0005_description.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('channels', '0004_like_index'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='channel', 14 | name='description', 15 | field=models.TextField(max_length=256, blank=True), 16 | preserve_default=True, 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/channels/migrations/0007_default_channel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | 5 | def create_channel(apps, schema_editor): 6 | Channel = apps.get_model('channels', 'Channel') 7 | Channel.objects.update_or_create({ 8 | 'type': 1, # Channel.TYPES.SEPARATE_ROOMS 9 | 'options': {'custom_publish': 'True'}, 10 | 'acl': {'*': ['read', 'write', 'subscribe', 'publish', 'custom_publish']}, 11 | }, name='default') 12 | 13 | 14 | class Migration(migrations.Migration): 15 | dependencies = [ 16 | ('channels', '0006_acl'), 17 | ] 18 | 19 | operations = [ 20 | migrations.RunPython(create_channel) 21 | ] 22 | -------------------------------------------------------------------------------- /apps/channels/migrations/0008_longer_room.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | import apps.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('channels', '0007_default_channel'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='change', 16 | name='room', 17 | field=apps.core.fields.LowercaseCharField(max_length=128, null=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/channels/migrations/0009_eventlog_channel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | 5 | def create_channel(apps, schema_editor): 6 | Channel = apps.get_model('channels', 'Channel') 7 | Channel.objects.update_or_create({ 8 | 'type': 1, # Channel.TYPES.SEPARATE_ROOMS 9 | 'options': {'custom_publish': 'False'}, 10 | 'acl': {}, 11 | }, name='eventlog') 12 | 13 | 14 | class Migration(migrations.Migration): 15 | dependencies = [ 16 | ('channels', '0008_longer_room'), 17 | ] 18 | 19 | operations = [ 20 | migrations.RunPython(create_channel) 21 | ] 22 | -------------------------------------------------------------------------------- /apps/channels/migrations/0010_remove_change.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('channels', '0009_eventlog_channel'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AlterIndexTogether( 12 | name='change', 13 | index_together=set(), 14 | ), 15 | migrations.RemoveField( 16 | model_name='change', 17 | name='channel', 18 | ), 19 | migrations.DeleteModel( 20 | name='Change', 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /apps/channels/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/channels/migrations/__init__.py -------------------------------------------------------------------------------- /apps/channels/sql/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/channels/sql/__init__.py -------------------------------------------------------------------------------- /apps/channels/sql/channel.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX channel_name_like ON channels_channel 2 | USING BTREE (name varchar_pattern_ops); 3 | 4 | CREATE INDEX channel_acl_users ON channels_channel 5 | USING GIN (_users); 6 | 7 | CREATE INDEX channel_acl_groups ON channels_channel 8 | USING GIN (_groups); 9 | 10 | CREATE INDEX channel_acl_public ON channels_channel 11 | USING BTREE (_public) WHERE _public = true; 12 | -------------------------------------------------------------------------------- /apps/channels/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/channels/tests/__init__.py -------------------------------------------------------------------------------- /apps/channels/throttling.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.billing.models import AdminLimit 3 | from apps.instances.throttling import InstanceRateThrottle 4 | 5 | 6 | class ChannelPollRateThrottle(InstanceRateThrottle): 7 | def get_instance_rate(self, request, view): 8 | return AdminLimit.get_for_admin(request.instance.owner_id).get_poll_rate() 9 | -------------------------------------------------------------------------------- /apps/channels/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/channels/v1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.channels.v1 import views 3 | from apps.core.routers import NestedSimpleRouter 4 | 5 | router = NestedSimpleRouter() 6 | channel_router = router.register('channels', views.ChannelViewSet) 7 | channel_router.register('history', views.ChangeViewSet, 8 | base_name='change', 9 | parents_query_lookups=['channel']) 10 | 11 | urlpatterns = router.urls 12 | -------------------------------------------------------------------------------- /apps/channels/v2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/channels/v2/__init__.py -------------------------------------------------------------------------------- /apps/channels/v2/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.channels.v2 import views as v2_views 3 | from apps.core.routers import NestedSimpleRouter 4 | 5 | router = NestedSimpleRouter() 6 | channel_router = router.register('channels', v2_views.ChannelViewSet) 7 | channel_router.register('history', v2_views.ChangeViewSet, 8 | base_name='change', 9 | parents_query_lookups=['channel']) 10 | 11 | urlpatterns = router.urls 12 | -------------------------------------------------------------------------------- /apps/codeboxes/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.codeboxes.appconfig.AppConfig' 2 | -------------------------------------------------------------------------------- /apps/codeboxes/appconfig.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.apps import AppConfig as _AppConfig 3 | 4 | 5 | class AppConfig(_AppConfig): 6 | name = 'apps.codeboxes' 7 | 8 | def ready(self): 9 | from . import signal_handlers # noqa 10 | -------------------------------------------------------------------------------- /apps/codeboxes/backup.py: -------------------------------------------------------------------------------- 1 | from apps.backups import site 2 | 3 | from .models import CodeBox, CodeBoxSchedule 4 | 5 | site.register([CodeBox, CodeBoxSchedule]) 6 | -------------------------------------------------------------------------------- /apps/codeboxes/managers.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.db import models 3 | from django.utils.timezone import now 4 | 5 | 6 | class SchedulerManager(models.Manager): 7 | def get_for_process(self): 8 | return self.get_queryset().filter(scheduled_next__lte=now(), codebox___is_live=True) 9 | -------------------------------------------------------------------------------- /apps/codeboxes/migrations/0032_remove_codeboxschedule__is_live.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('codeboxes', '0001_squashed_0032'), 9 | ] 10 | 11 | operations = [ 12 | ] 13 | -------------------------------------------------------------------------------- /apps/codeboxes/migrations/0035_codebox_socket.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2016-11-29 20:00 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('sockets', '0009_remove_obsolete_fields'), 11 | ('codeboxes', '0034_auto_add_encoding'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='codebox', 17 | name='socket', 18 | field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='sockets.Socket'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/codeboxes/migrations/0036_codeboxschedule_socket.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2017-01-03 11:52 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('sockets', '0010_socket_version'), 11 | ('codeboxes', '0035_codebox_socket'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='codeboxschedule', 17 | name='socket', 18 | field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='sockets.Socket'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/codeboxes/migrations/0037_add_checksum.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('codeboxes', '0036_codeboxschedule_socket'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name='codebox', 14 | name='checksum', 15 | field=models.CharField(blank=True, default=None, max_length=32, null=True), 16 | ), 17 | migrations.AlterIndexTogether( 18 | name='codebox', 19 | index_together=set([('socket', 'checksum')]), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /apps/codeboxes/migrations/0039_path_unique_with_socket.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2017-03-15 22:23 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('codeboxes', '0038_add_path'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterUniqueTogether( 14 | name='codebox', 15 | unique_together=set([('socket', 'path')]), 16 | ), 17 | migrations.AlterIndexTogether( 18 | name='codebox', 19 | index_together=set([]), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /apps/codeboxes/migrations/0040_codeboxschedule_event_handler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('codeboxes', '0039_path_unique_with_socket'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name='codeboxschedule', 14 | name='event_handler', 15 | field=models.TextField(default=None, null=True), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/codeboxes/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/codeboxes/migrations/__init__.py -------------------------------------------------------------------------------- /apps/codeboxes/permissions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework.permissions import BasePermission 3 | 4 | 5 | class ProtectScriptAccess(BasePermission): 6 | """ 7 | Disallow editing scripts that are bound to socket. 8 | """ 9 | allowed_actions = ('retrieve',) 10 | 11 | def has_object_permission(self, request, view, obj): 12 | return getattr(obj, 'socket_id', None) is None or view.action in self.allowed_actions 13 | 14 | 15 | class ProtectScheduleAccess(ProtectScriptAccess): 16 | """ 17 | Disallow editing schedules that are bound to socket. 18 | """ 19 | allowed_actions = ('retrieve',) 20 | -------------------------------------------------------------------------------- /apps/codeboxes/signals.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.dispatch import Signal 3 | 4 | codebox_finished = Signal(providing_args=["instance", "object_id", "trace"]) 5 | -------------------------------------------------------------------------------- /apps/codeboxes/sql/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/codeboxes/sql/__init__.py -------------------------------------------------------------------------------- /apps/codeboxes/sql/codebox.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX codebox_label_like ON codeboxes_codebox 2 | USING BTREE (label varchar_pattern_ops); 3 | -------------------------------------------------------------------------------- /apps/codeboxes/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/codeboxes/tests/__init__.py -------------------------------------------------------------------------------- /apps/codeboxes/tests/mixins.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.codeboxes.container_manager import ContainerManager 3 | from apps.core.tests.mixins import CleanupTestCaseMixin 4 | 5 | 6 | class CodeBoxCleanupTestMixin(CleanupTestCaseMixin): 7 | def tearDown(self): 8 | super().tearDown() 9 | ContainerManager.dispose_all_containers() 10 | -------------------------------------------------------------------------------- /apps/codeboxes/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/codeboxes/v1_1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/codeboxes/v1_1/serializers.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.core.mixins.serializers import RemapperMixin 3 | 4 | from ..v1.serializers import CodeBoxScheduleSerializer as _CodeBoxScheduleSerializer 5 | 6 | 7 | class CodeBoxScheduleSerializer(RemapperMixin, _CodeBoxScheduleSerializer): 8 | hyperlinks = ( 9 | ('self', 'codebox-schedule-detail', ( 10 | 'instance.name', 11 | 'pk', 12 | )), 13 | ('traces', 'schedule-trace-list', ( 14 | 'instance.name', 15 | 'pk', 16 | )), 17 | ('script', 'codebox-detail', ( 18 | 'instance.name', 19 | 'codebox_id', 20 | )), 21 | ) 22 | 23 | field_mappings = {'codebox': 'script'} 24 | -------------------------------------------------------------------------------- /apps/codeboxes/v1_1/views.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.codeboxes.v1_1.serializers import CodeBoxScheduleSerializer 3 | 4 | from ..v1 import views as v1_views 5 | 6 | 7 | class ScheduleViewSet(v1_views.ScheduleViewSet): 8 | serializer_class = CodeBoxScheduleSerializer 9 | -------------------------------------------------------------------------------- /apps/codeboxes/v2/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/controlpanel/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/controlpanel/__init__.py -------------------------------------------------------------------------------- /apps/controlpanel/filters.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | import django_filters 3 | 4 | from apps.admins.models import Admin 5 | 6 | 7 | class AdminViewFilter(django_filters.FilterSet): 8 | email = django_filters.CharFilter(name="email", lookup_expr="istartswith") 9 | 10 | class Meta: 11 | model = Admin 12 | fields = ['email', 'first_name', 'last_name'] 13 | -------------------------------------------------------------------------------- /apps/controlpanel/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/controlpanel/migrations/__init__.py -------------------------------------------------------------------------------- /apps/controlpanel/permissions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework import permissions 3 | 4 | 5 | class IsStaffUser(permissions.BasePermission): 6 | """ 7 | Allow access only to admins with is_staff flag set, 8 | or users with valid STAFF_KEY set. 9 | """ 10 | 11 | def has_permission(self, request, view): 12 | return (request.user and request.user.is_staff) or request.staff_user is not None 13 | -------------------------------------------------------------------------------- /apps/controlpanel/serializer.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework import serializers 3 | from rest_framework.serializers import Serializer 4 | 5 | 6 | class ExtendSerializer(Serializer): 7 | days = serializers.IntegerField(min_value=1, max_value=365, default=30) 8 | -------------------------------------------------------------------------------- /apps/controlpanel/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/controlpanel/tests/__init__.py -------------------------------------------------------------------------------- /apps/controlpanel/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/controlpanel/v1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework.routers import SimpleRouter 3 | 4 | from apps.controlpanel import views 5 | 6 | router = SimpleRouter() 7 | router.register('admins', views.AdminViewSet, base_name='cp-admin') 8 | 9 | urlpatterns = router.urls 10 | -------------------------------------------------------------------------------- /apps/core/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.core.appconfig.AppConfig' 2 | -------------------------------------------------------------------------------- /apps/core/backends/__init__.py: -------------------------------------------------------------------------------- 1 | def make_cache_key(key, key_prefix, version): 2 | return key 3 | -------------------------------------------------------------------------------- /apps/core/contextmanagers.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from contextlib import contextmanager 3 | 4 | 5 | def blank_function(*args, **kwargs): 6 | pass 7 | 8 | 9 | @contextmanager 10 | def ignore_signal(*signals): 11 | for signal in signals: 12 | signal._send = signal.send 13 | signal.send = blank_function 14 | try: 15 | yield 16 | finally: 17 | for signal in signals: 18 | signal.send = signal._send 19 | -------------------------------------------------------------------------------- /apps/core/filter_fields.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | 3 | from django.forms import NullBooleanField, Select 4 | from django_filters import Filter 5 | from rest_framework.fields import BooleanField 6 | 7 | 8 | class LowercaseBooleanSelect(Select): 9 | 10 | def value_from_datadict(self, data, files, name): 11 | value = data.get(name) 12 | try: 13 | if value in BooleanField.TRUE_VALUES: 14 | return True 15 | elif value in BooleanField.FALSE_VALUES: 16 | return False 17 | except TypeError: 18 | pass 19 | 20 | 21 | class LowercaseNullBooleanField(NullBooleanField): 22 | widget = LowercaseBooleanSelect 23 | 24 | 25 | class LowercaseBooleanFilter(Filter): 26 | field_class = LowercaseNullBooleanField 27 | -------------------------------------------------------------------------------- /apps/core/fixtures/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/core/fixtures/__init__.py -------------------------------------------------------------------------------- /apps/core/fixtures/development.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pk":1, 4 | "model":"sites.site", 5 | "fields":{ 6 | "domain":"syncano.test", 7 | "name":"Syncano" 8 | } 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /apps/core/fixtures/production.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "pk":1, 4 | "model":"sites.site", 5 | "fields":{ 6 | "domain":"syncano.io", 7 | "name":"Syncano" 8 | } 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /apps/core/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/core/management/__init__.py -------------------------------------------------------------------------------- /apps/core/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/core/management/commands/__init__.py -------------------------------------------------------------------------------- /apps/core/management/commands/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/core/managers.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from livefield.managers import LiveManagerBase 3 | 4 | from apps.core.querysets import LiveQuerySet 5 | 6 | LiveManager = LiveManagerBase.from_queryset(LiveQuerySet) 7 | -------------------------------------------------------------------------------- /apps/core/models.py: -------------------------------------------------------------------------------- 1 | # even if we don't have anything here, file is needed so that models in tests are created 2 | -------------------------------------------------------------------------------- /apps/core/postgresql_backend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/core/postgresql_backend/__init__.py -------------------------------------------------------------------------------- /apps/core/response.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.http import HttpResponse 3 | 4 | 5 | class JSONResponse(HttpResponse): 6 | def __init__(self, *args, **kwargs): 7 | kwargs.setdefault('content_type', 'application/json') 8 | super().__init__(*args, **kwargs) 9 | -------------------------------------------------------------------------------- /apps/core/serializers.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework import serializers 3 | 4 | from apps.core.mixins.serializers import AclMixin 5 | 6 | 7 | class NewNameSerializer(serializers.Serializer): 8 | new_name = serializers.CharField() 9 | 10 | 11 | class EndpointAclSerializer(AclMixin, serializers.Serializer): 12 | is_endpoint_acl = True 13 | -------------------------------------------------------------------------------- /apps/core/signals.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.db.models.signals import ModelSignal 3 | from django.dispatch import Signal 4 | 5 | pre_soft_delete = ModelSignal(providing_args=['instance', 'using'], use_caching=True) 6 | post_soft_delete = ModelSignal(providing_args=['instance', 'using'], use_caching=True) 7 | 8 | pre_tenant_migrate = Signal(providing_args=['tenant', 'verbosity', 'using']) 9 | post_tenant_migrate = Signal(providing_args=['tenant', 'verbosity', 'using', 'created', 'partial']) 10 | post_full_migrate = Signal(providing_args=['verbosity', 'using']) 11 | 12 | apiview_view_processed = Signal(providing_args=['view', 'instance', 'action']) 13 | apiview_finalize_response = Signal(providing_args=['view', 'request', 'response']) 14 | -------------------------------------------------------------------------------- /apps/core/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/core/tests/__init__.py -------------------------------------------------------------------------------- /apps/core/tests/test_helpers.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.test import TestCase 3 | from django.utils.translation import ugettext_lazy 4 | 5 | from apps.core.helpers import evaluate_promises 6 | 7 | 8 | class TestHelpers(TestCase): 9 | def test_evaluate_promises(self): 10 | real_text = 'something lazy' 11 | lazy_text = ugettext_lazy(real_text) 12 | self.assertEqual(evaluate_promises(lazy_text), real_text) 13 | self.assertEqual(evaluate_promises({'key': ['something', lazy_text]}), {'key': ['something', real_text]}) 14 | -------------------------------------------------------------------------------- /apps/core/tests/test_views.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse 2 | from rest_framework import status 3 | from rest_framework.test import APITestCase 4 | 5 | from apps.core.tests.mixins import CleanupTestCaseMixin 6 | 7 | 8 | class TestViews(CleanupTestCaseMixin, APITestCase): 9 | def assert_url_works(self, url): 10 | response = self.client.get(url) 11 | self.assertEqual(response.status_code, status.HTTP_200_OK) 12 | 13 | def test_if_links_endpoint_works(self): 14 | self.assert_url_works(reverse('v1:links')) 15 | self.assert_url_works(reverse('v1.1:links')) 16 | self.assert_url_works(reverse('v2:links')) 17 | -------------------------------------------------------------------------------- /apps/core/tokens.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.tokens import PasswordResetTokenGenerator 2 | from django.utils.crypto import salted_hmac 3 | from django.utils.http import int_to_base36 4 | 5 | 6 | class TokenGenerator(PasswordResetTokenGenerator): 7 | 8 | def _make_token_with_timestamp(self, user, timestamp): 9 | ts_b36 = int_to_base36(timestamp) 10 | key_salt = 'apps.core.tokens.TokenGenerator' 11 | value = (str(user.pk) + user.password + str(timestamp)) 12 | hash = salted_hmac(key_salt, value).hexdigest()[::2] 13 | return '%s-%s' % (ts_b36, hash) 14 | 15 | 16 | default_token_generator = TokenGenerator() 17 | -------------------------------------------------------------------------------- /apps/core/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.conf import settings 3 | from django.urls import path 4 | 5 | from apps.core.views import ApiLinksView, loader_token 6 | 7 | urlpatterns = [ 8 | path('%s/' % settings.LOADERIO_TOKEN, loader_token), 9 | path('', ApiLinksView.as_view(), name='api-links'), 10 | ] 11 | -------------------------------------------------------------------------------- /apps/core/versioning.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework.versioning import NamespaceVersioning as _NamespaceVersioning 3 | 4 | 5 | class NamespaceVersioning(_NamespaceVersioning): 6 | def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra): 7 | if request.version is not None: 8 | viewname = self.get_versioned_viewname(viewname, request) 9 | # We do not want absolute uris so just pass None for request 10 | return super(_NamespaceVersioning, self).reverse( 11 | viewname, args, kwargs, None, format, **extra 12 | ) 13 | -------------------------------------------------------------------------------- /apps/data/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.data.appconfig.AppConfig' 2 | -------------------------------------------------------------------------------- /apps/data/adapters.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.contrib.gis.db.backends.postgis.adapter import PostGISAdapter as _PostGISAdapter 3 | from psycopg2.extras import Json as _Json 4 | 5 | 6 | class Json(_Json): 7 | # Json adapter with eq and repr 8 | 9 | def __eq__(self, other): 10 | return ( 11 | isinstance(other, Json) and 12 | self.adapted == other.adapted 13 | ) 14 | 15 | def __repr__(self): 16 | return str(self.adapted) 17 | 18 | 19 | class PostGISAdapter(_PostGISAdapter): 20 | def getquoted(self): 21 | # Casts to text so it is properly handled in hstore 22 | return '%s::text' % super().getquoted() 23 | -------------------------------------------------------------------------------- /apps/data/appconfig.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.apps import AppConfig as _AppConfig 3 | 4 | 5 | class AppConfig(_AppConfig): 6 | name = 'apps.data' 7 | 8 | def ready(self): 9 | from . import signal_handlers # noqa 10 | -------------------------------------------------------------------------------- /apps/data/contextmanagers.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from contextlib import contextmanager 3 | 4 | from apps.data.models import DataObject 5 | 6 | 7 | @contextmanager 8 | def loaded_klass(klass): 9 | old_klass = getattr(DataObject, 'loaded_klass', None) 10 | DataObject.load_klass(klass) 11 | try: 12 | yield 13 | finally: 14 | if old_klass: 15 | DataObject.load_klass(old_klass) 16 | -------------------------------------------------------------------------------- /apps/data/exceptions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework import status 3 | 4 | from apps.core.exceptions import SyncanoException 5 | 6 | 7 | class InvalidQuery(SyncanoException): 8 | status_code = status.HTTP_400_BAD_REQUEST 9 | field = 'query' 10 | default_detail = 'Invalid query provided.' 11 | 12 | 13 | class KlassCountExceeded(SyncanoException): 14 | status_code = status.HTTP_400_BAD_REQUEST 15 | default_detail_fmt = 'Class count exceeded (%d).' 16 | 17 | def __init__(self, limit): 18 | detail = self.default_detail_fmt % limit 19 | super().__init__(detail) 20 | 21 | 22 | class ChannelPublishNotAllowed(SyncanoException): 23 | status_code = status.HTTP_403_FORBIDDEN 24 | default_detail = 'You do not have permission to publish data to specified channel.' 25 | -------------------------------------------------------------------------------- /apps/data/migrations/0016_estimate_fix.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | 5 | class Migration(migrations.Migration): 6 | dependencies = [ 7 | ('data', '0001_squashed_0016'), 8 | ] 9 | 10 | operations = [ 11 | ] 12 | -------------------------------------------------------------------------------- /apps/data/migrations/0017_to_intarray.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | SQL = """ 5 | CREATE FUNCTION to_intarray(TEXT) 6 | RETURNS INTEGER[] AS $$ 7 | SELECT $1 :: INTEGER[]; 8 | $$ LANGUAGE SQL IMMUTABLE; 9 | """ 10 | 11 | 12 | class Migration(migrations.Migration): 13 | dependencies = [ 14 | ('data', '0016_estimate_fix'), 15 | ] 16 | 17 | operations = [ 18 | migrations.RunSQL(SQL), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/data/migrations/0022_auto_20160606_0837.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2016-06-06 08:37 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('data', '0021_klass_acl'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='dataobject', 15 | name='channel_room', 16 | field=models.CharField(blank=True, db_index=True, default=None, max_length=64, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/data/migrations/0023_klass_acl_indexes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.6 on 2016-06-06 11:22 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('data', '0022_auto_20160606_0837'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RunSQL(""" 14 | CREATE INDEX klass_acl_users ON data_klass 15 | USING GIN (_users); 16 | 17 | CREATE INDEX klass_acl_groups ON data_klass 18 | USING GIN (_groups); 19 | 20 | CREATE INDEX klass_acl_public ON data_klass 21 | USING BTREE (_public) WHERE _public = true; 22 | """), 23 | ] 24 | -------------------------------------------------------------------------------- /apps/data/migrations/0025_klass_refs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import jsonfield.fields 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('data', '0024_klass_visible'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='klass', 15 | name='refs', 16 | field=jsonfield.fields.JSONField(default={}), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/data/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/data/migrations/__init__.py -------------------------------------------------------------------------------- /apps/data/querysets.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.core.querysets import CountEstimateLiveQuerySet 3 | 4 | 5 | class KlassQuerySet(CountEstimateLiveQuerySet): 6 | def include_object_count(self, real_limit=1000): 7 | alias = self.query.table_map.get(self.query.base_table)[0] 8 | query = "'SELECT id FROM data_dataobject WHERE _klass_id=' || \"{alias}\".\"id\"".format( 9 | alias=alias 10 | ) 11 | return self.add_count_estimate('_objects_count', query, real_limit=real_limit, raw=True) 12 | -------------------------------------------------------------------------------- /apps/data/sql/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/data/sql/__init__.py -------------------------------------------------------------------------------- /apps/data/sql/class.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX class_name_like ON data_klass 2 | USING BTREE (name varchar_pattern_ops); 3 | 4 | CREATE INDEX klass_acl_users ON data_klass 5 | USING GIN (_users); 6 | 7 | CREATE INDEX klass_acl_groups ON data_klass 8 | USING GIN (_groups); 9 | 10 | CREATE INDEX klass_acl_public ON data_klass 11 | USING BTREE (_public) WHERE _public = true; 12 | -------------------------------------------------------------------------------- /apps/data/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/data/tests/__init__.py -------------------------------------------------------------------------------- /apps/data/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/data/v1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.core.routers import NestedSimpleRouter 3 | from apps.data.v1 import views 4 | 5 | router = NestedSimpleRouter() 6 | class_router = router.register('classes', views.KlassViewSet) 7 | class_router.register('objects', views.ObjectViewSet, 8 | base_name='dataobject', 9 | parents_query_lookups=['_klass'], 10 | post_as_update=True) 11 | 12 | urlpatterns = router.urls 13 | -------------------------------------------------------------------------------- /apps/data/v2/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/data/v2/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.core.routers import NestedSimpleRouter 3 | from apps.data.v2 import views as v2_views 4 | 5 | router = NestedSimpleRouter() 6 | class_router = router.register('classes', v2_views.KlassViewSet) 7 | class_router.register('objects', v2_views.ObjectViewSet, 8 | base_name='dataobject', 9 | parents_query_lookups=['_klass']) 10 | 11 | urlpatterns = router.urls 12 | -------------------------------------------------------------------------------- /apps/endpoints/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/endpoints/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/endpoints/tests/test_api.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse 2 | from rest_framework import status 3 | 4 | from apps.core.tests.testcases import SyncanoAPITestBase 5 | 6 | 7 | class TestEndpointsAPI(SyncanoAPITestBase): 8 | def assert_url_works(self, url): 9 | response = self.client.get(url) 10 | self.assertEqual(response.status_code, status.HTTP_200_OK) 11 | 12 | def test_if_endpoints_list_works(self): 13 | self.assert_url_works(reverse('v1.1:endpoints', args=(self.instance.name,))) 14 | self.assert_url_works(reverse('v2:endpoints', args=(self.instance.name,))) 15 | -------------------------------------------------------------------------------- /apps/endpoints/v1_1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/endpoints/v1_1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.urls import path 3 | 4 | from apps.endpoints.v1_1 import views 5 | 6 | urlpatterns = [ 7 | path('endpoints/', views.TopEndpointsLinkView.as_view(), name='endpoints'), 8 | ] 9 | -------------------------------------------------------------------------------- /apps/endpoints/v1_1/views.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework.reverse import reverse 3 | 4 | from apps.core.views import LinksView 5 | from apps.instances.mixins import InstanceBasedMixin 6 | 7 | 8 | class TopEndpointsLinkView(InstanceBasedMixin, LinksView): 9 | links = ( 10 | ('scripts', 'webhook-list'), 11 | ('data', 'hla-objects-list'), 12 | ) 13 | 14 | def generate_links(self): 15 | return {name: reverse(viewname, args=(self.request.instance.name,), request=self.request) 16 | for name, viewname in self.links} 17 | -------------------------------------------------------------------------------- /apps/endpoints/v2/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/endpoints/v2/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.urls import path 3 | 4 | from apps.endpoints.v2 import views 5 | 6 | urlpatterns = [ 7 | path('endpoints/', views.TopEndpointsLinkView.as_view(), name='endpoints'), 8 | ] 9 | -------------------------------------------------------------------------------- /apps/endpoints/v2/views.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.endpoints.v1_1 import views as v1_1_views 3 | 4 | 5 | class TopEndpointsLinkView(v1_1_views.TopEndpointsLinkView): 6 | links = ( 7 | ('scripts', 'webhook-list'), 8 | ('data', 'hla-objects-list'), 9 | ('sockets', 'socket-endpoint-list'), 10 | ) 11 | -------------------------------------------------------------------------------- /apps/high_level/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/high_level/__init__.py -------------------------------------------------------------------------------- /apps/high_level/backup.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.backups import site 3 | from apps.backups.options import ModelBackupByName 4 | 5 | from .models import DataObjectHighLevelApi 6 | 7 | site.register(DataObjectHighLevelApi, ModelBackupByName) 8 | -------------------------------------------------------------------------------- /apps/high_level/migrations/0002_dataobjecthighlevelapi_query.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import jsonfield.fields 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('high_level', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='dataobjecthighlevelapi', 15 | name='query', 16 | field=jsonfield.fields.JSONField(default={}, blank=True), 17 | preserve_default=True, 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/high_level/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/high_level/migrations/__init__.py -------------------------------------------------------------------------------- /apps/high_level/permissions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework.permissions import BasePermission 3 | 4 | 5 | class AllowedToClearCache(BasePermission): 6 | """ 7 | Allow clearing cache only users with write permissions to instance. 8 | """ 9 | 10 | def has_permission(self, request, view): 11 | return request.user.is_authenticated and request.user.is_role_satisfied(request.instance, 'write') 12 | 13 | 14 | class AllowEndpointAction(BasePermission): 15 | """ 16 | Allow all endpoint actions as they are proxied to data permission checks. 17 | """ 18 | 19 | def has_permission(self, request, view): 20 | return view.action.startswith('endpoint_') 21 | -------------------------------------------------------------------------------- /apps/high_level/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/high_level/tests/__init__.py -------------------------------------------------------------------------------- /apps/high_level/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/high_level/v1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework_extensions.routers import SimpleRouter 3 | 4 | from apps.high_level.v1 import views 5 | 6 | router = SimpleRouter() 7 | router.register('api/objects', views.DataObjectHighLevelApiViewSet, base_name='hla-objects') 8 | 9 | urlpatterns = router.urls 10 | -------------------------------------------------------------------------------- /apps/high_level/v1_1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/high_level/v1_1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework_extensions.routers import SimpleRouter 3 | 4 | from apps.high_level.v1 import views as v1_views 5 | 6 | router = SimpleRouter() 7 | router.register('endpoints/data', v1_views.DataObjectHighLevelApiViewSet, base_name='hla-objects') 8 | 9 | urlpatterns = router.urls 10 | -------------------------------------------------------------------------------- /apps/high_level/v2/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/high_level/v2/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.core.routers import EndpointRouter 3 | from apps.high_level.v2 import views 4 | 5 | router = EndpointRouter() 6 | router.register('endpoints/data', views.DataObjectHighLevelApiViewSet, base_name='hla-objects') 7 | 8 | urlpatterns = router.urls 9 | -------------------------------------------------------------------------------- /apps/high_level/validators.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.core.validators import RegexValidator 3 | 4 | from apps.data.validators import SchemaValidator 5 | 6 | 7 | def data_field_list_validator(value): 8 | slug_regex = SchemaValidator.name_regex 9 | validator = RegexValidator(slug_regex, message='Enter a comma separated list.') 10 | for parameter in value.split(','): 11 | validator(parameter) 12 | -------------------------------------------------------------------------------- /apps/hosting/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | default_app_config = 'apps.hosting.appconfig.AppConfig' 3 | -------------------------------------------------------------------------------- /apps/hosting/appconfig.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.apps import AppConfig as _AppConfig 3 | 4 | 5 | class AppConfig(_AppConfig): 6 | name = 'apps.hosting' 7 | 8 | def ready(self): 9 | from . import signal_handlers # noqa 10 | -------------------------------------------------------------------------------- /apps/hosting/helpers.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | 3 | 4 | def add_domains_to_syncano_instance(syncano_instance, domains): 5 | for domain in domains: 6 | if domain not in syncano_instance.domains: 7 | syncano_instance.domains.append(domain) 8 | 9 | 10 | def remove_domains_from_syncano_instance(syncano_instance, domains): 11 | for domain in domains: 12 | if domain in syncano_instance.domains: 13 | syncano_instance.domains.remove(domain) 14 | -------------------------------------------------------------------------------- /apps/hosting/migrations/0002_hosting_domains.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-18 09:03 3 | import django.contrib.postgres.fields 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('hosting', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='hosting', 16 | name='domains', 17 | field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=253), default=[], size=None), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/hosting/migrations/0004_hosting_is_active.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2016-10-27 11:19 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('hosting', '0003_hosting_label_rename_name'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='hosting', 15 | name='is_active', 16 | field=models.BooleanField(default=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/hosting/migrations/0007_hostingfile_checksum.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2016-12-02 13:05 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('hosting', '0006_hosting_ssl_status'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='hostingfile', 15 | name='checksum', 16 | field=models.CharField(max_length=32, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/hosting/migrations/0008_hosting_socket.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import django.db.models.deletion 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('sockets', '0009_remove_obsolete_fields'), 10 | ('hosting', '0007_hostingfile_checksum'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='hosting', 16 | name='socket', 17 | field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='sockets.Socket'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/hosting/migrations/0009_hosting_ssl_status_info.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2017-01-04 15:42 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('hosting', '0008_hosting_socket'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='hosting', 15 | name='ssl_status_info', 16 | field=models.SmallIntegerField(choices=[(0, 'ok'), (1, 'invalid_domain'), (2, 'cname_not_set'), (3, 'wrong_cname'), (4, 'unknown')], default=2), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/hosting/migrations/0010_removed_ssl_status_info.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2017-01-12 17:31 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('hosting', '0009_hosting_ssl_status_info'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='hosting', 15 | name='ssl_status_info', 16 | ), 17 | migrations.AlterField( 18 | model_name='hosting', 19 | name='ssl_status', 20 | field=models.SmallIntegerField(choices=[(-1, 'checking'), (0, 'off'), (1, 'on'), (2, 'invalid_domain'), (3, 'cname_not_set'), (4, 'wrong_cname'), (5, 'unknown')], default=0), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /apps/hosting/migrations/0011_hosting_auth.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2017-02-16 14:55 3 | import django.contrib.postgres.fields.hstore 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('hosting', '0010_removed_ssl_status_info'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='hosting', 16 | name='auth', 17 | field=django.contrib.postgres.fields.hstore.HStoreField(default={}), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/hosting/migrations/0012_hosting_config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | import apps.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('hosting', '0011_hosting_auth'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='hosting', 16 | name='config', 17 | field=apps.core.fields.NullableJSONField(blank=True, default=None, null=True), 18 | ), 19 | migrations.AlterField( 20 | model_name='hosting', 21 | name='config', 22 | field=apps.core.fields.NullableJSONField(blank=True, default={}, null=True), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /apps/hosting/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/hosting/migrations/__init__.py -------------------------------------------------------------------------------- /apps/hosting/negotiations.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework.negotiation import BaseContentNegotiation 3 | 4 | 5 | class IgnoreClientContentNegotiation(BaseContentNegotiation): 6 | def select_parser(self, request, parsers): 7 | return 8 | 9 | def select_renderer(self, request, renderers, format_suffix): 10 | """ 11 | Select the first renderer in the `.renderer_classes` list. 12 | """ 13 | return (renderers[0], renderers[0].media_type) 14 | -------------------------------------------------------------------------------- /apps/hosting/permissions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework.permissions import BasePermission 3 | 4 | 5 | class ProtectHostingAccess(BasePermission): 6 | """ 7 | Disallow editing hosting objects that are bound to socket. 8 | """ 9 | allowed_actions = ('retrieve', 'set_default', 'enable_ssl',) 10 | 11 | def has_object_permission(self, request, view, obj): 12 | return getattr(obj, 'socket_id', None) is None or view.action in self.allowed_actions 13 | -------------------------------------------------------------------------------- /apps/hosting/sql/hosting.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX hosting_file_order_level ON hosting_hostingfile 2 | USING BTREE (path, level DESC) WHERE _is_live=true; 3 | -------------------------------------------------------------------------------- /apps/hosting/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/hosting/v1_1/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/hosting/v1_1/__init__.py -------------------------------------------------------------------------------- /apps/hosting/v1_1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.core.routers import NestedSimpleRouter 3 | from apps.hosting.v1_1 import views 4 | 5 | router = NestedSimpleRouter() 6 | 7 | hosting_router = router.register( 8 | 'hosting', 9 | views.HostingViewSet, 10 | base_name='hosting' 11 | ) 12 | 13 | hosting_router.register( 14 | 'files', 15 | views.HostingFileViewSet, 16 | base_name='hosting-file', 17 | parents_query_lookups=[ 18 | 'hosting', 19 | ] 20 | ) 21 | 22 | urlpatterns = router.urls 23 | -------------------------------------------------------------------------------- /apps/hosting/v2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/hosting/v2/__init__.py -------------------------------------------------------------------------------- /apps/hosting/v2/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.core.routers import NestedSimpleRouter 3 | from apps.hosting.v1_1 import views as v1_1_views 4 | from apps.hosting.v2 import views as v2_views 5 | 6 | router = NestedSimpleRouter() 7 | 8 | hosting_router = router.register( 9 | 'hosting', 10 | v2_views.HostingViewSet, 11 | base_name='hosting' 12 | ) 13 | 14 | hosting_router.register( 15 | 'files', 16 | v1_1_views.HostingFileViewSet, 17 | base_name='hosting-file', 18 | parents_query_lookups=[ 19 | 'hosting', 20 | ] 21 | ) 22 | 23 | urlpatterns = router.urls 24 | -------------------------------------------------------------------------------- /apps/hosting/v2/views.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.hosting.v1_1 import views as v1_1_views 3 | from apps.hosting.v2.serializers import HostingDetailSerializer, HostingSerializer 4 | 5 | 6 | class HostingViewSet(v1_1_views.HostingViewSet): 7 | lookup_field = 'name' 8 | lookup_value_regex = '[^/]+' 9 | serializer_class = HostingSerializer 10 | serializer_detail_class = HostingDetailSerializer 11 | hosting_serializer_class = HostingSerializer 12 | 13 | def get_queryset(self): 14 | return super().get_queryset().select_related('socket') 15 | 16 | 17 | class HostingFileViewSet(v1_1_views.HostingFileViewSet): 18 | pass 19 | -------------------------------------------------------------------------------- /apps/instances/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.instances.appconfig.AppConfig' 2 | -------------------------------------------------------------------------------- /apps/instances/appconfig.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.apps import AppConfig as _AppConfig 3 | 4 | 5 | class AppConfig(_AppConfig): 6 | name = 'apps.instances' 7 | 8 | def ready(self): 9 | from . import signal_handlers # noqa 10 | -------------------------------------------------------------------------------- /apps/instances/contextmanagers.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from contextlib import contextmanager 3 | 4 | from apps.instances.helpers import get_current_instance, set_current_instance 5 | 6 | 7 | @contextmanager 8 | def instance_context(instance): 9 | previous_instance = get_current_instance() 10 | try: 11 | set_current_instance(instance) 12 | yield 13 | finally: 14 | set_current_instance(previous_instance) 15 | -------------------------------------------------------------------------------- /apps/instances/fixtures/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/instances/fixtures/__init__.py -------------------------------------------------------------------------------- /apps/instances/migrations/0004_instance_metadata.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import jsonfield 3 | from django.db import migrations, models 4 | 5 | import apps.core.fields 6 | import apps.core.validators 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('instances', '0003_auto_20150127_1712'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='instance', 18 | name='metadata', 19 | field=jsonfield.JSONField(default={}, blank=True, validators=[apps.core.validators.validate_metadata]), 20 | preserve_default=True, 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /apps/instances/migrations/0008_like_index.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('instances', '0007_add_instance_indicator__model'), 9 | ] 10 | 11 | operations = [ 12 | migrations.RunSQL(""" 13 | CREATE INDEX instance_name_like ON instances_instance 14 | USING BTREE (name varchar_pattern_ops); 15 | """) 16 | ] 17 | -------------------------------------------------------------------------------- /apps/instances/migrations/0010_remove_instance_old_type.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('instances', '0009_validators'), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveField( 13 | model_name='instance', 14 | name='old_type', 15 | ), 16 | ] 17 | -------------------------------------------------------------------------------- /apps/instances/migrations/0013_instance_database.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.2 on 2016-02-24 14:21 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('instances', '0012_instance_noticed_at'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='instance', 15 | name='database', 16 | field=models.CharField(default=None, max_length=128, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/instances/migrations/0014_instance_storage_prefix.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.2 on 2016-03-14 08:29 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('instances', '0013_instance_database'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='instance', 15 | name='storage_prefix', 16 | field=models.CharField(default=None, max_length=64, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/instances/migrations/0015_auto_20160407_1144.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.3 on 2016-04-07 11:44 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('instances', '0014_instance_storage_prefix'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='instanceindicator', 15 | name='type', 16 | field=models.SmallIntegerField(choices=[(0, 'schedules_count'), (1, 'storage_size'), (2, 'apns_devices')]), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/instances/migrations/0016_instance_config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.4 on 2016-04-15 08:07 3 | import jsonfield.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('instances', '0015_auto_20160407_1144'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='instance', 16 | name='config', 17 | field=jsonfield.fields.JSONField(blank=True, default={}), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/instances/migrations/0021_remove_last_access_fields.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-07-04 08:46 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('instances', '0020_instance_groups_acl'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='instance', 15 | name='last_access', 16 | ), 17 | migrations.RemoveField( 18 | model_name='instance', 19 | name='noticed_at', 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /apps/instances/migrations/0023_instance_version.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.6 on 2018-07-30 11:43 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('instances', '0022_instance_domains'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='instance', 15 | name='version', 16 | field=models.IntegerField(default=1), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/instances/migrations/0024_instance_location.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('instances', '0023_instance_version'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name='instance', 14 | name='location', 15 | field=models.TextField(db_index=True, default=settings.LOCATION), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/instances/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/instances/migrations/__init__.py -------------------------------------------------------------------------------- /apps/instances/postgresql_backend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/instances/postgresql_backend/__init__.py -------------------------------------------------------------------------------- /apps/instances/sql/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/instances/sql/__init__.py -------------------------------------------------------------------------------- /apps/instances/sql/instance.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX instance_name_like ON instances_instance 2 | USING BTREE (name varchar_pattern_ops); 3 | -------------------------------------------------------------------------------- /apps/instances/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/instances/tests/__init__.py -------------------------------------------------------------------------------- /apps/instances/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/instances/v1_1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/instances/v1_1/views.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | 3 | from apps.instances.v1 import views as v1_views 4 | from apps.instances.v1_1.serializers import InstanceDetailSerializer, InstanceSerializer 5 | 6 | 7 | class InstanceViewSet(v1_views.InstanceViewSet): 8 | serializer_class = InstanceSerializer 9 | serializer_detail_class = InstanceDetailSerializer 10 | -------------------------------------------------------------------------------- /apps/instances/v2/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/instances/v2/views.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.instances.v1_1 import views as v1_1_views 3 | from apps.instances.v2.serializers import InstanceDetailSerializer, InstanceSerializer 4 | 5 | 6 | class InstanceViewSet(v1_1_views.InstanceViewSet): 7 | serializer_class = InstanceSerializer 8 | serializer_detail_class = InstanceDetailSerializer 9 | -------------------------------------------------------------------------------- /apps/invitations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/invitations/__init__.py -------------------------------------------------------------------------------- /apps/invitations/exceptions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework import status 3 | 4 | from apps.core.exceptions import SyncanoException 5 | 6 | 7 | class InstanceRoleAlreadyExists(SyncanoException): 8 | status_code = status.HTTP_400_BAD_REQUEST 9 | field = 'email' 10 | default_detail = 'Admin with specified Email is already a part of this Instance.' 11 | 12 | 13 | class InvitationAlreadyExists(SyncanoException): 14 | status_code = status.HTTP_400_BAD_REQUEST 15 | default_detail = 'Admin with specified Email already has an invitation created.' 16 | -------------------------------------------------------------------------------- /apps/invitations/migrations/0003_invitation_inviter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.conf import settings 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 10 | ('invitations', '0002_auto_20150130_1243'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='invitation', 16 | name='inviter', 17 | field=models.ForeignKey(related_name='sent_invitations', to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/invitations/migrations/0004_role_as_integer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.conf import settings 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('invitations', '0003_invitation_inviter'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='invitation', 15 | name='role', 16 | field=models.IntegerField() 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/invitations/migrations/0005_role_as_fk.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('invitations', '0004_role_as_integer'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='invitation', 14 | name='role', 15 | field=models.ForeignKey(to='admins.Role', on_delete=models.CASCADE), 16 | preserve_default=True, 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/invitations/migrations/0007_admin_data.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | 5 | def connect_invitations(apps, schema_editor): 6 | Admin = apps.get_model('admins.Admin') 7 | Invitation = apps.get_model('invitations.Invitation') 8 | for invitation in Invitation.objects.iterator(): 9 | try: 10 | invitation.admin = Admin.objects.get(email__iexact=invitation.email) 11 | invitation.save(update_fields=('admin',)) 12 | except Admin.DoesNotExist: 13 | pass 14 | 15 | 16 | class Migration(migrations.Migration): 17 | dependencies = [ 18 | ('admins', '0003_merge'), 19 | ('invitations', '0006_invitation_admin'), 20 | ] 21 | 22 | operations = [ 23 | migrations.RunPython(connect_invitations), 24 | ] 25 | -------------------------------------------------------------------------------- /apps/invitations/migrations/0008_cleanup_indexes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('invitations', '0007_admin_data'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterUniqueTogether( 13 | name='invitation', 14 | unique_together=set([('email', 'instance')]), 15 | ), 16 | migrations.AlterIndexTogether( 17 | name='invitation', 18 | index_together=set([]), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/invitations/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/invitations/migrations/__init__.py -------------------------------------------------------------------------------- /apps/invitations/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/invitations/tests/__init__.py -------------------------------------------------------------------------------- /apps/invitations/tests/test_invitations.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from django_dynamic_fixture import G 3 | 4 | from apps.admins.models import Admin, Role 5 | from apps.invitations.models import Invitation 6 | 7 | 8 | class TestInvitationModel(TestCase): 9 | def test_accepting_invitation(self): 10 | admin = G(Admin, email='john@doe.com') 11 | self.assertFalse(admin.instances()) 12 | invitation = G(Invitation, role=Role.objects.first(), 13 | email=admin.email, state=Invitation.STATE_CHOICES.NEW) 14 | invitation.accept(admin) 15 | self.assertTrue(admin.instances()) 16 | -------------------------------------------------------------------------------- /apps/invitations/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/invitations/v1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework.routers import SimpleRouter 3 | 4 | from apps.invitations import views 5 | 6 | router = SimpleRouter() 7 | router.register('invitations', views.InvitationViewSet) 8 | 9 | urlpatterns = router.urls 10 | -------------------------------------------------------------------------------- /apps/metrics/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.metrics.appconfig.AppConfig' 2 | -------------------------------------------------------------------------------- /apps/metrics/appconfig.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.apps import AppConfig as _AppConfig 3 | 4 | 5 | class AppConfig(_AppConfig): 6 | name = 'apps.metrics' 7 | 8 | def ready(self): 9 | from . import signal_handlers # noqa 10 | -------------------------------------------------------------------------------- /apps/metrics/exceptions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework import status 3 | 4 | from apps.core.exceptions import SyncanoException 5 | 6 | 7 | class IncorrectQueryValue(SyncanoException): 8 | status_code = status.HTTP_400_BAD_REQUEST 9 | default_detail = 'Incorrect value.' 10 | -------------------------------------------------------------------------------- /apps/metrics/helpers.py: -------------------------------------------------------------------------------- 1 | import calendar 2 | import datetime 3 | from math import floor 4 | 5 | import pytz 6 | 7 | 8 | def timestamp_to_datetime(ts): 9 | """ 10 | Converts UNIX timestamp to UTC datetime 11 | """ 12 | return datetime.datetime.utcfromtimestamp(int(ts)).replace(tzinfo=pytz.utc) 13 | 14 | 15 | def datetime_to_timestamp(dt): 16 | """ 17 | Converts UTC datetime to UNIX timestamp 18 | """ 19 | return calendar.timegm(dt.timetuple()) 20 | 21 | 22 | def floor_to_base(x, base=5): 23 | # Supports datetime 24 | try: 25 | return int(base * floor(float(x) / base)) 26 | except TypeError: 27 | return timestamp_to_datetime(floor_to_base(datetime_to_timestamp(x), base.total_seconds())) 28 | -------------------------------------------------------------------------------- /apps/metrics/migrations/0016_ordering.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('metrics', '0015_after_trigger_source_rename'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterModelOptions( 13 | name='dayaggregate', 14 | options={'ordering': ('id',)}, 15 | ), 16 | migrations.AlterModelOptions( 17 | name='houraggregate', 18 | options={'ordering': ('id',)}, 19 | ), 20 | migrations.AlterModelOptions( 21 | name='minuteaggregate', 22 | options={'ordering': ('id',)}, 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /apps/metrics/migrations/0017_cleanup_nulls_data.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | def cleanup_nulls(apps, schema_editor): 6 | DayAggregate = apps.get_model('metrics', 'DayAggregate') 7 | HourAggregate = apps.get_model('metrics', 'HourAggregate') 8 | MinuteAggregate = apps.get_model('metrics', 'MinuteAggregate') 9 | models = (DayAggregate, HourAggregate, MinuteAggregate) 10 | 11 | for model in models: 12 | model.objects.filter(admin__isnull=True).delete() 13 | 14 | 15 | class Migration(migrations.Migration): 16 | dependencies = [ 17 | ('metrics', '0016_ordering'), 18 | ] 19 | 20 | operations = [ 21 | migrations.RunPython(cleanup_nulls), 22 | ] 23 | -------------------------------------------------------------------------------- /apps/metrics/migrations/0020_worklogentry_location.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('metrics', '0019_codebox_time'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name='worklogentry', 14 | name='location', 15 | field=models.TextField(db_index=True, default=settings.LOCATION), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/metrics/migrations/0021_worklog_unique_index.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0.6 on 2018-10-10 18:02 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('metrics', '0020_worklogentry_location'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterUniqueTogether( 14 | name='worklogentry', 15 | unique_together={('left_boundary', 'right_boundary', 'location')}, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/metrics/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/metrics/migrations/__init__.py -------------------------------------------------------------------------------- /apps/metrics/signals.py: -------------------------------------------------------------------------------- 1 | from django.dispatch import Signal 2 | 3 | interval_aggregated = Signal(providing_args=["left_boundary", "right_boundary"]) 4 | -------------------------------------------------------------------------------- /apps/metrics/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/metrics/tests/__init__.py -------------------------------------------------------------------------------- /apps/metrics/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/metrics/v1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.urls import path 3 | from rest_framework.routers import SimpleRouter 4 | 5 | from apps.metrics import views 6 | 7 | router = SimpleRouter() 8 | router.register('hourly', views.HourlyStatsViewSet, base_name='hour-aggregate') 9 | router.register('daily', views.DailyStatsViewSet, base_name='day-aggregate') 10 | 11 | urlpatterns = [ 12 | path('', views.StatsLinksView.as_view(), name='stats'), 13 | ] + router.urls 14 | -------------------------------------------------------------------------------- /apps/push_notifications/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.push_notifications.appconfig.AppConfig' 2 | -------------------------------------------------------------------------------- /apps/push_notifications/apns/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/push_notifications/apns/__init__.py -------------------------------------------------------------------------------- /apps/push_notifications/appconfig.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.apps import AppConfig as _AppConfig 3 | 4 | 5 | class AppConfig(_AppConfig): 6 | name = 'apps.push_notifications' 7 | 8 | def ready(self): 9 | from . import signal_handlers # noqa 10 | -------------------------------------------------------------------------------- /apps/push_notifications/backup.py: -------------------------------------------------------------------------------- 1 | from apps.backups import site 2 | 3 | from .models import APNSConfig, APNSDevice, GCMConfig, GCMDevice 4 | 5 | site.register([GCMDevice, GCMConfig, APNSDevice, APNSConfig]) 6 | -------------------------------------------------------------------------------- /apps/push_notifications/fields.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | from django.db import models 4 | 5 | 6 | class HexIntegerField(models.BigIntegerField): 7 | 8 | def get_prep_value(self, value): 9 | if value is None or value == '': 10 | return None 11 | if isinstance(value, str): 12 | value = int(value, 16) 13 | 14 | return struct.unpack('q', struct.pack('Q', value))[0] 15 | 16 | def from_db_value(self, value, expression, connection, context): 17 | if value is None: 18 | return '' 19 | 20 | return hex(struct.unpack('Q', struct.pack('q', value))[0]) 21 | 22 | def run_validators(self, value): 23 | # make sure validation is performed on integer value not string value 24 | return super().run_validators(self.get_prep_value(value)) 25 | -------------------------------------------------------------------------------- /apps/push_notifications/forms.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django import forms 3 | 4 | 5 | class HexIntegerFormField(forms.IntegerField): 6 | default_error_messages = { 7 | 'invalid': 'Enter a valid hex value."', 8 | } 9 | 10 | def to_python(self, value): 11 | if value in self.empty_values: 12 | return None 13 | try: 14 | return int(value, 16) 15 | except (ValueError, TypeError): 16 | raise forms.ValidationError(self.error_messages['invalid'], code='invalid') 17 | -------------------------------------------------------------------------------- /apps/push_notifications/migrations/0011_auto_20160303_1509.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.3 on 2016-03-03 15:09 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('push_notifications', '0001_squashed_0011'), 11 | ] 12 | 13 | operations = [ 14 | ] 15 | -------------------------------------------------------------------------------- /apps/push_notifications/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/push_notifications/migrations/__init__.py -------------------------------------------------------------------------------- /apps/push_notifications/permissions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework.permissions import BasePermission 3 | 4 | 5 | class HasDevicePermission(BasePermission): 6 | """ 7 | Allows access only when instance user owns a device. 8 | """ 9 | 10 | def has_object_permission(self, request, view, obj): 11 | if view.action == 'retrieve': 12 | # Devices are filtered out anyway. 13 | return True 14 | 15 | if request.auth_user: 16 | return obj.user == request.auth_user 17 | 18 | return False 19 | -------------------------------------------------------------------------------- /apps/push_notifications/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/push_notifications/tests/__init__.py -------------------------------------------------------------------------------- /apps/push_notifications/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/redis_storage/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/redis_storage/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/redis_storage/tests/__init__.py -------------------------------------------------------------------------------- /apps/response_templates/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.response_templates.appconfig.AppConfig' 2 | -------------------------------------------------------------------------------- /apps/response_templates/appconfig.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.apps import AppConfig as _AppConfig 3 | 4 | 5 | class AppConfig(_AppConfig): 6 | name = 'apps.response_templates' 7 | 8 | def ready(self): 9 | from . import signal_handlers # noqa 10 | -------------------------------------------------------------------------------- /apps/response_templates/backup.py: -------------------------------------------------------------------------------- 1 | from apps.backups import site 2 | from apps.backups.options import ModelBackupByName 3 | 4 | from .models import ResponseTemplate 5 | 6 | site.register(ResponseTemplate, ModelBackupByName) 7 | -------------------------------------------------------------------------------- /apps/response_templates/jinja2_environments.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | import lazy_object_proxy 3 | import rapidjson as json 4 | from jinja2.sandbox import SandboxedEnvironment 5 | 6 | 7 | def jinja_finalizer(value): 8 | if isinstance(value, (list, dict)): 9 | return json.dumps(value) 10 | return value 11 | 12 | 13 | jinja2_env = lazy_object_proxy.Proxy(lambda: SandboxedEnvironment(trim_blocks=True, 14 | lstrip_blocks=True, 15 | finalize=jinja_finalizer)) 16 | -------------------------------------------------------------------------------- /apps/response_templates/migrations/0002_auto_20151218_1535.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | import apps.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('response_templates', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='responsetemplate', 16 | name='name', 17 | field=apps.core.fields.StrippedSlugField(unique=True, max_length=64), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/response_templates/migrations/0003_add_default_reponse_templates.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | from apps.response_templates.predefined_templates import PredefinedTemplates 5 | 6 | 7 | def create_default_templates(apps, schema_editor): 8 | ResponseTemplate = apps.get_model('response_templates.ResponseTemplate') 9 | PredefinedTemplates.create_template_responses(check=True, model=ResponseTemplate) 10 | 11 | 12 | class Migration(migrations.Migration): 13 | 14 | dependencies = [ 15 | ('response_templates', '0002_auto_20151218_1535'), 16 | ] 17 | 18 | operations = [ 19 | migrations.RunPython(create_default_templates), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/response_templates/migrations/0004_responsetemplate_description.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2016-09-07 14:23 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('response_templates', '0003_add_default_reponse_templates'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='responsetemplate', 15 | name='description', 16 | field=models.TextField(blank=True, max_length=256), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/response_templates/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/response_templates/migrations/__init__.py -------------------------------------------------------------------------------- /apps/response_templates/permissions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework.permissions import BasePermission 3 | 4 | 5 | class AllowApiKeyRenderAccess(BasePermission): 6 | """ 7 | Disallow deletion of User Profile klass. 8 | """ 9 | 10 | def has_object_permission(self, request, view, obj): 11 | return request.auth is not None and view.action == 'render' 12 | -------------------------------------------------------------------------------- /apps/response_templates/signal_handlers.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.dispatch import receiver 3 | 4 | from apps.core.signals import post_tenant_migrate 5 | from apps.response_templates.predefined_templates import PredefinedTemplates 6 | 7 | 8 | # Response templates signal handlers 9 | @receiver(post_tenant_migrate, dispatch_uid='create_response_templates_after_tenant_migrate') 10 | def create_response_templates_after_tenant_migrate(sender, tenant, created, partial, **kwargs): 11 | if created: 12 | PredefinedTemplates.create_template_responses(check=partial) 13 | -------------------------------------------------------------------------------- /apps/response_templates/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/response_templates/tests/__init__.py -------------------------------------------------------------------------------- /apps/response_templates/utils.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | import os 3 | 4 | 5 | def get_current_virtual_mememory_size(size='vsz'): 6 | return int(os.popen('ps -p %d -o %s | tail -1' % (os.getpid(), size)).read()) 7 | -------------------------------------------------------------------------------- /apps/response_templates/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/response_templates/v1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework.routers import SimpleRouter 3 | 4 | from apps.response_templates import views 5 | 6 | router = SimpleRouter() 7 | 8 | router.register( 9 | 'snippets/templates', 10 | views.ResponseTemplateViewSet, 11 | base_name='response-templates' 12 | ) 13 | 14 | urlpatterns = router.urls 15 | -------------------------------------------------------------------------------- /apps/response_templates/validators.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from jinja2.exceptions import TemplateSyntaxError 3 | from rest_framework import serializers 4 | 5 | from apps.response_templates.jinja2_environments import jinja2_env 6 | 7 | 8 | class Jinja2TemplateValidator: 9 | 10 | def __call__(self, value): 11 | try: 12 | jinja2_env.from_string(value) 13 | except TemplateSyntaxError as e: 14 | raise serializers.ValidationError(e.message) 15 | -------------------------------------------------------------------------------- /apps/snippets/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/snippets/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework.serializers import ModelSerializer 2 | 3 | from apps.core.field_serializers import JSONField 4 | from apps.core.validators import validate_config 5 | from apps.instances.models import Instance 6 | 7 | 8 | class InstanceConfigSerializer(ModelSerializer): 9 | config = JSONField(validators=[validate_config], default={}) 10 | 11 | class Meta: 12 | model = Instance 13 | fields = ('config',) 14 | -------------------------------------------------------------------------------- /apps/snippets/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/snippets/tests/__init__.py -------------------------------------------------------------------------------- /apps/snippets/v1_1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/snippets/v1_1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.urls import path 3 | 4 | from apps.snippets import views 5 | 6 | urlpatterns = [ 7 | path('snippets/', views.TopSnippetsLinkView.as_view(), name='snippets'), 8 | path('snippets/config/', views.InstanceConfigView.as_view(), name='instance-config'), 9 | ] 10 | -------------------------------------------------------------------------------- /apps/sockets/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.sockets.appconfig.AppConfig' 2 | -------------------------------------------------------------------------------- /apps/sockets/appconfig.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.apps import AppConfig as _AppConfig 3 | 4 | 5 | class AppConfig(_AppConfig): 6 | name = 'apps.sockets' 7 | 8 | def ready(self): 9 | from . import signal_handlers # noqa 10 | -------------------------------------------------------------------------------- /apps/sockets/migrations/0002_socket_description.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2016-08-18 11:48 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('sockets', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='socket', 15 | name='description', 16 | field=models.TextField(blank=True, max_length=256), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/sockets/migrations/0004_socketendpoint_metadata.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2016-09-15 11:47 3 | import jsonfield.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('sockets', '0003_socket_url'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='socketendpoint', 16 | name='metadata', 17 | field=jsonfield.fields.JSONField(blank=True, default={}), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/sockets/migrations/0005_socket_config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-09-21 11:32 3 | import jsonfield.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('sockets', '0004_socketendpoint_metadata'), 11 | ] 12 | 13 | operations = [ 14 | migrations.RunSQL( 15 | 'ALTER TABLE "sockets_socket" ADD COLUMN "config" TEXT DEFAULT \'{}\' NOT NULL', 16 | 'ALTER TABLE "sockets_socket" DROP COLUMN "config" CASCADE', 17 | [migrations.AddField( 18 | model_name='socket', 19 | name='config', 20 | field=jsonfield.fields.JSONField(blank=True, default={}), 21 | )]), 22 | ] 23 | -------------------------------------------------------------------------------- /apps/sockets/migrations/0006_socket_installed_deps.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2016-09-27 11:39 3 | from django.db import migrations 4 | 5 | import apps.core.fields 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('sockets', '0005_socket_config'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='socket', 17 | name='installed_deps', 18 | field=apps.core.fields.NullableJSONField(blank=True, default={}, null=True), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/sockets/migrations/0007_socketendpoint_acl.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2016-09-30 13:54 3 | from django.db import migrations 4 | 5 | import apps.core.fields 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('sockets', '0006_socket_installed_deps'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='socketendpoint', 17 | name='acl', 18 | field=apps.core.fields.NullableJSONField(blank=True, default=None, null=True), 19 | ), 20 | migrations.AlterField( 21 | model_name='socketendpoint', 22 | name='acl', 23 | field=apps.core.fields.NullableJSONField(blank=True, default={}, null=True), 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /apps/sockets/migrations/0008_socket_zip_file.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-10-05 13:51 3 | from django.core.files.storage import default_storage 4 | from django.db import migrations, models 5 | 6 | import apps.sockets.models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('sockets', '0007_socketendpoint_acl'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='socket', 18 | name='zip_file', 19 | field=models.FileField(blank=True, null=True, 20 | upload_to=apps.sockets.models.upload_custom_socket_file_to, 21 | storage=default_storage), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /apps/sockets/migrations/0009_remove_obsolete_fields.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2016-11-29 15:40 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('sockets', '0008_socket_zip_file'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='socket', 15 | name='dependencies', 16 | ), 17 | migrations.RemoveField( 18 | model_name='socket', 19 | name='endpoints', 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /apps/sockets/migrations/0010_socket_version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2016-12-27 16:41 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('sockets', '0009_remove_obsolete_fields'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='socket', 15 | name='version', 16 | field=models.CharField(default='0.1', max_length=32), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/sockets/migrations/0013_alter_status_info.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import jsonfield.fields 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('sockets', '0012_add_installed'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='socket', 15 | name='status_info', 16 | field=jsonfield.fields.JSONField(default=None, null=True), 17 | ), 18 | migrations.RunSQL( 19 | """ 20 | UPDATE sockets_socket SET status_info=NULL 21 | """ 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /apps/sockets/migrations/0015_socket_zip_file_list.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2017-10-10 13:12 3 | import jsonfield.fields 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('sockets', '0014_socket_environment'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='socket', 16 | name='zip_file_list', 17 | field=jsonfield.fields.JSONField(null=True), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /apps/sockets/migrations/0016_socketenvironment_checksum.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('sockets', '0015_socket_zip_file_list'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name='socketenvironment', 14 | name='checksum', 15 | field=models.CharField(max_length=32, null=True), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/sockets/migrations/0017_socketenvironment_name_unique.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | import apps.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('sockets', '0016_socketenvironment_checksum'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='socketenvironment', 16 | name='_is_live', 17 | field=apps.core.fields.LiveField(default=True), 18 | ), 19 | migrations.AlterUniqueTogether( 20 | name='socketenvironment', 21 | unique_together=set([('name', '_is_live')]), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /apps/sockets/migrations/0019_socket_install_config.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations 2 | 3 | import apps.core.fields 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('sockets', '0018_socket_checksum'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='socket', 15 | name='install_config', 16 | field=apps.core.fields.NullableJSONField(blank=True, default={}, null=True), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/sockets/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/sockets/migrations/__init__.py -------------------------------------------------------------------------------- /apps/sockets/permissions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.core.permissions import CheckAclPermissionBase 3 | 4 | 5 | class CheckEndpointAclPermission(CheckAclPermissionBase): 6 | """ 7 | Check ACL for current action on object level if ACL is set. 8 | """ 9 | 10 | def has_object_permission(self, request, view, obj): 11 | if not obj.acl: 12 | return True 13 | return self.has_object_level_permission(request, view, obj) 14 | -------------------------------------------------------------------------------- /apps/sockets/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/sockets/v2/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/triggers/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.triggers.appconfig.AppConfig' 2 | -------------------------------------------------------------------------------- /apps/triggers/appconfig.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.apps import AppConfig as _AppConfig 3 | 4 | 5 | class AppConfig(_AppConfig): 6 | name = 'apps.triggers' 7 | 8 | def ready(self): 9 | from . import signal_handlers # noqa 10 | -------------------------------------------------------------------------------- /apps/triggers/backup.py: -------------------------------------------------------------------------------- 1 | from apps.backups import site 2 | 3 | from .models import Trigger 4 | 5 | site.register(Trigger) 6 | -------------------------------------------------------------------------------- /apps/triggers/migrations/0006_description.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('triggers', '0001_squashed_0006'), 9 | ] 10 | 11 | operations = [ 12 | ] 13 | -------------------------------------------------------------------------------- /apps/triggers/migrations/0008_event_data.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-06-20 09:50 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('triggers', '0007_event_and_signals'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RunSQL(""" 14 | UPDATE triggers_trigger AS tt 15 | SET event=('source=>dataobject,class=>' || dk.name)::hstore, 16 | signals=array[substring(tt.signal from 6)] 17 | FROM data_klass AS dk 18 | WHERE dk.id=tt.klass_id 19 | """, reverse_sql="") 20 | ] 21 | -------------------------------------------------------------------------------- /apps/triggers/migrations/0009_cleanup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-06-20 10:16 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('triggers', '0008_event_data'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterIndexTogether( 14 | name='trigger', 15 | index_together=set([]), 16 | ), 17 | migrations.RemoveField( 18 | model_name='trigger', 19 | name='klass', 20 | ), 21 | migrations.RemoveField( 22 | model_name='trigger', 23 | name='signal', 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /apps/triggers/migrations/0010_trigger_socket.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.8 on 2016-12-30 12:43 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('sockets', '0010_socket_version'), 11 | ('triggers', '0009_cleanup'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='trigger', 17 | name='socket', 18 | field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='sockets.Socket'), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /apps/triggers/migrations/0011_trigger_signals.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import django.contrib.postgres.fields 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('triggers', '0010_trigger_socket'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='trigger', 15 | name='signals', 16 | field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField()), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/triggers/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/triggers/migrations/__init__.py -------------------------------------------------------------------------------- /apps/triggers/permissions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework.permissions import BasePermission 3 | 4 | 5 | class ProtectTriggerAccess(BasePermission): 6 | """ 7 | Disallow editing triggers that are bound to socket. 8 | """ 9 | allowed_actions = ('retrieve', 'emit',) 10 | 11 | def has_object_permission(self, request, view, obj): 12 | return getattr(obj, 'socket_id', None) is None or view.action in self.allowed_actions 13 | -------------------------------------------------------------------------------- /apps/triggers/querysets.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.db.models import QuerySet 3 | 4 | 5 | class TriggerQuerySet(QuerySet): 6 | def match(self, event, signal): 7 | return self.filter(event__contains=event, signals__contains=[signal]) 8 | -------------------------------------------------------------------------------- /apps/triggers/sql/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/triggers/sql/__init__.py -------------------------------------------------------------------------------- /apps/triggers/sql/trigger.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX trigger_event_signal ON triggers_trigger 2 | USING GIN(event, signals); 3 | -------------------------------------------------------------------------------- /apps/triggers/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/triggers/tests/__init__.py -------------------------------------------------------------------------------- /apps/triggers/tests/test_api_v1_1.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.urls import reverse 3 | from django_dynamic_fixture import G 4 | from rest_framework import status 5 | 6 | from apps.data.models import Klass 7 | from apps.triggers.models import Trigger 8 | from apps.triggers.tests.test_api import TriggerTestBase 9 | 10 | 11 | class TestTriggerListAPI(TriggerTestBase): 12 | def setUp(self): 13 | super().setUp() 14 | self.url = reverse('v1.1:trigger-list', args=(self.instance.name,)) 15 | 16 | def test_list_trigger(self): 17 | G(Trigger, klass=G(Klass, name='test')) 18 | response = self.client.get(self.url) 19 | self.assertEqual(response.status_code, status.HTTP_200_OK) 20 | self.assertIn(b'script', response.content) 21 | self.assertNotIn(b'codebox', response.content) 22 | -------------------------------------------------------------------------------- /apps/triggers/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/triggers/v1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.core.routers import NestedSimpleRouter 3 | from apps.triggers.v1 import views 4 | 5 | router = NestedSimpleRouter() 6 | trigger_router = router.register( 7 | 'triggers', 8 | views.TriggerViewSet, 9 | base_name='trigger' 10 | ) 11 | 12 | trigger_router.register( 13 | 'traces', 14 | views.TriggerTraceViewSet, 15 | base_name='trigger-trace', 16 | parents_query_lookups=[ 17 | 'trigger', 18 | ] 19 | ) 20 | 21 | urlpatterns = router.urls 22 | -------------------------------------------------------------------------------- /apps/triggers/v1_1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/triggers/v1_1/serializers.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.triggers.v1 import serializers as v1_serializers 3 | 4 | 5 | class TriggerSerializer(v1_serializers.TriggerSerializer): 6 | hyperlinks = ( 7 | ('self', 'trigger-detail', ( 8 | 'instance.name', 9 | 'pk', 10 | )), 11 | ('script', 'codebox-detail', ( 12 | 'instance.name', 13 | 'codebox.id', 14 | )), 15 | ('class', 'klass-detail', ( 16 | 'instance.name', 17 | 'klass.name', 18 | )), 19 | ('traces', 'trigger-trace-list', ( 20 | 'instance.name', 21 | 'pk', 22 | )), 23 | ) 24 | 25 | field_mappings = {'codebox': 'script', 26 | 'klass': 'class'} 27 | -------------------------------------------------------------------------------- /apps/triggers/v1_1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.core.routers import NestedSimpleRouter 3 | from apps.triggers.v1 import views as v1_views 4 | from apps.triggers.v1_1 import views 5 | 6 | router = NestedSimpleRouter() 7 | trigger_router = router.register( 8 | 'triggers', 9 | views.TriggerViewSet, 10 | base_name='trigger' 11 | ) 12 | 13 | trigger_router.register( 14 | 'traces', 15 | v1_views.TriggerTraceViewSet, 16 | base_name='trigger-trace', 17 | parents_query_lookups=[ 18 | 'trigger', 19 | ] 20 | ) 21 | 22 | urlpatterns = router.urls 23 | -------------------------------------------------------------------------------- /apps/triggers/v1_1/views.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.triggers.v1 import views as v1_views 3 | from apps.triggers.v1_1.serializers import TriggerSerializer 4 | 5 | 6 | class TriggerViewSet(v1_views.TriggerViewSet): 7 | serializer_class = TriggerSerializer 8 | -------------------------------------------------------------------------------- /apps/triggers/v2/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/triggers/v2/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.core.routers import NestedSimpleRouter 3 | from apps.triggers.v2 import views 4 | 5 | router = NestedSimpleRouter() 6 | trigger_router = router.register( 7 | 'triggers', 8 | views.TriggerViewSet, 9 | base_name='trigger' 10 | ) 11 | 12 | trigger_router.register( 13 | 'traces', 14 | views.TriggerTraceViewSet, 15 | base_name='trigger-trace', 16 | parents_query_lookups=[ 17 | 'trigger', 18 | ] 19 | ) 20 | 21 | urlpatterns = router.urls 22 | -------------------------------------------------------------------------------- /apps/triggers/validators.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | import re 3 | 4 | from rest_framework import serializers 5 | 6 | SIGNAL_REGEX = re.compile(r'^[a-z][a-z0-9-_/\.]{,126}[a-z0-9]$', re.IGNORECASE) 7 | 8 | 9 | class SignalValidator: 10 | def __call__(self, value): 11 | if not isinstance(value, str) or not SIGNAL_REGEX.match(value): 12 | raise serializers.ValidationError('Signal contains invalid or too long value.') 13 | -------------------------------------------------------------------------------- /apps/users/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'apps.users.appconfig.AppConfig' 2 | -------------------------------------------------------------------------------- /apps/users/appconfig.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.apps import AppConfig as _AppConfig 3 | 4 | 5 | class AppConfig(_AppConfig): 6 | name = 'apps.users' 7 | 8 | def ready(self): 9 | from . import signal_handlers # noqa 10 | -------------------------------------------------------------------------------- /apps/users/backup.py: -------------------------------------------------------------------------------- 1 | from apps.backups import site 2 | 3 | from .models import Group, Membership, User, UserSocialProfile 4 | 5 | site.register([Group, User, Membership, UserSocialProfile]) 6 | -------------------------------------------------------------------------------- /apps/users/exceptions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.conf import settings 3 | from rest_framework import status 4 | 5 | from apps.core.exceptions import SyncanoException 6 | 7 | 8 | class UserGroupCountExceeded(SyncanoException): 9 | status_code = status.HTTP_400_BAD_REQUEST 10 | default_detail = "User's group count exceeded (%d)." % settings.USER_GROUP_MAX_COUNT 11 | 12 | 13 | class MembershipAlreadyExists(SyncanoException): 14 | status_code = status.HTTP_400_BAD_REQUEST 15 | default_detail = 'User is already a part of that group.' 16 | -------------------------------------------------------------------------------- /apps/users/migrations/0011_auto_20151116_1124.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('users', '0001_squashed_0011'), 9 | ] 10 | 11 | operations = [ 12 | ] 13 | -------------------------------------------------------------------------------- /apps/users/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/users/migrations/__init__.py -------------------------------------------------------------------------------- /apps/users/mixins.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.data.mixins import ObjectSchemaProcessViewMixin 3 | from apps.data.models import DataObject, Klass 4 | 5 | 6 | class UserProfileViewMixin(ObjectSchemaProcessViewMixin): 7 | model = DataObject 8 | 9 | def initialize_request(self, request, *args, **kwargs): 10 | request = super().initialize_request(request, *args, **kwargs) 11 | if request.instance: 12 | self.klass = Klass.get_user_profile() 13 | return request 14 | -------------------------------------------------------------------------------- /apps/users/signals.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.dispatch import Signal 3 | 4 | social_user_created = Signal(providing_args=['view', 'instance']) 5 | -------------------------------------------------------------------------------- /apps/users/sql/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/users/sql/__init__.py -------------------------------------------------------------------------------- /apps/users/sql/user.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX user_username_like ON users_user 2 | USING GIN (username gin_trgm_ops); 3 | 4 | CREATE INDEX group_acl_users ON users_group 5 | USING GIN (_users); 6 | 7 | CREATE INDEX group_acl_groups ON users_group 8 | USING GIN (_groups); 9 | 10 | CREATE INDEX group_acl_public ON users_group 11 | USING BTREE (_public) WHERE _public = true; 12 | -------------------------------------------------------------------------------- /apps/users/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/users/tests/__init__.py -------------------------------------------------------------------------------- /apps/users/tests/test_groupmembership_api_v2.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse 2 | 3 | from apps.users.tests import test_groupmembership_api as tests_v1 4 | 5 | 6 | class TestGroupUserList(tests_v1.TestGroupUserList): 7 | def setUp(self): 8 | super().setUp() 9 | self.url = reverse('v2:group-user-list', args=(self.instance.name, self.group.id)) 10 | 11 | 12 | class TestGroupUserDetail(tests_v1.TestGroupUserDetail): 13 | def setUp(self): 14 | super().setUp() 15 | self.url = reverse('v2:group-user-detail', args=(self.instance.name, self.group.id, self.user.id)) 16 | -------------------------------------------------------------------------------- /apps/users/tests/test_usermembership_api_v2.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse 2 | 3 | from apps.users.tests import test_usermembership_api as tests_v1 4 | 5 | 6 | class TestUserGroupList(tests_v1.TestUserGroupList): 7 | def setUp(self): 8 | super().setUp() 9 | self.url = reverse('v2:user-group-list', args=(self.instance.name, self.user.id)) 10 | 11 | 12 | class TestUserGroupListKeyAccess(tests_v1.TestUserGroupListKeyAccess): 13 | def setUp(self): 14 | super().setUp() 15 | self.url = reverse('v2:user-group-list', args=(self.instance.name, self.user.id)) 16 | 17 | 18 | class TestUserGroupDetail(tests_v1.TestUserGroupDetail): 19 | def setUp(self): 20 | super().setUp() 21 | self.url = reverse('v2:user-group-detail', args=(self.instance.name, self.user.id, self.group.id)) 22 | -------------------------------------------------------------------------------- /apps/users/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/users/v2/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/webhooks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/webhooks/__init__.py -------------------------------------------------------------------------------- /apps/webhooks/backup.py: -------------------------------------------------------------------------------- 1 | from apps.backups import site 2 | from apps.backups.options import ModelBackupByName 3 | 4 | from .models import Webhook 5 | 6 | site.register(Webhook, ModelBackupByName) 7 | -------------------------------------------------------------------------------- /apps/webhooks/exceptions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from rest_framework import status 3 | 4 | from apps.core.exceptions import SyncanoException 5 | 6 | 7 | class UnsupportedPayload(SyncanoException): 8 | status_code = status.HTTP_400_BAD_REQUEST 9 | default_detail = 'Unsupported payload provided that cannot be serialized.' 10 | -------------------------------------------------------------------------------- /apps/webhooks/migrations/0012_auto_20160225_1642.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from django.db import migrations 3 | 4 | import apps.core.fields 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('webhooks', '0001_squashed_0012'), 11 | ] 12 | 13 | operations = [ 14 | ] 15 | -------------------------------------------------------------------------------- /apps/webhooks/migrations/0014_webhook_socket.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.9.7 on 2016-09-21 11:32 3 | import django.db.models.deletion 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('sockets', '0005_socket_config'), 11 | ('webhooks', '0013_acl'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='webhook', 17 | name='socket', 18 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, 19 | to='sockets.Socket', default=None), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /apps/webhooks/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/webhooks/migrations/__init__.py -------------------------------------------------------------------------------- /apps/webhooks/permissions.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.codeboxes.permissions import ProtectScriptAccess 3 | 4 | 5 | class ProtectScriptEndpointAccess(ProtectScriptAccess): 6 | """ 7 | Disallow editing script endpoints that are not bound to socket. 8 | """ 9 | allowed_actions = ('retrieve', 'run', 'endpoint_get', 'endpoint_post', 10 | 'endpoint_patch', 'endpoint_put', 'endpoint_delete') 11 | -------------------------------------------------------------------------------- /apps/webhooks/sql/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/webhooks/sql/__init__.py -------------------------------------------------------------------------------- /apps/webhooks/sql/webhook.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX webhook_acl_users ON webhooks_webhook 2 | USING GIN (_users); 3 | 4 | CREATE INDEX webhook_acl_groups ON webhooks_webhook 5 | USING GIN (_groups); 6 | 7 | CREATE INDEX webhook_acl_public ON webhooks_webhook 8 | USING BTREE (_public) WHERE _public = true; 9 | -------------------------------------------------------------------------------- /apps/webhooks/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/apps/webhooks/tests/__init__.py -------------------------------------------------------------------------------- /apps/webhooks/v1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/webhooks/v1_1/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/webhooks/v1_1/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.urls import path 3 | 4 | from apps.core.routers import NestedSimpleRouter 5 | from apps.webhooks.v1 import views as v1_views 6 | from apps.webhooks.v1_1 import views 7 | 8 | router = NestedSimpleRouter() 9 | 10 | webhook_router = router.register('endpoints/scripts', views.WebhookViewSet, base_name='webhook') 11 | 12 | webhook_router.register( 13 | 'traces', 14 | v1_views.WebhookTraceViewSet, 15 | base_name='webhook-trace', 16 | parents_query_lookups=[ 17 | 'webhook', 18 | ] 19 | ) 20 | 21 | urlpatterns = [ 22 | path('endpoints/scripts/p///', 23 | v1_views.WebhookPublicView.as_view(), 24 | name='webhook-public-run-with-name'), 25 | ] 26 | 27 | urlpatterns += router.urls 28 | -------------------------------------------------------------------------------- /apps/webhooks/v1_1/views.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.webhooks.v1 import views as v1_views 3 | from apps.webhooks.v1_1.serializers import WebhookDetailSerializer, WebhookSerializer 4 | 5 | 6 | class WebhookViewSet(v1_views.WebhookViewSet): 7 | serializer_class = WebhookSerializer 8 | serializer_detail_class = WebhookDetailSerializer 9 | -------------------------------------------------------------------------------- /apps/webhooks/v2/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /apps/webhooks/v2/urls.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from apps.core.routers import NestedEndpointRouter 3 | from apps.webhooks.v2 import views 4 | 5 | router = NestedEndpointRouter() 6 | 7 | webhook_router = router.register('endpoints/scripts', views.WebhookViewSet, base_name='webhook') 8 | 9 | webhook_router.register( 10 | 'traces', 11 | views.WebhookTraceViewSet, 12 | base_name='webhook-trace', 13 | parents_query_lookups=[ 14 | 'webhook', 15 | ] 16 | ) 17 | 18 | urlpatterns = router.urls 19 | -------------------------------------------------------------------------------- /collect_sql_migrations.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd /home/syncano/app/apps 4 | ( 5 | for app in `find ./ -maxdepth 1 -type d | sed 's#./##g' | grep -v '^$'` 6 | do 7 | for migration in `ls ${app}/migrations/*.py` 8 | do 9 | docker-compose run web ./manage.py sqlmigrate ${app} `basename ${migration} | cut -d'_' -f1` 10 | done 11 | done 12 | ) | sed -n '/^BEGIN;/,/^COMMIT;/p' 13 | cd - 14 | -------------------------------------------------------------------------------- /conf/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/conf/__init__.py -------------------------------------------------------------------------------- /conf/haproxy_test.cfg: -------------------------------------------------------------------------------- 1 | defaults 2 | timeout connect 5s 3 | timeout client 360s 4 | timeout server 360s 5 | maxconn 25000 6 | mode http 7 | option forwardfor 8 | 9 | global 10 | hard-stop-after 400s 11 | 12 | frontend api 13 | bind *:80 14 | http-request set-header Host api.syncano.test 15 | http-request set-header Host-Type api 16 | use_backend web_http 17 | 18 | frontend hosting 19 | bind *:90 20 | http-request set-header Host syncano.test 21 | http-request set-header Host-Type hosting 22 | use_backend web_http 23 | 24 | backend web_http 25 | http-request set-header X-Real-IP %[src] 26 | http-request set-header X-Forwarded-Proto https 27 | server web1 web:8000 28 | 29 | listen stats 30 | bind :9000 31 | mode http 32 | stats enable 33 | stats uri / 34 | -------------------------------------------------------------------------------- /conf/nginx/conf.d/uwsgi.conf: -------------------------------------------------------------------------------- 1 | # uwsgi.conf 2 | 3 | upstream syncano_uwsgi { 4 | server 127.0.0.1:8100; 5 | } 6 | -------------------------------------------------------------------------------- /conf/nginx/uwsgi_params: -------------------------------------------------------------------------------- 1 | uwsgi_param QUERY_STRING $query_string; 2 | uwsgi_param REQUEST_METHOD $request_method; 3 | uwsgi_param CONTENT_TYPE $content_type; 4 | uwsgi_param CONTENT_LENGTH $content_length; 5 | 6 | uwsgi_param REQUEST_URI $request_uri; 7 | uwsgi_param PATH_INFO $document_uri; 8 | uwsgi_param DOCUMENT_ROOT $document_root; 9 | uwsgi_param SERVER_PROTOCOL $server_protocol; 10 | uwsgi_param REQUEST_SCHEME $scheme; 11 | uwsgi_param HTTPS $https if_not_empty; 12 | 13 | uwsgi_param REMOTE_ADDR $remote_addr; 14 | uwsgi_param REMOTE_PORT $remote_port; 15 | uwsgi_param SERVER_PORT $server_port; 16 | uwsgi_param SERVER_NAME $server_name; 17 | -------------------------------------------------------------------------------- /conf/supervisor/conf.d/codebox.conf: -------------------------------------------------------------------------------- 1 | ; ======================================= 2 | ; celeryd codebox supervisor 3 | ; ======================================= 4 | 5 | [program:codebox_runner] 6 | environment=PYTHONPATH="$PYTHONPATH:/home/syncano/app" 7 | command=newrelic-admin run-program /home/syncano/app/run_celery.sh codebox_runner 8 | user=root 9 | stdout_logfile=/dev/stdout 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/dev/stderr 12 | stderr_logfile_maxbytes=0 13 | autorestart=true 14 | startretries=9999 15 | startsecs=3 16 | stopwaitsecs=360 17 | killasgroup=true 18 | -------------------------------------------------------------------------------- /conf/uwsgi/uwsgi_offload.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | chdir = /home/syncano/app/ 3 | log-format = %(addr) - [%(ltime)] %(host) %(method) %(uri) => generated %(size) bytes in %(msecs) msecs (%(proto) %(status)) 4 | 5 | master = true 6 | vacuum = true 7 | module = apps.async_tasks.wsgi 8 | buffer-size = 8192 9 | listen = 128 10 | 11 | touch-reload = settings/common.py 12 | if-env = AUTORELOAD 13 | py-autoreload = 1 14 | endif 15 | 16 | socket = :8003 17 | 18 | uid = syncano 19 | gid = syncano 20 | 21 | die-on-term = true 22 | need-app = true 23 | single-interpreter = true 24 | enable-threads = true 25 | 26 | worker-reload-mercy = 310 27 | reload-mercy = 310 28 | 29 | gevent = 1024 30 | 31 | safe-pidfile = /tmp/uwsgi_offload.pid 32 | -------------------------------------------------------------------------------- /conf/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for syncano 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/1.6/howto/deployment/wsgi/ 8 | """ 9 | import os 10 | 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.development") 12 | 13 | from django.core.wsgi import get_wsgi_application # noqa, isort:skip 14 | application = get_wsgi_application() 15 | -------------------------------------------------------------------------------- /db/00alter_settings.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cat <> ${PGDATA}/postgresql.conf 3 | log_min_messages = 'info' 4 | # log_statement = 'all' # log all statements (can be one of none, ddl, mod, all) 5 | log_min_duration_statement = 100 #log statements that take longer then 100 ms 6 | EOF 7 | -------------------------------------------------------------------------------- /db/01extensions.sql: -------------------------------------------------------------------------------- 1 | CREATE EXTENSION hstore; 2 | CREATE EXTENSION pg_trgm; 3 | CREATE EXTENSION postgis; 4 | -------------------------------------------------------------------------------- /db/02template_extensions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | psql -U syncano template1 -f /docker-entrypoint-initdb.d/01extensions.sql 3 | -------------------------------------------------------------------------------- /db/03createdb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # createdb instances 3 | createdb -U syncano -O syncano instances 4 | -------------------------------------------------------------------------------- /deploy/env/eu1.secrets.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/deploy/env/eu1.secrets.gpg -------------------------------------------------------------------------------- /deploy/env/staging.secrets.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/deploy/env/staging.secrets.gpg -------------------------------------------------------------------------------- /deploy/yaml/web-hpa.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: autoscaling/v2beta2 2 | kind: HorizontalPodAutoscaler 3 | metadata: 4 | name: platform-web 5 | spec: 6 | scaleTargetRef: 7 | apiVersion: apps/v1 8 | kind: Deployment 9 | name: platform-web 10 | minReplicas: {{ WEB_MIN }} 11 | maxReplicas: {{ WEB_MAX }} 12 | metrics: 13 | - type: Resource 14 | resource: 15 | name: cpu 16 | target: 17 | type: Utilization 18 | averageUtilization: 75 -------------------------------------------------------------------------------- /deploy/yaml/web-service.yml.j2: -------------------------------------------------------------------------------- 1 | kind: Service 2 | apiVersion: v1 3 | metadata: 4 | name: platform-web 5 | labels: 6 | k8s-app: platform-web 7 | spec: 8 | type: ClusterIP 9 | selector: 10 | app: platform 11 | type: web 12 | ports: 13 | - name: http 14 | protocol: TCP 15 | port: 80 16 | targetPort: http 17 | -------------------------------------------------------------------------------- /deploy/yaml/worker-hpa.yml.j2: -------------------------------------------------------------------------------- 1 | apiVersion: autoscaling/v2beta2 2 | kind: HorizontalPodAutoscaler 3 | metadata: 4 | name: platform-worker 5 | spec: 6 | scaleTargetRef: 7 | apiVersion: apps/v1 8 | kind: Deployment 9 | name: platform-worker 10 | minReplicas: {{ WORKER_MIN }} 11 | maxReplicas: {{ WORKER_MAX }} 12 | metrics: 13 | - type: Resource 14 | resource: 15 | name: cpu 16 | target: 17 | type: Utilization 18 | averageUtilization: 70 19 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.development") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/modules/__init__.py -------------------------------------------------------------------------------- /modules/serializer/python/py_defines.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #if PY_MAJOR_VERSION >= 3 6 | 7 | #define PyInt_Check PyLong_Check 8 | #define PyInt_AS_LONG PyLong_AsLong 9 | #define PyInt_FromLong PyLong_FromLong 10 | 11 | #define PyString_Check PyBytes_Check 12 | #define PyString_GET_SIZE PyBytes_GET_SIZE 13 | #define PyString_AS_STRING PyBytes_AS_STRING 14 | 15 | #define PyString_FromString PyUnicode_FromString 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /modules/serializer/python/version.h: -------------------------------------------------------------------------------- 1 | #define SERIALIZER_VERSION "1.2.3" 2 | -------------------------------------------------------------------------------- /requirements_development.txt: -------------------------------------------------------------------------------- 1 | autopep8==1.4.3 2 | coverage==4.5.2 3 | django-dynamic-fixture==2.0.0 4 | flake8==3.7.9 5 | isort==4.3.4 6 | pycodestyle==2.5.0 7 | -------------------------------------------------------------------------------- /run_celery.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eo pipefail 3 | 4 | CELERY_TYPE="$1" 5 | 6 | # Worker environment 7 | if [ "$CELERY_TYPE" = "beat" ]; then 8 | exec single-beat celery beat -A settings.celeryconf -s /tmp/celerybeat-schedule ${@:2} 9 | else 10 | if [ "$CELERY_TYPE" = "codebox_runner" ]; then 11 | python tools/setup_codebox.py 12 | CELERY_OPTS="-c ${LEGACY_CODEBOX_RUNNER_CONCURRENCY:-2}" 13 | fi 14 | 15 | exec newrelic-admin run-program celery worker -A settings.celeryconf -Q ${CELERY_TYPE} \ 16 | -n ${CELERY_TYPE}@%h --without-gossip --without-mingle --without-heartbeat $CELERY_OPTS ${@:2} 17 | fi 18 | -------------------------------------------------------------------------------- /scripts/certs/issue.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DOMAIN=$1 3 | LAST_UPDATE=$(date -Iseconds -u)+$RANDOM 4 | 5 | acme.sh \ 6 | --issue \ 7 | -d $DOMAIN \ 8 | --stateless 9 | 10 | RETCODE=$? 11 | 12 | if [ "$RETCODE" -ne 0 ] && [ "$RETCODE" -ne 2 ]; then 13 | exit $RETCODE 14 | fi 15 | 16 | # Install PEM and update last_update file so proxy gets reloaded 17 | acme.sh \ 18 | --install-cert \ 19 | -d $DOMAIN \ 20 | --reloadcmd \ 21 | "cat \$CERT_KEY_PATH \$CERT_FULLCHAIN_PATH > $CERT_HOME/pem/$DOMAIN.pem && \ 22 | echo $LAST_UPDATE > $CERT_HOME/.last_update" 23 | -------------------------------------------------------------------------------- /scripts/certs/remove.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | DOMAIN=$1 3 | LAST_UPDATE=$(date -Iseconds -u)+$RANDOM 4 | 5 | # If domain directory does not exist, exit 6 | [ ! -d $CERT_HOME/$DOMAIN ] && exit 0 7 | 8 | # Delete domain certs and PEM 9 | rm -rf $CERT_HOME/$DOMAIN $CERT_HOME/pem/$DOMAIN.pem 10 | 11 | # Update last_update file so proxy gets reloaded 12 | echo $LAST_UPDATE > $CERT_HOME/.last_update 13 | -------------------------------------------------------------------------------- /scripts/certs/renew.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | DOMAIN=$1 5 | LAST_UPDATE=$(date -Iseconds -u)+$RANDOM 6 | 7 | acme.sh \ 8 | --renew \ 9 | -d $DOMAIN \ 10 | --renew-hook \ 11 | "acme.sh \ 12 | --install-cert \ 13 | -d $DOMAIN \ 14 | --reloadcmd \ 15 | 'cat \$CERT_KEY_PATH \$CERT_FULLCHAIN_PATH > $CERT_HOME/pem/$DOMAIN.pem && \ 16 | echo $LAST_UPDATE > $CERT_HOME/.last_update'" 17 | -------------------------------------------------------------------------------- /settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/settings/__init__.py -------------------------------------------------------------------------------- /settings/celeryconf.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | import os 3 | 4 | from celery import Celery 5 | from celery.concurrency import asynpool 6 | from django.conf import settings 7 | 8 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.development") 9 | 10 | asynpool.PROC_ALIVE_TIMEOUT = settings.CELERY_PROC_ALIVE_TIMEOUT 11 | app = Celery('syncano', task_cls='apps.core.tasks:BaseTask') 12 | 13 | CELERY_TIMEZONE = 'UTC' 14 | 15 | app.config_from_object('django.conf:settings', namespace='CELERY') 16 | app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) 17 | 18 | 19 | def register_task(cls): 20 | return app.register_task(cls()) 21 | -------------------------------------------------------------------------------- /settings/celeryconf_debug.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from settings.celeryconf import * # noqa 3 | from settings.common import LOGGING 4 | 5 | # Logging override 6 | LOGGING['loggers']['celery_tasks']['level'] = 'DEBUG' 7 | LOGGING['loggers']['django']['level'] = 'DEBUG' 8 | -------------------------------------------------------------------------------- /settings/development.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from settings.common import * # noqa 3 | 4 | # Quick settings 5 | CACHE = True 6 | LOCAL_MEDIA_STORAGE = True 7 | 8 | MIDDLEWARE = ('apps.core.middleware.InstrumentMiddleware',) + MIDDLEWARE 9 | 10 | REST_FRAMEWORK['DEFAULT_RENDERER_CLASSES'] += ('rest_framework.renderers.BrowsableAPIRenderer',) 11 | 12 | # Cache 13 | if not CACHE: 14 | CACHES = { 15 | 'default': { 16 | 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', 17 | } 18 | } 19 | 20 | 21 | # Logging override 22 | LOGGING['loggers']['celery.redirected']['level'] = 'WARNING' 23 | -------------------------------------------------------------------------------- /settings/production.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from settings.common import * # noqa 3 | 4 | CONFIG_NAME = 'production' 5 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | exclude = docs,*/migrations/*,dev,ansible-playbooks,syncano-ansible,media,mount_codebox*,.history,.vscode 4 | max-complexity = 10 5 | ignore = W504,W503,F405 6 | 7 | [isort] 8 | skip_glob = docs,dev,ansible-playbooks,syncano-ansible,media,mount_codebox*,.history,.vscode 9 | line_length = 120 10 | known_first_party = apps,serializer 11 | multi_line_output = 3 12 | default_section = THIRDPARTY 13 | 14 | [pep8] 15 | max-line-length = 120 16 | exclude = docs,*/migrations/*,dev,ansible-playbooks,syncano-ansible,media,mount_codebox*,.history,.vscode 17 | recursive = true 18 | -------------------------------------------------------------------------------- /supervisord.conf: -------------------------------------------------------------------------------- 1 | [unix_http_server] 2 | file=/tmp/supervisord.sock 3 | username=dummy 4 | password=dummy 5 | 6 | [supervisord] 7 | logfile=/dev/stdout 8 | logfile_maxbytes=0 9 | loglevel=warn 10 | nodaemon=true 11 | 12 | [rpcinterface:supervisor] 13 | supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface 14 | 15 | [supervisorctl] 16 | serverurl=unix:///tmp/supervisord.sock 17 | username=dummy 18 | password=dummy 19 | 20 | [include] 21 | files=/home/syncano/supervisor/*.conf 22 | -------------------------------------------------------------------------------- /urls/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Syncano/syncano-platform/879111874d1ef70418b4890cf970720b0a2be4d8/urls/__init__.py -------------------------------------------------------------------------------- /urls/api/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | -------------------------------------------------------------------------------- /urls/api/v1.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.conf import settings 3 | from django.urls import include, path 4 | from rest_framework.urlpatterns import format_suffix_patterns 5 | 6 | from apps.core.views import TopLevelApiLinksView 7 | 8 | urlpatterns = [ 9 | path('', TopLevelApiLinksView.as_view(), name='links'), 10 | path('backups/', include('apps.backups.v1.urls_toplevel')), 11 | path('instances/', include('apps.instances.v1.urls')), 12 | path('cp/', include('apps.controlpanel.v1.urls')) 13 | ] 14 | 15 | if settings.MAIN_LOCATION: 16 | urlpatterns += [ 17 | path('account/', include('apps.admins.v1.urls')), 18 | path('billing/', include('apps.billing.v1.urls')), 19 | path('usage/', include('apps.metrics.v1.urls')), 20 | ] 21 | 22 | urlpatterns = format_suffix_patterns(urlpatterns) 23 | -------------------------------------------------------------------------------- /urls/api/v1_1.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.conf import settings 3 | from django.urls import include, path 4 | from rest_framework.urlpatterns import format_suffix_patterns 5 | 6 | from apps.core.views import TopLevelApiLinksView 7 | 8 | urlpatterns = [ 9 | path('', TopLevelApiLinksView.as_view(), name='links'), 10 | path('backups/', include('apps.backups.v1.urls_toplevel')), 11 | path('instances/', include('apps.instances.v1_1.urls')), 12 | path('cp/', include('apps.controlpanel.v1.urls')), 13 | ] 14 | 15 | if settings.MAIN_LOCATION: 16 | urlpatterns += [ 17 | path('account/', include('apps.admins.v1.urls')), 18 | path('billing/', include('apps.billing.v1.urls')), 19 | path('usage/', include('apps.metrics.v1.urls')), 20 | ] 21 | 22 | urlpatterns = format_suffix_patterns(urlpatterns) 23 | -------------------------------------------------------------------------------- /urls/api/v2.py: -------------------------------------------------------------------------------- 1 | # coding=UTF8 2 | from django.conf import settings 3 | from django.urls import include, path 4 | from rest_framework.urlpatterns import format_suffix_patterns 5 | 6 | from apps.core.views import TopLevelApiLinksView 7 | 8 | urlpatterns = [ 9 | path('', TopLevelApiLinksView.as_view(), name='links'), 10 | path('backups/', include('apps.backups.v1.urls_toplevel')), 11 | path('instances/', include('apps.instances.v2.urls')), 12 | path('cp/', include('apps.controlpanel.v1.urls')), 13 | ] 14 | 15 | if settings.MAIN_LOCATION: 16 | urlpatterns += [ 17 | path('account/', include('apps.admins.v1.urls')), 18 | path('billing/', include('apps.billing.v1.urls')), 19 | path('usage/', include('apps.metrics.v1.urls')), 20 | ] 21 | 22 | urlpatterns = format_suffix_patterns(urlpatterns) 23 | -------------------------------------------------------------------------------- /urls/backend.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.conf.urls.static import static 3 | from django.urls import include, path 4 | 5 | urlpatterns = [ 6 | path('', include(('apps.core.urls'))), 7 | path('v1/', include(('urls.api.v1', 'v1'))), 8 | path('v1.1/', include(('urls.api.v1_1', 'v1.1'))), 9 | path('v2/', include(('urls.api.v2', 'v2'))), 10 | ] 11 | 12 | if settings.LOCAL_MEDIA_STORAGE: 13 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 14 | --------------------------------------------------------------------------------