From fbd5c3a85e3ba3e4b0d33d1ede1d261cf1297433 Mon Sep 17 00:00:00 2001 From: tgstation-server Date: Fri, 21 Sep 2018 16:11:50 +0000 Subject: [PATCH] Deploy code docs to GitHub Pages for Appveyor build 957 Commit: 71d42b6da380902b286766fb3a4c2811c0f32297 --- ..._initial_create_8_designer_8cs_source.html | 4 +- ..._initial_create_8_designer_8cs_source.html | 4 +- ...43029___m_y_initial_create_8cs_source.html | 4 +- ...inimum_security_8_designer_8cs_source.html | 4 +- ...__m_y_add_minimum_security_8cs_source.html | 4 +- ...inimum_security_8_designer_8cs_source.html | 4 +- ...__m_s_add_minimum_security_8cs_source.html | 4 +- ...ign_key_cleanup_8_designer_8cs_source.html | 4 +- ...le_and_foreign_key_cleanup_8cs_source.html | 4 +- ...ign_key_cleanup_8_designer_8cs_source.html | 4 +- ...le_and_foreign_key_cleanup_8cs_source.html | 4 +- _administration_8cs_source.html | 3 +- _administration_client_8cs_source.html | 3 +- _api_client_8cs_source.html | 4 +- _api_client_factory_8cs_source.html | 4 +- _api_conflict_exception_8cs_source.html | 4 +- _api_controller_8cs_source.html | 4 +- _api_mismatch_exception_8cs_source.html | 4 +- ...ication_builder_extensions_8cs_source.html | 4 +- ...entication_context_factory_8cs_source.html | 4 +- _byond_8cs_source.html | 4 +- _byond_client_8cs_source.html | 3 +- _byond_command_8cs_source.html | 4 +- _byond_controller_8cs_source.html | 4 +- _byond_manager_8cs_source.html | 4 +- _byond_rights_8cs_source.html | 4 +- _channel_mapping_8cs_source.html | 4 +- _chat_8cs_source.html | 12 +- _chat_bot_rights_8cs_source.html | 4 +- _chat_bots_client_8cs_source.html | 6 +- _chat_connection_string_builder_8cs.html | 120 ++++ ..._connection_string_builder_8cs_source.html | 106 +++ _chat_controller_8cs_source.html | 12 +- _chat_factory_8cs_source.html | 4 +- _chat_provider_8cs_source.html | 4 +- _claims_injector_8cs_source.html | 4 +- _client_exception_8cs_source.html | 4 +- _comm_command_8cs_source.html | 4 +- _comm_context_8cs_source.html | 4 +- _command_factory_8cs_source.html | 4 +- ...ts_2_watchdog_2_i_watchdog_8cs_source.html | 6 +- ...chdog_2_i_watchdog_factory_8cs_source.html | 6 +- ...dog_2_reattach_information_8cs_source.html | 4 +- ...ents_2_watchdog_2_watchdog_8cs_source.html | 6 +- ...atchdog_2_watchdog_factory_8cs_source.html | 6 +- ...chdog_reattach_information_8cs_source.html | 4 +- _configuration_client_8cs_source.html | 4 +- _configuration_controller_8cs_source.html | 4 +- _configuration_file_8cs_source.html | 2 +- _configuration_rights_8cs_source.html | 4 +- _configuration_test_8cs_source.html | 4 +- _configuration_type_8cs_source.html | 4 +- _conflict_exception_8cs_source.html | 4 +- _constants_8cs_source.html | 2 +- _cryptography_suite_8cs_source.html | 4 +- _custom_command_8cs_source.html | 4 +- _database_context_8cs_source.html | 4 +- _database_context_factory_8cs_source.html | 4 +- _database_seeder_8cs_source.html | 4 +- _database_type_8cs_source.html | 4 +- ...db_context_factory_helpers_8cs_source.html | 4 +- _discord_connection_string_builder_8cs.html | 120 ++++ ..._connection_string_builder_8cs_source.html | 114 ++++ _discord_provider_8cs_source.html | 4 +- _dmb_provider_8cs_source.html | 4 +- _dream_daemon_8cs_source.html | 6 +- _dream_daemon_client_8cs_source.html | 4 +- _dream_daemon_controller_8cs_source.html | 4 +- ...m_daemon_launch_parameters_8cs_source.html | 4 +- _dream_daemon_rights_8cs_source.html | 4 +- _dream_daemon_security_8cs_source.html | 4 +- _dream_daemon_status_8cs_source.html | 4 +- _dream_maker_client_8cs_source.html | 4 +- _dream_maker_controller_8cs_source.html | 4 +- _dream_maker_rights_8cs_source.html | 4 +- _dream_maker_settings_8cs_source.html | 4 +- _error_message_8cs_source.html | 4 +- _event_consumer_8cs_source.html | 4 +- _event_notification_8cs_source.html | 4 +- _event_type_8cs_source.html | 4 +- _general_configuration_8cs_source.html | 2 +- _git_hub_client_factory_8cs_source.html | 4 +- _home_controller_8cs_source.html | 4 +- _http_client_8cs_source.html | 4 +- _i_administration_client_8cs_source.html | 4 +- _i_api_client_8cs_source.html | 4 +- _i_api_client_factory_8cs_source.html | 4 +- _i_application_8cs_source.html | 4 +- _i_authentication_context_8cs_source.html | 4 +- ...entication_context_factory_8cs_source.html | 4 +- _i_byond_client_8cs_source.html | 4 +- _i_byond_executable_lock_8cs_source.html | 4 +- _i_byond_installer_8cs_source.html | 4 +- _i_byond_manager_8cs_source.html | 4 +- _i_chat_8cs_source.html | 8 +- _i_chat_bots_client_8cs_source.html | 4 +- _i_chat_factory_8cs_source.html | 4 +- _i_claims_injector_8cs_source.html | 4 +- _i_comm_context_8cs_source.html | 4 +- _i_comm_handler_8cs_source.html | 4 +- _i_command_8cs_source.html | 4 +- _i_command_factory_8cs_source.html | 4 +- _i_compile_job_consumer_8cs_source.html | 4 +- _i_configuration_8cs_source.html | 4 +- _i_configuration_client_8cs_source.html | 4 +- _i_credentials_provider_8cs_source.html | 4 +- _i_cryptography_suite_8cs_source.html | 4 +- _i_custom_command_handler_8cs_source.html | 4 +- _i_database_context_8cs_source.html | 4 +- _i_database_context_factory_8cs_source.html | 4 +- _i_database_seeder_8cs_source.html | 4 +- _i_dmb_factory_8cs_source.html | 4 +- _i_dmb_provider_8cs_source.html | 4 +- _i_dream_daemon_client_8cs_source.html | 4 +- _i_dream_maker_8cs_source.html | 4 +- _i_dream_maker_client_8cs_source.html | 4 +- _i_event_consumer_8cs_source.html | 4 +- _i_git_hub_client_factory_8cs_source.html | 4 +- _i_http_client_8cs_source.html | 4 +- _i_i_o_manager_8cs_source.html | 4 +- _i_identity_cache_8cs_source.html | 4 +- _i_instance_8cs_source.html | 4 +- _i_instance_client_8cs_source.html | 4 +- _i_instance_factory_8cs_source.html | 4 +- _i_instance_manager_8cs_source.html | 4 +- _i_instance_manager_client_8cs_source.html | 4 +- _i_instance_user_client_8cs_source.html | 4 +- _i_job_manager_8cs_source.html | 4 +- _i_jobs_client_8cs_source.html | 4 +- _i_json_tracking_context_8cs_source.html | 4 +- _i_post_write_handler_8cs_source.html | 4 +- _i_process_8cs_source.html | 4 +- _i_process_base_8cs_source.html | 4 +- _i_process_executor_8cs_source.html | 4 +- _i_provider_8cs_source.html | 4 +- _i_provider_factory_8cs_source.html | 8 +- _i_reattach_info_handler_8cs_source.html | 4 +- _i_repository_8cs_source.html | 4 +- _i_repository_client_8cs_source.html | 4 +- _i_repository_manager_8cs_source.html | 4 +- _i_request_logger_8cs_source.html | 4 +- _i_server_8cs_source.html | 4 +- _i_server_client_8cs_source.html | 4 +- _i_server_client_factory_8cs_source.html | 4 +- _i_server_control_8cs_source.html | 4 +- _i_server_factory_8cs_source.html | 4 +- _i_session_controller_8cs_source.html | 4 +- _i_session_controller_factory_8cs_source.html | 6 +- _i_symlink_factory_8cs_source.html | 4 +- _i_synchronous_i_o_manager_8cs_source.html | 4 +- _i_system_identity_8cs_source.html | 4 +- _i_system_identity_factory_8cs_source.html | 4 +- _i_token_factory_8cs_source.html | 4 +- _i_users_client_8cs_source.html | 4 +- _identity_cache_8cs_source.html | 4 +- _identity_cache_object_8cs_source.html | 4 +- _instance_client_8cs_source.html | 4 +- _instance_controller_8cs_source.html | 4 +- _instance_factory_8cs_source.html | 4 +- _instance_manager_8cs_source.html | 4 +- _instance_manager_client_8cs_source.html | 4 +- _instance_manager_rights_8cs_source.html | 4 +- _instance_manager_test_8cs_source.html | 4 +- _instance_test_8cs_source.html | 4 +- _instance_user_client_8cs_source.html | 4 +- _instance_user_controller_8cs_source.html | 4 +- _instance_user_rights_8cs_source.html | 4 +- ...ient_permissions_exception_8cs_source.html | 4 +- _irc_connection_string_builder_8cs.html | 120 ++++ ..._connection_string_builder_8cs_source.html | 116 ++++ _irc_password_type_8cs.html | 16 +- _irc_password_type_8cs_source.html | 13 +- _irc_provider_8cs_source.html | 6 +- _job_controller_8cs_source.html | 4 +- _job_exception_8cs_source.html | 4 +- _job_handler_8cs_source.html | 4 +- _job_manager_8cs_source.html | 4 +- _jobs_client_8cs_source.html | 4 +- _json_file_8cs_source.html | 6 +- _json_sub_file_list_8cs_source.html | 4 +- _json_tracking_context_8cs_source.html | 4 +- _kek_command_8cs_source.html | 4 +- _launch_result_8cs_source.html | 4 +- ...od_not_supported_exception_8cs_source.html | 4 +- _model_controller_8cs_source.html | 4 +- ...els_2_reattach_information_8cs_source.html | 4 +- ...chdog_reattach_information_8cs_source.html | 4 +- _monitor_action_8cs_source.html | 4 +- _monitor_activation_reason_8cs_source.html | 4 +- _monitor_state_8cs_source.html | 4 +- _my_sql_database_context_8cs_source.html | 4 +- ...ase_context_model_snapshot_8cs_source.html | 4 +- ...gn_time_db_context_factory_8cs_source.html | 4 +- _native_methods_8cs_source.html | 4 +- _posix_byond_installer_8cs_source.html | 4 +- _posix_post_write_handler_8cs_source.html | 4 +- _posix_symlink_factory_8cs_source.html | 4 +- ...ix_system_identity_factory_8cs_source.html | 4 +- _process_8cs_source.html | 4 +- _process_executor_8cs_source.html | 4 +- _program_8cs_source.html | 4 +- _provider_8cs_source.html | 4 +- _provider_factory_8cs_source.html | 7 +- _pull_requests_command_8cs_source.html | 4 +- _rate_limit_exception_8cs_source.html | 4 +- _reattach_info_handler_8cs_source.html | 4 +- _reattach_information_base_8cs_source.html | 4 +- _reboot_state_8cs_source.html | 4 +- _repository_client_8cs_source.html | 4 +- _repository_controller_8cs_source.html | 4 +- _repository_manager_8cs_source.html | 6 +- _repository_rights_8cs_source.html | 4 +- _request_timeout_exception_8cs_source.html | 4 +- _resolving_i_o_manager_8cs_source.html | 4 +- _response_8cs_source.html | 4 +- _rev_info_test_merge_8cs_source.html | 4 +- _revision_command_8cs_source.html | 4 +- _rights_helper_8cs_source.html | 4 +- _rights_type_8cs_source.html | 4 +- _routes_8cs_source.html | 4 +- _semaphore_slim_context_8cs_source.html | 4 +- _server_8cs_source.html | 4 +- _server_client_8cs_source.html | 4 +- _server_client_factory_8cs_source.html | 4 +- _server_error_exception_8cs_source.html | 4 +- _server_factory_8cs_source.html | 4 +- _server_information_8cs_source.html | 4 +- _server_service_8cs_source.html | 4 +- _server_side_modifications_8cs_source.html | 4 +- ...vice_unavailable_exception_8cs_source.html | 4 +- _session_controller_8cs_source.html | 4 +- _session_controller_factory_8cs_source.html | 6 +- _sql_server_database_context_8cs_source.html | 4 +- ...ase_context_model_snapshot_8cs_source.html | 4 +- ...gn_time_db_context_factory_8cs_source.html | 4 +- _synchronous_i_o_manager_8cs_source.html | 4 +- _temporary_dmb_provider_8cs_source.html | 4 +- _test_api_client_8cs_source.html | 3 +- _test_dream_daemon_client_8cs_source.html | 3 +- _test_merge_base_8cs_source.html | 4 +- _test_merge_parameters_8cs_source.html | 4 +- _test_server_client_factory_8cs_source.html | 4 +- _test_watchdog_factory_8cs_source.html | 4 +- _tgs_authorize_attribute_8cs_source.html | 4 +- ..._8_api_2_models_2_chat_bot_8cs_source.html | 4 +- ...pi_2_models_2_chat_channel_8cs_source.html | 4 +- ...api_2_models_2_compile_job_8cs_source.html | 4 +- ...api_2_models_2_dream_maker_8cs_source.html | 4 +- ..._8_api_2_models_2_instance_8cs_source.html | 4 +- ...i_2_models_2_instance_user_8cs_source.html | 4 +- ...dels_2_internal_2_chat_bot_8cs_source.html | 8 +- ...s_2_internal_2_compile_job_8cs_source.html | 4 +- ...al_2_dream_daemon_settings_8cs_source.html | 4 +- ..._2_models_2_internal_2_job_8cs_source.html | 4 +- ...rnal_2_repository_settings_8cs_source.html | 4 +- ...nal_2_revision_information_8cs_source.html | 4 +- ...ls_2_internal_2_test_merge_8cs_source.html | 4 +- ...2_models_2_internal_2_user_8cs_source.html | 4 +- ...erver_8_api_2_models_2_job_8cs_source.html | 2 +- ..._api_2_models_2_repository_8cs_source.html | 4 +- ...els_2_revision_information_8cs_source.html | 4 +- ..._api_2_models_2_test_merge_8cs_source.html | 4 +- ...rver_8_api_2_models_2_user_8cs_source.html | 2 +- ...on_8_server_8_api_8_assembly_info_8cs.html | 4 +- ...rver_8_api_8_assembly_info_8cs_source.html | 6 +- ...ver_8_api_8_tests_8_assembly_info_8cs.html | 4 +- ...pi_8_tests_8_assembly_info_8cs_source.html | 6 +- ..._8_server_8_api_8_tests_8_program_8cs.html | 4 +- ...er_8_api_8_tests_8_program_8cs_source.html | 4 +- ...8_server_8_client_8_assembly_info_8cs.html | 4 +- ...r_8_client_8_assembly_info_8cs_source.html | 6 +- ..._8_client_8_tests_8_assembly_info_8cs.html | 4 +- ...nt_8_tests_8_assembly_info_8cs_source.html | 6 +- ...server_8_client_8_tests_8_program_8cs.html | 4 +- ...8_client_8_tests_8_program_8cs_source.html | 4 +- ...2_components_2_chat_2_user_8cs_source.html | 4 +- ...s_2_compiler_2_dream_maker_8cs_source.html | 6 +- ...st_2_components_2_instance_8cs_source.html | 4 +- ...nts_2_interop_2_test_merge_8cs_source.html | 6 +- ..._2_repository_2_repository_8cs_source.html | 4 +- ...st_2_models_2_chat_channel_8cs_source.html | 4 +- ...ost_2_models_2_compile_job_8cs_source.html | 4 +- ...ls_2_dream_daemon_settings_8cs_source.html | 4 +- ...8_host_2_models_2_instance_8cs_source.html | 4 +- ...t_2_models_2_instance_user_8cs_source.html | 4 +- ...rver_8_host_2_models_2_job_8cs_source.html | 4 +- ...dels_2_repository_settings_8cs_source.html | 4 +- ...els_2_revision_information_8cs_source.html | 4 +- ...host_2_models_2_test_merge_8cs_source.html | 4 +- ...ver_8_host_2_models_2_user_8cs_source.html | 4 +- ...n_8_server_8_host_8_assembly_info_8cs.html | 4 +- ...ver_8_host_8_assembly_info_8cs_source.html | 6 +- ..._8_host_8_console_8_assembly_info_8cs.html | 4 +- ..._8_console_8_assembly_info_8cs_source.html | 6 +- ...8_console_8_tests_8_assembly_info_8cs.html | 4 +- ...le_8_tests_8_assembly_info_8cs_source.html | 6 +- ..._host_8_console_8_tests_8_program_8cs.html | 4 +- ..._console_8_tests_8_program_8cs_source.html | 4 +- ...rver_8_host_8_razor_assembly_info_8cs.html | 4 +- ...host_8_razor_assembly_info_8cs_source.html | 4 +- ...er_8_host_8_tests_8_assembly_info_8cs.html | 4 +- ...st_8_tests_8_assembly_info_8cs_source.html | 6 +- ...8_server_8_host_8_tests_8_program_8cs.html | 4 +- ...r_8_host_8_tests_8_program_8cs_source.html | 4 +- ...8_host_8_watchdog_8_assembly_info_8cs.html | 4 +- ...8_watchdog_8_assembly_info_8cs_source.html | 6 +- ..._watchdog_8_tests_8_assembly_info_8cs.html | 4 +- ...og_8_tests_8_assembly_info_8cs_source.html | 6 +- ...host_8_watchdog_8_tests_8_program_8cs.html | 4 +- ...watchdog_8_tests_8_program_8cs_source.html | 4 +- ..._8_server_8_tests_8_assembly_info_8cs.html | 4 +- ...er_8_tests_8_assembly_info_8cs_source.html | 6 +- ...tation_8_server_8_tests_8_program_8cs.html | 4 +- ...8_server_8_tests_8_program_8cs_source.html | 4 +- _token_8cs_source.html | 4 +- _token_factory_8cs_source.html | 4 +- _unauthorized_exception_8cs_source.html | 4 +- ...ognized_response_exception_8cs_source.html | 4 +- _updates_configuration_8cs_source.html | 4 +- _user_controller_8cs_source.html | 4 +- _user_update_8cs_source.html | 2 +- _users_client_8cs_source.html | 4 +- _version_command_8cs_source.html | 4 +- ...chdog_2_i_watchdog_factory_8cs_source.html | 2 +- _watchdog_2_watchdog_8cs_source.html | 4 +- _watchdog_2_watchdog_factory_8cs_source.html | 4 +- _watchdog_launch_result_8cs_source.html | 4 +- ..._reattach_information_base_8cs_source.html | 4 +- _windows_byond_installer_8cs_source.html | 4 +- _windows_post_write_handler_8cs_source.html | 4 +- _windows_symlink_factory_8cs_source.html | 4 +- _windows_system_identity_8cs_source.html | 4 +- ...ws_system_identity_factory_8cs_source.html | 4 +- annotated.html | 49 +- api.html | 12 +- class_auto_generated_program.html | 12 +- ...1_api_1_1_models_1_1_chat_bot-members.html | 11 +- ...erver_1_1_api_1_1_models_1_1_chat_bot.html | 9 +- ...ord_connection_string_builder-members.html | 114 ++++ ...1_1_discord_connection_string_builder.html | 276 ++++++++ ..._1_1_discord_connection_string_builder.png | Bin 0 -> 1386 bytes ...erver_1_1_api_1_1_models_1_1_instance.html | 2 +- ...els_1_1_internal_1_1_chat_bot-members.html | 9 +- ..._1_1_models_1_1_internal_1_1_chat_bot.html | 39 +- ...hat_connection_string_builder-members.html | 111 +++ ...al_1_1_chat_connection_string_builder.html | 197 ++++++ ...nal_1_1_chat_connection_string_builder.png | Bin 0 -> 1858 bytes ...irc_connection_string_builder-members.html | 119 ++++ ...els_1_1_irc_connection_string_builder.html | 424 ++++++++++++ ...dels_1_1_irc_connection_string_builder.png | Bin 0 -> 1370 bytes ...t_1_1_components_1_1_chat_bots_client.html | 2 +- ...host_1_1_components_1_1_chat_1_1_chat.html | 6 +- ...ponents_1_1_chat_1_1_provider_factory.html | 5 +- ...1_chat_1_1_providers_1_1_irc_provider.html | 12 +- ...t_1_1_controllers_1_1_chat_controller.html | 20 +- ..._host_1_1_models_1_1_chat_bot-members.html | 15 +- ...rver_1_1_host_1_1_models_1_1_chat_bot.html | 3 + ..._host_1_1_models_1_1_database_context.html | 6 +- ...1_security_1_1_authentication_context.html | 2 +- classes.html | 165 ++--- ... dir_019c6d3af924151b681494cc466be1ac.html | 8 +- ... dir_0b6a380fe7fc1e1d640d1ac854fc3fb6.html | 8 +- dir_103b33cf1ab7043de6f87dfa61afa23f.html | 2 +- ... dir_1083fcf52a7d94a2efffb6ca88246acf.html | 4 +- dir_177c182c6d1f29bec543c3fdad3072d5.html | 2 +- ... dir_2553b13cbb871373b0c30fb90d66217c.html | 4 +- ... dir_2b9d136d9ffdab429a63a74fca28b300.html | 8 +- ... dir_2e67631cb23401d696bcfdbe81f86f0f.html | 8 +- dir_446bdfc1cd19b69c11765b5665ebfd45.html | 2 +- ... dir_48968f849f7ddb061f7130e079fe66a6.html | 8 +- ... dir_4c0c7e2f4fc60f51e2347a2d8dda94ce.html | 4 +- dir_5e0b464d3b4be546c63c5188ffeb96e4.html | 2 - dir_61d7974a657c30011f457b376d274ee7.html | 6 + dir_685b5aa2d8a661ab5bc3f68b331887b0.html | 2 +- dir_689f0584ee97ae9ca308532531d9c5d2.html | 2 +- dir_74017a19412e2f90cc6ad11fcac82495.html | 2 +- ... dir_87039fa7b841d848ca75981d64237477.html | 8 +- dir_87a4f2035ed18e30422f4b60aebc4381.html | 2 +- ... dir_9257d56e926145e494a5aa5279887044.html | 4 +- ... dir_a3714ab037275c284988fc90b4d045a5.html | 4 +- ... dir_a8807eef6592489c6a360d587f5b1a66.html | 4 +- dir_b524b00ec2259c3c83b44909fd47f6e3.html | 2 +- ... dir_b551a02288ef39e099594710382363a0.html | 4 +- ... dir_b87cbb2c60cf682b61fbfcceb9bced43.html | 4 +- ... dir_b92573f47b933d7a3661c26d1af2098b.html | 8 +- ... dir_be9f3ed3485a880e3213506a311823d1.html | 8 +- ... dir_c5f98c05147bcbba78e71856485f5049.html | 4 +- ... dir_d8a279f7113e0073f6cd65dd050c1475.html | 4 +- dir_e0c1bccb03249d93bc42ac0968ce43fd.html | 2 +- ... dir_e97d380c3eebaf0856a4ec180d749620.html | 8 +- ... dir_ea36b5ab01ad6e8b607438a02ae91964.html | 8 +- dir_f0581cdd528a911f791df57d0f438bcb.html | 2 +- dir_f2a82faa4c4beef364292694cefca86d.html | 2 + ... dir_f88948f9bde0555374e8a59a663bdfc9.html | 4 +- ... dir_fab1362e86740a2c573ef875dd474a67.html | 8 +- dir_fb12273b841438c2f2c9b5bc5398c636.html | 2 +- files.html | 103 +-- functions.html | 9 +- functions_b.html | 3 + functions_c.html | 21 +- functions_d.html | 3 + functions_func_d.html | 5 +- functions_func_i.html | 3 + functions_func_t.html | 5 +- functions_i.html | 3 + functions_n.html | 3 + functions_p.html | 7 + functions_prop.html | 3 + functions_prop_b.html | 3 + functions_prop_c.html | 3 + functions_prop_n.html | 3 + functions_prop_p.html | 7 +- functions_prop_u.html | 3 + functions_prop_v.html | 3 + functions_r.html | 4 +- functions_t.html | 5 +- functions_u.html | 3 + functions_v.html | 5 + functions_vars_v.html | 4 + functions_w.html | 2 +- hierarchy.html | 645 +++++++++--------- ...station_1_1_server_1_1_api_1_1_models.html | 50 ++ ...erver_1_1_api_1_1_models_1_1_internal.html | 3 + ...1_1_components_1_1_chat_1_1_providers.html | 51 +- namespacemembers.html | 2 +- namespacemembers_enum.html | 2 +- search/all_1.js | 2 +- search/all_10.js | 12 +- search/all_12.js | 18 +- search/all_13.js | 4 +- search/all_14.js | 8 +- search/all_15.js | 13 +- search/all_16.js | 3 +- search/all_17.js | 4 +- search/all_2.js | 4 +- search/all_3.js | 27 +- search/all_4.js | 11 +- search/all_5.js | 2 +- search/all_9.js | 33 +- search/all_c.js | 2 +- search/all_e.js | 4 +- search/classes_10.js | 4 +- search/classes_11.js | 4 +- search/classes_13.js | 2 +- search/classes_2.js | 5 +- search/classes_3.js | 3 +- search/classes_7.js | 9 +- search/classes_d.js | 2 +- search/classes_e.js | 6 +- search/enums_5.js | 2 +- search/enumvalues_9.js | 2 +- search/enumvalues_c.js | 4 +- search/files_3.js | 5 +- search/files_4.js | 5 +- search/files_8.js | 7 +- search/functions_12.js | 2 +- search/functions_3.js | 1 + search/functions_8.js | 1 + search/properties_0.js | 1 + search/properties_1.js | 1 + search/properties_12.js | 3 +- search/properties_13.js | 1 + search/properties_2.js | 1 + search/properties_c.js | 1 + search/properties_e.js | 5 +- search/variables_11.js | 2 +- search/variables_15.js | 1 + search/variables_2.js | 4 +- 468 files changed, 3583 insertions(+), 1483 deletions(-) create mode 100644 _chat_connection_string_builder_8cs.html create mode 100644 _chat_connection_string_builder_8cs_source.html create mode 100644 _discord_connection_string_builder_8cs.html create mode 100644 _discord_connection_string_builder_8cs_source.html create mode 100644 _irc_connection_string_builder_8cs.html create mode 100644 _irc_connection_string_builder_8cs_source.html create mode 100644 class_tgstation_1_1_server_1_1_api_1_1_models_1_1_discord_connection_string_builder-members.html create mode 100644 class_tgstation_1_1_server_1_1_api_1_1_models_1_1_discord_connection_string_builder.html create mode 100644 class_tgstation_1_1_server_1_1_api_1_1_models_1_1_discord_connection_string_builder.png create mode 100644 class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_connection_string_builder-members.html create mode 100644 class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_connection_string_builder.html create mode 100644 class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_connection_string_builder.png create mode 100644 class_tgstation_1_1_server_1_1_api_1_1_models_1_1_irc_connection_string_builder-members.html create mode 100644 class_tgstation_1_1_server_1_1_api_1_1_models_1_1_irc_connection_string_builder.html create mode 100644 class_tgstation_1_1_server_1_1_api_1_1_models_1_1_irc_connection_string_builder.png rename dir_91e2bbee1291fcb0db7e0417b617d01e.html => dir_019c6d3af924151b681494cc466be1ac.html (93%) rename dir_53c056aa6a96f034cca75f793964be6e.html => dir_0b6a380fe7fc1e1d640d1ac854fc3fb6.html (92%) rename dir_b2c89c850c962495793d09ea3a2ee385.html => dir_1083fcf52a7d94a2efffb6ca88246acf.html (95%) rename dir_53aae571c66a052729f8e64ccc397130.html => dir_2553b13cbb871373b0c30fb90d66217c.html (94%) rename dir_90400719279795a0c5ea912516a4c874.html => dir_2b9d136d9ffdab429a63a74fca28b300.html (92%) rename dir_7ae0efc0613c36c848fa310214e0aee3.html => dir_2e67631cb23401d696bcfdbe81f86f0f.html (92%) rename dir_4122b363a9d577bed19bef337e757ff3.html => dir_48968f849f7ddb061f7130e079fe66a6.html (92%) rename dir_d45f494620bc0048ee4a31fa4f7391e1.html => dir_4c0c7e2f4fc60f51e2347a2d8dda94ce.html (94%) rename dir_0e792d76844bf2d7c79795a56a50ebec.html => dir_87039fa7b841d848ca75981d64237477.html (92%) rename dir_6db5af7fcfa17a34c7abf23e195af4db.html => dir_9257d56e926145e494a5aa5279887044.html (94%) rename dir_13bee0ed20ecc83b9bcd4b869b726d42.html => dir_a3714ab037275c284988fc90b4d045a5.html (95%) rename dir_fcc41a8faa41492aba200a49fc7d0aed.html => dir_a8807eef6592489c6a360d587f5b1a66.html (93%) rename dir_900257602d294c4a4799e88420919068.html => dir_b551a02288ef39e099594710382363a0.html (95%) rename dir_e928c7e494884ad322b5f6005c2954e1.html => dir_b87cbb2c60cf682b61fbfcceb9bced43.html (94%) rename dir_40138fd4849573e743490d583f6d06d6.html => dir_b92573f47b933d7a3661c26d1af2098b.html (93%) rename dir_18a5e30c14613227f95d12bd9fddc614.html => dir_be9f3ed3485a880e3213506a311823d1.html (92%) rename dir_298f2a364723f4f4cea115538f944a94.html => dir_c5f98c05147bcbba78e71856485f5049.html (95%) rename dir_83a6f600c846cd444f33e44bb9b4f9cb.html => dir_d8a279f7113e0073f6cd65dd050c1475.html (94%) rename dir_c65d377d3c73fb4a07b3637f9c0caf4d.html => dir_e97d380c3eebaf0856a4ec180d749620.html (93%) rename dir_6c8d8f20501e8e412458f542d0998195.html => dir_ea36b5ab01ad6e8b607438a02ae91964.html (93%) rename dir_383ca23c40357ede4cca827b4a365c76.html => dir_f88948f9bde0555374e8a59a663bdfc9.html (94%) rename dir_7a9d035dcfcd6d9c7aa1efef4b6b58a4.html => dir_fab1362e86740a2c573ef875dd474a67.html (92%) diff --git a/20180906135553___m_s_initial_create_8_designer_8cs_source.html b/20180906135553___m_s_initial_create_8_designer_8cs_source.html index fb7b9d4ee8..413dff62ab 100644 --- a/20180906135553___m_s_initial_create_8_designer_8cs_source.html +++ b/20180906135553___m_s_initial_create_8_designer_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
20180906135553_MSInitialCreate.Designer.cs
-Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Metadata;
6 using Microsoft.EntityFrameworkCore.Migrations;
7 using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 
9 namespace Tgstation.Server.Host.Models.Migrations
10 {
11  [DbContext(typeof(SqlServerDatabaseContext))]
12  [Migration("20180906135553_MSInitialCreate")]
13  partial class MSInitialCreate
14  {
19  protected override void BuildTargetModel(ModelBuilder modelBuilder)
20  {
21 #pragma warning disable 612, 618
22  modelBuilder
23  .HasAnnotation("ProductVersion", "2.1.2-rtm-30932")
24  .HasAnnotation("Relational:MaxIdentifierLength", 128)
25  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
26 
27  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
28  {
29  b.Property<long>("Id")
30  .ValueGeneratedOnAdd()
31  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
32 
33  b.Property<string>("ConnectionString")
34  .IsRequired();
35 
36  b.Property<bool?>("Enabled");
37 
38  b.Property<long>("InstanceId");
39 
40  b.Property<string>("Name")
41  .IsRequired();
42 
43  b.Property<int?>("Provider");
44 
45  b.HasKey("Id");
46 
47  b.HasIndex("InstanceId");
48 
49  b.HasIndex("Name")
50  .IsUnique();
51 
52  b.ToTable("ChatBots");
53  });
54 
55  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
56  {
57  b.Property<long>("Id")
58  .ValueGeneratedOnAdd()
59  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
60 
61  b.Property<long>("ChatSettingsId");
62 
63  b.Property<decimal?>("DiscordChannelId")
64  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
65 
66  b.Property<string>("IrcChannel");
67 
68  b.Property<bool?>("IsAdminChannel")
69  .IsRequired();
70 
71  b.Property<bool?>("IsUpdatesChannel")
72  .IsRequired();
73 
74  b.Property<bool?>("IsWatchdogChannel")
75  .IsRequired();
76 
77  b.Property<string>("Tag");
78 
79  b.HasKey("Id");
80 
81  b.HasIndex("ChatSettingsId", "DiscordChannelId")
82  .IsUnique()
83  .HasFilter("[DiscordChannelId] IS NOT NULL");
84 
85  b.HasIndex("ChatSettingsId", "IrcChannel")
86  .IsUnique()
87  .HasFilter("[IrcChannel] IS NOT NULL");
88 
89  b.ToTable("ChatChannels");
90  });
91 
92  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
93  {
94  b.Property<long>("Id")
95  .ValueGeneratedOnAdd()
96  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
97 
98  b.Property<string>("ByondVersion")
99  .IsRequired();
100 
101  b.Property<Guid?>("DirectoryName");
102 
103  b.Property<string>("DmeName");
104 
105  b.Property<long?>("JobId");
106 
107  b.Property<string>("Output");
108 
109  b.Property<long>("RevisionInformationId");
110 
111  b.HasKey("Id");
112 
113  b.HasIndex("DirectoryName");
114 
115  b.HasIndex("JobId");
116 
117  b.HasIndex("RevisionInformationId");
118 
119  b.ToTable("CompileJobs");
120  });
121 
122  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
123  {
124  b.Property<long>("Id")
125  .ValueGeneratedOnAdd()
126  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
127 
128  b.Property<string>("AccessToken");
129 
130  b.Property<bool?>("AllowWebClient")
131  .IsRequired();
132 
133  b.Property<bool?>("AutoStart")
134  .IsRequired();
135 
136  b.Property<long>("InstanceId");
137 
138  b.Property<int>("PrimaryPort");
139 
140  b.Property<int?>("ProcessId");
141 
142  b.Property<int>("SecondaryPort");
143 
144  b.Property<int>("SecurityLevel");
145 
146  b.Property<bool?>("SoftRestart")
147  .IsRequired();
148 
149  b.Property<bool?>("SoftShutdown")
150  .IsRequired();
151 
152  b.Property<long>("StartupTimeout");
153 
154  b.HasKey("Id");
155 
156  b.HasIndex("InstanceId")
157  .IsUnique();
158 
159  b.ToTable("DreamDaemonSettings");
160  });
161 
162  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
163  {
164  b.Property<long>("Id")
165  .ValueGeneratedOnAdd()
166  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
167 
168  b.Property<int>("ApiValidationPort");
169 
170  b.Property<long>("InstanceId");
171 
172  b.Property<string>("ProjectName");
173 
174  b.HasKey("Id");
175 
176  b.HasIndex("InstanceId")
177  .IsUnique();
178 
179  b.ToTable("DreamMakerSettings");
180  });
181 
182  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
183  {
184  b.Property<long>("Id")
185  .ValueGeneratedOnAdd()
186  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
187 
188  b.Property<long>("AutoUpdateInterval");
189 
190  b.Property<int>("ConfigurationType");
191 
192  b.Property<string>("Name")
193  .IsRequired();
194 
195  b.Property<bool?>("Online")
196  .IsRequired();
197 
198  b.Property<string>("Path")
199  .IsRequired();
200 
201  b.Property<long?>("WatchdogReattachInformationId");
202 
203  b.HasKey("Id");
204 
205  b.HasIndex("Path")
206  .IsUnique();
207 
208  b.HasIndex("WatchdogReattachInformationId");
209 
210  b.ToTable("Instances");
211  });
212 
213  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
214  {
215  b.Property<long>("Id")
216  .ValueGeneratedOnAdd()
217  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
218 
219  b.Property<decimal>("ByondRights")
220  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
221 
222  b.Property<decimal>("ChatBotRights")
223  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
224 
225  b.Property<decimal>("ConfigurationRights")
226  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
227 
228  b.Property<decimal>("DreamDaemonRights")
229  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
230 
231  b.Property<decimal>("DreamMakerRights")
232  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
233 
234  b.Property<long>("InstanceId");
235 
236  b.Property<decimal>("InstanceUserRights")
237  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
238 
239  b.Property<decimal>("RepositoryRights")
240  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
241 
242  b.Property<long?>("UserId")
243  .IsRequired();
244 
245  b.HasKey("Id");
246 
247  b.HasIndex("InstanceId");
248 
249  b.HasIndex("UserId", "InstanceId")
250  .IsUnique();
251 
252  b.ToTable("InstanceUsers");
253  });
254 
255  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
256  {
257  b.Property<long>("Id")
258  .ValueGeneratedOnAdd()
259  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
260 
261  b.Property<decimal?>("CancelRight")
262  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
263 
264  b.Property<decimal?>("CancelRightsType")
265  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
266 
267  b.Property<bool?>("Cancelled")
268  .IsRequired();
269 
270  b.Property<long?>("CancelledById");
271 
272  b.Property<string>("Description")
273  .IsRequired();
274 
275  b.Property<string>("ExceptionDetails");
276 
277  b.Property<long>("InstanceId");
278 
279  b.Property<DateTimeOffset?>("StartedAt")
280  .IsRequired();
281 
282  b.Property<long>("StartedById");
283 
284  b.Property<DateTimeOffset?>("StoppedAt");
285 
286  b.HasKey("Id");
287 
288  b.HasIndex("CancelledById");
289 
290  b.HasIndex("InstanceId");
291 
292  b.HasIndex("StartedById");
293 
294  b.ToTable("Jobs");
295  });
296 
297  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
298  {
299  b.Property<long>("Id")
300  .ValueGeneratedOnAdd()
301  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
302 
303  b.Property<string>("AccessIdentifier")
304  .IsRequired();
305 
306  b.Property<string>("ChatChannelsJson")
307  .IsRequired();
308 
309  b.Property<string>("ChatCommandsJson")
310  .IsRequired();
311 
312  b.Property<long?>("CompileJobId");
313 
314  b.Property<bool>("IsPrimary");
315 
316  b.Property<int>("Port");
317 
318  b.Property<int>("ProcessId");
319 
320  b.Property<int>("RebootState");
321 
322  b.Property<string>("ServerCommandsJson")
323  .IsRequired();
324 
325  b.HasKey("Id");
326 
327  b.HasIndex("CompileJobId");
328 
329  b.ToTable("ReattachInformations");
330  });
331 
332  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
333  {
334  b.Property<long>("Id")
335  .ValueGeneratedOnAdd()
336  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
337 
338  b.Property<string>("AccessToken");
339 
340  b.Property<string>("AccessUser");
341 
342  b.Property<bool?>("AutoUpdatesKeepTestMerges")
343  .IsRequired();
344 
345  b.Property<bool?>("AutoUpdatesSynchronize")
346  .IsRequired();
347 
348  b.Property<string>("CommitterEmail")
349  .IsRequired();
350 
351  b.Property<string>("CommitterName")
352  .IsRequired();
353 
354  b.Property<long>("InstanceId");
355 
356  b.Property<bool?>("PushTestMergeCommits")
357  .IsRequired();
358 
359  b.Property<bool?>("ShowTestMergeCommitters")
360  .IsRequired();
361 
362  b.HasKey("Id");
363 
364  b.HasIndex("InstanceId")
365  .IsUnique();
366 
367  b.ToTable("RepositorySettings");
368  });
369 
370  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
371  {
372  b.Property<long>("Id")
373  .ValueGeneratedOnAdd()
374  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
375 
376  b.Property<long>("RevisionInformationId");
377 
378  b.Property<long>("TestMergeId");
379 
380  b.HasKey("Id");
381 
382  b.HasIndex("RevisionInformationId");
383 
384  b.HasIndex("TestMergeId");
385 
386  b.ToTable("RevInfoTestMerges");
387  });
388 
389  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
390  {
391  b.Property<long>("Id")
392  .ValueGeneratedOnAdd()
393  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
394 
395  b.Property<string>("CommitSha")
396  .IsRequired()
397  .HasMaxLength(40);
398 
399  b.Property<long>("InstanceId");
400 
401  b.Property<string>("OriginCommitSha")
402  .IsRequired()
403  .HasMaxLength(40);
404 
405  b.HasKey("Id");
406 
407  b.HasIndex("CommitSha")
408  .IsUnique();
409 
410  b.HasIndex("InstanceId");
411 
412  b.ToTable("RevisionInformations");
413  });
414 
415  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
416  {
417  b.Property<long>("Id")
418  .ValueGeneratedOnAdd()
419  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
420 
421  b.Property<string>("Author")
422  .IsRequired();
423 
424  b.Property<string>("BodyAtMerge")
425  .IsRequired();
426 
427  b.Property<string>("Comment");
428 
429  b.Property<DateTimeOffset>("MergedAt");
430 
431  b.Property<long>("MergedById");
432 
433  b.Property<int?>("Number")
434  .IsRequired();
435 
436  b.Property<long?>("PrimaryRevisionInformationId");
437 
438  b.Property<string>("PullRequestRevision")
439  .IsRequired();
440 
441  b.Property<string>("TitleAtMerge")
442  .IsRequired();
443 
444  b.Property<string>("Url")
445  .IsRequired();
446 
447  b.HasKey("Id");
448 
449  b.HasIndex("MergedById");
450 
451  b.HasIndex("PrimaryRevisionInformationId")
452  .IsUnique()
453  .HasFilter("[PrimaryRevisionInformationId] IS NOT NULL");
454 
455  b.ToTable("TestMerges");
456  });
457 
458  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
459  {
460  b.Property<long>("Id")
461  .ValueGeneratedOnAdd()
462  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
463 
464  b.Property<decimal>("AdministrationRights")
465  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
466 
467  b.Property<string>("CanonicalName")
468  .IsRequired();
469 
470  b.Property<DateTimeOffset?>("CreatedAt")
471  .IsRequired();
472 
473  b.Property<long?>("CreatedById");
474 
475  b.Property<bool?>("Enabled")
476  .IsRequired();
477 
478  b.Property<decimal>("InstanceManagerRights")
479  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
480 
481  b.Property<DateTimeOffset?>("LastPasswordUpdate");
482 
483  b.Property<string>("Name")
484  .IsRequired();
485 
486  b.Property<string>("PasswordHash");
487 
488  b.Property<string>("SystemIdentifier");
489 
490  b.HasKey("Id");
491 
492  b.HasIndex("CanonicalName")
493  .IsUnique();
494 
495  b.HasIndex("CreatedById");
496 
497  b.ToTable("Users");
498  });
499 
500  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
501  {
502  b.Property<long>("Id")
503  .ValueGeneratedOnAdd()
504  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
505 
506  b.Property<long?>("AlphaId");
507 
508  b.Property<bool>("AlphaIsActive");
509 
510  b.Property<long?>("BravoId");
511 
512  b.HasKey("Id");
513 
514  b.HasIndex("AlphaId");
515 
516  b.HasIndex("BravoId");
517 
518  b.ToTable("WatchdogReattachInformations");
519  });
520 
521  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
522  {
523  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
524  .WithMany("ChatSettings")
525  .HasForeignKey("InstanceId")
526  .OnDelete(DeleteBehavior.Cascade);
527  });
528 
529  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
530  {
531  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
532  .WithMany("Channels")
533  .HasForeignKey("ChatSettingsId")
534  .OnDelete(DeleteBehavior.Cascade);
535  });
536 
537  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
538  {
539  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
540  .WithMany()
541  .HasForeignKey("JobId");
542 
543  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
544  .WithMany("CompileJobs")
545  .HasForeignKey("RevisionInformationId")
546  .OnDelete(DeleteBehavior.Cascade);
547  });
548 
549  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
550  {
551  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
552  .WithOne("DreamDaemonSettings")
553  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
554  .OnDelete(DeleteBehavior.Cascade);
555  });
556 
557  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
558  {
559  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
560  .WithOne("DreamMakerSettings")
561  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
562  .OnDelete(DeleteBehavior.Cascade);
563  });
564 
565  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
566  {
567  b.HasOne("Tgstation.Server.Host.Models.WatchdogReattachInformation", "WatchdogReattachInformation")
568  .WithMany()
569  .HasForeignKey("WatchdogReattachInformationId");
570  });
571 
572  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
573  {
574  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
575  .WithMany("InstanceUsers")
576  .HasForeignKey("InstanceId")
577  .OnDelete(DeleteBehavior.Cascade);
578 
579  b.HasOne("Tgstation.Server.Host.Models.User")
580  .WithMany("InstanceUsers")
581  .HasForeignKey("UserId")
582  .OnDelete(DeleteBehavior.Cascade);
583  });
584 
585  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
586  {
587  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
588  .WithMany()
589  .HasForeignKey("CancelledById");
590 
591  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
592  .WithMany("Jobs")
593  .HasForeignKey("InstanceId")
594  .OnDelete(DeleteBehavior.Cascade);
595 
596  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
597  .WithMany()
598  .HasForeignKey("StartedById")
599  .OnDelete(DeleteBehavior.Cascade);
600  });
601 
602  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
603  {
604  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
605  .WithMany()
606  .HasForeignKey("CompileJobId");
607  });
608 
609  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
610  {
611  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
612  .WithOne("RepositorySettings")
613  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
614  .OnDelete(DeleteBehavior.Cascade);
615  });
616 
617  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
618  {
619  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
620  .WithMany("ActiveTestMerges")
621  .HasForeignKey("RevisionInformationId")
622  .OnDelete(DeleteBehavior.Cascade);
623 
624  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
625  .WithMany("RevisonInformations")
626  .HasForeignKey("TestMergeId")
627  .OnDelete(DeleteBehavior.Cascade);
628  });
629 
630  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
631  {
632  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
633  .WithMany("RevisionInformations")
634  .HasForeignKey("InstanceId")
635  .OnDelete(DeleteBehavior.Cascade);
636  });
637 
638  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
639  {
640  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
641  .WithMany("TestMerges")
642  .HasForeignKey("MergedById")
643  .OnDelete(DeleteBehavior.Restrict);
644 
645  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
646  .WithOne("PrimaryTestMerge")
647  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
648  .OnDelete(DeleteBehavior.SetNull);
649  });
650 
651  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
652  {
653  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
654  .WithMany("CreatedUsers")
655  .HasForeignKey("CreatedById");
656  });
657 
658  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
659  {
660  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
661  .WithMany()
662  .HasForeignKey("AlphaId");
663 
664  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
665  .WithMany()
666  .HasForeignKey("BravoId");
667  });
668 #pragma warning restore 612, 618
669  }
670  }
671 }
+Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Metadata;
6 using Microsoft.EntityFrameworkCore.Migrations;
7 using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 
9 namespace Tgstation.Server.Host.Models.Migrations
10 {
11  [DbContext(typeof(SqlServerDatabaseContext))]
12  [Migration("20180906135553_MSInitialCreate")]
13  partial class MSInitialCreate
14  {
19  protected override void BuildTargetModel(ModelBuilder modelBuilder)
20  {
21 #pragma warning disable 612, 618
22  modelBuilder
23  .HasAnnotation("ProductVersion", "2.1.2-rtm-30932")
24  .HasAnnotation("Relational:MaxIdentifierLength", 128)
25  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
26 
27  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
28  {
29  b.Property<long>("Id")
30  .ValueGeneratedOnAdd()
31  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
32 
33  b.Property<string>("ConnectionString")
34  .IsRequired();
35 
36  b.Property<bool?>("Enabled");
37 
38  b.Property<long>("InstanceId");
39 
40  b.Property<string>("Name")
41  .IsRequired();
42 
43  b.Property<int?>("Provider");
44 
45  b.HasKey("Id");
46 
47  b.HasIndex("InstanceId");
48 
49  b.HasIndex("Name")
50  .IsUnique();
51 
52  b.ToTable("ChatBots");
53  });
54 
55  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
56  {
57  b.Property<long>("Id")
58  .ValueGeneratedOnAdd()
59  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
60 
61  b.Property<long>("ChatSettingsId");
62 
63  b.Property<decimal?>("DiscordChannelId")
64  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
65 
66  b.Property<string>("IrcChannel");
67 
68  b.Property<bool?>("IsAdminChannel")
69  .IsRequired();
70 
71  b.Property<bool?>("IsUpdatesChannel")
72  .IsRequired();
73 
74  b.Property<bool?>("IsWatchdogChannel")
75  .IsRequired();
76 
77  b.Property<string>("Tag");
78 
79  b.HasKey("Id");
80 
81  b.HasIndex("ChatSettingsId", "DiscordChannelId")
82  .IsUnique()
83  .HasFilter("[DiscordChannelId] IS NOT NULL");
84 
85  b.HasIndex("ChatSettingsId", "IrcChannel")
86  .IsUnique()
87  .HasFilter("[IrcChannel] IS NOT NULL");
88 
89  b.ToTable("ChatChannels");
90  });
91 
92  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
93  {
94  b.Property<long>("Id")
95  .ValueGeneratedOnAdd()
96  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
97 
98  b.Property<string>("ByondVersion")
99  .IsRequired();
100 
101  b.Property<Guid?>("DirectoryName");
102 
103  b.Property<string>("DmeName");
104 
105  b.Property<long?>("JobId");
106 
107  b.Property<string>("Output");
108 
109  b.Property<long>("RevisionInformationId");
110 
111  b.HasKey("Id");
112 
113  b.HasIndex("DirectoryName");
114 
115  b.HasIndex("JobId");
116 
117  b.HasIndex("RevisionInformationId");
118 
119  b.ToTable("CompileJobs");
120  });
121 
122  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
123  {
124  b.Property<long>("Id")
125  .ValueGeneratedOnAdd()
126  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
127 
128  b.Property<string>("AccessToken");
129 
130  b.Property<bool?>("AllowWebClient")
131  .IsRequired();
132 
133  b.Property<bool?>("AutoStart")
134  .IsRequired();
135 
136  b.Property<long>("InstanceId");
137 
138  b.Property<int>("PrimaryPort");
139 
140  b.Property<int?>("ProcessId");
141 
142  b.Property<int>("SecondaryPort");
143 
144  b.Property<int>("SecurityLevel");
145 
146  b.Property<bool?>("SoftRestart")
147  .IsRequired();
148 
149  b.Property<bool?>("SoftShutdown")
150  .IsRequired();
151 
152  b.Property<long>("StartupTimeout");
153 
154  b.HasKey("Id");
155 
156  b.HasIndex("InstanceId")
157  .IsUnique();
158 
159  b.ToTable("DreamDaemonSettings");
160  });
161 
162  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
163  {
164  b.Property<long>("Id")
165  .ValueGeneratedOnAdd()
166  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
167 
168  b.Property<int>("ApiValidationPort");
169 
170  b.Property<long>("InstanceId");
171 
172  b.Property<string>("ProjectName");
173 
174  b.HasKey("Id");
175 
176  b.HasIndex("InstanceId")
177  .IsUnique();
178 
179  b.ToTable("DreamMakerSettings");
180  });
181 
182  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
183  {
184  b.Property<long>("Id")
185  .ValueGeneratedOnAdd()
186  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
187 
188  b.Property<long>("AutoUpdateInterval");
189 
190  b.Property<int>("ConfigurationType");
191 
192  b.Property<string>("Name")
193  .IsRequired();
194 
195  b.Property<bool?>("Online")
196  .IsRequired();
197 
198  b.Property<string>("Path")
199  .IsRequired();
200 
201  b.Property<long?>("WatchdogReattachInformationId");
202 
203  b.HasKey("Id");
204 
205  b.HasIndex("Path")
206  .IsUnique();
207 
208  b.HasIndex("WatchdogReattachInformationId");
209 
210  b.ToTable("Instances");
211  });
212 
213  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
214  {
215  b.Property<long>("Id")
216  .ValueGeneratedOnAdd()
217  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
218 
219  b.Property<decimal>("ByondRights")
220  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
221 
222  b.Property<decimal>("ChatBotRights")
223  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
224 
225  b.Property<decimal>("ConfigurationRights")
226  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
227 
228  b.Property<decimal>("DreamDaemonRights")
229  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
230 
231  b.Property<decimal>("DreamMakerRights")
232  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
233 
234  b.Property<long>("InstanceId");
235 
236  b.Property<decimal>("InstanceUserRights")
237  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
238 
239  b.Property<decimal>("RepositoryRights")
240  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
241 
242  b.Property<long?>("UserId")
243  .IsRequired();
244 
245  b.HasKey("Id");
246 
247  b.HasIndex("InstanceId");
248 
249  b.HasIndex("UserId", "InstanceId")
250  .IsUnique();
251 
252  b.ToTable("InstanceUsers");
253  });
254 
255  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
256  {
257  b.Property<long>("Id")
258  .ValueGeneratedOnAdd()
259  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
260 
261  b.Property<decimal?>("CancelRight")
262  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
263 
264  b.Property<decimal?>("CancelRightsType")
265  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
266 
267  b.Property<bool?>("Cancelled")
268  .IsRequired();
269 
270  b.Property<long?>("CancelledById");
271 
272  b.Property<string>("Description")
273  .IsRequired();
274 
275  b.Property<string>("ExceptionDetails");
276 
277  b.Property<long>("InstanceId");
278 
279  b.Property<DateTimeOffset?>("StartedAt")
280  .IsRequired();
281 
282  b.Property<long>("StartedById");
283 
284  b.Property<DateTimeOffset?>("StoppedAt");
285 
286  b.HasKey("Id");
287 
288  b.HasIndex("CancelledById");
289 
290  b.HasIndex("InstanceId");
291 
292  b.HasIndex("StartedById");
293 
294  b.ToTable("Jobs");
295  });
296 
297  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
298  {
299  b.Property<long>("Id")
300  .ValueGeneratedOnAdd()
301  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
302 
303  b.Property<string>("AccessIdentifier")
304  .IsRequired();
305 
306  b.Property<string>("ChatChannelsJson")
307  .IsRequired();
308 
309  b.Property<string>("ChatCommandsJson")
310  .IsRequired();
311 
312  b.Property<long?>("CompileJobId");
313 
314  b.Property<bool>("IsPrimary");
315 
316  b.Property<int>("Port");
317 
318  b.Property<int>("ProcessId");
319 
320  b.Property<int>("RebootState");
321 
322  b.Property<string>("ServerCommandsJson")
323  .IsRequired();
324 
325  b.HasKey("Id");
326 
327  b.HasIndex("CompileJobId");
328 
329  b.ToTable("ReattachInformations");
330  });
331 
332  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
333  {
334  b.Property<long>("Id")
335  .ValueGeneratedOnAdd()
336  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
337 
338  b.Property<string>("AccessToken");
339 
340  b.Property<string>("AccessUser");
341 
342  b.Property<bool?>("AutoUpdatesKeepTestMerges")
343  .IsRequired();
344 
345  b.Property<bool?>("AutoUpdatesSynchronize")
346  .IsRequired();
347 
348  b.Property<string>("CommitterEmail")
349  .IsRequired();
350 
351  b.Property<string>("CommitterName")
352  .IsRequired();
353 
354  b.Property<long>("InstanceId");
355 
356  b.Property<bool?>("PushTestMergeCommits")
357  .IsRequired();
358 
359  b.Property<bool?>("ShowTestMergeCommitters")
360  .IsRequired();
361 
362  b.HasKey("Id");
363 
364  b.HasIndex("InstanceId")
365  .IsUnique();
366 
367  b.ToTable("RepositorySettings");
368  });
369 
370  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
371  {
372  b.Property<long>("Id")
373  .ValueGeneratedOnAdd()
374  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
375 
376  b.Property<long>("RevisionInformationId");
377 
378  b.Property<long>("TestMergeId");
379 
380  b.HasKey("Id");
381 
382  b.HasIndex("RevisionInformationId");
383 
384  b.HasIndex("TestMergeId");
385 
386  b.ToTable("RevInfoTestMerges");
387  });
388 
389  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
390  {
391  b.Property<long>("Id")
392  .ValueGeneratedOnAdd()
393  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
394 
395  b.Property<string>("CommitSha")
396  .IsRequired()
397  .HasMaxLength(40);
398 
399  b.Property<long>("InstanceId");
400 
401  b.Property<string>("OriginCommitSha")
402  .IsRequired()
403  .HasMaxLength(40);
404 
405  b.HasKey("Id");
406 
407  b.HasIndex("CommitSha")
408  .IsUnique();
409 
410  b.HasIndex("InstanceId");
411 
412  b.ToTable("RevisionInformations");
413  });
414 
415  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
416  {
417  b.Property<long>("Id")
418  .ValueGeneratedOnAdd()
419  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
420 
421  b.Property<string>("Author")
422  .IsRequired();
423 
424  b.Property<string>("BodyAtMerge")
425  .IsRequired();
426 
427  b.Property<string>("Comment");
428 
429  b.Property<DateTimeOffset>("MergedAt");
430 
431  b.Property<long>("MergedById");
432 
433  b.Property<int?>("Number")
434  .IsRequired();
435 
436  b.Property<long?>("PrimaryRevisionInformationId");
437 
438  b.Property<string>("PullRequestRevision")
439  .IsRequired();
440 
441  b.Property<string>("TitleAtMerge")
442  .IsRequired();
443 
444  b.Property<string>("Url")
445  .IsRequired();
446 
447  b.HasKey("Id");
448 
449  b.HasIndex("MergedById");
450 
451  b.HasIndex("PrimaryRevisionInformationId")
452  .IsUnique()
453  .HasFilter("[PrimaryRevisionInformationId] IS NOT NULL");
454 
455  b.ToTable("TestMerges");
456  });
457 
458  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
459  {
460  b.Property<long>("Id")
461  .ValueGeneratedOnAdd()
462  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
463 
464  b.Property<decimal>("AdministrationRights")
465  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
466 
467  b.Property<string>("CanonicalName")
468  .IsRequired();
469 
470  b.Property<DateTimeOffset?>("CreatedAt")
471  .IsRequired();
472 
473  b.Property<long?>("CreatedById");
474 
475  b.Property<bool?>("Enabled")
476  .IsRequired();
477 
478  b.Property<decimal>("InstanceManagerRights")
479  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
480 
481  b.Property<DateTimeOffset?>("LastPasswordUpdate");
482 
483  b.Property<string>("Name")
484  .IsRequired();
485 
486  b.Property<string>("PasswordHash");
487 
488  b.Property<string>("SystemIdentifier");
489 
490  b.HasKey("Id");
491 
492  b.HasIndex("CanonicalName")
493  .IsUnique();
494 
495  b.HasIndex("CreatedById");
496 
497  b.ToTable("Users");
498  });
499 
500  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
501  {
502  b.Property<long>("Id")
503  .ValueGeneratedOnAdd()
504  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
505 
506  b.Property<long?>("AlphaId");
507 
508  b.Property<bool>("AlphaIsActive");
509 
510  b.Property<long?>("BravoId");
511 
512  b.HasKey("Id");
513 
514  b.HasIndex("AlphaId");
515 
516  b.HasIndex("BravoId");
517 
518  b.ToTable("WatchdogReattachInformations");
519  });
520 
521  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
522  {
523  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
524  .WithMany("ChatSettings")
525  .HasForeignKey("InstanceId")
526  .OnDelete(DeleteBehavior.Cascade);
527  });
528 
529  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
530  {
531  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
532  .WithMany("Channels")
533  .HasForeignKey("ChatSettingsId")
534  .OnDelete(DeleteBehavior.Cascade);
535  });
536 
537  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
538  {
539  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
540  .WithMany()
541  .HasForeignKey("JobId");
542 
543  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
544  .WithMany("CompileJobs")
545  .HasForeignKey("RevisionInformationId")
546  .OnDelete(DeleteBehavior.Cascade);
547  });
548 
549  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
550  {
551  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
552  .WithOne("DreamDaemonSettings")
553  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
554  .OnDelete(DeleteBehavior.Cascade);
555  });
556 
557  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
558  {
559  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
560  .WithOne("DreamMakerSettings")
561  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
562  .OnDelete(DeleteBehavior.Cascade);
563  });
564 
565  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
566  {
567  b.HasOne("Tgstation.Server.Host.Models.WatchdogReattachInformation", "WatchdogReattachInformation")
568  .WithMany()
569  .HasForeignKey("WatchdogReattachInformationId");
570  });
571 
572  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
573  {
574  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
575  .WithMany("InstanceUsers")
576  .HasForeignKey("InstanceId")
577  .OnDelete(DeleteBehavior.Cascade);
578 
579  b.HasOne("Tgstation.Server.Host.Models.User")
580  .WithMany("InstanceUsers")
581  .HasForeignKey("UserId")
582  .OnDelete(DeleteBehavior.Cascade);
583  });
584 
585  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
586  {
587  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
588  .WithMany()
589  .HasForeignKey("CancelledById");
590 
591  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
592  .WithMany("Jobs")
593  .HasForeignKey("InstanceId")
594  .OnDelete(DeleteBehavior.Cascade);
595 
596  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
597  .WithMany()
598  .HasForeignKey("StartedById")
599  .OnDelete(DeleteBehavior.Cascade);
600  });
601 
602  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
603  {
604  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
605  .WithMany()
606  .HasForeignKey("CompileJobId");
607  });
608 
609  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
610  {
611  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
612  .WithOne("RepositorySettings")
613  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
614  .OnDelete(DeleteBehavior.Cascade);
615  });
616 
617  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
618  {
619  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
620  .WithMany("ActiveTestMerges")
621  .HasForeignKey("RevisionInformationId")
622  .OnDelete(DeleteBehavior.Cascade);
623 
624  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
625  .WithMany("RevisonInformations")
626  .HasForeignKey("TestMergeId")
627  .OnDelete(DeleteBehavior.Cascade);
628  });
629 
630  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
631  {
632  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
633  .WithMany("RevisionInformations")
634  .HasForeignKey("InstanceId")
635  .OnDelete(DeleteBehavior.Cascade);
636  });
637 
638  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
639  {
640  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
641  .WithMany("TestMerges")
642  .HasForeignKey("MergedById")
643  .OnDelete(DeleteBehavior.Restrict);
644 
645  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
646  .WithOne("PrimaryTestMerge")
647  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
648  .OnDelete(DeleteBehavior.SetNull);
649  });
650 
651  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
652  {
653  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
654  .WithMany("CreatedUsers")
655  .HasForeignKey("CreatedById");
656  });
657 
658  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
659  {
660  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
661  .WithMany()
662  .HasForeignKey("AlphaId");
663 
664  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
665  .WithMany()
666  .HasForeignKey("BravoId");
667  });
668 #pragma warning restore 612, 618
669  }
670  }
671 }
+ -
override void BuildTargetModel(ModelBuilder modelBuilder)
Builds the target model
diff --git a/20180906143029___m_y_initial_create_8_designer_8cs_source.html b/20180906143029___m_y_initial_create_8_designer_8cs_source.html index 9600c38dd3..6432c06cda 100644 --- a/20180906143029___m_y_initial_create_8_designer_8cs_source.html +++ b/20180906143029___m_y_initial_create_8_designer_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
20180906143029_MYInitialCreate.Designer.cs
-Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Migrations;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
9  [DbContext(typeof(MySqlDatabaseContext))]
10  [Migration("20180906143029_MYInitialCreate")]
11  partial class MYInitialCreate
12  {
17  protected override void BuildTargetModel(ModelBuilder modelBuilder)
18  {
19 #pragma warning disable 612, 618
20  modelBuilder
21  .HasAnnotation("ProductVersion", "2.1.2-rtm-30932")
22  .HasAnnotation("Relational:MaxIdentifierLength", 64);
23 
24  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
25  {
26  b.Property<long>("Id")
27  .ValueGeneratedOnAdd();
28 
29  b.Property<string>("ConnectionString")
30  .IsRequired();
31 
32  b.Property<bool?>("Enabled");
33 
34  b.Property<long>("InstanceId");
35 
36  b.Property<string>("Name")
37  .IsRequired();
38 
39  b.Property<int?>("Provider");
40 
41  b.HasKey("Id");
42 
43  b.HasIndex("InstanceId");
44 
45  b.HasIndex("Name")
46  .IsUnique();
47 
48  b.ToTable("ChatBots");
49  });
50 
51  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
52  {
53  b.Property<long>("Id")
54  .ValueGeneratedOnAdd();
55 
56  b.Property<long>("ChatSettingsId");
57 
58  b.Property<ulong?>("DiscordChannelId");
59 
60  b.Property<string>("IrcChannel");
61 
62  b.Property<bool?>("IsAdminChannel")
63  .IsRequired();
64 
65  b.Property<bool?>("IsUpdatesChannel")
66  .IsRequired();
67 
68  b.Property<bool?>("IsWatchdogChannel")
69  .IsRequired();
70 
71  b.Property<string>("Tag");
72 
73  b.HasKey("Id");
74 
75  b.HasIndex("ChatSettingsId", "DiscordChannelId")
76  .IsUnique();
77 
78  b.HasIndex("ChatSettingsId", "IrcChannel")
79  .IsUnique();
80 
81  b.ToTable("ChatChannels");
82  });
83 
84  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
85  {
86  b.Property<long>("Id")
87  .ValueGeneratedOnAdd();
88 
89  b.Property<string>("ByondVersion")
90  .IsRequired();
91 
92  b.Property<Guid?>("DirectoryName");
93 
94  b.Property<string>("DmeName");
95 
96  b.Property<long?>("JobId");
97 
98  b.Property<string>("Output");
99 
100  b.Property<long>("RevisionInformationId");
101 
102  b.HasKey("Id");
103 
104  b.HasIndex("DirectoryName");
105 
106  b.HasIndex("JobId");
107 
108  b.HasIndex("RevisionInformationId");
109 
110  b.ToTable("CompileJobs");
111  });
112 
113  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
114  {
115  b.Property<long>("Id")
116  .ValueGeneratedOnAdd();
117 
118  b.Property<string>("AccessToken");
119 
120  b.Property<bool?>("AllowWebClient")
121  .IsRequired();
122 
123  b.Property<bool?>("AutoStart")
124  .IsRequired();
125 
126  b.Property<long>("InstanceId");
127 
128  b.Property<ushort?>("PrimaryPort")
129  .IsRequired();
130 
131  b.Property<int?>("ProcessId");
132 
133  b.Property<ushort?>("SecondaryPort")
134  .IsRequired();
135 
136  b.Property<int>("SecurityLevel");
137 
138  b.Property<bool?>("SoftRestart")
139  .IsRequired();
140 
141  b.Property<bool?>("SoftShutdown")
142  .IsRequired();
143 
144  b.Property<uint?>("StartupTimeout")
145  .IsRequired();
146 
147  b.HasKey("Id");
148 
149  b.HasIndex("InstanceId")
150  .IsUnique();
151 
152  b.ToTable("DreamDaemonSettings");
153  });
154 
155  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
156  {
157  b.Property<long>("Id")
158  .ValueGeneratedOnAdd();
159 
160  b.Property<ushort?>("ApiValidationPort")
161  .IsRequired();
162 
163  b.Property<long>("InstanceId");
164 
165  b.Property<string>("ProjectName");
166 
167  b.HasKey("Id");
168 
169  b.HasIndex("InstanceId")
170  .IsUnique();
171 
172  b.ToTable("DreamMakerSettings");
173  });
174 
175  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
176  {
177  b.Property<long>("Id")
178  .ValueGeneratedOnAdd();
179 
180  b.Property<uint?>("AutoUpdateInterval")
181  .IsRequired();
182 
183  b.Property<int>("ConfigurationType");
184 
185  b.Property<string>("Name")
186  .IsRequired();
187 
188  b.Property<bool?>("Online")
189  .IsRequired();
190 
191  b.Property<string>("Path")
192  .IsRequired();
193 
194  b.Property<long?>("WatchdogReattachInformationId");
195 
196  b.HasKey("Id");
197 
198  b.HasIndex("Path")
199  .IsUnique();
200 
201  b.HasIndex("WatchdogReattachInformationId");
202 
203  b.ToTable("Instances");
204  });
205 
206  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
207  {
208  b.Property<long>("Id")
209  .ValueGeneratedOnAdd();
210 
211  b.Property<ulong>("ByondRights");
212 
213  b.Property<ulong>("ChatBotRights");
214 
215  b.Property<ulong>("ConfigurationRights");
216 
217  b.Property<ulong>("DreamDaemonRights");
218 
219  b.Property<ulong>("DreamMakerRights");
220 
221  b.Property<long>("InstanceId");
222 
223  b.Property<ulong>("InstanceUserRights");
224 
225  b.Property<ulong>("RepositoryRights");
226 
227  b.Property<long?>("UserId")
228  .IsRequired();
229 
230  b.HasKey("Id");
231 
232  b.HasIndex("InstanceId");
233 
234  b.HasIndex("UserId", "InstanceId")
235  .IsUnique();
236 
237  b.ToTable("InstanceUsers");
238  });
239 
240  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
241  {
242  b.Property<long>("Id")
243  .ValueGeneratedOnAdd();
244 
245  b.Property<ulong?>("CancelRight");
246 
247  b.Property<ulong?>("CancelRightsType");
248 
249  b.Property<bool?>("Cancelled")
250  .IsRequired();
251 
252  b.Property<long?>("CancelledById");
253 
254  b.Property<string>("Description")
255  .IsRequired();
256 
257  b.Property<string>("ExceptionDetails");
258 
259  b.Property<long>("InstanceId");
260 
261  b.Property<DateTimeOffset?>("StartedAt")
262  .IsRequired();
263 
264  b.Property<long>("StartedById");
265 
266  b.Property<DateTimeOffset?>("StoppedAt");
267 
268  b.HasKey("Id");
269 
270  b.HasIndex("CancelledById");
271 
272  b.HasIndex("InstanceId");
273 
274  b.HasIndex("StartedById");
275 
276  b.ToTable("Jobs");
277  });
278 
279  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
280  {
281  b.Property<long>("Id")
282  .ValueGeneratedOnAdd();
283 
284  b.Property<string>("AccessIdentifier")
285  .IsRequired();
286 
287  b.Property<string>("ChatChannelsJson")
288  .IsRequired();
289 
290  b.Property<string>("ChatCommandsJson")
291  .IsRequired();
292 
293  b.Property<long?>("CompileJobId");
294 
295  b.Property<bool>("IsPrimary");
296 
297  b.Property<ushort>("Port");
298 
299  b.Property<int>("ProcessId");
300 
301  b.Property<int>("RebootState");
302 
303  b.Property<string>("ServerCommandsJson")
304  .IsRequired();
305 
306  b.HasKey("Id");
307 
308  b.HasIndex("CompileJobId");
309 
310  b.ToTable("ReattachInformations");
311  });
312 
313  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
314  {
315  b.Property<long>("Id")
316  .ValueGeneratedOnAdd();
317 
318  b.Property<string>("AccessToken");
319 
320  b.Property<string>("AccessUser");
321 
322  b.Property<bool?>("AutoUpdatesKeepTestMerges")
323  .IsRequired();
324 
325  b.Property<bool?>("AutoUpdatesSynchronize")
326  .IsRequired();
327 
328  b.Property<string>("CommitterEmail")
329  .IsRequired();
330 
331  b.Property<string>("CommitterName")
332  .IsRequired();
333 
334  b.Property<long>("InstanceId");
335 
336  b.Property<bool?>("PushTestMergeCommits")
337  .IsRequired();
338 
339  b.Property<bool?>("ShowTestMergeCommitters")
340  .IsRequired();
341 
342  b.HasKey("Id");
343 
344  b.HasIndex("InstanceId")
345  .IsUnique();
346 
347  b.ToTable("RepositorySettings");
348  });
349 
350  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
351  {
352  b.Property<long>("Id")
353  .ValueGeneratedOnAdd();
354 
355  b.Property<long>("RevisionInformationId");
356 
357  b.Property<long>("TestMergeId");
358 
359  b.HasKey("Id");
360 
361  b.HasIndex("RevisionInformationId");
362 
363  b.HasIndex("TestMergeId");
364 
365  b.ToTable("RevInfoTestMerges");
366  });
367 
368  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
369  {
370  b.Property<long>("Id")
371  .ValueGeneratedOnAdd();
372 
373  b.Property<string>("CommitSha")
374  .IsRequired()
375  .HasMaxLength(40);
376 
377  b.Property<long>("InstanceId");
378 
379  b.Property<string>("OriginCommitSha")
380  .IsRequired()
381  .HasMaxLength(40);
382 
383  b.HasKey("Id");
384 
385  b.HasIndex("CommitSha")
386  .IsUnique();
387 
388  b.HasIndex("InstanceId");
389 
390  b.ToTable("RevisionInformations");
391  });
392 
393  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
394  {
395  b.Property<long>("Id")
396  .ValueGeneratedOnAdd();
397 
398  b.Property<string>("Author")
399  .IsRequired();
400 
401  b.Property<string>("BodyAtMerge")
402  .IsRequired();
403 
404  b.Property<string>("Comment");
405 
406  b.Property<DateTimeOffset>("MergedAt");
407 
408  b.Property<long>("MergedById");
409 
410  b.Property<int?>("Number")
411  .IsRequired();
412 
413  b.Property<long?>("PrimaryRevisionInformationId");
414 
415  b.Property<string>("PullRequestRevision")
416  .IsRequired();
417 
418  b.Property<string>("TitleAtMerge")
419  .IsRequired();
420 
421  b.Property<string>("Url")
422  .IsRequired();
423 
424  b.HasKey("Id");
425 
426  b.HasIndex("MergedById");
427 
428  b.HasIndex("PrimaryRevisionInformationId")
429  .IsUnique();
430 
431  b.ToTable("TestMerges");
432  });
433 
434  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
435  {
436  b.Property<long>("Id")
437  .ValueGeneratedOnAdd();
438 
439  b.Property<ulong>("AdministrationRights");
440 
441  b.Property<string>("CanonicalName")
442  .IsRequired();
443 
444  b.Property<DateTimeOffset?>("CreatedAt")
445  .IsRequired();
446 
447  b.Property<long?>("CreatedById");
448 
449  b.Property<bool?>("Enabled")
450  .IsRequired();
451 
452  b.Property<ulong>("InstanceManagerRights");
453 
454  b.Property<DateTimeOffset?>("LastPasswordUpdate");
455 
456  b.Property<string>("Name")
457  .IsRequired();
458 
459  b.Property<string>("PasswordHash");
460 
461  b.Property<string>("SystemIdentifier");
462 
463  b.HasKey("Id");
464 
465  b.HasIndex("CanonicalName")
466  .IsUnique();
467 
468  b.HasIndex("CreatedById");
469 
470  b.ToTable("Users");
471  });
472 
473  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
474  {
475  b.Property<long>("Id")
476  .ValueGeneratedOnAdd();
477 
478  b.Property<long?>("AlphaId");
479 
480  b.Property<bool>("AlphaIsActive");
481 
482  b.Property<long?>("BravoId");
483 
484  b.HasKey("Id");
485 
486  b.HasIndex("AlphaId");
487 
488  b.HasIndex("BravoId");
489 
490  b.ToTable("WatchdogReattachInformations");
491  });
492 
493  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
494  {
495  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
496  .WithMany("ChatSettings")
497  .HasForeignKey("InstanceId")
498  .OnDelete(DeleteBehavior.Cascade);
499  });
500 
501  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
502  {
503  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
504  .WithMany("Channels")
505  .HasForeignKey("ChatSettingsId")
506  .OnDelete(DeleteBehavior.Cascade);
507  });
508 
509  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
510  {
511  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
512  .WithMany()
513  .HasForeignKey("JobId");
514 
515  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
516  .WithMany("CompileJobs")
517  .HasForeignKey("RevisionInformationId")
518  .OnDelete(DeleteBehavior.Cascade);
519  });
520 
521  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
522  {
523  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
524  .WithOne("DreamDaemonSettings")
525  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
526  .OnDelete(DeleteBehavior.Cascade);
527  });
528 
529  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
530  {
531  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
532  .WithOne("DreamMakerSettings")
533  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
534  .OnDelete(DeleteBehavior.Cascade);
535  });
536 
537  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
538  {
539  b.HasOne("Tgstation.Server.Host.Models.WatchdogReattachInformation", "WatchdogReattachInformation")
540  .WithMany()
541  .HasForeignKey("WatchdogReattachInformationId");
542  });
543 
544  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
545  {
546  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
547  .WithMany("InstanceUsers")
548  .HasForeignKey("InstanceId")
549  .OnDelete(DeleteBehavior.Cascade);
550 
551  b.HasOne("Tgstation.Server.Host.Models.User")
552  .WithMany("InstanceUsers")
553  .HasForeignKey("UserId")
554  .OnDelete(DeleteBehavior.Cascade);
555  });
556 
557  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
558  {
559  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
560  .WithMany()
561  .HasForeignKey("CancelledById");
562 
563  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
564  .WithMany("Jobs")
565  .HasForeignKey("InstanceId")
566  .OnDelete(DeleteBehavior.Cascade);
567 
568  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
569  .WithMany()
570  .HasForeignKey("StartedById")
571  .OnDelete(DeleteBehavior.Cascade);
572  });
573 
574  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
575  {
576  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
577  .WithMany()
578  .HasForeignKey("CompileJobId");
579  });
580 
581  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
582  {
583  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
584  .WithOne("RepositorySettings")
585  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
586  .OnDelete(DeleteBehavior.Cascade);
587  });
588 
589  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
590  {
591  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
592  .WithMany("ActiveTestMerges")
593  .HasForeignKey("RevisionInformationId")
594  .OnDelete(DeleteBehavior.Cascade);
595 
596  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
597  .WithMany("RevisonInformations")
598  .HasForeignKey("TestMergeId")
599  .OnDelete(DeleteBehavior.Cascade);
600  });
601 
602  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
603  {
604  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
605  .WithMany("RevisionInformations")
606  .HasForeignKey("InstanceId")
607  .OnDelete(DeleteBehavior.Cascade);
608  });
609 
610  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
611  {
612  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
613  .WithMany("TestMerges")
614  .HasForeignKey("MergedById")
615  .OnDelete(DeleteBehavior.Restrict);
616 
617  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
618  .WithOne("PrimaryTestMerge")
619  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
620  .OnDelete(DeleteBehavior.SetNull);
621  });
622 
623  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
624  {
625  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
626  .WithMany("CreatedUsers")
627  .HasForeignKey("CreatedById");
628  });
629 
630  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
631  {
632  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
633  .WithMany()
634  .HasForeignKey("AlphaId");
635 
636  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
637  .WithMany()
638  .HasForeignKey("BravoId");
639  });
640 #pragma warning restore 612, 618
641  }
642  }
643 }
+Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Migrations;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
9  [DbContext(typeof(MySqlDatabaseContext))]
10  [Migration("20180906143029_MYInitialCreate")]
11  partial class MYInitialCreate
12  {
17  protected override void BuildTargetModel(ModelBuilder modelBuilder)
18  {
19 #pragma warning disable 612, 618
20  modelBuilder
21  .HasAnnotation("ProductVersion", "2.1.2-rtm-30932")
22  .HasAnnotation("Relational:MaxIdentifierLength", 64);
23 
24  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
25  {
26  b.Property<long>("Id")
27  .ValueGeneratedOnAdd();
28 
29  b.Property<string>("ConnectionString")
30  .IsRequired();
31 
32  b.Property<bool?>("Enabled");
33 
34  b.Property<long>("InstanceId");
35 
36  b.Property<string>("Name")
37  .IsRequired();
38 
39  b.Property<int?>("Provider");
40 
41  b.HasKey("Id");
42 
43  b.HasIndex("InstanceId");
44 
45  b.HasIndex("Name")
46  .IsUnique();
47 
48  b.ToTable("ChatBots");
49  });
50 
51  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
52  {
53  b.Property<long>("Id")
54  .ValueGeneratedOnAdd();
55 
56  b.Property<long>("ChatSettingsId");
57 
58  b.Property<ulong?>("DiscordChannelId");
59 
60  b.Property<string>("IrcChannel");
61 
62  b.Property<bool?>("IsAdminChannel")
63  .IsRequired();
64 
65  b.Property<bool?>("IsUpdatesChannel")
66  .IsRequired();
67 
68  b.Property<bool?>("IsWatchdogChannel")
69  .IsRequired();
70 
71  b.Property<string>("Tag");
72 
73  b.HasKey("Id");
74 
75  b.HasIndex("ChatSettingsId", "DiscordChannelId")
76  .IsUnique();
77 
78  b.HasIndex("ChatSettingsId", "IrcChannel")
79  .IsUnique();
80 
81  b.ToTable("ChatChannels");
82  });
83 
84  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
85  {
86  b.Property<long>("Id")
87  .ValueGeneratedOnAdd();
88 
89  b.Property<string>("ByondVersion")
90  .IsRequired();
91 
92  b.Property<Guid?>("DirectoryName");
93 
94  b.Property<string>("DmeName");
95 
96  b.Property<long?>("JobId");
97 
98  b.Property<string>("Output");
99 
100  b.Property<long>("RevisionInformationId");
101 
102  b.HasKey("Id");
103 
104  b.HasIndex("DirectoryName");
105 
106  b.HasIndex("JobId");
107 
108  b.HasIndex("RevisionInformationId");
109 
110  b.ToTable("CompileJobs");
111  });
112 
113  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
114  {
115  b.Property<long>("Id")
116  .ValueGeneratedOnAdd();
117 
118  b.Property<string>("AccessToken");
119 
120  b.Property<bool?>("AllowWebClient")
121  .IsRequired();
122 
123  b.Property<bool?>("AutoStart")
124  .IsRequired();
125 
126  b.Property<long>("InstanceId");
127 
128  b.Property<ushort?>("PrimaryPort")
129  .IsRequired();
130 
131  b.Property<int?>("ProcessId");
132 
133  b.Property<ushort?>("SecondaryPort")
134  .IsRequired();
135 
136  b.Property<int>("SecurityLevel");
137 
138  b.Property<bool?>("SoftRestart")
139  .IsRequired();
140 
141  b.Property<bool?>("SoftShutdown")
142  .IsRequired();
143 
144  b.Property<uint?>("StartupTimeout")
145  .IsRequired();
146 
147  b.HasKey("Id");
148 
149  b.HasIndex("InstanceId")
150  .IsUnique();
151 
152  b.ToTable("DreamDaemonSettings");
153  });
154 
155  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
156  {
157  b.Property<long>("Id")
158  .ValueGeneratedOnAdd();
159 
160  b.Property<ushort?>("ApiValidationPort")
161  .IsRequired();
162 
163  b.Property<long>("InstanceId");
164 
165  b.Property<string>("ProjectName");
166 
167  b.HasKey("Id");
168 
169  b.HasIndex("InstanceId")
170  .IsUnique();
171 
172  b.ToTable("DreamMakerSettings");
173  });
174 
175  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
176  {
177  b.Property<long>("Id")
178  .ValueGeneratedOnAdd();
179 
180  b.Property<uint?>("AutoUpdateInterval")
181  .IsRequired();
182 
183  b.Property<int>("ConfigurationType");
184 
185  b.Property<string>("Name")
186  .IsRequired();
187 
188  b.Property<bool?>("Online")
189  .IsRequired();
190 
191  b.Property<string>("Path")
192  .IsRequired();
193 
194  b.Property<long?>("WatchdogReattachInformationId");
195 
196  b.HasKey("Id");
197 
198  b.HasIndex("Path")
199  .IsUnique();
200 
201  b.HasIndex("WatchdogReattachInformationId");
202 
203  b.ToTable("Instances");
204  });
205 
206  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
207  {
208  b.Property<long>("Id")
209  .ValueGeneratedOnAdd();
210 
211  b.Property<ulong>("ByondRights");
212 
213  b.Property<ulong>("ChatBotRights");
214 
215  b.Property<ulong>("ConfigurationRights");
216 
217  b.Property<ulong>("DreamDaemonRights");
218 
219  b.Property<ulong>("DreamMakerRights");
220 
221  b.Property<long>("InstanceId");
222 
223  b.Property<ulong>("InstanceUserRights");
224 
225  b.Property<ulong>("RepositoryRights");
226 
227  b.Property<long?>("UserId")
228  .IsRequired();
229 
230  b.HasKey("Id");
231 
232  b.HasIndex("InstanceId");
233 
234  b.HasIndex("UserId", "InstanceId")
235  .IsUnique();
236 
237  b.ToTable("InstanceUsers");
238  });
239 
240  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
241  {
242  b.Property<long>("Id")
243  .ValueGeneratedOnAdd();
244 
245  b.Property<ulong?>("CancelRight");
246 
247  b.Property<ulong?>("CancelRightsType");
248 
249  b.Property<bool?>("Cancelled")
250  .IsRequired();
251 
252  b.Property<long?>("CancelledById");
253 
254  b.Property<string>("Description")
255  .IsRequired();
256 
257  b.Property<string>("ExceptionDetails");
258 
259  b.Property<long>("InstanceId");
260 
261  b.Property<DateTimeOffset?>("StartedAt")
262  .IsRequired();
263 
264  b.Property<long>("StartedById");
265 
266  b.Property<DateTimeOffset?>("StoppedAt");
267 
268  b.HasKey("Id");
269 
270  b.HasIndex("CancelledById");
271 
272  b.HasIndex("InstanceId");
273 
274  b.HasIndex("StartedById");
275 
276  b.ToTable("Jobs");
277  });
278 
279  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
280  {
281  b.Property<long>("Id")
282  .ValueGeneratedOnAdd();
283 
284  b.Property<string>("AccessIdentifier")
285  .IsRequired();
286 
287  b.Property<string>("ChatChannelsJson")
288  .IsRequired();
289 
290  b.Property<string>("ChatCommandsJson")
291  .IsRequired();
292 
293  b.Property<long?>("CompileJobId");
294 
295  b.Property<bool>("IsPrimary");
296 
297  b.Property<ushort>("Port");
298 
299  b.Property<int>("ProcessId");
300 
301  b.Property<int>("RebootState");
302 
303  b.Property<string>("ServerCommandsJson")
304  .IsRequired();
305 
306  b.HasKey("Id");
307 
308  b.HasIndex("CompileJobId");
309 
310  b.ToTable("ReattachInformations");
311  });
312 
313  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
314  {
315  b.Property<long>("Id")
316  .ValueGeneratedOnAdd();
317 
318  b.Property<string>("AccessToken");
319 
320  b.Property<string>("AccessUser");
321 
322  b.Property<bool?>("AutoUpdatesKeepTestMerges")
323  .IsRequired();
324 
325  b.Property<bool?>("AutoUpdatesSynchronize")
326  .IsRequired();
327 
328  b.Property<string>("CommitterEmail")
329  .IsRequired();
330 
331  b.Property<string>("CommitterName")
332  .IsRequired();
333 
334  b.Property<long>("InstanceId");
335 
336  b.Property<bool?>("PushTestMergeCommits")
337  .IsRequired();
338 
339  b.Property<bool?>("ShowTestMergeCommitters")
340  .IsRequired();
341 
342  b.HasKey("Id");
343 
344  b.HasIndex("InstanceId")
345  .IsUnique();
346 
347  b.ToTable("RepositorySettings");
348  });
349 
350  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
351  {
352  b.Property<long>("Id")
353  .ValueGeneratedOnAdd();
354 
355  b.Property<long>("RevisionInformationId");
356 
357  b.Property<long>("TestMergeId");
358 
359  b.HasKey("Id");
360 
361  b.HasIndex("RevisionInformationId");
362 
363  b.HasIndex("TestMergeId");
364 
365  b.ToTable("RevInfoTestMerges");
366  });
367 
368  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
369  {
370  b.Property<long>("Id")
371  .ValueGeneratedOnAdd();
372 
373  b.Property<string>("CommitSha")
374  .IsRequired()
375  .HasMaxLength(40);
376 
377  b.Property<long>("InstanceId");
378 
379  b.Property<string>("OriginCommitSha")
380  .IsRequired()
381  .HasMaxLength(40);
382 
383  b.HasKey("Id");
384 
385  b.HasIndex("CommitSha")
386  .IsUnique();
387 
388  b.HasIndex("InstanceId");
389 
390  b.ToTable("RevisionInformations");
391  });
392 
393  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
394  {
395  b.Property<long>("Id")
396  .ValueGeneratedOnAdd();
397 
398  b.Property<string>("Author")
399  .IsRequired();
400 
401  b.Property<string>("BodyAtMerge")
402  .IsRequired();
403 
404  b.Property<string>("Comment");
405 
406  b.Property<DateTimeOffset>("MergedAt");
407 
408  b.Property<long>("MergedById");
409 
410  b.Property<int?>("Number")
411  .IsRequired();
412 
413  b.Property<long?>("PrimaryRevisionInformationId");
414 
415  b.Property<string>("PullRequestRevision")
416  .IsRequired();
417 
418  b.Property<string>("TitleAtMerge")
419  .IsRequired();
420 
421  b.Property<string>("Url")
422  .IsRequired();
423 
424  b.HasKey("Id");
425 
426  b.HasIndex("MergedById");
427 
428  b.HasIndex("PrimaryRevisionInformationId")
429  .IsUnique();
430 
431  b.ToTable("TestMerges");
432  });
433 
434  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
435  {
436  b.Property<long>("Id")
437  .ValueGeneratedOnAdd();
438 
439  b.Property<ulong>("AdministrationRights");
440 
441  b.Property<string>("CanonicalName")
442  .IsRequired();
443 
444  b.Property<DateTimeOffset?>("CreatedAt")
445  .IsRequired();
446 
447  b.Property<long?>("CreatedById");
448 
449  b.Property<bool?>("Enabled")
450  .IsRequired();
451 
452  b.Property<ulong>("InstanceManagerRights");
453 
454  b.Property<DateTimeOffset?>("LastPasswordUpdate");
455 
456  b.Property<string>("Name")
457  .IsRequired();
458 
459  b.Property<string>("PasswordHash");
460 
461  b.Property<string>("SystemIdentifier");
462 
463  b.HasKey("Id");
464 
465  b.HasIndex("CanonicalName")
466  .IsUnique();
467 
468  b.HasIndex("CreatedById");
469 
470  b.ToTable("Users");
471  });
472 
473  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
474  {
475  b.Property<long>("Id")
476  .ValueGeneratedOnAdd();
477 
478  b.Property<long?>("AlphaId");
479 
480  b.Property<bool>("AlphaIsActive");
481 
482  b.Property<long?>("BravoId");
483 
484  b.HasKey("Id");
485 
486  b.HasIndex("AlphaId");
487 
488  b.HasIndex("BravoId");
489 
490  b.ToTable("WatchdogReattachInformations");
491  });
492 
493  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
494  {
495  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
496  .WithMany("ChatSettings")
497  .HasForeignKey("InstanceId")
498  .OnDelete(DeleteBehavior.Cascade);
499  });
500 
501  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
502  {
503  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
504  .WithMany("Channels")
505  .HasForeignKey("ChatSettingsId")
506  .OnDelete(DeleteBehavior.Cascade);
507  });
508 
509  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
510  {
511  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
512  .WithMany()
513  .HasForeignKey("JobId");
514 
515  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
516  .WithMany("CompileJobs")
517  .HasForeignKey("RevisionInformationId")
518  .OnDelete(DeleteBehavior.Cascade);
519  });
520 
521  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
522  {
523  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
524  .WithOne("DreamDaemonSettings")
525  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
526  .OnDelete(DeleteBehavior.Cascade);
527  });
528 
529  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
530  {
531  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
532  .WithOne("DreamMakerSettings")
533  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
534  .OnDelete(DeleteBehavior.Cascade);
535  });
536 
537  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
538  {
539  b.HasOne("Tgstation.Server.Host.Models.WatchdogReattachInformation", "WatchdogReattachInformation")
540  .WithMany()
541  .HasForeignKey("WatchdogReattachInformationId");
542  });
543 
544  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
545  {
546  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
547  .WithMany("InstanceUsers")
548  .HasForeignKey("InstanceId")
549  .OnDelete(DeleteBehavior.Cascade);
550 
551  b.HasOne("Tgstation.Server.Host.Models.User")
552  .WithMany("InstanceUsers")
553  .HasForeignKey("UserId")
554  .OnDelete(DeleteBehavior.Cascade);
555  });
556 
557  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
558  {
559  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
560  .WithMany()
561  .HasForeignKey("CancelledById");
562 
563  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
564  .WithMany("Jobs")
565  .HasForeignKey("InstanceId")
566  .OnDelete(DeleteBehavior.Cascade);
567 
568  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
569  .WithMany()
570  .HasForeignKey("StartedById")
571  .OnDelete(DeleteBehavior.Cascade);
572  });
573 
574  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
575  {
576  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
577  .WithMany()
578  .HasForeignKey("CompileJobId");
579  });
580 
581  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
582  {
583  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
584  .WithOne("RepositorySettings")
585  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
586  .OnDelete(DeleteBehavior.Cascade);
587  });
588 
589  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
590  {
591  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
592  .WithMany("ActiveTestMerges")
593  .HasForeignKey("RevisionInformationId")
594  .OnDelete(DeleteBehavior.Cascade);
595 
596  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
597  .WithMany("RevisonInformations")
598  .HasForeignKey("TestMergeId")
599  .OnDelete(DeleteBehavior.Cascade);
600  });
601 
602  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
603  {
604  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
605  .WithMany("RevisionInformations")
606  .HasForeignKey("InstanceId")
607  .OnDelete(DeleteBehavior.Cascade);
608  });
609 
610  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
611  {
612  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
613  .WithMany("TestMerges")
614  .HasForeignKey("MergedById")
615  .OnDelete(DeleteBehavior.Restrict);
616 
617  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
618  .WithOne("PrimaryTestMerge")
619  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
620  .OnDelete(DeleteBehavior.SetNull);
621  });
622 
623  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
624  {
625  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
626  .WithMany("CreatedUsers")
627  .HasForeignKey("CreatedById");
628  });
629 
630  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
631  {
632  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
633  .WithMany()
634  .HasForeignKey("AlphaId");
635 
636  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
637  .WithMany()
638  .HasForeignKey("BravoId");
639  });
640 #pragma warning restore 612, 618
641  }
642  }
643 }
+ -
override void BuildTargetModel(ModelBuilder modelBuilder)
Builds the target model
diff --git a/20180906143029___m_y_initial_create_8cs_source.html b/20180906143029___m_y_initial_create_8cs_source.html index 17c6bafa81..4d893365cd 100644 --- a/20180906143029___m_y_initial_create_8cs_source.html +++ b/20180906143029___m_y_initial_create_8cs_source.html @@ -92,9 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
20180906143029_MYInitialCreate.cs
-Go to the documentation of this file.
1 using System;
2 using Microsoft.EntityFrameworkCore.Metadata;
3 using Microsoft.EntityFrameworkCore.Migrations;
4 
5 namespace Tgstation.Server.Host.Models.Migrations
6 {
10  public partial class MYInitialCreate : Migration
11  {
16  protected override void Up(MigrationBuilder migrationBuilder)
17  {
18  migrationBuilder.CreateTable(
19  name: "Users",
20  columns: table => new
21  {
22  Id = table.Column<long>(nullable: false)
23  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
24  Enabled = table.Column<bool>(nullable: false),
25  CreatedAt = table.Column<DateTimeOffset>(nullable: false),
26  SystemIdentifier = table.Column<string>(nullable: true),
27  Name = table.Column<string>(nullable: false),
28  AdministrationRights = table.Column<ulong>(nullable: false),
29  InstanceManagerRights = table.Column<ulong>(nullable: false),
30  PasswordHash = table.Column<string>(nullable: true),
31  CreatedById = table.Column<long>(nullable: true),
32  CanonicalName = table.Column<string>(nullable: false),
33  LastPasswordUpdate = table.Column<DateTimeOffset>(nullable: true)
34  },
35  constraints: table =>
36  {
37  table.PrimaryKey("PK_Users", x => x.Id);
38  table.ForeignKey(
39  name: "FK_Users_Users_CreatedById",
40  column: x => x.CreatedById,
41  principalTable: "Users",
42  principalColumn: "Id",
43  onDelete: ReferentialAction.Restrict);
44  });
45 
46  migrationBuilder.CreateTable(
47  name: "ChatChannels",
48  columns: table => new
49  {
50  IrcChannel = table.Column<string>(nullable: true),
51  DiscordChannelId = table.Column<ulong>(nullable: true),
52  IsAdminChannel = table.Column<bool>(nullable: false),
53  IsWatchdogChannel = table.Column<bool>(nullable: false),
54  IsUpdatesChannel = table.Column<bool>(nullable: false),
55  Tag = table.Column<string>(nullable: true),
56  Id = table.Column<long>(nullable: false)
57  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
58  ChatSettingsId = table.Column<long>(nullable: false)
59  },
60  constraints: table =>
61  {
62  table.PrimaryKey("PK_ChatChannels", x => x.Id);
63  });
64 
65  migrationBuilder.CreateTable(
66  name: "ReattachInformations",
67  columns: table => new
68  {
69  ChatCommandsJson = table.Column<string>(nullable: false),
70  ChatChannelsJson = table.Column<string>(nullable: false),
71  ServerCommandsJson = table.Column<string>(nullable: false),
72  AccessIdentifier = table.Column<string>(nullable: false),
73  ProcessId = table.Column<int>(nullable: false),
74  IsPrimary = table.Column<bool>(nullable: false),
75  Port = table.Column<ushort>(nullable: false),
76  RebootState = table.Column<int>(nullable: false),
77  Id = table.Column<long>(nullable: false)
78  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
79  CompileJobId = table.Column<long>(nullable: true)
80  },
81  constraints: table =>
82  {
83  table.PrimaryKey("PK_ReattachInformations", x => x.Id);
84  });
85 
86  migrationBuilder.CreateTable(
87  name: "WatchdogReattachInformations",
88  columns: table => new
89  {
90  AlphaIsActive = table.Column<bool>(nullable: false),
91  Id = table.Column<long>(nullable: false)
92  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
93  AlphaId = table.Column<long>(nullable: true),
94  BravoId = table.Column<long>(nullable: true)
95  },
96  constraints: table =>
97  {
98  table.PrimaryKey("PK_WatchdogReattachInformations", x => x.Id);
99  table.ForeignKey(
100  name: "FK_WatchdogReattachInformations_ReattachInformations_AlphaId",
101  column: x => x.AlphaId,
102  principalTable: "ReattachInformations",
103  principalColumn: "Id",
104  onDelete: ReferentialAction.Restrict);
105  table.ForeignKey(
106  name: "FK_WatchdogReattachInformations_ReattachInformations_BravoId",
107  column: x => x.BravoId,
108  principalTable: "ReattachInformations",
109  principalColumn: "Id",
110  onDelete: ReferentialAction.Restrict);
111  });
112 
113  migrationBuilder.CreateTable(
114  name: "Instances",
115  columns: table => new
116  {
117  Id = table.Column<long>(nullable: false)
118  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
119  Name = table.Column<string>(nullable: false),
120  Path = table.Column<string>(nullable: false),
121  Online = table.Column<bool>(nullable: false),
122  ConfigurationType = table.Column<int>(nullable: false),
123  AutoUpdateInterval = table.Column<uint>(nullable: false),
124  WatchdogReattachInformationId = table.Column<long>(nullable: true)
125  },
126  constraints: table =>
127  {
128  table.PrimaryKey("PK_Instances", x => x.Id);
129  table.ForeignKey(
130  name: "FK_Instances_WatchdogReattachInformations_WatchdogReattachInfor~",
131  column: x => x.WatchdogReattachInformationId,
132  principalTable: "WatchdogReattachInformations",
133  principalColumn: "Id",
134  onDelete: ReferentialAction.Restrict);
135  });
136 
137  migrationBuilder.CreateTable(
138  name: "ChatBots",
139  columns: table => new
140  {
141  Id = table.Column<long>(nullable: false)
142  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
143  Name = table.Column<string>(nullable: false),
144  Enabled = table.Column<bool>(nullable: true),
145  Provider = table.Column<int>(nullable: true),
146  ConnectionString = table.Column<string>(nullable: false),
147  InstanceId = table.Column<long>(nullable: false)
148  },
149  constraints: table =>
150  {
151  table.PrimaryKey("PK_ChatBots", x => x.Id);
152  table.ForeignKey(
153  name: "FK_ChatBots_Instances_InstanceId",
154  column: x => x.InstanceId,
155  principalTable: "Instances",
156  principalColumn: "Id",
157  onDelete: ReferentialAction.Cascade);
158  });
159 
160  migrationBuilder.CreateTable(
161  name: "DreamDaemonSettings",
162  columns: table => new
163  {
164  AllowWebClient = table.Column<bool>(nullable: false),
165  SecurityLevel = table.Column<int>(nullable: false),
166  PrimaryPort = table.Column<ushort>(nullable: false),
167  SecondaryPort = table.Column<ushort>(nullable: false),
168  StartupTimeout = table.Column<uint>(nullable: false),
169  AutoStart = table.Column<bool>(nullable: false),
170  SoftRestart = table.Column<bool>(nullable: false),
171  SoftShutdown = table.Column<bool>(nullable: false),
172  Id = table.Column<long>(nullable: false)
173  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
174  ProcessId = table.Column<int>(nullable: true),
175  AccessToken = table.Column<string>(nullable: true),
176  InstanceId = table.Column<long>(nullable: false)
177  },
178  constraints: table =>
179  {
180  table.PrimaryKey("PK_DreamDaemonSettings", x => x.Id);
181  table.ForeignKey(
182  name: "FK_DreamDaemonSettings_Instances_InstanceId",
183  column: x => x.InstanceId,
184  principalTable: "Instances",
185  principalColumn: "Id",
186  onDelete: ReferentialAction.Cascade);
187  });
188 
189  migrationBuilder.CreateTable(
190  name: "DreamMakerSettings",
191  columns: table => new
192  {
193  ProjectName = table.Column<string>(nullable: true),
194  ApiValidationPort = table.Column<ushort>(nullable: false),
195  Id = table.Column<long>(nullable: false)
196  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
197  InstanceId = table.Column<long>(nullable: false)
198  },
199  constraints: table =>
200  {
201  table.PrimaryKey("PK_DreamMakerSettings", x => x.Id);
202  table.ForeignKey(
203  name: "FK_DreamMakerSettings_Instances_InstanceId",
204  column: x => x.InstanceId,
205  principalTable: "Instances",
206  principalColumn: "Id",
207  onDelete: ReferentialAction.Cascade);
208  });
209 
210  migrationBuilder.CreateTable(
211  name: "InstanceUsers",
212  columns: table => new
213  {
214  UserId = table.Column<long>(nullable: false),
215  InstanceUserRights = table.Column<ulong>(nullable: false),
216  ByondRights = table.Column<ulong>(nullable: false),
217  DreamDaemonRights = table.Column<ulong>(nullable: false),
218  DreamMakerRights = table.Column<ulong>(nullable: false),
219  RepositoryRights = table.Column<ulong>(nullable: false),
220  ChatBotRights = table.Column<ulong>(nullable: false),
221  ConfigurationRights = table.Column<ulong>(nullable: false),
222  Id = table.Column<long>(nullable: false)
223  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
224  InstanceId = table.Column<long>(nullable: false)
225  },
226  constraints: table =>
227  {
228  table.PrimaryKey("PK_InstanceUsers", x => x.Id);
229  table.ForeignKey(
230  name: "FK_InstanceUsers_Instances_InstanceId",
231  column: x => x.InstanceId,
232  principalTable: "Instances",
233  principalColumn: "Id",
234  onDelete: ReferentialAction.Cascade);
235  table.ForeignKey(
236  name: "FK_InstanceUsers_Users_UserId",
237  column: x => x.UserId,
238  principalTable: "Users",
239  principalColumn: "Id",
240  onDelete: ReferentialAction.Cascade);
241  });
242 
243  migrationBuilder.CreateTable(
244  name: "Jobs",
245  columns: table => new
246  {
247  Id = table.Column<long>(nullable: false)
248  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
249  Description = table.Column<string>(nullable: false),
250  ExceptionDetails = table.Column<string>(nullable: true),
251  StartedAt = table.Column<DateTimeOffset>(nullable: false),
252  StoppedAt = table.Column<DateTimeOffset>(nullable: true),
253  Cancelled = table.Column<bool>(nullable: false),
254  CancelRightsType = table.Column<ulong>(nullable: true),
255  CancelRight = table.Column<ulong>(nullable: true),
256  StartedById = table.Column<long>(nullable: false),
257  CancelledById = table.Column<long>(nullable: true),
258  InstanceId = table.Column<long>(nullable: false)
259  },
260  constraints: table =>
261  {
262  table.PrimaryKey("PK_Jobs", x => x.Id);
263  table.ForeignKey(
264  name: "FK_Jobs_Users_CancelledById",
265  column: x => x.CancelledById,
266  principalTable: "Users",
267  principalColumn: "Id",
268  onDelete: ReferentialAction.Restrict);
269  table.ForeignKey(
270  name: "FK_Jobs_Instances_InstanceId",
271  column: x => x.InstanceId,
272  principalTable: "Instances",
273  principalColumn: "Id",
274  onDelete: ReferentialAction.Cascade);
275  table.ForeignKey(
276  name: "FK_Jobs_Users_StartedById",
277  column: x => x.StartedById,
278  principalTable: "Users",
279  principalColumn: "Id",
280  onDelete: ReferentialAction.Cascade);
281  });
282 
283  migrationBuilder.CreateTable(
284  name: "RepositorySettings",
285  columns: table => new
286  {
287  CommitterName = table.Column<string>(nullable: false),
288  CommitterEmail = table.Column<string>(nullable: false),
289  AccessUser = table.Column<string>(nullable: true),
290  AccessToken = table.Column<string>(nullable: true),
291  PushTestMergeCommits = table.Column<bool>(nullable: false),
292  ShowTestMergeCommitters = table.Column<bool>(nullable: false),
293  AutoUpdatesKeepTestMerges = table.Column<bool>(nullable: false),
294  AutoUpdatesSynchronize = table.Column<bool>(nullable: false),
295  Id = table.Column<long>(nullable: false)
296  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
297  InstanceId = table.Column<long>(nullable: false)
298  },
299  constraints: table =>
300  {
301  table.PrimaryKey("PK_RepositorySettings", x => x.Id);
302  table.ForeignKey(
303  name: "FK_RepositorySettings_Instances_InstanceId",
304  column: x => x.InstanceId,
305  principalTable: "Instances",
306  principalColumn: "Id",
307  onDelete: ReferentialAction.Cascade);
308  });
309 
310  migrationBuilder.CreateTable(
311  name: "RevisionInformations",
312  columns: table => new
313  {
314  CommitSha = table.Column<string>(maxLength: 40, nullable: false),
315  OriginCommitSha = table.Column<string>(maxLength: 40, nullable: false),
316  Id = table.Column<long>(nullable: false)
317  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
318  InstanceId = table.Column<long>(nullable: false)
319  },
320  constraints: table =>
321  {
322  table.PrimaryKey("PK_RevisionInformations", x => x.Id);
323  table.ForeignKey(
324  name: "FK_RevisionInformations_Instances_InstanceId",
325  column: x => x.InstanceId,
326  principalTable: "Instances",
327  principalColumn: "Id",
328  onDelete: ReferentialAction.Cascade);
329  });
330 
331  migrationBuilder.CreateTable(
332  name: "CompileJobs",
333  columns: table => new
334  {
335  Id = table.Column<long>(nullable: false)
336  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
337  DmeName = table.Column<string>(nullable: true),
338  Output = table.Column<string>(nullable: true),
339  DirectoryName = table.Column<Guid>(nullable: true),
340  JobId = table.Column<long>(nullable: true),
341  RevisionInformationId = table.Column<long>(nullable: false),
342  ByondVersion = table.Column<string>(nullable: false)
343  },
344  constraints: table =>
345  {
346  table.PrimaryKey("PK_CompileJobs", x => x.Id);
347  table.ForeignKey(
348  name: "FK_CompileJobs_Jobs_JobId",
349  column: x => x.JobId,
350  principalTable: "Jobs",
351  principalColumn: "Id",
352  onDelete: ReferentialAction.Restrict);
353  table.ForeignKey(
354  name: "FK_CompileJobs_RevisionInformations_RevisionInformationId",
355  column: x => x.RevisionInformationId,
356  principalTable: "RevisionInformations",
357  principalColumn: "Id",
358  onDelete: ReferentialAction.Cascade);
359  });
360 
361  migrationBuilder.CreateTable(
362  name: "TestMerges",
363  columns: table => new
364  {
365  Number = table.Column<int>(nullable: false),
366  PullRequestRevision = table.Column<string>(nullable: false),
367  Comment = table.Column<string>(nullable: true),
368  TitleAtMerge = table.Column<string>(nullable: false),
369  BodyAtMerge = table.Column<string>(nullable: false),
370  Url = table.Column<string>(nullable: false),
371  Author = table.Column<string>(nullable: false),
372  Id = table.Column<long>(nullable: false)
373  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
374  MergedAt = table.Column<DateTimeOffset>(nullable: false),
375  MergedById = table.Column<long>(nullable: false),
376  PrimaryRevisionInformationId = table.Column<long>(nullable: true)
377  },
378  constraints: table =>
379  {
380  table.PrimaryKey("PK_TestMerges", x => x.Id);
381  table.ForeignKey(
382  name: "FK_TestMerges_Users_MergedById",
383  column: x => x.MergedById,
384  principalTable: "Users",
385  principalColumn: "Id",
386  onDelete: ReferentialAction.Restrict);
387  table.ForeignKey(
388  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
389  column: x => x.PrimaryRevisionInformationId,
390  principalTable: "RevisionInformations",
391  principalColumn: "Id",
392  onDelete: ReferentialAction.SetNull);
393  });
394 
395  migrationBuilder.CreateTable(
396  name: "RevInfoTestMerges",
397  columns: table => new
398  {
399  Id = table.Column<long>(nullable: false)
400  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
401  TestMergeId = table.Column<long>(nullable: false),
402  RevisionInformationId = table.Column<long>(nullable: false)
403  },
404  constraints: table =>
405  {
406  table.PrimaryKey("PK_RevInfoTestMerges", x => x.Id);
407  table.ForeignKey(
408  name: "FK_RevInfoTestMerges_RevisionInformations_RevisionInformationId",
409  column: x => x.RevisionInformationId,
410  principalTable: "RevisionInformations",
411  principalColumn: "Id",
412  onDelete: ReferentialAction.Cascade);
413  table.ForeignKey(
414  name: "FK_RevInfoTestMerges_TestMerges_TestMergeId",
415  column: x => x.TestMergeId,
416  principalTable: "TestMerges",
417  principalColumn: "Id",
418  onDelete: ReferentialAction.Cascade);
419  });
420 
421  migrationBuilder.CreateIndex(
422  name: "IX_ChatBots_InstanceId",
423  table: "ChatBots",
424  column: "InstanceId");
425 
426  migrationBuilder.CreateIndex(
427  name: "IX_ChatBots_Name",
428  table: "ChatBots",
429  column: "Name",
430  unique: true);
431 
432  migrationBuilder.CreateIndex(
433  name: "IX_ChatChannels_ChatSettingsId_DiscordChannelId",
434  table: "ChatChannels",
435  columns: new[] { "ChatSettingsId", "DiscordChannelId" },
436  unique: true);
437 
438  migrationBuilder.CreateIndex(
439  name: "IX_ChatChannels_ChatSettingsId_IrcChannel",
440  table: "ChatChannels",
441  columns: new[] { "ChatSettingsId", "IrcChannel" },
442  unique: true);
443 
444  migrationBuilder.CreateIndex(
445  name: "IX_CompileJobs_DirectoryName",
446  table: "CompileJobs",
447  column: "DirectoryName");
448 
449  migrationBuilder.CreateIndex(
450  name: "IX_CompileJobs_JobId",
451  table: "CompileJobs",
452  column: "JobId");
453 
454  migrationBuilder.CreateIndex(
455  name: "IX_CompileJobs_RevisionInformationId",
456  table: "CompileJobs",
457  column: "RevisionInformationId");
458 
459  migrationBuilder.CreateIndex(
460  name: "IX_DreamDaemonSettings_InstanceId",
461  table: "DreamDaemonSettings",
462  column: "InstanceId",
463  unique: true);
464 
465  migrationBuilder.CreateIndex(
466  name: "IX_DreamMakerSettings_InstanceId",
467  table: "DreamMakerSettings",
468  column: "InstanceId",
469  unique: true);
470 
471  migrationBuilder.CreateIndex(
472  name: "IX_Instances_Path",
473  table: "Instances",
474  column: "Path",
475  unique: true);
476 
477  migrationBuilder.CreateIndex(
478  name: "IX_Instances_WatchdogReattachInformationId",
479  table: "Instances",
480  column: "WatchdogReattachInformationId");
481 
482  migrationBuilder.CreateIndex(
483  name: "IX_InstanceUsers_InstanceId",
484  table: "InstanceUsers",
485  column: "InstanceId");
486 
487  migrationBuilder.CreateIndex(
488  name: "IX_InstanceUsers_UserId_InstanceId",
489  table: "InstanceUsers",
490  columns: new[] { "UserId", "InstanceId" },
491  unique: true);
492 
493  migrationBuilder.CreateIndex(
494  name: "IX_Jobs_CancelledById",
495  table: "Jobs",
496  column: "CancelledById");
497 
498  migrationBuilder.CreateIndex(
499  name: "IX_Jobs_InstanceId",
500  table: "Jobs",
501  column: "InstanceId");
502 
503  migrationBuilder.CreateIndex(
504  name: "IX_Jobs_StartedById",
505  table: "Jobs",
506  column: "StartedById");
507 
508  migrationBuilder.CreateIndex(
509  name: "IX_ReattachInformations_CompileJobId",
510  table: "ReattachInformations",
511  column: "CompileJobId");
512 
513  migrationBuilder.CreateIndex(
514  name: "IX_RepositorySettings_InstanceId",
515  table: "RepositorySettings",
516  column: "InstanceId",
517  unique: true);
518 
519  migrationBuilder.CreateIndex(
520  name: "IX_RevInfoTestMerges_RevisionInformationId",
521  table: "RevInfoTestMerges",
522  column: "RevisionInformationId");
523 
524  migrationBuilder.CreateIndex(
525  name: "IX_RevInfoTestMerges_TestMergeId",
526  table: "RevInfoTestMerges",
527  column: "TestMergeId");
528 
529  migrationBuilder.CreateIndex(
530  name: "IX_RevisionInformations_CommitSha",
531  table: "RevisionInformations",
532  column: "CommitSha",
533  unique: true);
534 
535  migrationBuilder.CreateIndex(
536  name: "IX_RevisionInformations_InstanceId",
537  table: "RevisionInformations",
538  column: "InstanceId");
539 
540  migrationBuilder.CreateIndex(
541  name: "IX_TestMerges_MergedById",
542  table: "TestMerges",
543  column: "MergedById");
544 
545  migrationBuilder.CreateIndex(
546  name: "IX_TestMerges_PrimaryRevisionInformationId",
547  table: "TestMerges",
548  column: "PrimaryRevisionInformationId",
549  unique: true);
550 
551  migrationBuilder.CreateIndex(
552  name: "IX_Users_CanonicalName",
553  table: "Users",
554  column: "CanonicalName",
555  unique: true);
556 
557  migrationBuilder.CreateIndex(
558  name: "IX_Users_CreatedById",
559  table: "Users",
560  column: "CreatedById");
561 
562  migrationBuilder.CreateIndex(
563  name: "IX_WatchdogReattachInformations_AlphaId",
564  table: "WatchdogReattachInformations",
565  column: "AlphaId");
566 
567  migrationBuilder.CreateIndex(
568  name: "IX_WatchdogReattachInformations_BravoId",
569  table: "WatchdogReattachInformations",
570  column: "BravoId");
571 
572  migrationBuilder.AddForeignKey(
573  name: "FK_ChatChannels_ChatBots_ChatSettingsId",
574  table: "ChatChannels",
575  column: "ChatSettingsId",
576  principalTable: "ChatBots",
577  principalColumn: "Id",
578  onDelete: ReferentialAction.Cascade);
579 
580  migrationBuilder.AddForeignKey(
581  name: "FK_ReattachInformations_CompileJobs_CompileJobId",
582  table: "ReattachInformations",
583  column: "CompileJobId",
584  principalTable: "CompileJobs",
585  principalColumn: "Id",
586  onDelete: ReferentialAction.Restrict);
587  }
588 
593  protected override void Down(MigrationBuilder migrationBuilder)
594  {
595  migrationBuilder.DropForeignKey(
596  name: "FK_Jobs_Instances_InstanceId",
597  table: "Jobs");
598 
599  migrationBuilder.DropForeignKey(
600  name: "FK_RevisionInformations_Instances_InstanceId",
601  table: "RevisionInformations");
602 
603  migrationBuilder.DropTable(
604  name: "ChatChannels");
605 
606  migrationBuilder.DropTable(
607  name: "DreamDaemonSettings");
608 
609  migrationBuilder.DropTable(
610  name: "DreamMakerSettings");
611 
612  migrationBuilder.DropTable(
613  name: "InstanceUsers");
614 
615  migrationBuilder.DropTable(
616  name: "RepositorySettings");
617 
618  migrationBuilder.DropTable(
619  name: "RevInfoTestMerges");
620 
621  migrationBuilder.DropTable(
622  name: "ChatBots");
623 
624  migrationBuilder.DropTable(
625  name: "TestMerges");
626 
627  migrationBuilder.DropTable(
628  name: "Instances");
629 
630  migrationBuilder.DropTable(
631  name: "WatchdogReattachInformations");
632 
633  migrationBuilder.DropTable(
634  name: "ReattachInformations");
635 
636  migrationBuilder.DropTable(
637  name: "CompileJobs");
638 
639  migrationBuilder.DropTable(
640  name: "Jobs");
641 
642  migrationBuilder.DropTable(
643  name: "RevisionInformations");
644 
645  migrationBuilder.DropTable(
646  name: "Users");
647  }
648  }
649 }
ConfigurationRights
Rights for Models.ConfigurationFile
+Go to the documentation of this file.
1 using System;
2 using Microsoft.EntityFrameworkCore.Metadata;
3 using Microsoft.EntityFrameworkCore.Migrations;
4 
5 namespace Tgstation.Server.Host.Models.Migrations
6 {
10  public partial class MYInitialCreate : Migration
11  {
16  protected override void Up(MigrationBuilder migrationBuilder)
17  {
18  migrationBuilder.CreateTable(
19  name: "Users",
20  columns: table => new
21  {
22  Id = table.Column<long>(nullable: false)
23  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
24  Enabled = table.Column<bool>(nullable: false),
25  CreatedAt = table.Column<DateTimeOffset>(nullable: false),
26  SystemIdentifier = table.Column<string>(nullable: true),
27  Name = table.Column<string>(nullable: false),
28  AdministrationRights = table.Column<ulong>(nullable: false),
29  InstanceManagerRights = table.Column<ulong>(nullable: false),
30  PasswordHash = table.Column<string>(nullable: true),
31  CreatedById = table.Column<long>(nullable: true),
32  CanonicalName = table.Column<string>(nullable: false),
33  LastPasswordUpdate = table.Column<DateTimeOffset>(nullable: true)
34  },
35  constraints: table =>
36  {
37  table.PrimaryKey("PK_Users", x => x.Id);
38  table.ForeignKey(
39  name: "FK_Users_Users_CreatedById",
40  column: x => x.CreatedById,
41  principalTable: "Users",
42  principalColumn: "Id",
43  onDelete: ReferentialAction.Restrict);
44  });
45 
46  migrationBuilder.CreateTable(
47  name: "ChatChannels",
48  columns: table => new
49  {
50  IrcChannel = table.Column<string>(nullable: true),
51  DiscordChannelId = table.Column<ulong>(nullable: true),
52  IsAdminChannel = table.Column<bool>(nullable: false),
53  IsWatchdogChannel = table.Column<bool>(nullable: false),
54  IsUpdatesChannel = table.Column<bool>(nullable: false),
55  Tag = table.Column<string>(nullable: true),
56  Id = table.Column<long>(nullable: false)
57  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
58  ChatSettingsId = table.Column<long>(nullable: false)
59  },
60  constraints: table =>
61  {
62  table.PrimaryKey("PK_ChatChannels", x => x.Id);
63  });
64 
65  migrationBuilder.CreateTable(
66  name: "ReattachInformations",
67  columns: table => new
68  {
69  ChatCommandsJson = table.Column<string>(nullable: false),
70  ChatChannelsJson = table.Column<string>(nullable: false),
71  ServerCommandsJson = table.Column<string>(nullable: false),
72  AccessIdentifier = table.Column<string>(nullable: false),
73  ProcessId = table.Column<int>(nullable: false),
74  IsPrimary = table.Column<bool>(nullable: false),
75  Port = table.Column<ushort>(nullable: false),
76  RebootState = table.Column<int>(nullable: false),
77  Id = table.Column<long>(nullable: false)
78  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
79  CompileJobId = table.Column<long>(nullable: true)
80  },
81  constraints: table =>
82  {
83  table.PrimaryKey("PK_ReattachInformations", x => x.Id);
84  });
85 
86  migrationBuilder.CreateTable(
87  name: "WatchdogReattachInformations",
88  columns: table => new
89  {
90  AlphaIsActive = table.Column<bool>(nullable: false),
91  Id = table.Column<long>(nullable: false)
92  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
93  AlphaId = table.Column<long>(nullable: true),
94  BravoId = table.Column<long>(nullable: true)
95  },
96  constraints: table =>
97  {
98  table.PrimaryKey("PK_WatchdogReattachInformations", x => x.Id);
99  table.ForeignKey(
100  name: "FK_WatchdogReattachInformations_ReattachInformations_AlphaId",
101  column: x => x.AlphaId,
102  principalTable: "ReattachInformations",
103  principalColumn: "Id",
104  onDelete: ReferentialAction.Restrict);
105  table.ForeignKey(
106  name: "FK_WatchdogReattachInformations_ReattachInformations_BravoId",
107  column: x => x.BravoId,
108  principalTable: "ReattachInformations",
109  principalColumn: "Id",
110  onDelete: ReferentialAction.Restrict);
111  });
112 
113  migrationBuilder.CreateTable(
114  name: "Instances",
115  columns: table => new
116  {
117  Id = table.Column<long>(nullable: false)
118  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
119  Name = table.Column<string>(nullable: false),
120  Path = table.Column<string>(nullable: false),
121  Online = table.Column<bool>(nullable: false),
122  ConfigurationType = table.Column<int>(nullable: false),
123  AutoUpdateInterval = table.Column<uint>(nullable: false),
124  WatchdogReattachInformationId = table.Column<long>(nullable: true)
125  },
126  constraints: table =>
127  {
128  table.PrimaryKey("PK_Instances", x => x.Id);
129  table.ForeignKey(
130  name: "FK_Instances_WatchdogReattachInformations_WatchdogReattachInfor~",
131  column: x => x.WatchdogReattachInformationId,
132  principalTable: "WatchdogReattachInformations",
133  principalColumn: "Id",
134  onDelete: ReferentialAction.Restrict);
135  });
136 
137  migrationBuilder.CreateTable(
138  name: "ChatBots",
139  columns: table => new
140  {
141  Id = table.Column<long>(nullable: false)
142  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
143  Name = table.Column<string>(nullable: false),
144  Enabled = table.Column<bool>(nullable: true),
145  Provider = table.Column<int>(nullable: true),
146  ConnectionString = table.Column<string>(nullable: false),
147  InstanceId = table.Column<long>(nullable: false)
148  },
149  constraints: table =>
150  {
151  table.PrimaryKey("PK_ChatBots", x => x.Id);
152  table.ForeignKey(
153  name: "FK_ChatBots_Instances_InstanceId",
154  column: x => x.InstanceId,
155  principalTable: "Instances",
156  principalColumn: "Id",
157  onDelete: ReferentialAction.Cascade);
158  });
159 
160  migrationBuilder.CreateTable(
161  name: "DreamDaemonSettings",
162  columns: table => new
163  {
164  AllowWebClient = table.Column<bool>(nullable: false),
165  SecurityLevel = table.Column<int>(nullable: false),
166  PrimaryPort = table.Column<ushort>(nullable: false),
167  SecondaryPort = table.Column<ushort>(nullable: false),
168  StartupTimeout = table.Column<uint>(nullable: false),
169  AutoStart = table.Column<bool>(nullable: false),
170  SoftRestart = table.Column<bool>(nullable: false),
171  SoftShutdown = table.Column<bool>(nullable: false),
172  Id = table.Column<long>(nullable: false)
173  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
174  ProcessId = table.Column<int>(nullable: true),
175  AccessToken = table.Column<string>(nullable: true),
176  InstanceId = table.Column<long>(nullable: false)
177  },
178  constraints: table =>
179  {
180  table.PrimaryKey("PK_DreamDaemonSettings", x => x.Id);
181  table.ForeignKey(
182  name: "FK_DreamDaemonSettings_Instances_InstanceId",
183  column: x => x.InstanceId,
184  principalTable: "Instances",
185  principalColumn: "Id",
186  onDelete: ReferentialAction.Cascade);
187  });
188 
189  migrationBuilder.CreateTable(
190  name: "DreamMakerSettings",
191  columns: table => new
192  {
193  ProjectName = table.Column<string>(nullable: true),
194  ApiValidationPort = table.Column<ushort>(nullable: false),
195  Id = table.Column<long>(nullable: false)
196  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
197  InstanceId = table.Column<long>(nullable: false)
198  },
199  constraints: table =>
200  {
201  table.PrimaryKey("PK_DreamMakerSettings", x => x.Id);
202  table.ForeignKey(
203  name: "FK_DreamMakerSettings_Instances_InstanceId",
204  column: x => x.InstanceId,
205  principalTable: "Instances",
206  principalColumn: "Id",
207  onDelete: ReferentialAction.Cascade);
208  });
209 
210  migrationBuilder.CreateTable(
211  name: "InstanceUsers",
212  columns: table => new
213  {
214  UserId = table.Column<long>(nullable: false),
215  InstanceUserRights = table.Column<ulong>(nullable: false),
216  ByondRights = table.Column<ulong>(nullable: false),
217  DreamDaemonRights = table.Column<ulong>(nullable: false),
218  DreamMakerRights = table.Column<ulong>(nullable: false),
219  RepositoryRights = table.Column<ulong>(nullable: false),
220  ChatBotRights = table.Column<ulong>(nullable: false),
221  ConfigurationRights = table.Column<ulong>(nullable: false),
222  Id = table.Column<long>(nullable: false)
223  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
224  InstanceId = table.Column<long>(nullable: false)
225  },
226  constraints: table =>
227  {
228  table.PrimaryKey("PK_InstanceUsers", x => x.Id);
229  table.ForeignKey(
230  name: "FK_InstanceUsers_Instances_InstanceId",
231  column: x => x.InstanceId,
232  principalTable: "Instances",
233  principalColumn: "Id",
234  onDelete: ReferentialAction.Cascade);
235  table.ForeignKey(
236  name: "FK_InstanceUsers_Users_UserId",
237  column: x => x.UserId,
238  principalTable: "Users",
239  principalColumn: "Id",
240  onDelete: ReferentialAction.Cascade);
241  });
242 
243  migrationBuilder.CreateTable(
244  name: "Jobs",
245  columns: table => new
246  {
247  Id = table.Column<long>(nullable: false)
248  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
249  Description = table.Column<string>(nullable: false),
250  ExceptionDetails = table.Column<string>(nullable: true),
251  StartedAt = table.Column<DateTimeOffset>(nullable: false),
252  StoppedAt = table.Column<DateTimeOffset>(nullable: true),
253  Cancelled = table.Column<bool>(nullable: false),
254  CancelRightsType = table.Column<ulong>(nullable: true),
255  CancelRight = table.Column<ulong>(nullable: true),
256  StartedById = table.Column<long>(nullable: false),
257  CancelledById = table.Column<long>(nullable: true),
258  InstanceId = table.Column<long>(nullable: false)
259  },
260  constraints: table =>
261  {
262  table.PrimaryKey("PK_Jobs", x => x.Id);
263  table.ForeignKey(
264  name: "FK_Jobs_Users_CancelledById",
265  column: x => x.CancelledById,
266  principalTable: "Users",
267  principalColumn: "Id",
268  onDelete: ReferentialAction.Restrict);
269  table.ForeignKey(
270  name: "FK_Jobs_Instances_InstanceId",
271  column: x => x.InstanceId,
272  principalTable: "Instances",
273  principalColumn: "Id",
274  onDelete: ReferentialAction.Cascade);
275  table.ForeignKey(
276  name: "FK_Jobs_Users_StartedById",
277  column: x => x.StartedById,
278  principalTable: "Users",
279  principalColumn: "Id",
280  onDelete: ReferentialAction.Cascade);
281  });
282 
283  migrationBuilder.CreateTable(
284  name: "RepositorySettings",
285  columns: table => new
286  {
287  CommitterName = table.Column<string>(nullable: false),
288  CommitterEmail = table.Column<string>(nullable: false),
289  AccessUser = table.Column<string>(nullable: true),
290  AccessToken = table.Column<string>(nullable: true),
291  PushTestMergeCommits = table.Column<bool>(nullable: false),
292  ShowTestMergeCommitters = table.Column<bool>(nullable: false),
293  AutoUpdatesKeepTestMerges = table.Column<bool>(nullable: false),
294  AutoUpdatesSynchronize = table.Column<bool>(nullable: false),
295  Id = table.Column<long>(nullable: false)
296  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
297  InstanceId = table.Column<long>(nullable: false)
298  },
299  constraints: table =>
300  {
301  table.PrimaryKey("PK_RepositorySettings", x => x.Id);
302  table.ForeignKey(
303  name: "FK_RepositorySettings_Instances_InstanceId",
304  column: x => x.InstanceId,
305  principalTable: "Instances",
306  principalColumn: "Id",
307  onDelete: ReferentialAction.Cascade);
308  });
309 
310  migrationBuilder.CreateTable(
311  name: "RevisionInformations",
312  columns: table => new
313  {
314  CommitSha = table.Column<string>(maxLength: 40, nullable: false),
315  OriginCommitSha = table.Column<string>(maxLength: 40, nullable: false),
316  Id = table.Column<long>(nullable: false)
317  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
318  InstanceId = table.Column<long>(nullable: false)
319  },
320  constraints: table =>
321  {
322  table.PrimaryKey("PK_RevisionInformations", x => x.Id);
323  table.ForeignKey(
324  name: "FK_RevisionInformations_Instances_InstanceId",
325  column: x => x.InstanceId,
326  principalTable: "Instances",
327  principalColumn: "Id",
328  onDelete: ReferentialAction.Cascade);
329  });
330 
331  migrationBuilder.CreateTable(
332  name: "CompileJobs",
333  columns: table => new
334  {
335  Id = table.Column<long>(nullable: false)
336  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
337  DmeName = table.Column<string>(nullable: true),
338  Output = table.Column<string>(nullable: true),
339  DirectoryName = table.Column<Guid>(nullable: true),
340  JobId = table.Column<long>(nullable: true),
341  RevisionInformationId = table.Column<long>(nullable: false),
342  ByondVersion = table.Column<string>(nullable: false)
343  },
344  constraints: table =>
345  {
346  table.PrimaryKey("PK_CompileJobs", x => x.Id);
347  table.ForeignKey(
348  name: "FK_CompileJobs_Jobs_JobId",
349  column: x => x.JobId,
350  principalTable: "Jobs",
351  principalColumn: "Id",
352  onDelete: ReferentialAction.Restrict);
353  table.ForeignKey(
354  name: "FK_CompileJobs_RevisionInformations_RevisionInformationId",
355  column: x => x.RevisionInformationId,
356  principalTable: "RevisionInformations",
357  principalColumn: "Id",
358  onDelete: ReferentialAction.Cascade);
359  });
360 
361  migrationBuilder.CreateTable(
362  name: "TestMerges",
363  columns: table => new
364  {
365  Number = table.Column<int>(nullable: false),
366  PullRequestRevision = table.Column<string>(nullable: false),
367  Comment = table.Column<string>(nullable: true),
368  TitleAtMerge = table.Column<string>(nullable: false),
369  BodyAtMerge = table.Column<string>(nullable: false),
370  Url = table.Column<string>(nullable: false),
371  Author = table.Column<string>(nullable: false),
372  Id = table.Column<long>(nullable: false)
373  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
374  MergedAt = table.Column<DateTimeOffset>(nullable: false),
375  MergedById = table.Column<long>(nullable: false),
376  PrimaryRevisionInformationId = table.Column<long>(nullable: true)
377  },
378  constraints: table =>
379  {
380  table.PrimaryKey("PK_TestMerges", x => x.Id);
381  table.ForeignKey(
382  name: "FK_TestMerges_Users_MergedById",
383  column: x => x.MergedById,
384  principalTable: "Users",
385  principalColumn: "Id",
386  onDelete: ReferentialAction.Restrict);
387  table.ForeignKey(
388  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
389  column: x => x.PrimaryRevisionInformationId,
390  principalTable: "RevisionInformations",
391  principalColumn: "Id",
392  onDelete: ReferentialAction.SetNull);
393  });
394 
395  migrationBuilder.CreateTable(
396  name: "RevInfoTestMerges",
397  columns: table => new
398  {
399  Id = table.Column<long>(nullable: false)
400  .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
401  TestMergeId = table.Column<long>(nullable: false),
402  RevisionInformationId = table.Column<long>(nullable: false)
403  },
404  constraints: table =>
405  {
406  table.PrimaryKey("PK_RevInfoTestMerges", x => x.Id);
407  table.ForeignKey(
408  name: "FK_RevInfoTestMerges_RevisionInformations_RevisionInformationId",
409  column: x => x.RevisionInformationId,
410  principalTable: "RevisionInformations",
411  principalColumn: "Id",
412  onDelete: ReferentialAction.Cascade);
413  table.ForeignKey(
414  name: "FK_RevInfoTestMerges_TestMerges_TestMergeId",
415  column: x => x.TestMergeId,
416  principalTable: "TestMerges",
417  principalColumn: "Id",
418  onDelete: ReferentialAction.Cascade);
419  });
420 
421  migrationBuilder.CreateIndex(
422  name: "IX_ChatBots_InstanceId",
423  table: "ChatBots",
424  column: "InstanceId");
425 
426  migrationBuilder.CreateIndex(
427  name: "IX_ChatBots_Name",
428  table: "ChatBots",
429  column: "Name",
430  unique: true);
431 
432  migrationBuilder.CreateIndex(
433  name: "IX_ChatChannels_ChatSettingsId_DiscordChannelId",
434  table: "ChatChannels",
435  columns: new[] { "ChatSettingsId", "DiscordChannelId" },
436  unique: true);
437 
438  migrationBuilder.CreateIndex(
439  name: "IX_ChatChannels_ChatSettingsId_IrcChannel",
440  table: "ChatChannels",
441  columns: new[] { "ChatSettingsId", "IrcChannel" },
442  unique: true);
443 
444  migrationBuilder.CreateIndex(
445  name: "IX_CompileJobs_DirectoryName",
446  table: "CompileJobs",
447  column: "DirectoryName");
448 
449  migrationBuilder.CreateIndex(
450  name: "IX_CompileJobs_JobId",
451  table: "CompileJobs",
452  column: "JobId");
453 
454  migrationBuilder.CreateIndex(
455  name: "IX_CompileJobs_RevisionInformationId",
456  table: "CompileJobs",
457  column: "RevisionInformationId");
458 
459  migrationBuilder.CreateIndex(
460  name: "IX_DreamDaemonSettings_InstanceId",
461  table: "DreamDaemonSettings",
462  column: "InstanceId",
463  unique: true);
464 
465  migrationBuilder.CreateIndex(
466  name: "IX_DreamMakerSettings_InstanceId",
467  table: "DreamMakerSettings",
468  column: "InstanceId",
469  unique: true);
470 
471  migrationBuilder.CreateIndex(
472  name: "IX_Instances_Path",
473  table: "Instances",
474  column: "Path",
475  unique: true);
476 
477  migrationBuilder.CreateIndex(
478  name: "IX_Instances_WatchdogReattachInformationId",
479  table: "Instances",
480  column: "WatchdogReattachInformationId");
481 
482  migrationBuilder.CreateIndex(
483  name: "IX_InstanceUsers_InstanceId",
484  table: "InstanceUsers",
485  column: "InstanceId");
486 
487  migrationBuilder.CreateIndex(
488  name: "IX_InstanceUsers_UserId_InstanceId",
489  table: "InstanceUsers",
490  columns: new[] { "UserId", "InstanceId" },
491  unique: true);
492 
493  migrationBuilder.CreateIndex(
494  name: "IX_Jobs_CancelledById",
495  table: "Jobs",
496  column: "CancelledById");
497 
498  migrationBuilder.CreateIndex(
499  name: "IX_Jobs_InstanceId",
500  table: "Jobs",
501  column: "InstanceId");
502 
503  migrationBuilder.CreateIndex(
504  name: "IX_Jobs_StartedById",
505  table: "Jobs",
506  column: "StartedById");
507 
508  migrationBuilder.CreateIndex(
509  name: "IX_ReattachInformations_CompileJobId",
510  table: "ReattachInformations",
511  column: "CompileJobId");
512 
513  migrationBuilder.CreateIndex(
514  name: "IX_RepositorySettings_InstanceId",
515  table: "RepositorySettings",
516  column: "InstanceId",
517  unique: true);
518 
519  migrationBuilder.CreateIndex(
520  name: "IX_RevInfoTestMerges_RevisionInformationId",
521  table: "RevInfoTestMerges",
522  column: "RevisionInformationId");
523 
524  migrationBuilder.CreateIndex(
525  name: "IX_RevInfoTestMerges_TestMergeId",
526  table: "RevInfoTestMerges",
527  column: "TestMergeId");
528 
529  migrationBuilder.CreateIndex(
530  name: "IX_RevisionInformations_CommitSha",
531  table: "RevisionInformations",
532  column: "CommitSha",
533  unique: true);
534 
535  migrationBuilder.CreateIndex(
536  name: "IX_RevisionInformations_InstanceId",
537  table: "RevisionInformations",
538  column: "InstanceId");
539 
540  migrationBuilder.CreateIndex(
541  name: "IX_TestMerges_MergedById",
542  table: "TestMerges",
543  column: "MergedById");
544 
545  migrationBuilder.CreateIndex(
546  name: "IX_TestMerges_PrimaryRevisionInformationId",
547  table: "TestMerges",
548  column: "PrimaryRevisionInformationId",
549  unique: true);
550 
551  migrationBuilder.CreateIndex(
552  name: "IX_Users_CanonicalName",
553  table: "Users",
554  column: "CanonicalName",
555  unique: true);
556 
557  migrationBuilder.CreateIndex(
558  name: "IX_Users_CreatedById",
559  table: "Users",
560  column: "CreatedById");
561 
562  migrationBuilder.CreateIndex(
563  name: "IX_WatchdogReattachInformations_AlphaId",
564  table: "WatchdogReattachInformations",
565  column: "AlphaId");
566 
567  migrationBuilder.CreateIndex(
568  name: "IX_WatchdogReattachInformations_BravoId",
569  table: "WatchdogReattachInformations",
570  column: "BravoId");
571 
572  migrationBuilder.AddForeignKey(
573  name: "FK_ChatChannels_ChatBots_ChatSettingsId",
574  table: "ChatChannels",
575  column: "ChatSettingsId",
576  principalTable: "ChatBots",
577  principalColumn: "Id",
578  onDelete: ReferentialAction.Cascade);
579 
580  migrationBuilder.AddForeignKey(
581  name: "FK_ReattachInformations_CompileJobs_CompileJobId",
582  table: "ReattachInformations",
583  column: "CompileJobId",
584  principalTable: "CompileJobs",
585  principalColumn: "Id",
586  onDelete: ReferentialAction.Restrict);
587  }
588 
593  protected override void Down(MigrationBuilder migrationBuilder)
594  {
595  migrationBuilder.DropForeignKey(
596  name: "FK_Jobs_Instances_InstanceId",
597  table: "Jobs");
598 
599  migrationBuilder.DropForeignKey(
600  name: "FK_RevisionInformations_Instances_InstanceId",
601  table: "RevisionInformations");
602 
603  migrationBuilder.DropTable(
604  name: "ChatChannels");
605 
606  migrationBuilder.DropTable(
607  name: "DreamDaemonSettings");
608 
609  migrationBuilder.DropTable(
610  name: "DreamMakerSettings");
611 
612  migrationBuilder.DropTable(
613  name: "InstanceUsers");
614 
615  migrationBuilder.DropTable(
616  name: "RepositorySettings");
617 
618  migrationBuilder.DropTable(
619  name: "RevInfoTestMerges");
620 
621  migrationBuilder.DropTable(
622  name: "ChatBots");
623 
624  migrationBuilder.DropTable(
625  name: "TestMerges");
626 
627  migrationBuilder.DropTable(
628  name: "Instances");
629 
630  migrationBuilder.DropTable(
631  name: "WatchdogReattachInformations");
632 
633  migrationBuilder.DropTable(
634  name: "ReattachInformations");
635 
636  migrationBuilder.DropTable(
637  name: "CompileJobs");
638 
639  migrationBuilder.DropTable(
640  name: "Jobs");
641 
642  migrationBuilder.DropTable(
643  name: "RevisionInformations");
644 
645  migrationBuilder.DropTable(
646  name: "Users");
647  }
648  }
649 }
ConfigurationRights
Rights for Models.ConfigurationFile
ByondRights
Rights for Models.Byond
Definition: ByondRights.cs:9
InstanceUserRights
Rights for an Models.Instance
+
AdministrationRights
Rights for Models.Administration
ConfigurationType
The type of configuration allowed on an Instance
@@ -102,7 +103,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamDaemonRights
Rights for Models.DreamDaemon
InstanceManagerRights
Rights for managing Models.Instances
DreamMakerRights
Rights for Models.DreamMaker
-
override void Up(MigrationBuilder migrationBuilder)
Applies the migration
RepositoryRights
Rights for a Models.Repository
The initial database migration for MySQL/MariaDB
diff --git a/20180918020726___m_y_add_minimum_security_8_designer_8cs_source.html b/20180918020726___m_y_add_minimum_security_8_designer_8cs_source.html index f5c9f79773..cc98992d8a 100644 --- a/20180918020726___m_y_add_minimum_security_8_designer_8cs_source.html +++ b/20180918020726___m_y_add_minimum_security_8_designer_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
20180918020726_MYAddMinimumSecurity.Designer.cs
-Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Migrations;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
9  [DbContext(typeof(MySqlDatabaseContext))]
10  [Migration("20180918020726_MYAddMinimumSecurity")]
11  partial class MYAddMinimumSecurity
12  {
17  protected override void BuildTargetModel(ModelBuilder modelBuilder)
18  {
19 #pragma warning disable 612, 618
20  modelBuilder
21  .HasAnnotation("ProductVersion", "2.1.3-rtm-32065")
22  .HasAnnotation("Relational:MaxIdentifierLength", 64);
23 
24  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
25  {
26  b.Property<long>("Id")
27  .ValueGeneratedOnAdd();
28 
29  b.Property<string>("ConnectionString")
30  .IsRequired();
31 
32  b.Property<bool?>("Enabled");
33 
34  b.Property<long>("InstanceId");
35 
36  b.Property<string>("Name")
37  .IsRequired();
38 
39  b.Property<int?>("Provider");
40 
41  b.HasKey("Id");
42 
43  b.HasIndex("InstanceId");
44 
45  b.HasIndex("Name")
46  .IsUnique();
47 
48  b.ToTable("ChatBots");
49  });
50 
51  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
52  {
53  b.Property<long>("Id")
54  .ValueGeneratedOnAdd();
55 
56  b.Property<long>("ChatSettingsId");
57 
58  b.Property<ulong?>("DiscordChannelId");
59 
60  b.Property<string>("IrcChannel");
61 
62  b.Property<bool?>("IsAdminChannel")
63  .IsRequired();
64 
65  b.Property<bool?>("IsUpdatesChannel")
66  .IsRequired();
67 
68  b.Property<bool?>("IsWatchdogChannel")
69  .IsRequired();
70 
71  b.Property<string>("Tag");
72 
73  b.HasKey("Id");
74 
75  b.HasIndex("ChatSettingsId", "DiscordChannelId")
76  .IsUnique();
77 
78  b.HasIndex("ChatSettingsId", "IrcChannel")
79  .IsUnique();
80 
81  b.ToTable("ChatChannels");
82  });
83 
84  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
85  {
86  b.Property<long>("Id")
87  .ValueGeneratedOnAdd();
88 
89  b.Property<string>("ByondVersion")
90  .IsRequired();
91 
92  b.Property<Guid?>("DirectoryName");
93 
94  b.Property<string>("DmeName");
95 
96  b.Property<long?>("JobId");
97 
98  b.Property<int>("MinimumSecurityLevel");
99 
100  b.Property<string>("Output");
101 
102  b.Property<long>("RevisionInformationId");
103 
104  b.HasKey("Id");
105 
106  b.HasIndex("DirectoryName");
107 
108  b.HasIndex("JobId");
109 
110  b.HasIndex("RevisionInformationId");
111 
112  b.ToTable("CompileJobs");
113  });
114 
115  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
116  {
117  b.Property<long>("Id")
118  .ValueGeneratedOnAdd();
119 
120  b.Property<string>("AccessToken");
121 
122  b.Property<bool?>("AllowWebClient")
123  .IsRequired();
124 
125  b.Property<bool?>("AutoStart")
126  .IsRequired();
127 
128  b.Property<long>("InstanceId");
129 
130  b.Property<ushort?>("PrimaryPort")
131  .IsRequired();
132 
133  b.Property<int?>("ProcessId");
134 
135  b.Property<ushort?>("SecondaryPort")
136  .IsRequired();
137 
138  b.Property<int>("SecurityLevel");
139 
140  b.Property<bool?>("SoftRestart")
141  .IsRequired();
142 
143  b.Property<bool?>("SoftShutdown")
144  .IsRequired();
145 
146  b.Property<uint?>("StartupTimeout")
147  .IsRequired();
148 
149  b.HasKey("Id");
150 
151  b.HasIndex("InstanceId")
152  .IsUnique();
153 
154  b.ToTable("DreamDaemonSettings");
155  });
156 
157  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
158  {
159  b.Property<long>("Id")
160  .ValueGeneratedOnAdd();
161 
162  b.Property<ushort?>("ApiValidationPort")
163  .IsRequired();
164 
165  b.Property<int>("ApiValidationSecurityLevel");
166 
167  b.Property<long>("InstanceId");
168 
169  b.Property<string>("ProjectName");
170 
171  b.HasKey("Id");
172 
173  b.HasIndex("InstanceId")
174  .IsUnique();
175 
176  b.ToTable("DreamMakerSettings");
177  });
178 
179  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
180  {
181  b.Property<long>("Id")
182  .ValueGeneratedOnAdd();
183 
184  b.Property<uint?>("AutoUpdateInterval")
185  .IsRequired();
186 
187  b.Property<int>("ConfigurationType");
188 
189  b.Property<string>("Name")
190  .IsRequired();
191 
192  b.Property<bool?>("Online")
193  .IsRequired();
194 
195  b.Property<string>("Path")
196  .IsRequired();
197 
198  b.Property<long?>("WatchdogReattachInformationId");
199 
200  b.HasKey("Id");
201 
202  b.HasIndex("Path")
203  .IsUnique();
204 
205  b.HasIndex("WatchdogReattachInformationId");
206 
207  b.ToTable("Instances");
208  });
209 
210  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
211  {
212  b.Property<long>("Id")
213  .ValueGeneratedOnAdd();
214 
215  b.Property<ulong>("ByondRights");
216 
217  b.Property<ulong>("ChatBotRights");
218 
219  b.Property<ulong>("ConfigurationRights");
220 
221  b.Property<ulong>("DreamDaemonRights");
222 
223  b.Property<ulong>("DreamMakerRights");
224 
225  b.Property<long>("InstanceId");
226 
227  b.Property<ulong>("InstanceUserRights");
228 
229  b.Property<ulong>("RepositoryRights");
230 
231  b.Property<long?>("UserId")
232  .IsRequired();
233 
234  b.HasKey("Id");
235 
236  b.HasIndex("InstanceId");
237 
238  b.HasIndex("UserId", "InstanceId")
239  .IsUnique();
240 
241  b.ToTable("InstanceUsers");
242  });
243 
244  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
245  {
246  b.Property<long>("Id")
247  .ValueGeneratedOnAdd();
248 
249  b.Property<ulong?>("CancelRight");
250 
251  b.Property<ulong?>("CancelRightsType");
252 
253  b.Property<bool?>("Cancelled")
254  .IsRequired();
255 
256  b.Property<long?>("CancelledById");
257 
258  b.Property<string>("Description")
259  .IsRequired();
260 
261  b.Property<string>("ExceptionDetails");
262 
263  b.Property<long>("InstanceId");
264 
265  b.Property<DateTimeOffset?>("StartedAt")
266  .IsRequired();
267 
268  b.Property<long>("StartedById");
269 
270  b.Property<DateTimeOffset?>("StoppedAt");
271 
272  b.HasKey("Id");
273 
274  b.HasIndex("CancelledById");
275 
276  b.HasIndex("InstanceId");
277 
278  b.HasIndex("StartedById");
279 
280  b.ToTable("Jobs");
281  });
282 
283  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
284  {
285  b.Property<long>("Id")
286  .ValueGeneratedOnAdd();
287 
288  b.Property<string>("AccessIdentifier")
289  .IsRequired();
290 
291  b.Property<string>("ChatChannelsJson")
292  .IsRequired();
293 
294  b.Property<string>("ChatCommandsJson")
295  .IsRequired();
296 
297  b.Property<long?>("CompileJobId");
298 
299  b.Property<bool>("IsPrimary");
300 
301  b.Property<ushort>("Port");
302 
303  b.Property<int>("ProcessId");
304 
305  b.Property<int>("RebootState");
306 
307  b.Property<string>("ServerCommandsJson")
308  .IsRequired();
309 
310  b.HasKey("Id");
311 
312  b.HasIndex("CompileJobId");
313 
314  b.ToTable("ReattachInformations");
315  });
316 
317  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
318  {
319  b.Property<long>("Id")
320  .ValueGeneratedOnAdd();
321 
322  b.Property<string>("AccessToken");
323 
324  b.Property<string>("AccessUser");
325 
326  b.Property<bool?>("AutoUpdatesKeepTestMerges")
327  .IsRequired();
328 
329  b.Property<bool?>("AutoUpdatesSynchronize")
330  .IsRequired();
331 
332  b.Property<string>("CommitterEmail")
333  .IsRequired();
334 
335  b.Property<string>("CommitterName")
336  .IsRequired();
337 
338  b.Property<long>("InstanceId");
339 
340  b.Property<bool?>("PushTestMergeCommits")
341  .IsRequired();
342 
343  b.Property<bool?>("ShowTestMergeCommitters")
344  .IsRequired();
345 
346  b.HasKey("Id");
347 
348  b.HasIndex("InstanceId")
349  .IsUnique();
350 
351  b.ToTable("RepositorySettings");
352  });
353 
354  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
355  {
356  b.Property<long>("Id")
357  .ValueGeneratedOnAdd();
358 
359  b.Property<long>("RevisionInformationId");
360 
361  b.Property<long>("TestMergeId");
362 
363  b.HasKey("Id");
364 
365  b.HasIndex("RevisionInformationId");
366 
367  b.HasIndex("TestMergeId");
368 
369  b.ToTable("RevInfoTestMerges");
370  });
371 
372  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
373  {
374  b.Property<long>("Id")
375  .ValueGeneratedOnAdd();
376 
377  b.Property<string>("CommitSha")
378  .IsRequired()
379  .HasMaxLength(40);
380 
381  b.Property<long>("InstanceId");
382 
383  b.Property<string>("OriginCommitSha")
384  .IsRequired()
385  .HasMaxLength(40);
386 
387  b.HasKey("Id");
388 
389  b.HasIndex("CommitSha")
390  .IsUnique();
391 
392  b.HasIndex("InstanceId");
393 
394  b.ToTable("RevisionInformations");
395  });
396 
397  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
398  {
399  b.Property<long>("Id")
400  .ValueGeneratedOnAdd();
401 
402  b.Property<string>("Author")
403  .IsRequired();
404 
405  b.Property<string>("BodyAtMerge")
406  .IsRequired();
407 
408  b.Property<string>("Comment");
409 
410  b.Property<DateTimeOffset>("MergedAt");
411 
412  b.Property<long>("MergedById");
413 
414  b.Property<int?>("Number")
415  .IsRequired();
416 
417  b.Property<long?>("PrimaryRevisionInformationId");
418 
419  b.Property<string>("PullRequestRevision")
420  .IsRequired();
421 
422  b.Property<string>("TitleAtMerge")
423  .IsRequired();
424 
425  b.Property<string>("Url")
426  .IsRequired();
427 
428  b.HasKey("Id");
429 
430  b.HasIndex("MergedById");
431 
432  b.HasIndex("PrimaryRevisionInformationId")
433  .IsUnique();
434 
435  b.ToTable("TestMerges");
436  });
437 
438  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
439  {
440  b.Property<long>("Id")
441  .ValueGeneratedOnAdd();
442 
443  b.Property<ulong>("AdministrationRights");
444 
445  b.Property<string>("CanonicalName")
446  .IsRequired();
447 
448  b.Property<DateTimeOffset?>("CreatedAt")
449  .IsRequired();
450 
451  b.Property<long?>("CreatedById");
452 
453  b.Property<bool?>("Enabled")
454  .IsRequired();
455 
456  b.Property<ulong>("InstanceManagerRights");
457 
458  b.Property<DateTimeOffset?>("LastPasswordUpdate");
459 
460  b.Property<string>("Name")
461  .IsRequired();
462 
463  b.Property<string>("PasswordHash");
464 
465  b.Property<string>("SystemIdentifier");
466 
467  b.HasKey("Id");
468 
469  b.HasIndex("CanonicalName")
470  .IsUnique();
471 
472  b.HasIndex("CreatedById");
473 
474  b.ToTable("Users");
475  });
476 
477  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
478  {
479  b.Property<long>("Id")
480  .ValueGeneratedOnAdd();
481 
482  b.Property<long?>("AlphaId");
483 
484  b.Property<bool>("AlphaIsActive");
485 
486  b.Property<long?>("BravoId");
487 
488  b.HasKey("Id");
489 
490  b.HasIndex("AlphaId");
491 
492  b.HasIndex("BravoId");
493 
494  b.ToTable("WatchdogReattachInformations");
495  });
496 
497  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
498  {
499  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
500  .WithMany("ChatSettings")
501  .HasForeignKey("InstanceId")
502  .OnDelete(DeleteBehavior.Cascade);
503  });
504 
505  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
506  {
507  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
508  .WithMany("Channels")
509  .HasForeignKey("ChatSettingsId")
510  .OnDelete(DeleteBehavior.Cascade);
511  });
512 
513  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
514  {
515  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
516  .WithMany()
517  .HasForeignKey("JobId");
518 
519  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
520  .WithMany("CompileJobs")
521  .HasForeignKey("RevisionInformationId")
522  .OnDelete(DeleteBehavior.Cascade);
523  });
524 
525  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
526  {
527  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
528  .WithOne("DreamDaemonSettings")
529  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
530  .OnDelete(DeleteBehavior.Cascade);
531  });
532 
533  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
534  {
535  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
536  .WithOne("DreamMakerSettings")
537  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
538  .OnDelete(DeleteBehavior.Cascade);
539  });
540 
541  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
542  {
543  b.HasOne("Tgstation.Server.Host.Models.WatchdogReattachInformation", "WatchdogReattachInformation")
544  .WithMany()
545  .HasForeignKey("WatchdogReattachInformationId");
546  });
547 
548  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
549  {
550  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
551  .WithMany("InstanceUsers")
552  .HasForeignKey("InstanceId")
553  .OnDelete(DeleteBehavior.Cascade);
554 
555  b.HasOne("Tgstation.Server.Host.Models.User")
556  .WithMany("InstanceUsers")
557  .HasForeignKey("UserId")
558  .OnDelete(DeleteBehavior.Cascade);
559  });
560 
561  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
562  {
563  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
564  .WithMany()
565  .HasForeignKey("CancelledById");
566 
567  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
568  .WithMany("Jobs")
569  .HasForeignKey("InstanceId")
570  .OnDelete(DeleteBehavior.Cascade);
571 
572  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
573  .WithMany()
574  .HasForeignKey("StartedById")
575  .OnDelete(DeleteBehavior.Cascade);
576  });
577 
578  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
579  {
580  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
581  .WithMany()
582  .HasForeignKey("CompileJobId");
583  });
584 
585  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
586  {
587  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
588  .WithOne("RepositorySettings")
589  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
590  .OnDelete(DeleteBehavior.Cascade);
591  });
592 
593  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
594  {
595  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
596  .WithMany("ActiveTestMerges")
597  .HasForeignKey("RevisionInformationId")
598  .OnDelete(DeleteBehavior.Cascade);
599 
600  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
601  .WithMany("RevisonInformations")
602  .HasForeignKey("TestMergeId")
603  .OnDelete(DeleteBehavior.Cascade);
604  });
605 
606  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
607  {
608  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
609  .WithMany("RevisionInformations")
610  .HasForeignKey("InstanceId")
611  .OnDelete(DeleteBehavior.Cascade);
612  });
613 
614  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
615  {
616  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
617  .WithMany("TestMerges")
618  .HasForeignKey("MergedById")
619  .OnDelete(DeleteBehavior.Restrict);
620 
621  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
622  .WithOne("PrimaryTestMerge")
623  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
624  .OnDelete(DeleteBehavior.SetNull);
625  });
626 
627  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
628  {
629  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
630  .WithMany("CreatedUsers")
631  .HasForeignKey("CreatedById");
632  });
633 
634  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
635  {
636  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
637  .WithMany()
638  .HasForeignKey("AlphaId");
639 
640  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
641  .WithMany()
642  .HasForeignKey("BravoId");
643  });
644 #pragma warning restore 612, 618
645  }
646  }
647 }
override void BuildTargetModel(ModelBuilder modelBuilder)
Builds the target model
+Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Migrations;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
9  [DbContext(typeof(MySqlDatabaseContext))]
10  [Migration("20180918020726_MYAddMinimumSecurity")]
11  partial class MYAddMinimumSecurity
12  {
17  protected override void BuildTargetModel(ModelBuilder modelBuilder)
18  {
19 #pragma warning disable 612, 618
20  modelBuilder
21  .HasAnnotation("ProductVersion", "2.1.3-rtm-32065")
22  .HasAnnotation("Relational:MaxIdentifierLength", 64);
23 
24  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
25  {
26  b.Property<long>("Id")
27  .ValueGeneratedOnAdd();
28 
29  b.Property<string>("ConnectionString")
30  .IsRequired();
31 
32  b.Property<bool?>("Enabled");
33 
34  b.Property<long>("InstanceId");
35 
36  b.Property<string>("Name")
37  .IsRequired();
38 
39  b.Property<int?>("Provider");
40 
41  b.HasKey("Id");
42 
43  b.HasIndex("InstanceId");
44 
45  b.HasIndex("Name")
46  .IsUnique();
47 
48  b.ToTable("ChatBots");
49  });
50 
51  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
52  {
53  b.Property<long>("Id")
54  .ValueGeneratedOnAdd();
55 
56  b.Property<long>("ChatSettingsId");
57 
58  b.Property<ulong?>("DiscordChannelId");
59 
60  b.Property<string>("IrcChannel");
61 
62  b.Property<bool?>("IsAdminChannel")
63  .IsRequired();
64 
65  b.Property<bool?>("IsUpdatesChannel")
66  .IsRequired();
67 
68  b.Property<bool?>("IsWatchdogChannel")
69  .IsRequired();
70 
71  b.Property<string>("Tag");
72 
73  b.HasKey("Id");
74 
75  b.HasIndex("ChatSettingsId", "DiscordChannelId")
76  .IsUnique();
77 
78  b.HasIndex("ChatSettingsId", "IrcChannel")
79  .IsUnique();
80 
81  b.ToTable("ChatChannels");
82  });
83 
84  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
85  {
86  b.Property<long>("Id")
87  .ValueGeneratedOnAdd();
88 
89  b.Property<string>("ByondVersion")
90  .IsRequired();
91 
92  b.Property<Guid?>("DirectoryName");
93 
94  b.Property<string>("DmeName");
95 
96  b.Property<long?>("JobId");
97 
98  b.Property<int>("MinimumSecurityLevel");
99 
100  b.Property<string>("Output");
101 
102  b.Property<long>("RevisionInformationId");
103 
104  b.HasKey("Id");
105 
106  b.HasIndex("DirectoryName");
107 
108  b.HasIndex("JobId");
109 
110  b.HasIndex("RevisionInformationId");
111 
112  b.ToTable("CompileJobs");
113  });
114 
115  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
116  {
117  b.Property<long>("Id")
118  .ValueGeneratedOnAdd();
119 
120  b.Property<string>("AccessToken");
121 
122  b.Property<bool?>("AllowWebClient")
123  .IsRequired();
124 
125  b.Property<bool?>("AutoStart")
126  .IsRequired();
127 
128  b.Property<long>("InstanceId");
129 
130  b.Property<ushort?>("PrimaryPort")
131  .IsRequired();
132 
133  b.Property<int?>("ProcessId");
134 
135  b.Property<ushort?>("SecondaryPort")
136  .IsRequired();
137 
138  b.Property<int>("SecurityLevel");
139 
140  b.Property<bool?>("SoftRestart")
141  .IsRequired();
142 
143  b.Property<bool?>("SoftShutdown")
144  .IsRequired();
145 
146  b.Property<uint?>("StartupTimeout")
147  .IsRequired();
148 
149  b.HasKey("Id");
150 
151  b.HasIndex("InstanceId")
152  .IsUnique();
153 
154  b.ToTable("DreamDaemonSettings");
155  });
156 
157  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
158  {
159  b.Property<long>("Id")
160  .ValueGeneratedOnAdd();
161 
162  b.Property<ushort?>("ApiValidationPort")
163  .IsRequired();
164 
165  b.Property<int>("ApiValidationSecurityLevel");
166 
167  b.Property<long>("InstanceId");
168 
169  b.Property<string>("ProjectName");
170 
171  b.HasKey("Id");
172 
173  b.HasIndex("InstanceId")
174  .IsUnique();
175 
176  b.ToTable("DreamMakerSettings");
177  });
178 
179  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
180  {
181  b.Property<long>("Id")
182  .ValueGeneratedOnAdd();
183 
184  b.Property<uint?>("AutoUpdateInterval")
185  .IsRequired();
186 
187  b.Property<int>("ConfigurationType");
188 
189  b.Property<string>("Name")
190  .IsRequired();
191 
192  b.Property<bool?>("Online")
193  .IsRequired();
194 
195  b.Property<string>("Path")
196  .IsRequired();
197 
198  b.Property<long?>("WatchdogReattachInformationId");
199 
200  b.HasKey("Id");
201 
202  b.HasIndex("Path")
203  .IsUnique();
204 
205  b.HasIndex("WatchdogReattachInformationId");
206 
207  b.ToTable("Instances");
208  });
209 
210  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
211  {
212  b.Property<long>("Id")
213  .ValueGeneratedOnAdd();
214 
215  b.Property<ulong>("ByondRights");
216 
217  b.Property<ulong>("ChatBotRights");
218 
219  b.Property<ulong>("ConfigurationRights");
220 
221  b.Property<ulong>("DreamDaemonRights");
222 
223  b.Property<ulong>("DreamMakerRights");
224 
225  b.Property<long>("InstanceId");
226 
227  b.Property<ulong>("InstanceUserRights");
228 
229  b.Property<ulong>("RepositoryRights");
230 
231  b.Property<long?>("UserId")
232  .IsRequired();
233 
234  b.HasKey("Id");
235 
236  b.HasIndex("InstanceId");
237 
238  b.HasIndex("UserId", "InstanceId")
239  .IsUnique();
240 
241  b.ToTable("InstanceUsers");
242  });
243 
244  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
245  {
246  b.Property<long>("Id")
247  .ValueGeneratedOnAdd();
248 
249  b.Property<ulong?>("CancelRight");
250 
251  b.Property<ulong?>("CancelRightsType");
252 
253  b.Property<bool?>("Cancelled")
254  .IsRequired();
255 
256  b.Property<long?>("CancelledById");
257 
258  b.Property<string>("Description")
259  .IsRequired();
260 
261  b.Property<string>("ExceptionDetails");
262 
263  b.Property<long>("InstanceId");
264 
265  b.Property<DateTimeOffset?>("StartedAt")
266  .IsRequired();
267 
268  b.Property<long>("StartedById");
269 
270  b.Property<DateTimeOffset?>("StoppedAt");
271 
272  b.HasKey("Id");
273 
274  b.HasIndex("CancelledById");
275 
276  b.HasIndex("InstanceId");
277 
278  b.HasIndex("StartedById");
279 
280  b.ToTable("Jobs");
281  });
282 
283  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
284  {
285  b.Property<long>("Id")
286  .ValueGeneratedOnAdd();
287 
288  b.Property<string>("AccessIdentifier")
289  .IsRequired();
290 
291  b.Property<string>("ChatChannelsJson")
292  .IsRequired();
293 
294  b.Property<string>("ChatCommandsJson")
295  .IsRequired();
296 
297  b.Property<long?>("CompileJobId");
298 
299  b.Property<bool>("IsPrimary");
300 
301  b.Property<ushort>("Port");
302 
303  b.Property<int>("ProcessId");
304 
305  b.Property<int>("RebootState");
306 
307  b.Property<string>("ServerCommandsJson")
308  .IsRequired();
309 
310  b.HasKey("Id");
311 
312  b.HasIndex("CompileJobId");
313 
314  b.ToTable("ReattachInformations");
315  });
316 
317  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
318  {
319  b.Property<long>("Id")
320  .ValueGeneratedOnAdd();
321 
322  b.Property<string>("AccessToken");
323 
324  b.Property<string>("AccessUser");
325 
326  b.Property<bool?>("AutoUpdatesKeepTestMerges")
327  .IsRequired();
328 
329  b.Property<bool?>("AutoUpdatesSynchronize")
330  .IsRequired();
331 
332  b.Property<string>("CommitterEmail")
333  .IsRequired();
334 
335  b.Property<string>("CommitterName")
336  .IsRequired();
337 
338  b.Property<long>("InstanceId");
339 
340  b.Property<bool?>("PushTestMergeCommits")
341  .IsRequired();
342 
343  b.Property<bool?>("ShowTestMergeCommitters")
344  .IsRequired();
345 
346  b.HasKey("Id");
347 
348  b.HasIndex("InstanceId")
349  .IsUnique();
350 
351  b.ToTable("RepositorySettings");
352  });
353 
354  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
355  {
356  b.Property<long>("Id")
357  .ValueGeneratedOnAdd();
358 
359  b.Property<long>("RevisionInformationId");
360 
361  b.Property<long>("TestMergeId");
362 
363  b.HasKey("Id");
364 
365  b.HasIndex("RevisionInformationId");
366 
367  b.HasIndex("TestMergeId");
368 
369  b.ToTable("RevInfoTestMerges");
370  });
371 
372  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
373  {
374  b.Property<long>("Id")
375  .ValueGeneratedOnAdd();
376 
377  b.Property<string>("CommitSha")
378  .IsRequired()
379  .HasMaxLength(40);
380 
381  b.Property<long>("InstanceId");
382 
383  b.Property<string>("OriginCommitSha")
384  .IsRequired()
385  .HasMaxLength(40);
386 
387  b.HasKey("Id");
388 
389  b.HasIndex("CommitSha")
390  .IsUnique();
391 
392  b.HasIndex("InstanceId");
393 
394  b.ToTable("RevisionInformations");
395  });
396 
397  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
398  {
399  b.Property<long>("Id")
400  .ValueGeneratedOnAdd();
401 
402  b.Property<string>("Author")
403  .IsRequired();
404 
405  b.Property<string>("BodyAtMerge")
406  .IsRequired();
407 
408  b.Property<string>("Comment");
409 
410  b.Property<DateTimeOffset>("MergedAt");
411 
412  b.Property<long>("MergedById");
413 
414  b.Property<int?>("Number")
415  .IsRequired();
416 
417  b.Property<long?>("PrimaryRevisionInformationId");
418 
419  b.Property<string>("PullRequestRevision")
420  .IsRequired();
421 
422  b.Property<string>("TitleAtMerge")
423  .IsRequired();
424 
425  b.Property<string>("Url")
426  .IsRequired();
427 
428  b.HasKey("Id");
429 
430  b.HasIndex("MergedById");
431 
432  b.HasIndex("PrimaryRevisionInformationId")
433  .IsUnique();
434 
435  b.ToTable("TestMerges");
436  });
437 
438  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
439  {
440  b.Property<long>("Id")
441  .ValueGeneratedOnAdd();
442 
443  b.Property<ulong>("AdministrationRights");
444 
445  b.Property<string>("CanonicalName")
446  .IsRequired();
447 
448  b.Property<DateTimeOffset?>("CreatedAt")
449  .IsRequired();
450 
451  b.Property<long?>("CreatedById");
452 
453  b.Property<bool?>("Enabled")
454  .IsRequired();
455 
456  b.Property<ulong>("InstanceManagerRights");
457 
458  b.Property<DateTimeOffset?>("LastPasswordUpdate");
459 
460  b.Property<string>("Name")
461  .IsRequired();
462 
463  b.Property<string>("PasswordHash");
464 
465  b.Property<string>("SystemIdentifier");
466 
467  b.HasKey("Id");
468 
469  b.HasIndex("CanonicalName")
470  .IsUnique();
471 
472  b.HasIndex("CreatedById");
473 
474  b.ToTable("Users");
475  });
476 
477  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
478  {
479  b.Property<long>("Id")
480  .ValueGeneratedOnAdd();
481 
482  b.Property<long?>("AlphaId");
483 
484  b.Property<bool>("AlphaIsActive");
485 
486  b.Property<long?>("BravoId");
487 
488  b.HasKey("Id");
489 
490  b.HasIndex("AlphaId");
491 
492  b.HasIndex("BravoId");
493 
494  b.ToTable("WatchdogReattachInformations");
495  });
496 
497  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
498  {
499  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
500  .WithMany("ChatSettings")
501  .HasForeignKey("InstanceId")
502  .OnDelete(DeleteBehavior.Cascade);
503  });
504 
505  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
506  {
507  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
508  .WithMany("Channels")
509  .HasForeignKey("ChatSettingsId")
510  .OnDelete(DeleteBehavior.Cascade);
511  });
512 
513  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
514  {
515  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
516  .WithMany()
517  .HasForeignKey("JobId");
518 
519  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
520  .WithMany("CompileJobs")
521  .HasForeignKey("RevisionInformationId")
522  .OnDelete(DeleteBehavior.Cascade);
523  });
524 
525  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
526  {
527  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
528  .WithOne("DreamDaemonSettings")
529  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
530  .OnDelete(DeleteBehavior.Cascade);
531  });
532 
533  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
534  {
535  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
536  .WithOne("DreamMakerSettings")
537  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
538  .OnDelete(DeleteBehavior.Cascade);
539  });
540 
541  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
542  {
543  b.HasOne("Tgstation.Server.Host.Models.WatchdogReattachInformation", "WatchdogReattachInformation")
544  .WithMany()
545  .HasForeignKey("WatchdogReattachInformationId");
546  });
547 
548  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
549  {
550  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
551  .WithMany("InstanceUsers")
552  .HasForeignKey("InstanceId")
553  .OnDelete(DeleteBehavior.Cascade);
554 
555  b.HasOne("Tgstation.Server.Host.Models.User")
556  .WithMany("InstanceUsers")
557  .HasForeignKey("UserId")
558  .OnDelete(DeleteBehavior.Cascade);
559  });
560 
561  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
562  {
563  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
564  .WithMany()
565  .HasForeignKey("CancelledById");
566 
567  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
568  .WithMany("Jobs")
569  .HasForeignKey("InstanceId")
570  .OnDelete(DeleteBehavior.Cascade);
571 
572  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
573  .WithMany()
574  .HasForeignKey("StartedById")
575  .OnDelete(DeleteBehavior.Cascade);
576  });
577 
578  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
579  {
580  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
581  .WithMany()
582  .HasForeignKey("CompileJobId");
583  });
584 
585  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
586  {
587  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
588  .WithOne("RepositorySettings")
589  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
590  .OnDelete(DeleteBehavior.Cascade);
591  });
592 
593  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
594  {
595  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
596  .WithMany("ActiveTestMerges")
597  .HasForeignKey("RevisionInformationId")
598  .OnDelete(DeleteBehavior.Cascade);
599 
600  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
601  .WithMany("RevisonInformations")
602  .HasForeignKey("TestMergeId")
603  .OnDelete(DeleteBehavior.Cascade);
604  });
605 
606  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
607  {
608  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
609  .WithMany("RevisionInformations")
610  .HasForeignKey("InstanceId")
611  .OnDelete(DeleteBehavior.Cascade);
612  });
613 
614  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
615  {
616  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
617  .WithMany("TestMerges")
618  .HasForeignKey("MergedById")
619  .OnDelete(DeleteBehavior.Restrict);
620 
621  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
622  .WithOne("PrimaryTestMerge")
623  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
624  .OnDelete(DeleteBehavior.SetNull);
625  });
626 
627  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
628  {
629  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
630  .WithMany("CreatedUsers")
631  .HasForeignKey("CreatedById");
632  });
633 
634  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
635  {
636  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
637  .WithMany()
638  .HasForeignKey("AlphaId");
639 
640  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
641  .WithMany()
642  .HasForeignKey("BravoId");
643  });
644 #pragma warning restore 612, 618
645  }
646  }
647 }
override void BuildTargetModel(ModelBuilder modelBuilder)
Builds the target model
+ -
diff --git a/20180918020726___m_y_add_minimum_security_8cs_source.html b/20180918020726___m_y_add_minimum_security_8cs_source.html index 85b14042d0..d0c2242406 100644 --- a/20180918020726___m_y_add_minimum_security_8cs_source.html +++ b/20180918020726___m_y_add_minimum_security_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
20180918020726_MYAddMinimumSecurity.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore.Migrations;
3 
4 namespace Tgstation.Server.Host.Models.Migrations
5 {
9  public partial class MYAddMinimumSecurity : Migration
10  {
15  protected override void Up(MigrationBuilder migrationBuilder)
16  {
17  migrationBuilder.AddColumn<int>(
18  name: "ApiValidationSecurityLevel",
19  table: "DreamMakerSettings",
20  nullable: false,
21  defaultValue: (int)DreamDaemonSecurity.Safe);
22 
23  migrationBuilder.AddColumn<int>(
24  name: "MinimumSecurityLevel",
25  table: "CompileJobs",
26  nullable: false,
27  defaultValue: (int)DreamDaemonSecurity.Safe);
28  }
29 
34  protected override void Down(MigrationBuilder migrationBuilder)
35  {
36  migrationBuilder.DropColumn(
37  name: "ApiValidationSecurityLevel",
38  table: "DreamMakerSettings");
39 
40  migrationBuilder.DropColumn(
41  name: "MinimumSecurityLevel",
42  table: "CompileJobs");
43  }
44  }
45 }
override void Down(MigrationBuilder migrationBuilder)
Unapplies the migration
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore.Migrations;
3 
4 namespace Tgstation.Server.Host.Models.Migrations
5 {
9  public partial class MYAddMinimumSecurity : Migration
10  {
15  protected override void Up(MigrationBuilder migrationBuilder)
16  {
17  migrationBuilder.AddColumn<int>(
18  name: "ApiValidationSecurityLevel",
19  table: "DreamMakerSettings",
20  nullable: false,
21  defaultValue: (int)DreamDaemonSecurity.Safe);
22 
23  migrationBuilder.AddColumn<int>(
24  name: "MinimumSecurityLevel",
25  table: "CompileJobs",
26  nullable: false,
27  defaultValue: (int)DreamDaemonSecurity.Safe);
28  }
29 
34  protected override void Down(MigrationBuilder migrationBuilder)
35  {
36  migrationBuilder.DropColumn(
37  name: "ApiValidationSecurityLevel",
38  table: "DreamMakerSettings");
39 
40  migrationBuilder.DropColumn(
41  name: "MinimumSecurityLevel",
42  table: "CompileJobs");
43  }
44  }
45 }
override void Down(MigrationBuilder migrationBuilder)
Unapplies the migration
+
Add the Api.Models.Internal.CompileJob.MinimumSecurityLevel and DreamMaker.ApiValidationSecurityLevel...
-
DreamDaemonSecurity
DreamDaemon&#39;s security level
diff --git a/20180918021228___m_s_add_minimum_security_8_designer_8cs_source.html b/20180918021228___m_s_add_minimum_security_8_designer_8cs_source.html index 92088ea016..6d8dee7f1c 100644 --- a/20180918021228___m_s_add_minimum_security_8_designer_8cs_source.html +++ b/20180918021228___m_s_add_minimum_security_8_designer_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
20180918021228_MSAddMinimumSecurity.Designer.cs
-Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Metadata;
6 using Microsoft.EntityFrameworkCore.Migrations;
7 using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 
9 namespace Tgstation.Server.Host.Models.Migrations
10 {
11  [DbContext(typeof(SqlServerDatabaseContext))]
12  [Migration("20180918021228_MSAddMinimumSecurity")]
13  partial class MSAddMinimumSecurity
14  {
19  protected override void BuildTargetModel(ModelBuilder modelBuilder)
20  {
21 #pragma warning disable 612, 618
22  modelBuilder
23  .HasAnnotation("ProductVersion", "2.1.3-rtm-32065")
24  .HasAnnotation("Relational:MaxIdentifierLength", 128)
25  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
26 
27  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
28  {
29  b.Property<long>("Id")
30  .ValueGeneratedOnAdd()
31  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
32 
33  b.Property<string>("ConnectionString")
34  .IsRequired();
35 
36  b.Property<bool?>("Enabled");
37 
38  b.Property<long>("InstanceId");
39 
40  b.Property<string>("Name")
41  .IsRequired();
42 
43  b.Property<int?>("Provider");
44 
45  b.HasKey("Id");
46 
47  b.HasIndex("InstanceId");
48 
49  b.HasIndex("Name")
50  .IsUnique();
51 
52  b.ToTable("ChatBots");
53  });
54 
55  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
56  {
57  b.Property<long>("Id")
58  .ValueGeneratedOnAdd()
59  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
60 
61  b.Property<long>("ChatSettingsId");
62 
63  b.Property<decimal?>("DiscordChannelId")
64  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
65 
66  b.Property<string>("IrcChannel");
67 
68  b.Property<bool?>("IsAdminChannel")
69  .IsRequired();
70 
71  b.Property<bool?>("IsUpdatesChannel")
72  .IsRequired();
73 
74  b.Property<bool?>("IsWatchdogChannel")
75  .IsRequired();
76 
77  b.Property<string>("Tag");
78 
79  b.HasKey("Id");
80 
81  b.HasIndex("ChatSettingsId", "DiscordChannelId")
82  .IsUnique()
83  .HasFilter("[DiscordChannelId] IS NOT NULL");
84 
85  b.HasIndex("ChatSettingsId", "IrcChannel")
86  .IsUnique()
87  .HasFilter("[IrcChannel] IS NOT NULL");
88 
89  b.ToTable("ChatChannels");
90  });
91 
92  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
93  {
94  b.Property<long>("Id")
95  .ValueGeneratedOnAdd()
96  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
97 
98  b.Property<string>("ByondVersion")
99  .IsRequired();
100 
101  b.Property<Guid?>("DirectoryName");
102 
103  b.Property<string>("DmeName");
104 
105  b.Property<long?>("JobId");
106 
107  b.Property<int>("MinimumSecurityLevel");
108 
109  b.Property<string>("Output");
110 
111  b.Property<long>("RevisionInformationId");
112 
113  b.HasKey("Id");
114 
115  b.HasIndex("DirectoryName");
116 
117  b.HasIndex("JobId");
118 
119  b.HasIndex("RevisionInformationId");
120 
121  b.ToTable("CompileJobs");
122  });
123 
124  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
125  {
126  b.Property<long>("Id")
127  .ValueGeneratedOnAdd()
128  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
129 
130  b.Property<string>("AccessToken");
131 
132  b.Property<bool?>("AllowWebClient")
133  .IsRequired();
134 
135  b.Property<bool?>("AutoStart")
136  .IsRequired();
137 
138  b.Property<long>("InstanceId");
139 
140  b.Property<int>("PrimaryPort");
141 
142  b.Property<int?>("ProcessId");
143 
144  b.Property<int>("SecondaryPort");
145 
146  b.Property<int>("SecurityLevel");
147 
148  b.Property<bool?>("SoftRestart")
149  .IsRequired();
150 
151  b.Property<bool?>("SoftShutdown")
152  .IsRequired();
153 
154  b.Property<long>("StartupTimeout");
155 
156  b.HasKey("Id");
157 
158  b.HasIndex("InstanceId")
159  .IsUnique();
160 
161  b.ToTable("DreamDaemonSettings");
162  });
163 
164  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
165  {
166  b.Property<long>("Id")
167  .ValueGeneratedOnAdd()
168  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
169 
170  b.Property<int>("ApiValidationPort");
171 
172  b.Property<int>("ApiValidationSecurityLevel");
173 
174  b.Property<long>("InstanceId");
175 
176  b.Property<string>("ProjectName");
177 
178  b.HasKey("Id");
179 
180  b.HasIndex("InstanceId")
181  .IsUnique();
182 
183  b.ToTable("DreamMakerSettings");
184  });
185 
186  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
187  {
188  b.Property<long>("Id")
189  .ValueGeneratedOnAdd()
190  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
191 
192  b.Property<long>("AutoUpdateInterval");
193 
194  b.Property<int>("ConfigurationType");
195 
196  b.Property<string>("Name")
197  .IsRequired();
198 
199  b.Property<bool?>("Online")
200  .IsRequired();
201 
202  b.Property<string>("Path")
203  .IsRequired();
204 
205  b.Property<long?>("WatchdogReattachInformationId");
206 
207  b.HasKey("Id");
208 
209  b.HasIndex("Path")
210  .IsUnique();
211 
212  b.HasIndex("WatchdogReattachInformationId");
213 
214  b.ToTable("Instances");
215  });
216 
217  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
218  {
219  b.Property<long>("Id")
220  .ValueGeneratedOnAdd()
221  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
222 
223  b.Property<decimal>("ByondRights")
224  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
225 
226  b.Property<decimal>("ChatBotRights")
227  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
228 
229  b.Property<decimal>("ConfigurationRights")
230  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
231 
232  b.Property<decimal>("DreamDaemonRights")
233  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
234 
235  b.Property<decimal>("DreamMakerRights")
236  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
237 
238  b.Property<long>("InstanceId");
239 
240  b.Property<decimal>("InstanceUserRights")
241  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
242 
243  b.Property<decimal>("RepositoryRights")
244  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
245 
246  b.Property<long?>("UserId")
247  .IsRequired();
248 
249  b.HasKey("Id");
250 
251  b.HasIndex("InstanceId");
252 
253  b.HasIndex("UserId", "InstanceId")
254  .IsUnique();
255 
256  b.ToTable("InstanceUsers");
257  });
258 
259  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
260  {
261  b.Property<long>("Id")
262  .ValueGeneratedOnAdd()
263  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
264 
265  b.Property<decimal?>("CancelRight")
266  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
267 
268  b.Property<decimal?>("CancelRightsType")
269  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
270 
271  b.Property<bool?>("Cancelled")
272  .IsRequired();
273 
274  b.Property<long?>("CancelledById");
275 
276  b.Property<string>("Description")
277  .IsRequired();
278 
279  b.Property<string>("ExceptionDetails");
280 
281  b.Property<long>("InstanceId");
282 
283  b.Property<DateTimeOffset?>("StartedAt")
284  .IsRequired();
285 
286  b.Property<long>("StartedById");
287 
288  b.Property<DateTimeOffset?>("StoppedAt");
289 
290  b.HasKey("Id");
291 
292  b.HasIndex("CancelledById");
293 
294  b.HasIndex("InstanceId");
295 
296  b.HasIndex("StartedById");
297 
298  b.ToTable("Jobs");
299  });
300 
301  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
302  {
303  b.Property<long>("Id")
304  .ValueGeneratedOnAdd()
305  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
306 
307  b.Property<string>("AccessIdentifier")
308  .IsRequired();
309 
310  b.Property<string>("ChatChannelsJson")
311  .IsRequired();
312 
313  b.Property<string>("ChatCommandsJson")
314  .IsRequired();
315 
316  b.Property<long?>("CompileJobId");
317 
318  b.Property<bool>("IsPrimary");
319 
320  b.Property<int>("Port");
321 
322  b.Property<int>("ProcessId");
323 
324  b.Property<int>("RebootState");
325 
326  b.Property<string>("ServerCommandsJson")
327  .IsRequired();
328 
329  b.HasKey("Id");
330 
331  b.HasIndex("CompileJobId");
332 
333  b.ToTable("ReattachInformations");
334  });
335 
336  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
337  {
338  b.Property<long>("Id")
339  .ValueGeneratedOnAdd()
340  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
341 
342  b.Property<string>("AccessToken");
343 
344  b.Property<string>("AccessUser");
345 
346  b.Property<bool?>("AutoUpdatesKeepTestMerges")
347  .IsRequired();
348 
349  b.Property<bool?>("AutoUpdatesSynchronize")
350  .IsRequired();
351 
352  b.Property<string>("CommitterEmail")
353  .IsRequired();
354 
355  b.Property<string>("CommitterName")
356  .IsRequired();
357 
358  b.Property<long>("InstanceId");
359 
360  b.Property<bool?>("PushTestMergeCommits")
361  .IsRequired();
362 
363  b.Property<bool?>("ShowTestMergeCommitters")
364  .IsRequired();
365 
366  b.HasKey("Id");
367 
368  b.HasIndex("InstanceId")
369  .IsUnique();
370 
371  b.ToTable("RepositorySettings");
372  });
373 
374  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
375  {
376  b.Property<long>("Id")
377  .ValueGeneratedOnAdd()
378  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
379 
380  b.Property<long>("RevisionInformationId");
381 
382  b.Property<long>("TestMergeId");
383 
384  b.HasKey("Id");
385 
386  b.HasIndex("RevisionInformationId");
387 
388  b.HasIndex("TestMergeId");
389 
390  b.ToTable("RevInfoTestMerges");
391  });
392 
393  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
394  {
395  b.Property<long>("Id")
396  .ValueGeneratedOnAdd()
397  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
398 
399  b.Property<string>("CommitSha")
400  .IsRequired()
401  .HasMaxLength(40);
402 
403  b.Property<long>("InstanceId");
404 
405  b.Property<string>("OriginCommitSha")
406  .IsRequired()
407  .HasMaxLength(40);
408 
409  b.HasKey("Id");
410 
411  b.HasIndex("CommitSha")
412  .IsUnique();
413 
414  b.HasIndex("InstanceId");
415 
416  b.ToTable("RevisionInformations");
417  });
418 
419  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
420  {
421  b.Property<long>("Id")
422  .ValueGeneratedOnAdd()
423  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
424 
425  b.Property<string>("Author")
426  .IsRequired();
427 
428  b.Property<string>("BodyAtMerge")
429  .IsRequired();
430 
431  b.Property<string>("Comment");
432 
433  b.Property<DateTimeOffset>("MergedAt");
434 
435  b.Property<long>("MergedById");
436 
437  b.Property<int?>("Number")
438  .IsRequired();
439 
440  b.Property<long?>("PrimaryRevisionInformationId");
441 
442  b.Property<string>("PullRequestRevision")
443  .IsRequired();
444 
445  b.Property<string>("TitleAtMerge")
446  .IsRequired();
447 
448  b.Property<string>("Url")
449  .IsRequired();
450 
451  b.HasKey("Id");
452 
453  b.HasIndex("MergedById");
454 
455  b.HasIndex("PrimaryRevisionInformationId")
456  .IsUnique()
457  .HasFilter("[PrimaryRevisionInformationId] IS NOT NULL");
458 
459  b.ToTable("TestMerges");
460  });
461 
462  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
463  {
464  b.Property<long>("Id")
465  .ValueGeneratedOnAdd()
466  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
467 
468  b.Property<decimal>("AdministrationRights")
469  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
470 
471  b.Property<string>("CanonicalName")
472  .IsRequired();
473 
474  b.Property<DateTimeOffset?>("CreatedAt")
475  .IsRequired();
476 
477  b.Property<long?>("CreatedById");
478 
479  b.Property<bool?>("Enabled")
480  .IsRequired();
481 
482  b.Property<decimal>("InstanceManagerRights")
483  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
484 
485  b.Property<DateTimeOffset?>("LastPasswordUpdate");
486 
487  b.Property<string>("Name")
488  .IsRequired();
489 
490  b.Property<string>("PasswordHash");
491 
492  b.Property<string>("SystemIdentifier");
493 
494  b.HasKey("Id");
495 
496  b.HasIndex("CanonicalName")
497  .IsUnique();
498 
499  b.HasIndex("CreatedById");
500 
501  b.ToTable("Users");
502  });
503 
504  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
505  {
506  b.Property<long>("Id")
507  .ValueGeneratedOnAdd()
508  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
509 
510  b.Property<long?>("AlphaId");
511 
512  b.Property<bool>("AlphaIsActive");
513 
514  b.Property<long?>("BravoId");
515 
516  b.HasKey("Id");
517 
518  b.HasIndex("AlphaId");
519 
520  b.HasIndex("BravoId");
521 
522  b.ToTable("WatchdogReattachInformations");
523  });
524 
525  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
526  {
527  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
528  .WithMany("ChatSettings")
529  .HasForeignKey("InstanceId")
530  .OnDelete(DeleteBehavior.Cascade);
531  });
532 
533  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
534  {
535  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
536  .WithMany("Channels")
537  .HasForeignKey("ChatSettingsId")
538  .OnDelete(DeleteBehavior.Cascade);
539  });
540 
541  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
542  {
543  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
544  .WithMany()
545  .HasForeignKey("JobId");
546 
547  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
548  .WithMany("CompileJobs")
549  .HasForeignKey("RevisionInformationId")
550  .OnDelete(DeleteBehavior.Cascade);
551  });
552 
553  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
554  {
555  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
556  .WithOne("DreamDaemonSettings")
557  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
558  .OnDelete(DeleteBehavior.Cascade);
559  });
560 
561  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
562  {
563  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
564  .WithOne("DreamMakerSettings")
565  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
566  .OnDelete(DeleteBehavior.Cascade);
567  });
568 
569  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
570  {
571  b.HasOne("Tgstation.Server.Host.Models.WatchdogReattachInformation", "WatchdogReattachInformation")
572  .WithMany()
573  .HasForeignKey("WatchdogReattachInformationId");
574  });
575 
576  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
577  {
578  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
579  .WithMany("InstanceUsers")
580  .HasForeignKey("InstanceId")
581  .OnDelete(DeleteBehavior.Cascade);
582 
583  b.HasOne("Tgstation.Server.Host.Models.User")
584  .WithMany("InstanceUsers")
585  .HasForeignKey("UserId")
586  .OnDelete(DeleteBehavior.Cascade);
587  });
588 
589  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
590  {
591  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
592  .WithMany()
593  .HasForeignKey("CancelledById");
594 
595  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
596  .WithMany("Jobs")
597  .HasForeignKey("InstanceId")
598  .OnDelete(DeleteBehavior.Cascade);
599 
600  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
601  .WithMany()
602  .HasForeignKey("StartedById")
603  .OnDelete(DeleteBehavior.Cascade);
604  });
605 
606  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
607  {
608  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
609  .WithMany()
610  .HasForeignKey("CompileJobId");
611  });
612 
613  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
614  {
615  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
616  .WithOne("RepositorySettings")
617  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
618  .OnDelete(DeleteBehavior.Cascade);
619  });
620 
621  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
622  {
623  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
624  .WithMany("ActiveTestMerges")
625  .HasForeignKey("RevisionInformationId")
626  .OnDelete(DeleteBehavior.Cascade);
627 
628  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
629  .WithMany("RevisonInformations")
630  .HasForeignKey("TestMergeId")
631  .OnDelete(DeleteBehavior.Cascade);
632  });
633 
634  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
635  {
636  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
637  .WithMany("RevisionInformations")
638  .HasForeignKey("InstanceId")
639  .OnDelete(DeleteBehavior.Cascade);
640  });
641 
642  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
643  {
644  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
645  .WithMany("TestMerges")
646  .HasForeignKey("MergedById")
647  .OnDelete(DeleteBehavior.Restrict);
648 
649  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
650  .WithOne("PrimaryTestMerge")
651  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
652  .OnDelete(DeleteBehavior.SetNull);
653  });
654 
655  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
656  {
657  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
658  .WithMany("CreatedUsers")
659  .HasForeignKey("CreatedById");
660  });
661 
662  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
663  {
664  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
665  .WithMany()
666  .HasForeignKey("AlphaId");
667 
668  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
669  .WithMany()
670  .HasForeignKey("BravoId");
671  });
672 #pragma warning restore 612, 618
673  }
674  }
675 }
+Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Metadata;
6 using Microsoft.EntityFrameworkCore.Migrations;
7 using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 
9 namespace Tgstation.Server.Host.Models.Migrations
10 {
11  [DbContext(typeof(SqlServerDatabaseContext))]
12  [Migration("20180918021228_MSAddMinimumSecurity")]
13  partial class MSAddMinimumSecurity
14  {
19  protected override void BuildTargetModel(ModelBuilder modelBuilder)
20  {
21 #pragma warning disable 612, 618
22  modelBuilder
23  .HasAnnotation("ProductVersion", "2.1.3-rtm-32065")
24  .HasAnnotation("Relational:MaxIdentifierLength", 128)
25  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
26 
27  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
28  {
29  b.Property<long>("Id")
30  .ValueGeneratedOnAdd()
31  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
32 
33  b.Property<string>("ConnectionString")
34  .IsRequired();
35 
36  b.Property<bool?>("Enabled");
37 
38  b.Property<long>("InstanceId");
39 
40  b.Property<string>("Name")
41  .IsRequired();
42 
43  b.Property<int?>("Provider");
44 
45  b.HasKey("Id");
46 
47  b.HasIndex("InstanceId");
48 
49  b.HasIndex("Name")
50  .IsUnique();
51 
52  b.ToTable("ChatBots");
53  });
54 
55  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
56  {
57  b.Property<long>("Id")
58  .ValueGeneratedOnAdd()
59  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
60 
61  b.Property<long>("ChatSettingsId");
62 
63  b.Property<decimal?>("DiscordChannelId")
64  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
65 
66  b.Property<string>("IrcChannel");
67 
68  b.Property<bool?>("IsAdminChannel")
69  .IsRequired();
70 
71  b.Property<bool?>("IsUpdatesChannel")
72  .IsRequired();
73 
74  b.Property<bool?>("IsWatchdogChannel")
75  .IsRequired();
76 
77  b.Property<string>("Tag");
78 
79  b.HasKey("Id");
80 
81  b.HasIndex("ChatSettingsId", "DiscordChannelId")
82  .IsUnique()
83  .HasFilter("[DiscordChannelId] IS NOT NULL");
84 
85  b.HasIndex("ChatSettingsId", "IrcChannel")
86  .IsUnique()
87  .HasFilter("[IrcChannel] IS NOT NULL");
88 
89  b.ToTable("ChatChannels");
90  });
91 
92  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
93  {
94  b.Property<long>("Id")
95  .ValueGeneratedOnAdd()
96  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
97 
98  b.Property<string>("ByondVersion")
99  .IsRequired();
100 
101  b.Property<Guid?>("DirectoryName");
102 
103  b.Property<string>("DmeName");
104 
105  b.Property<long?>("JobId");
106 
107  b.Property<int>("MinimumSecurityLevel");
108 
109  b.Property<string>("Output");
110 
111  b.Property<long>("RevisionInformationId");
112 
113  b.HasKey("Id");
114 
115  b.HasIndex("DirectoryName");
116 
117  b.HasIndex("JobId");
118 
119  b.HasIndex("RevisionInformationId");
120 
121  b.ToTable("CompileJobs");
122  });
123 
124  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
125  {
126  b.Property<long>("Id")
127  .ValueGeneratedOnAdd()
128  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
129 
130  b.Property<string>("AccessToken");
131 
132  b.Property<bool?>("AllowWebClient")
133  .IsRequired();
134 
135  b.Property<bool?>("AutoStart")
136  .IsRequired();
137 
138  b.Property<long>("InstanceId");
139 
140  b.Property<int>("PrimaryPort");
141 
142  b.Property<int?>("ProcessId");
143 
144  b.Property<int>("SecondaryPort");
145 
146  b.Property<int>("SecurityLevel");
147 
148  b.Property<bool?>("SoftRestart")
149  .IsRequired();
150 
151  b.Property<bool?>("SoftShutdown")
152  .IsRequired();
153 
154  b.Property<long>("StartupTimeout");
155 
156  b.HasKey("Id");
157 
158  b.HasIndex("InstanceId")
159  .IsUnique();
160 
161  b.ToTable("DreamDaemonSettings");
162  });
163 
164  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
165  {
166  b.Property<long>("Id")
167  .ValueGeneratedOnAdd()
168  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
169 
170  b.Property<int>("ApiValidationPort");
171 
172  b.Property<int>("ApiValidationSecurityLevel");
173 
174  b.Property<long>("InstanceId");
175 
176  b.Property<string>("ProjectName");
177 
178  b.HasKey("Id");
179 
180  b.HasIndex("InstanceId")
181  .IsUnique();
182 
183  b.ToTable("DreamMakerSettings");
184  });
185 
186  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
187  {
188  b.Property<long>("Id")
189  .ValueGeneratedOnAdd()
190  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
191 
192  b.Property<long>("AutoUpdateInterval");
193 
194  b.Property<int>("ConfigurationType");
195 
196  b.Property<string>("Name")
197  .IsRequired();
198 
199  b.Property<bool?>("Online")
200  .IsRequired();
201 
202  b.Property<string>("Path")
203  .IsRequired();
204 
205  b.Property<long?>("WatchdogReattachInformationId");
206 
207  b.HasKey("Id");
208 
209  b.HasIndex("Path")
210  .IsUnique();
211 
212  b.HasIndex("WatchdogReattachInformationId");
213 
214  b.ToTable("Instances");
215  });
216 
217  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
218  {
219  b.Property<long>("Id")
220  .ValueGeneratedOnAdd()
221  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
222 
223  b.Property<decimal>("ByondRights")
224  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
225 
226  b.Property<decimal>("ChatBotRights")
227  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
228 
229  b.Property<decimal>("ConfigurationRights")
230  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
231 
232  b.Property<decimal>("DreamDaemonRights")
233  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
234 
235  b.Property<decimal>("DreamMakerRights")
236  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
237 
238  b.Property<long>("InstanceId");
239 
240  b.Property<decimal>("InstanceUserRights")
241  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
242 
243  b.Property<decimal>("RepositoryRights")
244  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
245 
246  b.Property<long?>("UserId")
247  .IsRequired();
248 
249  b.HasKey("Id");
250 
251  b.HasIndex("InstanceId");
252 
253  b.HasIndex("UserId", "InstanceId")
254  .IsUnique();
255 
256  b.ToTable("InstanceUsers");
257  });
258 
259  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
260  {
261  b.Property<long>("Id")
262  .ValueGeneratedOnAdd()
263  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
264 
265  b.Property<decimal?>("CancelRight")
266  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
267 
268  b.Property<decimal?>("CancelRightsType")
269  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
270 
271  b.Property<bool?>("Cancelled")
272  .IsRequired();
273 
274  b.Property<long?>("CancelledById");
275 
276  b.Property<string>("Description")
277  .IsRequired();
278 
279  b.Property<string>("ExceptionDetails");
280 
281  b.Property<long>("InstanceId");
282 
283  b.Property<DateTimeOffset?>("StartedAt")
284  .IsRequired();
285 
286  b.Property<long>("StartedById");
287 
288  b.Property<DateTimeOffset?>("StoppedAt");
289 
290  b.HasKey("Id");
291 
292  b.HasIndex("CancelledById");
293 
294  b.HasIndex("InstanceId");
295 
296  b.HasIndex("StartedById");
297 
298  b.ToTable("Jobs");
299  });
300 
301  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
302  {
303  b.Property<long>("Id")
304  .ValueGeneratedOnAdd()
305  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
306 
307  b.Property<string>("AccessIdentifier")
308  .IsRequired();
309 
310  b.Property<string>("ChatChannelsJson")
311  .IsRequired();
312 
313  b.Property<string>("ChatCommandsJson")
314  .IsRequired();
315 
316  b.Property<long?>("CompileJobId");
317 
318  b.Property<bool>("IsPrimary");
319 
320  b.Property<int>("Port");
321 
322  b.Property<int>("ProcessId");
323 
324  b.Property<int>("RebootState");
325 
326  b.Property<string>("ServerCommandsJson")
327  .IsRequired();
328 
329  b.HasKey("Id");
330 
331  b.HasIndex("CompileJobId");
332 
333  b.ToTable("ReattachInformations");
334  });
335 
336  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
337  {
338  b.Property<long>("Id")
339  .ValueGeneratedOnAdd()
340  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
341 
342  b.Property<string>("AccessToken");
343 
344  b.Property<string>("AccessUser");
345 
346  b.Property<bool?>("AutoUpdatesKeepTestMerges")
347  .IsRequired();
348 
349  b.Property<bool?>("AutoUpdatesSynchronize")
350  .IsRequired();
351 
352  b.Property<string>("CommitterEmail")
353  .IsRequired();
354 
355  b.Property<string>("CommitterName")
356  .IsRequired();
357 
358  b.Property<long>("InstanceId");
359 
360  b.Property<bool?>("PushTestMergeCommits")
361  .IsRequired();
362 
363  b.Property<bool?>("ShowTestMergeCommitters")
364  .IsRequired();
365 
366  b.HasKey("Id");
367 
368  b.HasIndex("InstanceId")
369  .IsUnique();
370 
371  b.ToTable("RepositorySettings");
372  });
373 
374  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
375  {
376  b.Property<long>("Id")
377  .ValueGeneratedOnAdd()
378  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
379 
380  b.Property<long>("RevisionInformationId");
381 
382  b.Property<long>("TestMergeId");
383 
384  b.HasKey("Id");
385 
386  b.HasIndex("RevisionInformationId");
387 
388  b.HasIndex("TestMergeId");
389 
390  b.ToTable("RevInfoTestMerges");
391  });
392 
393  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
394  {
395  b.Property<long>("Id")
396  .ValueGeneratedOnAdd()
397  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
398 
399  b.Property<string>("CommitSha")
400  .IsRequired()
401  .HasMaxLength(40);
402 
403  b.Property<long>("InstanceId");
404 
405  b.Property<string>("OriginCommitSha")
406  .IsRequired()
407  .HasMaxLength(40);
408 
409  b.HasKey("Id");
410 
411  b.HasIndex("CommitSha")
412  .IsUnique();
413 
414  b.HasIndex("InstanceId");
415 
416  b.ToTable("RevisionInformations");
417  });
418 
419  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
420  {
421  b.Property<long>("Id")
422  .ValueGeneratedOnAdd()
423  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
424 
425  b.Property<string>("Author")
426  .IsRequired();
427 
428  b.Property<string>("BodyAtMerge")
429  .IsRequired();
430 
431  b.Property<string>("Comment");
432 
433  b.Property<DateTimeOffset>("MergedAt");
434 
435  b.Property<long>("MergedById");
436 
437  b.Property<int?>("Number")
438  .IsRequired();
439 
440  b.Property<long?>("PrimaryRevisionInformationId");
441 
442  b.Property<string>("PullRequestRevision")
443  .IsRequired();
444 
445  b.Property<string>("TitleAtMerge")
446  .IsRequired();
447 
448  b.Property<string>("Url")
449  .IsRequired();
450 
451  b.HasKey("Id");
452 
453  b.HasIndex("MergedById");
454 
455  b.HasIndex("PrimaryRevisionInformationId")
456  .IsUnique()
457  .HasFilter("[PrimaryRevisionInformationId] IS NOT NULL");
458 
459  b.ToTable("TestMerges");
460  });
461 
462  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
463  {
464  b.Property<long>("Id")
465  .ValueGeneratedOnAdd()
466  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
467 
468  b.Property<decimal>("AdministrationRights")
469  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
470 
471  b.Property<string>("CanonicalName")
472  .IsRequired();
473 
474  b.Property<DateTimeOffset?>("CreatedAt")
475  .IsRequired();
476 
477  b.Property<long?>("CreatedById");
478 
479  b.Property<bool?>("Enabled")
480  .IsRequired();
481 
482  b.Property<decimal>("InstanceManagerRights")
483  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
484 
485  b.Property<DateTimeOffset?>("LastPasswordUpdate");
486 
487  b.Property<string>("Name")
488  .IsRequired();
489 
490  b.Property<string>("PasswordHash");
491 
492  b.Property<string>("SystemIdentifier");
493 
494  b.HasKey("Id");
495 
496  b.HasIndex("CanonicalName")
497  .IsUnique();
498 
499  b.HasIndex("CreatedById");
500 
501  b.ToTable("Users");
502  });
503 
504  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
505  {
506  b.Property<long>("Id")
507  .ValueGeneratedOnAdd()
508  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
509 
510  b.Property<long?>("AlphaId");
511 
512  b.Property<bool>("AlphaIsActive");
513 
514  b.Property<long?>("BravoId");
515 
516  b.HasKey("Id");
517 
518  b.HasIndex("AlphaId");
519 
520  b.HasIndex("BravoId");
521 
522  b.ToTable("WatchdogReattachInformations");
523  });
524 
525  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
526  {
527  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
528  .WithMany("ChatSettings")
529  .HasForeignKey("InstanceId")
530  .OnDelete(DeleteBehavior.Cascade);
531  });
532 
533  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
534  {
535  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
536  .WithMany("Channels")
537  .HasForeignKey("ChatSettingsId")
538  .OnDelete(DeleteBehavior.Cascade);
539  });
540 
541  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
542  {
543  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
544  .WithMany()
545  .HasForeignKey("JobId");
546 
547  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
548  .WithMany("CompileJobs")
549  .HasForeignKey("RevisionInformationId")
550  .OnDelete(DeleteBehavior.Cascade);
551  });
552 
553  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
554  {
555  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
556  .WithOne("DreamDaemonSettings")
557  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
558  .OnDelete(DeleteBehavior.Cascade);
559  });
560 
561  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
562  {
563  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
564  .WithOne("DreamMakerSettings")
565  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
566  .OnDelete(DeleteBehavior.Cascade);
567  });
568 
569  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
570  {
571  b.HasOne("Tgstation.Server.Host.Models.WatchdogReattachInformation", "WatchdogReattachInformation")
572  .WithMany()
573  .HasForeignKey("WatchdogReattachInformationId");
574  });
575 
576  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
577  {
578  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
579  .WithMany("InstanceUsers")
580  .HasForeignKey("InstanceId")
581  .OnDelete(DeleteBehavior.Cascade);
582 
583  b.HasOne("Tgstation.Server.Host.Models.User")
584  .WithMany("InstanceUsers")
585  .HasForeignKey("UserId")
586  .OnDelete(DeleteBehavior.Cascade);
587  });
588 
589  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
590  {
591  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
592  .WithMany()
593  .HasForeignKey("CancelledById");
594 
595  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
596  .WithMany("Jobs")
597  .HasForeignKey("InstanceId")
598  .OnDelete(DeleteBehavior.Cascade);
599 
600  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
601  .WithMany()
602  .HasForeignKey("StartedById")
603  .OnDelete(DeleteBehavior.Cascade);
604  });
605 
606  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
607  {
608  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
609  .WithMany()
610  .HasForeignKey("CompileJobId");
611  });
612 
613  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
614  {
615  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
616  .WithOne("RepositorySettings")
617  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
618  .OnDelete(DeleteBehavior.Cascade);
619  });
620 
621  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
622  {
623  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
624  .WithMany("ActiveTestMerges")
625  .HasForeignKey("RevisionInformationId")
626  .OnDelete(DeleteBehavior.Cascade);
627 
628  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
629  .WithMany("RevisonInformations")
630  .HasForeignKey("TestMergeId")
631  .OnDelete(DeleteBehavior.Cascade);
632  });
633 
634  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
635  {
636  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
637  .WithMany("RevisionInformations")
638  .HasForeignKey("InstanceId")
639  .OnDelete(DeleteBehavior.Cascade);
640  });
641 
642  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
643  {
644  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
645  .WithMany("TestMerges")
646  .HasForeignKey("MergedById")
647  .OnDelete(DeleteBehavior.Restrict);
648 
649  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
650  .WithOne("PrimaryTestMerge")
651  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
652  .OnDelete(DeleteBehavior.SetNull);
653  });
654 
655  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
656  {
657  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
658  .WithMany("CreatedUsers")
659  .HasForeignKey("CreatedById");
660  });
661 
662  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
663  {
664  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
665  .WithMany()
666  .HasForeignKey("AlphaId");
667 
668  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
669  .WithMany()
670  .HasForeignKey("BravoId");
671  });
672 #pragma warning restore 612, 618
673  }
674  }
675 }
+ -
override void BuildTargetModel(ModelBuilder modelBuilder)
Builds the target model
diff --git a/20180918021228___m_s_add_minimum_security_8cs_source.html b/20180918021228___m_s_add_minimum_security_8cs_source.html index af855e33a8..5575f75a98 100644 --- a/20180918021228___m_s_add_minimum_security_8cs_source.html +++ b/20180918021228___m_s_add_minimum_security_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
20180918021228_MSAddMinimumSecurity.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore.Migrations;
3 
4 namespace Tgstation.Server.Host.Models.Migrations
5 {
9  public partial class MSAddMinimumSecurity : Migration
10  {
15  protected override void Up(MigrationBuilder migrationBuilder)
16  {
17  migrationBuilder.AddColumn<int>(
18  name: "ApiValidationSecurityLevel",
19  table: "DreamMakerSettings",
20  nullable: false,
21  defaultValue: (int)DreamDaemonSecurity.Safe);
22 
23  migrationBuilder.AddColumn<int>(
24  name: "MinimumSecurityLevel",
25  table: "CompileJobs",
26  nullable: false,
27  defaultValue: (int)DreamDaemonSecurity.Safe);
28  }
29 
34  protected override void Down(MigrationBuilder migrationBuilder)
35  {
36  migrationBuilder.DropColumn(
37  name: "ApiValidationSecurityLevel",
38  table: "DreamMakerSettings");
39 
40  migrationBuilder.DropColumn(
41  name: "MinimumSecurityLevel",
42  table: "CompileJobs");
43  }
44  }
45 }
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore.Migrations;
3 
4 namespace Tgstation.Server.Host.Models.Migrations
5 {
9  public partial class MSAddMinimumSecurity : Migration
10  {
15  protected override void Up(MigrationBuilder migrationBuilder)
16  {
17  migrationBuilder.AddColumn<int>(
18  name: "ApiValidationSecurityLevel",
19  table: "DreamMakerSettings",
20  nullable: false,
21  defaultValue: (int)DreamDaemonSecurity.Safe);
22 
23  migrationBuilder.AddColumn<int>(
24  name: "MinimumSecurityLevel",
25  table: "CompileJobs",
26  nullable: false,
27  defaultValue: (int)DreamDaemonSecurity.Safe);
28  }
29 
34  protected override void Down(MigrationBuilder migrationBuilder)
35  {
36  migrationBuilder.DropColumn(
37  name: "ApiValidationSecurityLevel",
38  table: "DreamMakerSettings");
39 
40  migrationBuilder.DropColumn(
41  name: "MinimumSecurityLevel",
42  table: "CompileJobs");
43  }
44  }
45 }
+
override void Down(MigrationBuilder migrationBuilder)
Unapplies the migration
Add the Api.Models.Internal.CompileJob.MinimumSecurityLevel and DreamMaker.ApiValidationSecurityLevel...
-
override void Up(MigrationBuilder migrationBuilder)
Applies the migration
DreamDaemonSecurity
DreamDaemon&#39;s security level
diff --git a/20180918204520___m_y_nullable_and_foreign_key_cleanup_8_designer_8cs_source.html b/20180918204520___m_y_nullable_and_foreign_key_cleanup_8_designer_8cs_source.html index fe9dd4efad..b1decfd7fa 100644 --- a/20180918204520___m_y_nullable_and_foreign_key_cleanup_8_designer_8cs_source.html +++ b/20180918204520___m_y_nullable_and_foreign_key_cleanup_8_designer_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
20180918204520_MYNullableAndForeignKeyCleanup.Designer.cs
-Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Migrations;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
9  [DbContext(typeof(MySqlDatabaseContext))]
10  [Migration("20180918204520_MYNullableAndForeignKeyCleanup")]
11  partial class MYNullableAndForeignKeyCleanup
12  {
17  protected override void BuildTargetModel(ModelBuilder modelBuilder)
18  {
19 #pragma warning disable 612, 618
20  modelBuilder
21  .HasAnnotation("ProductVersion", "2.1.3-rtm-32065")
22  .HasAnnotation("Relational:MaxIdentifierLength", 64);
23 
24  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
25  {
26  b.Property<long>("Id")
27  .ValueGeneratedOnAdd();
28 
29  b.Property<string>("ConnectionString")
30  .IsRequired();
31 
32  b.Property<bool?>("Enabled");
33 
34  b.Property<long>("InstanceId");
35 
36  b.Property<string>("Name")
37  .IsRequired();
38 
39  b.Property<int?>("Provider");
40 
41  b.HasKey("Id");
42 
43  b.HasIndex("InstanceId");
44 
45  b.HasIndex("Name")
46  .IsUnique();
47 
48  b.ToTable("ChatBots");
49  });
50 
51  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
52  {
53  b.Property<long>("Id")
54  .ValueGeneratedOnAdd();
55 
56  b.Property<long>("ChatSettingsId");
57 
58  b.Property<ulong?>("DiscordChannelId");
59 
60  b.Property<string>("IrcChannel");
61 
62  b.Property<bool?>("IsAdminChannel")
63  .IsRequired();
64 
65  b.Property<bool?>("IsUpdatesChannel")
66  .IsRequired();
67 
68  b.Property<bool?>("IsWatchdogChannel")
69  .IsRequired();
70 
71  b.Property<string>("Tag");
72 
73  b.HasKey("Id");
74 
75  b.HasIndex("ChatSettingsId", "DiscordChannelId")
76  .IsUnique();
77 
78  b.HasIndex("ChatSettingsId", "IrcChannel")
79  .IsUnique();
80 
81  b.ToTable("ChatChannels");
82  });
83 
84  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
85  {
86  b.Property<long>("Id")
87  .ValueGeneratedOnAdd();
88 
89  b.Property<string>("ByondVersion")
90  .IsRequired();
91 
92  b.Property<Guid?>("DirectoryName")
93  .IsRequired();
94 
95  b.Property<string>("DmeName")
96  .IsRequired();
97 
98  b.Property<long>("JobId");
99 
100  b.Property<int>("MinimumSecurityLevel");
101 
102  b.Property<string>("Output")
103  .IsRequired();
104 
105  b.Property<long>("RevisionInformationId");
106 
107  b.HasKey("Id");
108 
109  b.HasIndex("DirectoryName");
110 
111  b.HasIndex("JobId")
112  .IsUnique();
113 
114  b.HasIndex("RevisionInformationId");
115 
116  b.ToTable("CompileJobs");
117  });
118 
119  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
120  {
121  b.Property<long>("Id")
122  .ValueGeneratedOnAdd();
123 
124  b.Property<string>("AccessToken");
125 
126  b.Property<bool?>("AllowWebClient")
127  .IsRequired();
128 
129  b.Property<bool?>("AutoStart")
130  .IsRequired();
131 
132  b.Property<long>("InstanceId");
133 
134  b.Property<ushort?>("PrimaryPort")
135  .IsRequired();
136 
137  b.Property<int?>("ProcessId");
138 
139  b.Property<ushort?>("SecondaryPort")
140  .IsRequired();
141 
142  b.Property<int>("SecurityLevel");
143 
144  b.Property<bool?>("SoftRestart")
145  .IsRequired();
146 
147  b.Property<bool?>("SoftShutdown")
148  .IsRequired();
149 
150  b.Property<uint?>("StartupTimeout")
151  .IsRequired();
152 
153  b.HasKey("Id");
154 
155  b.HasIndex("InstanceId")
156  .IsUnique();
157 
158  b.ToTable("DreamDaemonSettings");
159  });
160 
161  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
162  {
163  b.Property<long>("Id")
164  .ValueGeneratedOnAdd();
165 
166  b.Property<ushort?>("ApiValidationPort")
167  .IsRequired();
168 
169  b.Property<int>("ApiValidationSecurityLevel");
170 
171  b.Property<long>("InstanceId");
172 
173  b.Property<string>("ProjectName");
174 
175  b.HasKey("Id");
176 
177  b.HasIndex("InstanceId")
178  .IsUnique();
179 
180  b.ToTable("DreamMakerSettings");
181  });
182 
183  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
184  {
185  b.Property<long>("Id")
186  .ValueGeneratedOnAdd();
187 
188  b.Property<uint?>("AutoUpdateInterval")
189  .IsRequired();
190 
191  b.Property<int>("ConfigurationType");
192 
193  b.Property<string>("Name")
194  .IsRequired();
195 
196  b.Property<bool?>("Online")
197  .IsRequired();
198 
199  b.Property<string>("Path")
200  .IsRequired();
201 
202  b.HasKey("Id");
203 
204  b.HasIndex("Path")
205  .IsUnique();
206 
207  b.ToTable("Instances");
208  });
209 
210  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
211  {
212  b.Property<long>("Id")
213  .ValueGeneratedOnAdd();
214 
215  b.Property<ulong>("ByondRights");
216 
217  b.Property<ulong>("ChatBotRights");
218 
219  b.Property<ulong>("ConfigurationRights");
220 
221  b.Property<ulong>("DreamDaemonRights");
222 
223  b.Property<ulong>("DreamMakerRights");
224 
225  b.Property<long>("InstanceId");
226 
227  b.Property<ulong>("InstanceUserRights");
228 
229  b.Property<ulong>("RepositoryRights");
230 
231  b.Property<long?>("UserId")
232  .IsRequired();
233 
234  b.HasKey("Id");
235 
236  b.HasIndex("InstanceId");
237 
238  b.HasIndex("UserId", "InstanceId")
239  .IsUnique();
240 
241  b.ToTable("InstanceUsers");
242  });
243 
244  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
245  {
246  b.Property<long>("Id")
247  .ValueGeneratedOnAdd();
248 
249  b.Property<ulong?>("CancelRight");
250 
251  b.Property<ulong?>("CancelRightsType");
252 
253  b.Property<bool?>("Cancelled")
254  .IsRequired();
255 
256  b.Property<long?>("CancelledById");
257 
258  b.Property<string>("Description")
259  .IsRequired();
260 
261  b.Property<string>("ExceptionDetails");
262 
263  b.Property<long>("InstanceId");
264 
265  b.Property<DateTimeOffset?>("StartedAt")
266  .IsRequired();
267 
268  b.Property<long>("StartedById");
269 
270  b.Property<DateTimeOffset?>("StoppedAt");
271 
272  b.HasKey("Id");
273 
274  b.HasIndex("CancelledById");
275 
276  b.HasIndex("InstanceId");
277 
278  b.HasIndex("StartedById");
279 
280  b.ToTable("Jobs");
281  });
282 
283  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
284  {
285  b.Property<long>("Id")
286  .ValueGeneratedOnAdd();
287 
288  b.Property<string>("AccessIdentifier")
289  .IsRequired();
290 
291  b.Property<string>("ChatChannelsJson")
292  .IsRequired();
293 
294  b.Property<string>("ChatCommandsJson")
295  .IsRequired();
296 
297  b.Property<long?>("CompileJobId");
298 
299  b.Property<bool>("IsPrimary");
300 
301  b.Property<ushort>("Port");
302 
303  b.Property<int>("ProcessId");
304 
305  b.Property<int>("RebootState");
306 
307  b.Property<string>("ServerCommandsJson")
308  .IsRequired();
309 
310  b.HasKey("Id");
311 
312  b.HasIndex("CompileJobId");
313 
314  b.ToTable("ReattachInformations");
315  });
316 
317  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
318  {
319  b.Property<long>("Id")
320  .ValueGeneratedOnAdd();
321 
322  b.Property<string>("AccessToken");
323 
324  b.Property<string>("AccessUser");
325 
326  b.Property<bool?>("AutoUpdatesKeepTestMerges")
327  .IsRequired();
328 
329  b.Property<bool?>("AutoUpdatesSynchronize")
330  .IsRequired();
331 
332  b.Property<string>("CommitterEmail")
333  .IsRequired();
334 
335  b.Property<string>("CommitterName")
336  .IsRequired();
337 
338  b.Property<long>("InstanceId");
339 
340  b.Property<bool?>("PushTestMergeCommits")
341  .IsRequired();
342 
343  b.Property<bool?>("ShowTestMergeCommitters")
344  .IsRequired();
345 
346  b.HasKey("Id");
347 
348  b.HasIndex("InstanceId")
349  .IsUnique();
350 
351  b.ToTable("RepositorySettings");
352  });
353 
354  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
355  {
356  b.Property<long>("Id")
357  .ValueGeneratedOnAdd();
358 
359  b.Property<long>("RevisionInformationId");
360 
361  b.Property<long>("TestMergeId");
362 
363  b.HasKey("Id");
364 
365  b.HasIndex("RevisionInformationId");
366 
367  b.HasIndex("TestMergeId");
368 
369  b.ToTable("RevInfoTestMerges");
370  });
371 
372  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
373  {
374  b.Property<long>("Id")
375  .ValueGeneratedOnAdd();
376 
377  b.Property<string>("CommitSha")
378  .IsRequired()
379  .HasMaxLength(40);
380 
381  b.Property<long>("InstanceId");
382 
383  b.Property<string>("OriginCommitSha")
384  .IsRequired()
385  .HasMaxLength(40);
386 
387  b.HasKey("Id");
388 
389  b.HasIndex("CommitSha")
390  .IsUnique();
391 
392  b.HasIndex("InstanceId");
393 
394  b.ToTable("RevisionInformations");
395  });
396 
397  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
398  {
399  b.Property<long>("Id")
400  .ValueGeneratedOnAdd();
401 
402  b.Property<string>("Author")
403  .IsRequired();
404 
405  b.Property<string>("BodyAtMerge")
406  .IsRequired();
407 
408  b.Property<string>("Comment");
409 
410  b.Property<DateTimeOffset>("MergedAt");
411 
412  b.Property<long>("MergedById");
413 
414  b.Property<int?>("Number")
415  .IsRequired();
416 
417  b.Property<long?>("PrimaryRevisionInformationId")
418  .IsRequired();
419 
420  b.Property<string>("PullRequestRevision")
421  .IsRequired();
422 
423  b.Property<string>("TitleAtMerge")
424  .IsRequired();
425 
426  b.Property<string>("Url")
427  .IsRequired();
428 
429  b.HasKey("Id");
430 
431  b.HasIndex("MergedById");
432 
433  b.HasIndex("PrimaryRevisionInformationId")
434  .IsUnique();
435 
436  b.ToTable("TestMerges");
437  });
438 
439  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
440  {
441  b.Property<long>("Id")
442  .ValueGeneratedOnAdd();
443 
444  b.Property<ulong>("AdministrationRights");
445 
446  b.Property<string>("CanonicalName")
447  .IsRequired();
448 
449  b.Property<DateTimeOffset?>("CreatedAt")
450  .IsRequired();
451 
452  b.Property<long?>("CreatedById");
453 
454  b.Property<bool?>("Enabled")
455  .IsRequired();
456 
457  b.Property<ulong>("InstanceManagerRights");
458 
459  b.Property<DateTimeOffset?>("LastPasswordUpdate");
460 
461  b.Property<string>("Name")
462  .IsRequired();
463 
464  b.Property<string>("PasswordHash");
465 
466  b.Property<string>("SystemIdentifier");
467 
468  b.HasKey("Id");
469 
470  b.HasIndex("CanonicalName")
471  .IsUnique();
472 
473  b.HasIndex("CreatedById");
474 
475  b.ToTable("Users");
476  });
477 
478  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
479  {
480  b.Property<long>("Id")
481  .ValueGeneratedOnAdd();
482 
483  b.Property<long?>("AlphaId");
484 
485  b.Property<bool>("AlphaIsActive");
486 
487  b.Property<long?>("BravoId");
488 
489  b.Property<long>("InstanceId");
490 
491  b.HasKey("Id");
492 
493  b.HasIndex("AlphaId");
494 
495  b.HasIndex("BravoId");
496 
497  b.HasIndex("InstanceId")
498  .IsUnique();
499 
500  b.ToTable("WatchdogReattachInformations");
501  });
502 
503  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
504  {
505  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
506  .WithMany("ChatSettings")
507  .HasForeignKey("InstanceId")
508  .OnDelete(DeleteBehavior.Cascade);
509  });
510 
511  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
512  {
513  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
514  .WithMany("Channels")
515  .HasForeignKey("ChatSettingsId")
516  .OnDelete(DeleteBehavior.Cascade);
517  });
518 
519  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
520  {
521  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
522  .WithOne()
523  .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
524  .OnDelete(DeleteBehavior.Restrict);
525 
526  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
527  .WithMany("CompileJobs")
528  .HasForeignKey("RevisionInformationId")
529  .OnDelete(DeleteBehavior.Cascade);
530  });
531 
532  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
533  {
534  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
535  .WithOne("DreamDaemonSettings")
536  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
537  .OnDelete(DeleteBehavior.Cascade);
538  });
539 
540  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
541  {
542  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
543  .WithOne("DreamMakerSettings")
544  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
545  .OnDelete(DeleteBehavior.Cascade);
546  });
547 
548  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
549  {
550  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
551  .WithMany("InstanceUsers")
552  .HasForeignKey("InstanceId")
553  .OnDelete(DeleteBehavior.Cascade);
554 
555  b.HasOne("Tgstation.Server.Host.Models.User")
556  .WithMany("InstanceUsers")
557  .HasForeignKey("UserId")
558  .OnDelete(DeleteBehavior.Cascade);
559  });
560 
561  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
562  {
563  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
564  .WithMany()
565  .HasForeignKey("CancelledById");
566 
567  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
568  .WithMany("Jobs")
569  .HasForeignKey("InstanceId")
570  .OnDelete(DeleteBehavior.Cascade);
571 
572  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
573  .WithMany()
574  .HasForeignKey("StartedById")
575  .OnDelete(DeleteBehavior.Cascade);
576  });
577 
578  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
579  {
580  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
581  .WithMany()
582  .HasForeignKey("CompileJobId");
583  });
584 
585  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
586  {
587  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
588  .WithOne("RepositorySettings")
589  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
590  .OnDelete(DeleteBehavior.Cascade);
591  });
592 
593  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
594  {
595  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
596  .WithMany("ActiveTestMerges")
597  .HasForeignKey("RevisionInformationId")
598  .OnDelete(DeleteBehavior.Cascade);
599 
600  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
601  .WithMany("RevisonInformations")
602  .HasForeignKey("TestMergeId")
603  .OnDelete(DeleteBehavior.Cascade);
604  });
605 
606  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
607  {
608  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
609  .WithMany("RevisionInformations")
610  .HasForeignKey("InstanceId")
611  .OnDelete(DeleteBehavior.Cascade);
612  });
613 
614  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
615  {
616  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
617  .WithMany("TestMerges")
618  .HasForeignKey("MergedById")
619  .OnDelete(DeleteBehavior.Restrict);
620 
621  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
622  .WithOne("PrimaryTestMerge")
623  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
624  .OnDelete(DeleteBehavior.Restrict);
625  });
626 
627  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
628  {
629  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
630  .WithMany("CreatedUsers")
631  .HasForeignKey("CreatedById");
632  });
633 
634  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
635  {
636  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
637  .WithMany()
638  .HasForeignKey("AlphaId");
639 
640  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
641  .WithMany()
642  .HasForeignKey("BravoId");
643 
644  b.HasOne("Tgstation.Server.Host.Models.Instance")
645  .WithOne("WatchdogReattachInformation")
646  .HasForeignKey("Tgstation.Server.Host.Models.WatchdogReattachInformation", "InstanceId")
647  .OnDelete(DeleteBehavior.Cascade);
648  });
649 #pragma warning restore 612, 618
650  }
651  }
652 }
+Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Migrations;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
9  [DbContext(typeof(MySqlDatabaseContext))]
10  [Migration("20180918204520_MYNullableAndForeignKeyCleanup")]
11  partial class MYNullableAndForeignKeyCleanup
12  {
17  protected override void BuildTargetModel(ModelBuilder modelBuilder)
18  {
19 #pragma warning disable 612, 618
20  modelBuilder
21  .HasAnnotation("ProductVersion", "2.1.3-rtm-32065")
22  .HasAnnotation("Relational:MaxIdentifierLength", 64);
23 
24  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
25  {
26  b.Property<long>("Id")
27  .ValueGeneratedOnAdd();
28 
29  b.Property<string>("ConnectionString")
30  .IsRequired();
31 
32  b.Property<bool?>("Enabled");
33 
34  b.Property<long>("InstanceId");
35 
36  b.Property<string>("Name")
37  .IsRequired();
38 
39  b.Property<int?>("Provider");
40 
41  b.HasKey("Id");
42 
43  b.HasIndex("InstanceId");
44 
45  b.HasIndex("Name")
46  .IsUnique();
47 
48  b.ToTable("ChatBots");
49  });
50 
51  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
52  {
53  b.Property<long>("Id")
54  .ValueGeneratedOnAdd();
55 
56  b.Property<long>("ChatSettingsId");
57 
58  b.Property<ulong?>("DiscordChannelId");
59 
60  b.Property<string>("IrcChannel");
61 
62  b.Property<bool?>("IsAdminChannel")
63  .IsRequired();
64 
65  b.Property<bool?>("IsUpdatesChannel")
66  .IsRequired();
67 
68  b.Property<bool?>("IsWatchdogChannel")
69  .IsRequired();
70 
71  b.Property<string>("Tag");
72 
73  b.HasKey("Id");
74 
75  b.HasIndex("ChatSettingsId", "DiscordChannelId")
76  .IsUnique();
77 
78  b.HasIndex("ChatSettingsId", "IrcChannel")
79  .IsUnique();
80 
81  b.ToTable("ChatChannels");
82  });
83 
84  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
85  {
86  b.Property<long>("Id")
87  .ValueGeneratedOnAdd();
88 
89  b.Property<string>("ByondVersion")
90  .IsRequired();
91 
92  b.Property<Guid?>("DirectoryName")
93  .IsRequired();
94 
95  b.Property<string>("DmeName")
96  .IsRequired();
97 
98  b.Property<long>("JobId");
99 
100  b.Property<int>("MinimumSecurityLevel");
101 
102  b.Property<string>("Output")
103  .IsRequired();
104 
105  b.Property<long>("RevisionInformationId");
106 
107  b.HasKey("Id");
108 
109  b.HasIndex("DirectoryName");
110 
111  b.HasIndex("JobId")
112  .IsUnique();
113 
114  b.HasIndex("RevisionInformationId");
115 
116  b.ToTable("CompileJobs");
117  });
118 
119  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
120  {
121  b.Property<long>("Id")
122  .ValueGeneratedOnAdd();
123 
124  b.Property<string>("AccessToken");
125 
126  b.Property<bool?>("AllowWebClient")
127  .IsRequired();
128 
129  b.Property<bool?>("AutoStart")
130  .IsRequired();
131 
132  b.Property<long>("InstanceId");
133 
134  b.Property<ushort?>("PrimaryPort")
135  .IsRequired();
136 
137  b.Property<int?>("ProcessId");
138 
139  b.Property<ushort?>("SecondaryPort")
140  .IsRequired();
141 
142  b.Property<int>("SecurityLevel");
143 
144  b.Property<bool?>("SoftRestart")
145  .IsRequired();
146 
147  b.Property<bool?>("SoftShutdown")
148  .IsRequired();
149 
150  b.Property<uint?>("StartupTimeout")
151  .IsRequired();
152 
153  b.HasKey("Id");
154 
155  b.HasIndex("InstanceId")
156  .IsUnique();
157 
158  b.ToTable("DreamDaemonSettings");
159  });
160 
161  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
162  {
163  b.Property<long>("Id")
164  .ValueGeneratedOnAdd();
165 
166  b.Property<ushort?>("ApiValidationPort")
167  .IsRequired();
168 
169  b.Property<int>("ApiValidationSecurityLevel");
170 
171  b.Property<long>("InstanceId");
172 
173  b.Property<string>("ProjectName");
174 
175  b.HasKey("Id");
176 
177  b.HasIndex("InstanceId")
178  .IsUnique();
179 
180  b.ToTable("DreamMakerSettings");
181  });
182 
183  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
184  {
185  b.Property<long>("Id")
186  .ValueGeneratedOnAdd();
187 
188  b.Property<uint?>("AutoUpdateInterval")
189  .IsRequired();
190 
191  b.Property<int>("ConfigurationType");
192 
193  b.Property<string>("Name")
194  .IsRequired();
195 
196  b.Property<bool?>("Online")
197  .IsRequired();
198 
199  b.Property<string>("Path")
200  .IsRequired();
201 
202  b.HasKey("Id");
203 
204  b.HasIndex("Path")
205  .IsUnique();
206 
207  b.ToTable("Instances");
208  });
209 
210  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
211  {
212  b.Property<long>("Id")
213  .ValueGeneratedOnAdd();
214 
215  b.Property<ulong>("ByondRights");
216 
217  b.Property<ulong>("ChatBotRights");
218 
219  b.Property<ulong>("ConfigurationRights");
220 
221  b.Property<ulong>("DreamDaemonRights");
222 
223  b.Property<ulong>("DreamMakerRights");
224 
225  b.Property<long>("InstanceId");
226 
227  b.Property<ulong>("InstanceUserRights");
228 
229  b.Property<ulong>("RepositoryRights");
230 
231  b.Property<long?>("UserId")
232  .IsRequired();
233 
234  b.HasKey("Id");
235 
236  b.HasIndex("InstanceId");
237 
238  b.HasIndex("UserId", "InstanceId")
239  .IsUnique();
240 
241  b.ToTable("InstanceUsers");
242  });
243 
244  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
245  {
246  b.Property<long>("Id")
247  .ValueGeneratedOnAdd();
248 
249  b.Property<ulong?>("CancelRight");
250 
251  b.Property<ulong?>("CancelRightsType");
252 
253  b.Property<bool?>("Cancelled")
254  .IsRequired();
255 
256  b.Property<long?>("CancelledById");
257 
258  b.Property<string>("Description")
259  .IsRequired();
260 
261  b.Property<string>("ExceptionDetails");
262 
263  b.Property<long>("InstanceId");
264 
265  b.Property<DateTimeOffset?>("StartedAt")
266  .IsRequired();
267 
268  b.Property<long>("StartedById");
269 
270  b.Property<DateTimeOffset?>("StoppedAt");
271 
272  b.HasKey("Id");
273 
274  b.HasIndex("CancelledById");
275 
276  b.HasIndex("InstanceId");
277 
278  b.HasIndex("StartedById");
279 
280  b.ToTable("Jobs");
281  });
282 
283  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
284  {
285  b.Property<long>("Id")
286  .ValueGeneratedOnAdd();
287 
288  b.Property<string>("AccessIdentifier")
289  .IsRequired();
290 
291  b.Property<string>("ChatChannelsJson")
292  .IsRequired();
293 
294  b.Property<string>("ChatCommandsJson")
295  .IsRequired();
296 
297  b.Property<long?>("CompileJobId");
298 
299  b.Property<bool>("IsPrimary");
300 
301  b.Property<ushort>("Port");
302 
303  b.Property<int>("ProcessId");
304 
305  b.Property<int>("RebootState");
306 
307  b.Property<string>("ServerCommandsJson")
308  .IsRequired();
309 
310  b.HasKey("Id");
311 
312  b.HasIndex("CompileJobId");
313 
314  b.ToTable("ReattachInformations");
315  });
316 
317  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
318  {
319  b.Property<long>("Id")
320  .ValueGeneratedOnAdd();
321 
322  b.Property<string>("AccessToken");
323 
324  b.Property<string>("AccessUser");
325 
326  b.Property<bool?>("AutoUpdatesKeepTestMerges")
327  .IsRequired();
328 
329  b.Property<bool?>("AutoUpdatesSynchronize")
330  .IsRequired();
331 
332  b.Property<string>("CommitterEmail")
333  .IsRequired();
334 
335  b.Property<string>("CommitterName")
336  .IsRequired();
337 
338  b.Property<long>("InstanceId");
339 
340  b.Property<bool?>("PushTestMergeCommits")
341  .IsRequired();
342 
343  b.Property<bool?>("ShowTestMergeCommitters")
344  .IsRequired();
345 
346  b.HasKey("Id");
347 
348  b.HasIndex("InstanceId")
349  .IsUnique();
350 
351  b.ToTable("RepositorySettings");
352  });
353 
354  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
355  {
356  b.Property<long>("Id")
357  .ValueGeneratedOnAdd();
358 
359  b.Property<long>("RevisionInformationId");
360 
361  b.Property<long>("TestMergeId");
362 
363  b.HasKey("Id");
364 
365  b.HasIndex("RevisionInformationId");
366 
367  b.HasIndex("TestMergeId");
368 
369  b.ToTable("RevInfoTestMerges");
370  });
371 
372  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
373  {
374  b.Property<long>("Id")
375  .ValueGeneratedOnAdd();
376 
377  b.Property<string>("CommitSha")
378  .IsRequired()
379  .HasMaxLength(40);
380 
381  b.Property<long>("InstanceId");
382 
383  b.Property<string>("OriginCommitSha")
384  .IsRequired()
385  .HasMaxLength(40);
386 
387  b.HasKey("Id");
388 
389  b.HasIndex("CommitSha")
390  .IsUnique();
391 
392  b.HasIndex("InstanceId");
393 
394  b.ToTable("RevisionInformations");
395  });
396 
397  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
398  {
399  b.Property<long>("Id")
400  .ValueGeneratedOnAdd();
401 
402  b.Property<string>("Author")
403  .IsRequired();
404 
405  b.Property<string>("BodyAtMerge")
406  .IsRequired();
407 
408  b.Property<string>("Comment");
409 
410  b.Property<DateTimeOffset>("MergedAt");
411 
412  b.Property<long>("MergedById");
413 
414  b.Property<int?>("Number")
415  .IsRequired();
416 
417  b.Property<long?>("PrimaryRevisionInformationId")
418  .IsRequired();
419 
420  b.Property<string>("PullRequestRevision")
421  .IsRequired();
422 
423  b.Property<string>("TitleAtMerge")
424  .IsRequired();
425 
426  b.Property<string>("Url")
427  .IsRequired();
428 
429  b.HasKey("Id");
430 
431  b.HasIndex("MergedById");
432 
433  b.HasIndex("PrimaryRevisionInformationId")
434  .IsUnique();
435 
436  b.ToTable("TestMerges");
437  });
438 
439  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
440  {
441  b.Property<long>("Id")
442  .ValueGeneratedOnAdd();
443 
444  b.Property<ulong>("AdministrationRights");
445 
446  b.Property<string>("CanonicalName")
447  .IsRequired();
448 
449  b.Property<DateTimeOffset?>("CreatedAt")
450  .IsRequired();
451 
452  b.Property<long?>("CreatedById");
453 
454  b.Property<bool?>("Enabled")
455  .IsRequired();
456 
457  b.Property<ulong>("InstanceManagerRights");
458 
459  b.Property<DateTimeOffset?>("LastPasswordUpdate");
460 
461  b.Property<string>("Name")
462  .IsRequired();
463 
464  b.Property<string>("PasswordHash");
465 
466  b.Property<string>("SystemIdentifier");
467 
468  b.HasKey("Id");
469 
470  b.HasIndex("CanonicalName")
471  .IsUnique();
472 
473  b.HasIndex("CreatedById");
474 
475  b.ToTable("Users");
476  });
477 
478  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
479  {
480  b.Property<long>("Id")
481  .ValueGeneratedOnAdd();
482 
483  b.Property<long?>("AlphaId");
484 
485  b.Property<bool>("AlphaIsActive");
486 
487  b.Property<long?>("BravoId");
488 
489  b.Property<long>("InstanceId");
490 
491  b.HasKey("Id");
492 
493  b.HasIndex("AlphaId");
494 
495  b.HasIndex("BravoId");
496 
497  b.HasIndex("InstanceId")
498  .IsUnique();
499 
500  b.ToTable("WatchdogReattachInformations");
501  });
502 
503  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
504  {
505  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
506  .WithMany("ChatSettings")
507  .HasForeignKey("InstanceId")
508  .OnDelete(DeleteBehavior.Cascade);
509  });
510 
511  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
512  {
513  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
514  .WithMany("Channels")
515  .HasForeignKey("ChatSettingsId")
516  .OnDelete(DeleteBehavior.Cascade);
517  });
518 
519  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
520  {
521  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
522  .WithOne()
523  .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
524  .OnDelete(DeleteBehavior.Restrict);
525 
526  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
527  .WithMany("CompileJobs")
528  .HasForeignKey("RevisionInformationId")
529  .OnDelete(DeleteBehavior.Cascade);
530  });
531 
532  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
533  {
534  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
535  .WithOne("DreamDaemonSettings")
536  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
537  .OnDelete(DeleteBehavior.Cascade);
538  });
539 
540  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
541  {
542  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
543  .WithOne("DreamMakerSettings")
544  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
545  .OnDelete(DeleteBehavior.Cascade);
546  });
547 
548  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
549  {
550  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
551  .WithMany("InstanceUsers")
552  .HasForeignKey("InstanceId")
553  .OnDelete(DeleteBehavior.Cascade);
554 
555  b.HasOne("Tgstation.Server.Host.Models.User")
556  .WithMany("InstanceUsers")
557  .HasForeignKey("UserId")
558  .OnDelete(DeleteBehavior.Cascade);
559  });
560 
561  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
562  {
563  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
564  .WithMany()
565  .HasForeignKey("CancelledById");
566 
567  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
568  .WithMany("Jobs")
569  .HasForeignKey("InstanceId")
570  .OnDelete(DeleteBehavior.Cascade);
571 
572  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
573  .WithMany()
574  .HasForeignKey("StartedById")
575  .OnDelete(DeleteBehavior.Cascade);
576  });
577 
578  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
579  {
580  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
581  .WithMany()
582  .HasForeignKey("CompileJobId");
583  });
584 
585  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
586  {
587  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
588  .WithOne("RepositorySettings")
589  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
590  .OnDelete(DeleteBehavior.Cascade);
591  });
592 
593  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
594  {
595  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
596  .WithMany("ActiveTestMerges")
597  .HasForeignKey("RevisionInformationId")
598  .OnDelete(DeleteBehavior.Cascade);
599 
600  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
601  .WithMany("RevisonInformations")
602  .HasForeignKey("TestMergeId")
603  .OnDelete(DeleteBehavior.Cascade);
604  });
605 
606  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
607  {
608  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
609  .WithMany("RevisionInformations")
610  .HasForeignKey("InstanceId")
611  .OnDelete(DeleteBehavior.Cascade);
612  });
613 
614  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
615  {
616  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
617  .WithMany("TestMerges")
618  .HasForeignKey("MergedById")
619  .OnDelete(DeleteBehavior.Restrict);
620 
621  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
622  .WithOne("PrimaryTestMerge")
623  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
624  .OnDelete(DeleteBehavior.Restrict);
625  });
626 
627  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
628  {
629  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
630  .WithMany("CreatedUsers")
631  .HasForeignKey("CreatedById");
632  });
633 
634  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
635  {
636  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
637  .WithMany()
638  .HasForeignKey("AlphaId");
639 
640  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
641  .WithMany()
642  .HasForeignKey("BravoId");
643 
644  b.HasOne("Tgstation.Server.Host.Models.Instance")
645  .WithOne("WatchdogReattachInformation")
646  .HasForeignKey("Tgstation.Server.Host.Models.WatchdogReattachInformation", "InstanceId")
647  .OnDelete(DeleteBehavior.Cascade);
648  });
649 #pragma warning restore 612, 618
650  }
651  }
652 }
+ -
diff --git a/20180918204520___m_y_nullable_and_foreign_key_cleanup_8cs_source.html b/20180918204520___m_y_nullable_and_foreign_key_cleanup_8cs_source.html index 0eb1be435e..4ce3489673 100644 --- a/20180918204520___m_y_nullable_and_foreign_key_cleanup_8cs_source.html +++ b/20180918204520___m_y_nullable_and_foreign_key_cleanup_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
20180918204520_MYNullableAndForeignKeyCleanup.cs
-Go to the documentation of this file.
1 using System;
2 using Microsoft.EntityFrameworkCore.Migrations;
3 
4 namespace Tgstation.Server.Host.Models.Migrations
5 {
10  {
15  protected override void Up(MigrationBuilder migrationBuilder)
16  {
17  migrationBuilder.DropForeignKey(
18  name: "FK_Instances_WatchdogReattachInformations_WatchdogReattachInfor~",
19  table: "Instances");
20 
21  migrationBuilder.DropForeignKey(
22  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
23  table: "TestMerges");
24 
25  migrationBuilder.DropForeignKey(
26  name: "FK_CompileJobs_Jobs_JobId",
27  table: "CompileJobs");
28 
29  migrationBuilder.DropIndex(
30  name: "IX_Instances_WatchdogReattachInformationId",
31  table: "Instances");
32 
33  migrationBuilder.DropIndex(
34  name: "IX_CompileJobs_JobId",
35  table: "CompileJobs");
36 
37  migrationBuilder.DropColumn(
38  name: "WatchdogReattachInformationId",
39  table: "Instances");
40 
41  migrationBuilder.AddColumn<long>(
42  name: "InstanceId",
43  table: "WatchdogReattachInformations",
44  nullable: false,
45  defaultValue: 0L);
46 
47  migrationBuilder.AlterColumn<long>(
48  name: "PrimaryRevisionInformationId",
49  table: "TestMerges",
50  nullable: false,
51  oldClrType: typeof(long),
52  oldNullable: true);
53 
54  migrationBuilder.AlterColumn<string>(
55  name: "Output",
56  table: "CompileJobs",
57  nullable: false,
58  oldClrType: typeof(string),
59  oldNullable: true);
60 
61  migrationBuilder.AlterColumn<long>(
62  name: "JobId",
63  table: "CompileJobs",
64  nullable: false,
65  oldClrType: typeof(long),
66  oldNullable: true);
67 
68  migrationBuilder.AlterColumn<string>(
69  name: "DmeName",
70  table: "CompileJobs",
71  nullable: false,
72  oldClrType: typeof(string),
73  oldNullable: true);
74 
75  migrationBuilder.AlterColumn<Guid>(
76  name: "DirectoryName",
77  table: "CompileJobs",
78  nullable: false,
79  oldClrType: typeof(Guid),
80  oldNullable: true);
81 
82  migrationBuilder.CreateIndex(
83  name: "IX_WatchdogReattachInformations_InstanceId",
84  table: "WatchdogReattachInformations",
85  column: "InstanceId",
86  unique: true);
87 
88  migrationBuilder.CreateIndex(
89  name: "IX_CompileJobs_JobId",
90  table: "CompileJobs",
91  column: "JobId",
92  unique: true);
93 
94  migrationBuilder.AddForeignKey(
95  name: "FK_CompileJobs_Jobs_JobId",
96  table: "CompileJobs",
97  column: "JobId",
98  principalTable: "Jobs",
99  principalColumn: "Id",
100  onDelete: ReferentialAction.Restrict);
101 
102  migrationBuilder.AddForeignKey(
103  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
104  table: "TestMerges",
105  column: "PrimaryRevisionInformationId",
106  principalTable: "RevisionInformations",
107  principalColumn: "Id",
108  onDelete: ReferentialAction.Restrict);
109 
110  migrationBuilder.AddForeignKey(
111  name: "FK_WatchdogReattachInformations_Instances_InstanceId",
112  table: "WatchdogReattachInformations",
113  column: "InstanceId",
114  principalTable: "Instances",
115  principalColumn: "Id",
116  onDelete: ReferentialAction.Cascade);
117  }
118 
123  protected override void Down(MigrationBuilder migrationBuilder)
124  {
125  migrationBuilder.DropForeignKey(
126  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
127  table: "TestMerges");
128 
129  migrationBuilder.DropForeignKey(
130  name: "FK_WatchdogReattachInformations_Instances_InstanceId",
131  table: "WatchdogReattachInformations");
132 
133  migrationBuilder.DropForeignKey(
134  name: "FK_CompileJobs_Jobs_JobId",
135  table: "CompileJobs");
136 
137  migrationBuilder.DropIndex(
138  name: "IX_WatchdogReattachInformations_InstanceId",
139  table: "WatchdogReattachInformations");
140 
141  migrationBuilder.DropIndex(
142  name: "IX_CompileJobs_JobId",
143  table: "CompileJobs");
144 
145  migrationBuilder.DropColumn(
146  name: "InstanceId",
147  table: "WatchdogReattachInformations");
148 
149  migrationBuilder.AlterColumn<long>(
150  name: "PrimaryRevisionInformationId",
151  table: "TestMerges",
152  nullable: true,
153  oldClrType: typeof(long));
154 
155  migrationBuilder.AddColumn<long>(
156  name: "WatchdogReattachInformationId",
157  table: "Instances",
158  nullable: true);
159 
160  migrationBuilder.AlterColumn<string>(
161  name: "Output",
162  table: "CompileJobs",
163  nullable: true,
164  oldClrType: typeof(string));
165 
166  migrationBuilder.AlterColumn<long>(
167  name: "JobId",
168  table: "CompileJobs",
169  nullable: true,
170  oldClrType: typeof(long));
171 
172  migrationBuilder.AlterColumn<string>(
173  name: "DmeName",
174  table: "CompileJobs",
175  nullable: true,
176  oldClrType: typeof(string));
177 
178  migrationBuilder.AlterColumn<Guid>(
179  name: "DirectoryName",
180  table: "CompileJobs",
181  nullable: true,
182  oldClrType: typeof(Guid));
183 
184  migrationBuilder.CreateIndex(
185  name: "IX_Instances_WatchdogReattachInformationId",
186  table: "Instances",
187  column: "WatchdogReattachInformationId");
188 
189  migrationBuilder.CreateIndex(
190  name: "IX_CompileJobs_JobId",
191  table: "CompileJobs",
192  column: "JobId");
193 
194  migrationBuilder.AddForeignKey(
195  name: "FK_CompileJobs_Jobs_JobId",
196  table: "CompileJobs",
197  column: "JobId",
198  principalTable: "Jobs",
199  principalColumn: "Id",
200  onDelete: ReferentialAction.Restrict);
201 
202  migrationBuilder.AddForeignKey(
203  name: "FK_Instances_WatchdogReattachInformations_WatchdogReattachInfor~",
204  table: "Instances",
205  column: "WatchdogReattachInformationId",
206  principalTable: "WatchdogReattachInformations",
207  principalColumn: "Id",
208  onDelete: ReferentialAction.Restrict);
209 
210  migrationBuilder.AddForeignKey(
211  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
212  table: "TestMerges",
213  column: "PrimaryRevisionInformationId",
214  principalTable: "RevisionInformations",
215  principalColumn: "Id",
216  onDelete: ReferentialAction.SetNull);
217  }
218  }
219 }
override void Up(MigrationBuilder migrationBuilder)
Applies the migration
+Go to the documentation of this file.
1 using System;
2 using Microsoft.EntityFrameworkCore.Migrations;
3 
4 namespace Tgstation.Server.Host.Models.Migrations
5 {
10  {
15  protected override void Up(MigrationBuilder migrationBuilder)
16  {
17  migrationBuilder.DropForeignKey(
18  name: "FK_Instances_WatchdogReattachInformations_WatchdogReattachInfor~",
19  table: "Instances");
20 
21  migrationBuilder.DropForeignKey(
22  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
23  table: "TestMerges");
24 
25  migrationBuilder.DropForeignKey(
26  name: "FK_CompileJobs_Jobs_JobId",
27  table: "CompileJobs");
28 
29  migrationBuilder.DropIndex(
30  name: "IX_Instances_WatchdogReattachInformationId",
31  table: "Instances");
32 
33  migrationBuilder.DropIndex(
34  name: "IX_CompileJobs_JobId",
35  table: "CompileJobs");
36 
37  migrationBuilder.DropColumn(
38  name: "WatchdogReattachInformationId",
39  table: "Instances");
40 
41  migrationBuilder.AddColumn<long>(
42  name: "InstanceId",
43  table: "WatchdogReattachInformations",
44  nullable: false,
45  defaultValue: 0L);
46 
47  migrationBuilder.AlterColumn<long>(
48  name: "PrimaryRevisionInformationId",
49  table: "TestMerges",
50  nullable: false,
51  oldClrType: typeof(long),
52  oldNullable: true);
53 
54  migrationBuilder.AlterColumn<string>(
55  name: "Output",
56  table: "CompileJobs",
57  nullable: false,
58  oldClrType: typeof(string),
59  oldNullable: true);
60 
61  migrationBuilder.AlterColumn<long>(
62  name: "JobId",
63  table: "CompileJobs",
64  nullable: false,
65  oldClrType: typeof(long),
66  oldNullable: true);
67 
68  migrationBuilder.AlterColumn<string>(
69  name: "DmeName",
70  table: "CompileJobs",
71  nullable: false,
72  oldClrType: typeof(string),
73  oldNullable: true);
74 
75  migrationBuilder.AlterColumn<Guid>(
76  name: "DirectoryName",
77  table: "CompileJobs",
78  nullable: false,
79  oldClrType: typeof(Guid),
80  oldNullable: true);
81 
82  migrationBuilder.CreateIndex(
83  name: "IX_WatchdogReattachInformations_InstanceId",
84  table: "WatchdogReattachInformations",
85  column: "InstanceId",
86  unique: true);
87 
88  migrationBuilder.CreateIndex(
89  name: "IX_CompileJobs_JobId",
90  table: "CompileJobs",
91  column: "JobId",
92  unique: true);
93 
94  migrationBuilder.AddForeignKey(
95  name: "FK_CompileJobs_Jobs_JobId",
96  table: "CompileJobs",
97  column: "JobId",
98  principalTable: "Jobs",
99  principalColumn: "Id",
100  onDelete: ReferentialAction.Restrict);
101 
102  migrationBuilder.AddForeignKey(
103  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
104  table: "TestMerges",
105  column: "PrimaryRevisionInformationId",
106  principalTable: "RevisionInformations",
107  principalColumn: "Id",
108  onDelete: ReferentialAction.Restrict);
109 
110  migrationBuilder.AddForeignKey(
111  name: "FK_WatchdogReattachInformations_Instances_InstanceId",
112  table: "WatchdogReattachInformations",
113  column: "InstanceId",
114  principalTable: "Instances",
115  principalColumn: "Id",
116  onDelete: ReferentialAction.Cascade);
117  }
118 
123  protected override void Down(MigrationBuilder migrationBuilder)
124  {
125  migrationBuilder.DropForeignKey(
126  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
127  table: "TestMerges");
128 
129  migrationBuilder.DropForeignKey(
130  name: "FK_WatchdogReattachInformations_Instances_InstanceId",
131  table: "WatchdogReattachInformations");
132 
133  migrationBuilder.DropForeignKey(
134  name: "FK_CompileJobs_Jobs_JobId",
135  table: "CompileJobs");
136 
137  migrationBuilder.DropIndex(
138  name: "IX_WatchdogReattachInformations_InstanceId",
139  table: "WatchdogReattachInformations");
140 
141  migrationBuilder.DropIndex(
142  name: "IX_CompileJobs_JobId",
143  table: "CompileJobs");
144 
145  migrationBuilder.DropColumn(
146  name: "InstanceId",
147  table: "WatchdogReattachInformations");
148 
149  migrationBuilder.AlterColumn<long>(
150  name: "PrimaryRevisionInformationId",
151  table: "TestMerges",
152  nullable: true,
153  oldClrType: typeof(long));
154 
155  migrationBuilder.AddColumn<long>(
156  name: "WatchdogReattachInformationId",
157  table: "Instances",
158  nullable: true);
159 
160  migrationBuilder.AlterColumn<string>(
161  name: "Output",
162  table: "CompileJobs",
163  nullable: true,
164  oldClrType: typeof(string));
165 
166  migrationBuilder.AlterColumn<long>(
167  name: "JobId",
168  table: "CompileJobs",
169  nullable: true,
170  oldClrType: typeof(long));
171 
172  migrationBuilder.AlterColumn<string>(
173  name: "DmeName",
174  table: "CompileJobs",
175  nullable: true,
176  oldClrType: typeof(string));
177 
178  migrationBuilder.AlterColumn<Guid>(
179  name: "DirectoryName",
180  table: "CompileJobs",
181  nullable: true,
182  oldClrType: typeof(Guid));
183 
184  migrationBuilder.CreateIndex(
185  name: "IX_Instances_WatchdogReattachInformationId",
186  table: "Instances",
187  column: "WatchdogReattachInformationId");
188 
189  migrationBuilder.CreateIndex(
190  name: "IX_CompileJobs_JobId",
191  table: "CompileJobs",
192  column: "JobId");
193 
194  migrationBuilder.AddForeignKey(
195  name: "FK_CompileJobs_Jobs_JobId",
196  table: "CompileJobs",
197  column: "JobId",
198  principalTable: "Jobs",
199  principalColumn: "Id",
200  onDelete: ReferentialAction.Restrict);
201 
202  migrationBuilder.AddForeignKey(
203  name: "FK_Instances_WatchdogReattachInformations_WatchdogReattachInfor~",
204  table: "Instances",
205  column: "WatchdogReattachInformationId",
206  principalTable: "WatchdogReattachInformations",
207  principalColumn: "Id",
208  onDelete: ReferentialAction.Restrict);
209 
210  migrationBuilder.AddForeignKey(
211  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
212  table: "TestMerges",
213  column: "PrimaryRevisionInformationId",
214  principalTable: "RevisionInformations",
215  principalColumn: "Id",
216  onDelete: ReferentialAction.SetNull);
217  }
218  }
219 }
override void Up(MigrationBuilder migrationBuilder)
Applies the migration
+
override void Down(MigrationBuilder migrationBuilder)
Unapplies the migration
-
diff --git a/20180918205224___m_s_nullable_and_foreign_key_cleanup_8_designer_8cs_source.html b/20180918205224___m_s_nullable_and_foreign_key_cleanup_8_designer_8cs_source.html index e7749c41a9..9f3a1505c6 100644 --- a/20180918205224___m_s_nullable_and_foreign_key_cleanup_8_designer_8cs_source.html +++ b/20180918205224___m_s_nullable_and_foreign_key_cleanup_8_designer_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
20180918205224_MSNullableAndForeignKeyCleanup.Designer.cs
-Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Metadata;
6 using Microsoft.EntityFrameworkCore.Migrations;
7 using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 
9 namespace Tgstation.Server.Host.Models.Migrations
10 {
11  [DbContext(typeof(SqlServerDatabaseContext))]
12  [Migration("20180918205224_MSNullableAndForeignKeyCleanup")]
13  partial class MSNullableAndForeignKeyCleanup
14  {
19  protected override void BuildTargetModel(ModelBuilder modelBuilder)
20  {
21 #pragma warning disable 612, 618
22  modelBuilder
23  .HasAnnotation("ProductVersion", "2.1.3-rtm-32065")
24  .HasAnnotation("Relational:MaxIdentifierLength", 128)
25  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
26 
27  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
28  {
29  b.Property<long>("Id")
30  .ValueGeneratedOnAdd()
31  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
32 
33  b.Property<string>("ConnectionString")
34  .IsRequired();
35 
36  b.Property<bool?>("Enabled");
37 
38  b.Property<long>("InstanceId");
39 
40  b.Property<string>("Name")
41  .IsRequired();
42 
43  b.Property<int?>("Provider");
44 
45  b.HasKey("Id");
46 
47  b.HasIndex("InstanceId");
48 
49  b.HasIndex("Name")
50  .IsUnique();
51 
52  b.ToTable("ChatBots");
53  });
54 
55  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
56  {
57  b.Property<long>("Id")
58  .ValueGeneratedOnAdd()
59  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
60 
61  b.Property<long>("ChatSettingsId");
62 
63  b.Property<decimal?>("DiscordChannelId")
64  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
65 
66  b.Property<string>("IrcChannel");
67 
68  b.Property<bool?>("IsAdminChannel")
69  .IsRequired();
70 
71  b.Property<bool?>("IsUpdatesChannel")
72  .IsRequired();
73 
74  b.Property<bool?>("IsWatchdogChannel")
75  .IsRequired();
76 
77  b.Property<string>("Tag");
78 
79  b.HasKey("Id");
80 
81  b.HasIndex("ChatSettingsId", "DiscordChannelId")
82  .IsUnique()
83  .HasFilter("[DiscordChannelId] IS NOT NULL");
84 
85  b.HasIndex("ChatSettingsId", "IrcChannel")
86  .IsUnique()
87  .HasFilter("[IrcChannel] IS NOT NULL");
88 
89  b.ToTable("ChatChannels");
90  });
91 
92  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
93  {
94  b.Property<long>("Id")
95  .ValueGeneratedOnAdd()
96  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
97 
98  b.Property<string>("ByondVersion")
99  .IsRequired();
100 
101  b.Property<Guid?>("DirectoryName")
102  .IsRequired();
103 
104  b.Property<string>("DmeName")
105  .IsRequired();
106 
107  b.Property<long>("JobId");
108 
109  b.Property<int>("MinimumSecurityLevel");
110 
111  b.Property<string>("Output")
112  .IsRequired();
113 
114  b.Property<long>("RevisionInformationId");
115 
116  b.HasKey("Id");
117 
118  b.HasIndex("DirectoryName");
119 
120  b.HasIndex("JobId")
121  .IsUnique();
122 
123  b.HasIndex("RevisionInformationId");
124 
125  b.ToTable("CompileJobs");
126  });
127 
128  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
129  {
130  b.Property<long>("Id")
131  .ValueGeneratedOnAdd()
132  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
133 
134  b.Property<string>("AccessToken");
135 
136  b.Property<bool?>("AllowWebClient")
137  .IsRequired();
138 
139  b.Property<bool?>("AutoStart")
140  .IsRequired();
141 
142  b.Property<long>("InstanceId");
143 
144  b.Property<int>("PrimaryPort");
145 
146  b.Property<int?>("ProcessId");
147 
148  b.Property<int>("SecondaryPort");
149 
150  b.Property<int>("SecurityLevel");
151 
152  b.Property<bool?>("SoftRestart")
153  .IsRequired();
154 
155  b.Property<bool?>("SoftShutdown")
156  .IsRequired();
157 
158  b.Property<long>("StartupTimeout");
159 
160  b.HasKey("Id");
161 
162  b.HasIndex("InstanceId")
163  .IsUnique();
164 
165  b.ToTable("DreamDaemonSettings");
166  });
167 
168  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
169  {
170  b.Property<long>("Id")
171  .ValueGeneratedOnAdd()
172  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
173 
174  b.Property<int>("ApiValidationPort");
175 
176  b.Property<int>("ApiValidationSecurityLevel");
177 
178  b.Property<long>("InstanceId");
179 
180  b.Property<string>("ProjectName");
181 
182  b.HasKey("Id");
183 
184  b.HasIndex("InstanceId")
185  .IsUnique();
186 
187  b.ToTable("DreamMakerSettings");
188  });
189 
190  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
191  {
192  b.Property<long>("Id")
193  .ValueGeneratedOnAdd()
194  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
195 
196  b.Property<long>("AutoUpdateInterval");
197 
198  b.Property<int>("ConfigurationType");
199 
200  b.Property<string>("Name")
201  .IsRequired();
202 
203  b.Property<bool?>("Online")
204  .IsRequired();
205 
206  b.Property<string>("Path")
207  .IsRequired();
208 
209  b.HasKey("Id");
210 
211  b.HasIndex("Path")
212  .IsUnique();
213 
214  b.ToTable("Instances");
215  });
216 
217  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
218  {
219  b.Property<long>("Id")
220  .ValueGeneratedOnAdd()
221  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
222 
223  b.Property<decimal>("ByondRights")
224  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
225 
226  b.Property<decimal>("ChatBotRights")
227  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
228 
229  b.Property<decimal>("ConfigurationRights")
230  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
231 
232  b.Property<decimal>("DreamDaemonRights")
233  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
234 
235  b.Property<decimal>("DreamMakerRights")
236  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
237 
238  b.Property<long>("InstanceId");
239 
240  b.Property<decimal>("InstanceUserRights")
241  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
242 
243  b.Property<decimal>("RepositoryRights")
244  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
245 
246  b.Property<long?>("UserId")
247  .IsRequired();
248 
249  b.HasKey("Id");
250 
251  b.HasIndex("InstanceId");
252 
253  b.HasIndex("UserId", "InstanceId")
254  .IsUnique();
255 
256  b.ToTable("InstanceUsers");
257  });
258 
259  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
260  {
261  b.Property<long>("Id")
262  .ValueGeneratedOnAdd()
263  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
264 
265  b.Property<decimal?>("CancelRight")
266  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
267 
268  b.Property<decimal?>("CancelRightsType")
269  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
270 
271  b.Property<bool?>("Cancelled")
272  .IsRequired();
273 
274  b.Property<long?>("CancelledById");
275 
276  b.Property<string>("Description")
277  .IsRequired();
278 
279  b.Property<string>("ExceptionDetails");
280 
281  b.Property<long>("InstanceId");
282 
283  b.Property<DateTimeOffset?>("StartedAt")
284  .IsRequired();
285 
286  b.Property<long>("StartedById");
287 
288  b.Property<DateTimeOffset?>("StoppedAt");
289 
290  b.HasKey("Id");
291 
292  b.HasIndex("CancelledById");
293 
294  b.HasIndex("InstanceId");
295 
296  b.HasIndex("StartedById");
297 
298  b.ToTable("Jobs");
299  });
300 
301  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
302  {
303  b.Property<long>("Id")
304  .ValueGeneratedOnAdd()
305  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
306 
307  b.Property<string>("AccessIdentifier")
308  .IsRequired();
309 
310  b.Property<string>("ChatChannelsJson")
311  .IsRequired();
312 
313  b.Property<string>("ChatCommandsJson")
314  .IsRequired();
315 
316  b.Property<long?>("CompileJobId");
317 
318  b.Property<bool>("IsPrimary");
319 
320  b.Property<int>("Port");
321 
322  b.Property<int>("ProcessId");
323 
324  b.Property<int>("RebootState");
325 
326  b.Property<string>("ServerCommandsJson")
327  .IsRequired();
328 
329  b.HasKey("Id");
330 
331  b.HasIndex("CompileJobId");
332 
333  b.ToTable("ReattachInformations");
334  });
335 
336  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
337  {
338  b.Property<long>("Id")
339  .ValueGeneratedOnAdd()
340  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
341 
342  b.Property<string>("AccessToken");
343 
344  b.Property<string>("AccessUser");
345 
346  b.Property<bool?>("AutoUpdatesKeepTestMerges")
347  .IsRequired();
348 
349  b.Property<bool?>("AutoUpdatesSynchronize")
350  .IsRequired();
351 
352  b.Property<string>("CommitterEmail")
353  .IsRequired();
354 
355  b.Property<string>("CommitterName")
356  .IsRequired();
357 
358  b.Property<long>("InstanceId");
359 
360  b.Property<bool?>("PushTestMergeCommits")
361  .IsRequired();
362 
363  b.Property<bool?>("ShowTestMergeCommitters")
364  .IsRequired();
365 
366  b.HasKey("Id");
367 
368  b.HasIndex("InstanceId")
369  .IsUnique();
370 
371  b.ToTable("RepositorySettings");
372  });
373 
374  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
375  {
376  b.Property<long>("Id")
377  .ValueGeneratedOnAdd()
378  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
379 
380  b.Property<long>("RevisionInformationId");
381 
382  b.Property<long>("TestMergeId");
383 
384  b.HasKey("Id");
385 
386  b.HasIndex("RevisionInformationId");
387 
388  b.HasIndex("TestMergeId");
389 
390  b.ToTable("RevInfoTestMerges");
391  });
392 
393  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
394  {
395  b.Property<long>("Id")
396  .ValueGeneratedOnAdd()
397  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
398 
399  b.Property<string>("CommitSha")
400  .IsRequired()
401  .HasMaxLength(40);
402 
403  b.Property<long>("InstanceId");
404 
405  b.Property<string>("OriginCommitSha")
406  .IsRequired()
407  .HasMaxLength(40);
408 
409  b.HasKey("Id");
410 
411  b.HasIndex("CommitSha")
412  .IsUnique();
413 
414  b.HasIndex("InstanceId");
415 
416  b.ToTable("RevisionInformations");
417  });
418 
419  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
420  {
421  b.Property<long>("Id")
422  .ValueGeneratedOnAdd()
423  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
424 
425  b.Property<string>("Author")
426  .IsRequired();
427 
428  b.Property<string>("BodyAtMerge")
429  .IsRequired();
430 
431  b.Property<string>("Comment");
432 
433  b.Property<DateTimeOffset>("MergedAt");
434 
435  b.Property<long>("MergedById");
436 
437  b.Property<int?>("Number")
438  .IsRequired();
439 
440  b.Property<long?>("PrimaryRevisionInformationId")
441  .IsRequired();
442 
443  b.Property<string>("PullRequestRevision")
444  .IsRequired();
445 
446  b.Property<string>("TitleAtMerge")
447  .IsRequired();
448 
449  b.Property<string>("Url")
450  .IsRequired();
451 
452  b.HasKey("Id");
453 
454  b.HasIndex("MergedById");
455 
456  b.HasIndex("PrimaryRevisionInformationId")
457  .IsUnique();
458 
459  b.ToTable("TestMerges");
460  });
461 
462  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
463  {
464  b.Property<long>("Id")
465  .ValueGeneratedOnAdd()
466  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
467 
468  b.Property<decimal>("AdministrationRights")
469  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
470 
471  b.Property<string>("CanonicalName")
472  .IsRequired();
473 
474  b.Property<DateTimeOffset?>("CreatedAt")
475  .IsRequired();
476 
477  b.Property<long?>("CreatedById");
478 
479  b.Property<bool?>("Enabled")
480  .IsRequired();
481 
482  b.Property<decimal>("InstanceManagerRights")
483  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
484 
485  b.Property<DateTimeOffset?>("LastPasswordUpdate");
486 
487  b.Property<string>("Name")
488  .IsRequired();
489 
490  b.Property<string>("PasswordHash");
491 
492  b.Property<string>("SystemIdentifier");
493 
494  b.HasKey("Id");
495 
496  b.HasIndex("CanonicalName")
497  .IsUnique();
498 
499  b.HasIndex("CreatedById");
500 
501  b.ToTable("Users");
502  });
503 
504  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
505  {
506  b.Property<long>("Id")
507  .ValueGeneratedOnAdd()
508  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
509 
510  b.Property<long?>("AlphaId");
511 
512  b.Property<bool>("AlphaIsActive");
513 
514  b.Property<long?>("BravoId");
515 
516  b.Property<long>("InstanceId");
517 
518  b.HasKey("Id");
519 
520  b.HasIndex("AlphaId");
521 
522  b.HasIndex("BravoId");
523 
524  b.HasIndex("InstanceId")
525  .IsUnique();
526 
527  b.ToTable("WatchdogReattachInformations");
528  });
529 
530  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
531  {
532  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
533  .WithMany("ChatSettings")
534  .HasForeignKey("InstanceId")
535  .OnDelete(DeleteBehavior.Cascade);
536  });
537 
538  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
539  {
540  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
541  .WithMany("Channels")
542  .HasForeignKey("ChatSettingsId")
543  .OnDelete(DeleteBehavior.Cascade);
544  });
545 
546  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
547  {
548  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
549  .WithOne()
550  .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
551  .OnDelete(DeleteBehavior.Restrict);
552 
553  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
554  .WithMany("CompileJobs")
555  .HasForeignKey("RevisionInformationId")
556  .OnDelete(DeleteBehavior.Cascade);
557  });
558 
559  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
560  {
561  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
562  .WithOne("DreamDaemonSettings")
563  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
564  .OnDelete(DeleteBehavior.Cascade);
565  });
566 
567  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
568  {
569  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
570  .WithOne("DreamMakerSettings")
571  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
572  .OnDelete(DeleteBehavior.Cascade);
573  });
574 
575  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
576  {
577  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
578  .WithMany("InstanceUsers")
579  .HasForeignKey("InstanceId")
580  .OnDelete(DeleteBehavior.Cascade);
581 
582  b.HasOne("Tgstation.Server.Host.Models.User")
583  .WithMany("InstanceUsers")
584  .HasForeignKey("UserId")
585  .OnDelete(DeleteBehavior.Cascade);
586  });
587 
588  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
589  {
590  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
591  .WithMany()
592  .HasForeignKey("CancelledById");
593 
594  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
595  .WithMany("Jobs")
596  .HasForeignKey("InstanceId")
597  .OnDelete(DeleteBehavior.Cascade);
598 
599  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
600  .WithMany()
601  .HasForeignKey("StartedById")
602  .OnDelete(DeleteBehavior.Cascade);
603  });
604 
605  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
606  {
607  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
608  .WithMany()
609  .HasForeignKey("CompileJobId");
610  });
611 
612  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
613  {
614  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
615  .WithOne("RepositorySettings")
616  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
617  .OnDelete(DeleteBehavior.Cascade);
618  });
619 
620  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
621  {
622  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
623  .WithMany("ActiveTestMerges")
624  .HasForeignKey("RevisionInformationId")
625  .OnDelete(DeleteBehavior.Cascade);
626 
627  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
628  .WithMany("RevisonInformations")
629  .HasForeignKey("TestMergeId")
630  .OnDelete(DeleteBehavior.Cascade);
631  });
632 
633  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
634  {
635  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
636  .WithMany("RevisionInformations")
637  .HasForeignKey("InstanceId")
638  .OnDelete(DeleteBehavior.Cascade);
639  });
640 
641  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
642  {
643  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
644  .WithMany("TestMerges")
645  .HasForeignKey("MergedById")
646  .OnDelete(DeleteBehavior.Restrict);
647 
648  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
649  .WithOne("PrimaryTestMerge")
650  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
651  .OnDelete(DeleteBehavior.Restrict);
652  });
653 
654  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
655  {
656  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
657  .WithMany("CreatedUsers")
658  .HasForeignKey("CreatedById");
659  });
660 
661  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
662  {
663  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
664  .WithMany()
665  .HasForeignKey("AlphaId");
666 
667  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
668  .WithMany()
669  .HasForeignKey("BravoId");
670 
671  b.HasOne("Tgstation.Server.Host.Models.Instance")
672  .WithOne("WatchdogReattachInformation")
673  .HasForeignKey("Tgstation.Server.Host.Models.WatchdogReattachInformation", "InstanceId")
674  .OnDelete(DeleteBehavior.Cascade);
675  });
676 #pragma warning restore 612, 618
677  }
678  }
679 }
+Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Metadata;
6 using Microsoft.EntityFrameworkCore.Migrations;
7 using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 
9 namespace Tgstation.Server.Host.Models.Migrations
10 {
11  [DbContext(typeof(SqlServerDatabaseContext))]
12  [Migration("20180918205224_MSNullableAndForeignKeyCleanup")]
13  partial class MSNullableAndForeignKeyCleanup
14  {
19  protected override void BuildTargetModel(ModelBuilder modelBuilder)
20  {
21 #pragma warning disable 612, 618
22  modelBuilder
23  .HasAnnotation("ProductVersion", "2.1.3-rtm-32065")
24  .HasAnnotation("Relational:MaxIdentifierLength", 128)
25  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
26 
27  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
28  {
29  b.Property<long>("Id")
30  .ValueGeneratedOnAdd()
31  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
32 
33  b.Property<string>("ConnectionString")
34  .IsRequired();
35 
36  b.Property<bool?>("Enabled");
37 
38  b.Property<long>("InstanceId");
39 
40  b.Property<string>("Name")
41  .IsRequired();
42 
43  b.Property<int?>("Provider");
44 
45  b.HasKey("Id");
46 
47  b.HasIndex("InstanceId");
48 
49  b.HasIndex("Name")
50  .IsUnique();
51 
52  b.ToTable("ChatBots");
53  });
54 
55  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
56  {
57  b.Property<long>("Id")
58  .ValueGeneratedOnAdd()
59  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
60 
61  b.Property<long>("ChatSettingsId");
62 
63  b.Property<decimal?>("DiscordChannelId")
64  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
65 
66  b.Property<string>("IrcChannel");
67 
68  b.Property<bool?>("IsAdminChannel")
69  .IsRequired();
70 
71  b.Property<bool?>("IsUpdatesChannel")
72  .IsRequired();
73 
74  b.Property<bool?>("IsWatchdogChannel")
75  .IsRequired();
76 
77  b.Property<string>("Tag");
78 
79  b.HasKey("Id");
80 
81  b.HasIndex("ChatSettingsId", "DiscordChannelId")
82  .IsUnique()
83  .HasFilter("[DiscordChannelId] IS NOT NULL");
84 
85  b.HasIndex("ChatSettingsId", "IrcChannel")
86  .IsUnique()
87  .HasFilter("[IrcChannel] IS NOT NULL");
88 
89  b.ToTable("ChatChannels");
90  });
91 
92  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
93  {
94  b.Property<long>("Id")
95  .ValueGeneratedOnAdd()
96  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
97 
98  b.Property<string>("ByondVersion")
99  .IsRequired();
100 
101  b.Property<Guid?>("DirectoryName")
102  .IsRequired();
103 
104  b.Property<string>("DmeName")
105  .IsRequired();
106 
107  b.Property<long>("JobId");
108 
109  b.Property<int>("MinimumSecurityLevel");
110 
111  b.Property<string>("Output")
112  .IsRequired();
113 
114  b.Property<long>("RevisionInformationId");
115 
116  b.HasKey("Id");
117 
118  b.HasIndex("DirectoryName");
119 
120  b.HasIndex("JobId")
121  .IsUnique();
122 
123  b.HasIndex("RevisionInformationId");
124 
125  b.ToTable("CompileJobs");
126  });
127 
128  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
129  {
130  b.Property<long>("Id")
131  .ValueGeneratedOnAdd()
132  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
133 
134  b.Property<string>("AccessToken");
135 
136  b.Property<bool?>("AllowWebClient")
137  .IsRequired();
138 
139  b.Property<bool?>("AutoStart")
140  .IsRequired();
141 
142  b.Property<long>("InstanceId");
143 
144  b.Property<int>("PrimaryPort");
145 
146  b.Property<int?>("ProcessId");
147 
148  b.Property<int>("SecondaryPort");
149 
150  b.Property<int>("SecurityLevel");
151 
152  b.Property<bool?>("SoftRestart")
153  .IsRequired();
154 
155  b.Property<bool?>("SoftShutdown")
156  .IsRequired();
157 
158  b.Property<long>("StartupTimeout");
159 
160  b.HasKey("Id");
161 
162  b.HasIndex("InstanceId")
163  .IsUnique();
164 
165  b.ToTable("DreamDaemonSettings");
166  });
167 
168  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
169  {
170  b.Property<long>("Id")
171  .ValueGeneratedOnAdd()
172  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
173 
174  b.Property<int>("ApiValidationPort");
175 
176  b.Property<int>("ApiValidationSecurityLevel");
177 
178  b.Property<long>("InstanceId");
179 
180  b.Property<string>("ProjectName");
181 
182  b.HasKey("Id");
183 
184  b.HasIndex("InstanceId")
185  .IsUnique();
186 
187  b.ToTable("DreamMakerSettings");
188  });
189 
190  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
191  {
192  b.Property<long>("Id")
193  .ValueGeneratedOnAdd()
194  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
195 
196  b.Property<long>("AutoUpdateInterval");
197 
198  b.Property<int>("ConfigurationType");
199 
200  b.Property<string>("Name")
201  .IsRequired();
202 
203  b.Property<bool?>("Online")
204  .IsRequired();
205 
206  b.Property<string>("Path")
207  .IsRequired();
208 
209  b.HasKey("Id");
210 
211  b.HasIndex("Path")
212  .IsUnique();
213 
214  b.ToTable("Instances");
215  });
216 
217  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
218  {
219  b.Property<long>("Id")
220  .ValueGeneratedOnAdd()
221  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
222 
223  b.Property<decimal>("ByondRights")
224  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
225 
226  b.Property<decimal>("ChatBotRights")
227  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
228 
229  b.Property<decimal>("ConfigurationRights")
230  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
231 
232  b.Property<decimal>("DreamDaemonRights")
233  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
234 
235  b.Property<decimal>("DreamMakerRights")
236  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
237 
238  b.Property<long>("InstanceId");
239 
240  b.Property<decimal>("InstanceUserRights")
241  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
242 
243  b.Property<decimal>("RepositoryRights")
244  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
245 
246  b.Property<long?>("UserId")
247  .IsRequired();
248 
249  b.HasKey("Id");
250 
251  b.HasIndex("InstanceId");
252 
253  b.HasIndex("UserId", "InstanceId")
254  .IsUnique();
255 
256  b.ToTable("InstanceUsers");
257  });
258 
259  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
260  {
261  b.Property<long>("Id")
262  .ValueGeneratedOnAdd()
263  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
264 
265  b.Property<decimal?>("CancelRight")
266  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
267 
268  b.Property<decimal?>("CancelRightsType")
269  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
270 
271  b.Property<bool?>("Cancelled")
272  .IsRequired();
273 
274  b.Property<long?>("CancelledById");
275 
276  b.Property<string>("Description")
277  .IsRequired();
278 
279  b.Property<string>("ExceptionDetails");
280 
281  b.Property<long>("InstanceId");
282 
283  b.Property<DateTimeOffset?>("StartedAt")
284  .IsRequired();
285 
286  b.Property<long>("StartedById");
287 
288  b.Property<DateTimeOffset?>("StoppedAt");
289 
290  b.HasKey("Id");
291 
292  b.HasIndex("CancelledById");
293 
294  b.HasIndex("InstanceId");
295 
296  b.HasIndex("StartedById");
297 
298  b.ToTable("Jobs");
299  });
300 
301  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
302  {
303  b.Property<long>("Id")
304  .ValueGeneratedOnAdd()
305  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
306 
307  b.Property<string>("AccessIdentifier")
308  .IsRequired();
309 
310  b.Property<string>("ChatChannelsJson")
311  .IsRequired();
312 
313  b.Property<string>("ChatCommandsJson")
314  .IsRequired();
315 
316  b.Property<long?>("CompileJobId");
317 
318  b.Property<bool>("IsPrimary");
319 
320  b.Property<int>("Port");
321 
322  b.Property<int>("ProcessId");
323 
324  b.Property<int>("RebootState");
325 
326  b.Property<string>("ServerCommandsJson")
327  .IsRequired();
328 
329  b.HasKey("Id");
330 
331  b.HasIndex("CompileJobId");
332 
333  b.ToTable("ReattachInformations");
334  });
335 
336  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
337  {
338  b.Property<long>("Id")
339  .ValueGeneratedOnAdd()
340  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
341 
342  b.Property<string>("AccessToken");
343 
344  b.Property<string>("AccessUser");
345 
346  b.Property<bool?>("AutoUpdatesKeepTestMerges")
347  .IsRequired();
348 
349  b.Property<bool?>("AutoUpdatesSynchronize")
350  .IsRequired();
351 
352  b.Property<string>("CommitterEmail")
353  .IsRequired();
354 
355  b.Property<string>("CommitterName")
356  .IsRequired();
357 
358  b.Property<long>("InstanceId");
359 
360  b.Property<bool?>("PushTestMergeCommits")
361  .IsRequired();
362 
363  b.Property<bool?>("ShowTestMergeCommitters")
364  .IsRequired();
365 
366  b.HasKey("Id");
367 
368  b.HasIndex("InstanceId")
369  .IsUnique();
370 
371  b.ToTable("RepositorySettings");
372  });
373 
374  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
375  {
376  b.Property<long>("Id")
377  .ValueGeneratedOnAdd()
378  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
379 
380  b.Property<long>("RevisionInformationId");
381 
382  b.Property<long>("TestMergeId");
383 
384  b.HasKey("Id");
385 
386  b.HasIndex("RevisionInformationId");
387 
388  b.HasIndex("TestMergeId");
389 
390  b.ToTable("RevInfoTestMerges");
391  });
392 
393  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
394  {
395  b.Property<long>("Id")
396  .ValueGeneratedOnAdd()
397  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
398 
399  b.Property<string>("CommitSha")
400  .IsRequired()
401  .HasMaxLength(40);
402 
403  b.Property<long>("InstanceId");
404 
405  b.Property<string>("OriginCommitSha")
406  .IsRequired()
407  .HasMaxLength(40);
408 
409  b.HasKey("Id");
410 
411  b.HasIndex("CommitSha")
412  .IsUnique();
413 
414  b.HasIndex("InstanceId");
415 
416  b.ToTable("RevisionInformations");
417  });
418 
419  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
420  {
421  b.Property<long>("Id")
422  .ValueGeneratedOnAdd()
423  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
424 
425  b.Property<string>("Author")
426  .IsRequired();
427 
428  b.Property<string>("BodyAtMerge")
429  .IsRequired();
430 
431  b.Property<string>("Comment");
432 
433  b.Property<DateTimeOffset>("MergedAt");
434 
435  b.Property<long>("MergedById");
436 
437  b.Property<int?>("Number")
438  .IsRequired();
439 
440  b.Property<long?>("PrimaryRevisionInformationId")
441  .IsRequired();
442 
443  b.Property<string>("PullRequestRevision")
444  .IsRequired();
445 
446  b.Property<string>("TitleAtMerge")
447  .IsRequired();
448 
449  b.Property<string>("Url")
450  .IsRequired();
451 
452  b.HasKey("Id");
453 
454  b.HasIndex("MergedById");
455 
456  b.HasIndex("PrimaryRevisionInformationId")
457  .IsUnique();
458 
459  b.ToTable("TestMerges");
460  });
461 
462  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
463  {
464  b.Property<long>("Id")
465  .ValueGeneratedOnAdd()
466  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
467 
468  b.Property<decimal>("AdministrationRights")
469  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
470 
471  b.Property<string>("CanonicalName")
472  .IsRequired();
473 
474  b.Property<DateTimeOffset?>("CreatedAt")
475  .IsRequired();
476 
477  b.Property<long?>("CreatedById");
478 
479  b.Property<bool?>("Enabled")
480  .IsRequired();
481 
482  b.Property<decimal>("InstanceManagerRights")
483  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
484 
485  b.Property<DateTimeOffset?>("LastPasswordUpdate");
486 
487  b.Property<string>("Name")
488  .IsRequired();
489 
490  b.Property<string>("PasswordHash");
491 
492  b.Property<string>("SystemIdentifier");
493 
494  b.HasKey("Id");
495 
496  b.HasIndex("CanonicalName")
497  .IsUnique();
498 
499  b.HasIndex("CreatedById");
500 
501  b.ToTable("Users");
502  });
503 
504  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
505  {
506  b.Property<long>("Id")
507  .ValueGeneratedOnAdd()
508  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
509 
510  b.Property<long?>("AlphaId");
511 
512  b.Property<bool>("AlphaIsActive");
513 
514  b.Property<long?>("BravoId");
515 
516  b.Property<long>("InstanceId");
517 
518  b.HasKey("Id");
519 
520  b.HasIndex("AlphaId");
521 
522  b.HasIndex("BravoId");
523 
524  b.HasIndex("InstanceId")
525  .IsUnique();
526 
527  b.ToTable("WatchdogReattachInformations");
528  });
529 
530  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
531  {
532  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
533  .WithMany("ChatSettings")
534  .HasForeignKey("InstanceId")
535  .OnDelete(DeleteBehavior.Cascade);
536  });
537 
538  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
539  {
540  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
541  .WithMany("Channels")
542  .HasForeignKey("ChatSettingsId")
543  .OnDelete(DeleteBehavior.Cascade);
544  });
545 
546  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
547  {
548  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
549  .WithOne()
550  .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
551  .OnDelete(DeleteBehavior.Restrict);
552 
553  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
554  .WithMany("CompileJobs")
555  .HasForeignKey("RevisionInformationId")
556  .OnDelete(DeleteBehavior.Cascade);
557  });
558 
559  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
560  {
561  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
562  .WithOne("DreamDaemonSettings")
563  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
564  .OnDelete(DeleteBehavior.Cascade);
565  });
566 
567  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
568  {
569  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
570  .WithOne("DreamMakerSettings")
571  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
572  .OnDelete(DeleteBehavior.Cascade);
573  });
574 
575  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
576  {
577  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
578  .WithMany("InstanceUsers")
579  .HasForeignKey("InstanceId")
580  .OnDelete(DeleteBehavior.Cascade);
581 
582  b.HasOne("Tgstation.Server.Host.Models.User")
583  .WithMany("InstanceUsers")
584  .HasForeignKey("UserId")
585  .OnDelete(DeleteBehavior.Cascade);
586  });
587 
588  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
589  {
590  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
591  .WithMany()
592  .HasForeignKey("CancelledById");
593 
594  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
595  .WithMany("Jobs")
596  .HasForeignKey("InstanceId")
597  .OnDelete(DeleteBehavior.Cascade);
598 
599  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
600  .WithMany()
601  .HasForeignKey("StartedById")
602  .OnDelete(DeleteBehavior.Cascade);
603  });
604 
605  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
606  {
607  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
608  .WithMany()
609  .HasForeignKey("CompileJobId");
610  });
611 
612  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
613  {
614  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
615  .WithOne("RepositorySettings")
616  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
617  .OnDelete(DeleteBehavior.Cascade);
618  });
619 
620  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
621  {
622  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
623  .WithMany("ActiveTestMerges")
624  .HasForeignKey("RevisionInformationId")
625  .OnDelete(DeleteBehavior.Cascade);
626 
627  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
628  .WithMany("RevisonInformations")
629  .HasForeignKey("TestMergeId")
630  .OnDelete(DeleteBehavior.Cascade);
631  });
632 
633  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
634  {
635  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
636  .WithMany("RevisionInformations")
637  .HasForeignKey("InstanceId")
638  .OnDelete(DeleteBehavior.Cascade);
639  });
640 
641  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
642  {
643  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
644  .WithMany("TestMerges")
645  .HasForeignKey("MergedById")
646  .OnDelete(DeleteBehavior.Restrict);
647 
648  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
649  .WithOne("PrimaryTestMerge")
650  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
651  .OnDelete(DeleteBehavior.Restrict);
652  });
653 
654  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
655  {
656  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
657  .WithMany("CreatedUsers")
658  .HasForeignKey("CreatedById");
659  });
660 
661  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
662  {
663  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
664  .WithMany()
665  .HasForeignKey("AlphaId");
666 
667  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
668  .WithMany()
669  .HasForeignKey("BravoId");
670 
671  b.HasOne("Tgstation.Server.Host.Models.Instance")
672  .WithOne("WatchdogReattachInformation")
673  .HasForeignKey("Tgstation.Server.Host.Models.WatchdogReattachInformation", "InstanceId")
674  .OnDelete(DeleteBehavior.Cascade);
675  });
676 #pragma warning restore 612, 618
677  }
678  }
679 }
+ -
diff --git a/20180918205224___m_s_nullable_and_foreign_key_cleanup_8cs_source.html b/20180918205224___m_s_nullable_and_foreign_key_cleanup_8cs_source.html index 8056c716bf..a8588c5485 100644 --- a/20180918205224___m_s_nullable_and_foreign_key_cleanup_8cs_source.html +++ b/20180918205224___m_s_nullable_and_foreign_key_cleanup_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
20180918205224_MSNullableAndForeignKeyCleanup.cs
-Go to the documentation of this file.
1 using System;
2 using Microsoft.EntityFrameworkCore.Migrations;
3 
4 namespace Tgstation.Server.Host.Models.Migrations
5 {
10  {
15  protected override void Up(MigrationBuilder migrationBuilder)
16  {
17  migrationBuilder.DropForeignKey(
18  name: "FK_Instances_WatchdogReattachInformations_WatchdogReattachInformationId",
19  table: "Instances");
20 
21  migrationBuilder.DropForeignKey(
22  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
23  table: "TestMerges");
24 
25  migrationBuilder.DropIndex(
26  name: "IX_TestMerges_PrimaryRevisionInformationId",
27  table: "TestMerges");
28 
29  migrationBuilder.DropIndex(
30  name: "IX_Instances_WatchdogReattachInformationId",
31  table: "Instances");
32 
33  migrationBuilder.DropIndex(
34  name: "IX_CompileJobs_JobId",
35  table: "CompileJobs");
36 
37  migrationBuilder.DropColumn(
38  name: "WatchdogReattachInformationId",
39  table: "Instances");
40 
41  migrationBuilder.AddColumn<long>(
42  name: "InstanceId",
43  table: "WatchdogReattachInformations",
44  nullable: false,
45  defaultValue: 0L);
46 
47  migrationBuilder.AlterColumn<long>(
48  name: "PrimaryRevisionInformationId",
49  table: "TestMerges",
50  nullable: false,
51  oldClrType: typeof(long),
52  oldNullable: true);
53 
54  migrationBuilder.AlterColumn<string>(
55  name: "Output",
56  table: "CompileJobs",
57  nullable: false,
58  oldClrType: typeof(string),
59  oldNullable: true);
60 
61  migrationBuilder.AlterColumn<long>(
62  name: "JobId",
63  table: "CompileJobs",
64  nullable: false,
65  oldClrType: typeof(long),
66  oldNullable: true);
67 
68  migrationBuilder.AlterColumn<string>(
69  name: "DmeName",
70  table: "CompileJobs",
71  nullable: false,
72  oldClrType: typeof(string),
73  oldNullable: true);
74 
75  migrationBuilder.AlterColumn<Guid>(
76  name: "DirectoryName",
77  table: "CompileJobs",
78  nullable: false,
79  oldClrType: typeof(Guid),
80  oldNullable: true);
81 
82  migrationBuilder.CreateIndex(
83  name: "IX_WatchdogReattachInformations_InstanceId",
84  table: "WatchdogReattachInformations",
85  column: "InstanceId",
86  unique: true);
87 
88  migrationBuilder.CreateIndex(
89  name: "IX_TestMerges_PrimaryRevisionInformationId",
90  table: "TestMerges",
91  column: "PrimaryRevisionInformationId",
92  unique: true);
93 
94  migrationBuilder.CreateIndex(
95  name: "IX_CompileJobs_JobId",
96  table: "CompileJobs",
97  column: "JobId",
98  unique: true);
99 
100  migrationBuilder.AddForeignKey(
101  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
102  table: "TestMerges",
103  column: "PrimaryRevisionInformationId",
104  principalTable: "RevisionInformations",
105  principalColumn: "Id",
106  onDelete: ReferentialAction.Restrict);
107 
108  migrationBuilder.AddForeignKey(
109  name: "FK_WatchdogReattachInformations_Instances_InstanceId",
110  table: "WatchdogReattachInformations",
111  column: "InstanceId",
112  principalTable: "Instances",
113  principalColumn: "Id",
114  onDelete: ReferentialAction.Cascade);
115  }
116 
121  protected override void Down(MigrationBuilder migrationBuilder)
122  {
123  migrationBuilder.DropForeignKey(
124  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
125  table: "TestMerges");
126 
127  migrationBuilder.DropForeignKey(
128  name: "FK_WatchdogReattachInformations_Instances_InstanceId",
129  table: "WatchdogReattachInformations");
130 
131  migrationBuilder.DropIndex(
132  name: "IX_WatchdogReattachInformations_InstanceId",
133  table: "WatchdogReattachInformations");
134 
135  migrationBuilder.DropIndex(
136  name: "IX_TestMerges_PrimaryRevisionInformationId",
137  table: "TestMerges");
138 
139  migrationBuilder.DropIndex(
140  name: "IX_CompileJobs_JobId",
141  table: "CompileJobs");
142 
143  migrationBuilder.DropColumn(
144  name: "InstanceId",
145  table: "WatchdogReattachInformations");
146 
147  migrationBuilder.AlterColumn<long>(
148  name: "PrimaryRevisionInformationId",
149  table: "TestMerges",
150  nullable: true,
151  oldClrType: typeof(long));
152 
153  migrationBuilder.AddColumn<long>(
154  name: "WatchdogReattachInformationId",
155  table: "Instances",
156  nullable: true);
157 
158  migrationBuilder.AlterColumn<string>(
159  name: "Output",
160  table: "CompileJobs",
161  nullable: true,
162  oldClrType: typeof(string));
163 
164  migrationBuilder.AlterColumn<long>(
165  name: "JobId",
166  table: "CompileJobs",
167  nullable: true,
168  oldClrType: typeof(long));
169 
170  migrationBuilder.AlterColumn<string>(
171  name: "DmeName",
172  table: "CompileJobs",
173  nullable: true,
174  oldClrType: typeof(string));
175 
176  migrationBuilder.AlterColumn<Guid>(
177  name: "DirectoryName",
178  table: "CompileJobs",
179  nullable: true,
180  oldClrType: typeof(Guid));
181 
182  migrationBuilder.CreateIndex(
183  name: "IX_TestMerges_PrimaryRevisionInformationId",
184  table: "TestMerges",
185  column: "PrimaryRevisionInformationId",
186  unique: true,
187  filter: "[PrimaryRevisionInformationId] IS NOT NULL");
188 
189  migrationBuilder.CreateIndex(
190  name: "IX_Instances_WatchdogReattachInformationId",
191  table: "Instances",
192  column: "WatchdogReattachInformationId");
193 
194  migrationBuilder.CreateIndex(
195  name: "IX_CompileJobs_JobId",
196  table: "CompileJobs",
197  column: "JobId");
198 
199  migrationBuilder.AddForeignKey(
200  name: "FK_Instances_WatchdogReattachInformations_WatchdogReattachInformationId",
201  table: "Instances",
202  column: "WatchdogReattachInformationId",
203  principalTable: "WatchdogReattachInformations",
204  principalColumn: "Id",
205  onDelete: ReferentialAction.Restrict);
206 
207  migrationBuilder.AddForeignKey(
208  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
209  table: "TestMerges",
210  column: "PrimaryRevisionInformationId",
211  principalTable: "RevisionInformations",
212  principalColumn: "Id",
213  onDelete: ReferentialAction.SetNull);
214  }
215  }
216 }
override void Down(MigrationBuilder migrationBuilder)
Unapplies the migration
+Go to the documentation of this file.
1 using System;
2 using Microsoft.EntityFrameworkCore.Migrations;
3 
4 namespace Tgstation.Server.Host.Models.Migrations
5 {
10  {
15  protected override void Up(MigrationBuilder migrationBuilder)
16  {
17  migrationBuilder.DropForeignKey(
18  name: "FK_Instances_WatchdogReattachInformations_WatchdogReattachInformationId",
19  table: "Instances");
20 
21  migrationBuilder.DropForeignKey(
22  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
23  table: "TestMerges");
24 
25  migrationBuilder.DropIndex(
26  name: "IX_TestMerges_PrimaryRevisionInformationId",
27  table: "TestMerges");
28 
29  migrationBuilder.DropIndex(
30  name: "IX_Instances_WatchdogReattachInformationId",
31  table: "Instances");
32 
33  migrationBuilder.DropIndex(
34  name: "IX_CompileJobs_JobId",
35  table: "CompileJobs");
36 
37  migrationBuilder.DropColumn(
38  name: "WatchdogReattachInformationId",
39  table: "Instances");
40 
41  migrationBuilder.AddColumn<long>(
42  name: "InstanceId",
43  table: "WatchdogReattachInformations",
44  nullable: false,
45  defaultValue: 0L);
46 
47  migrationBuilder.AlterColumn<long>(
48  name: "PrimaryRevisionInformationId",
49  table: "TestMerges",
50  nullable: false,
51  oldClrType: typeof(long),
52  oldNullable: true);
53 
54  migrationBuilder.AlterColumn<string>(
55  name: "Output",
56  table: "CompileJobs",
57  nullable: false,
58  oldClrType: typeof(string),
59  oldNullable: true);
60 
61  migrationBuilder.AlterColumn<long>(
62  name: "JobId",
63  table: "CompileJobs",
64  nullable: false,
65  oldClrType: typeof(long),
66  oldNullable: true);
67 
68  migrationBuilder.AlterColumn<string>(
69  name: "DmeName",
70  table: "CompileJobs",
71  nullable: false,
72  oldClrType: typeof(string),
73  oldNullable: true);
74 
75  migrationBuilder.AlterColumn<Guid>(
76  name: "DirectoryName",
77  table: "CompileJobs",
78  nullable: false,
79  oldClrType: typeof(Guid),
80  oldNullable: true);
81 
82  migrationBuilder.CreateIndex(
83  name: "IX_WatchdogReattachInformations_InstanceId",
84  table: "WatchdogReattachInformations",
85  column: "InstanceId",
86  unique: true);
87 
88  migrationBuilder.CreateIndex(
89  name: "IX_TestMerges_PrimaryRevisionInformationId",
90  table: "TestMerges",
91  column: "PrimaryRevisionInformationId",
92  unique: true);
93 
94  migrationBuilder.CreateIndex(
95  name: "IX_CompileJobs_JobId",
96  table: "CompileJobs",
97  column: "JobId",
98  unique: true);
99 
100  migrationBuilder.AddForeignKey(
101  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
102  table: "TestMerges",
103  column: "PrimaryRevisionInformationId",
104  principalTable: "RevisionInformations",
105  principalColumn: "Id",
106  onDelete: ReferentialAction.Restrict);
107 
108  migrationBuilder.AddForeignKey(
109  name: "FK_WatchdogReattachInformations_Instances_InstanceId",
110  table: "WatchdogReattachInformations",
111  column: "InstanceId",
112  principalTable: "Instances",
113  principalColumn: "Id",
114  onDelete: ReferentialAction.Cascade);
115  }
116 
121  protected override void Down(MigrationBuilder migrationBuilder)
122  {
123  migrationBuilder.DropForeignKey(
124  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
125  table: "TestMerges");
126 
127  migrationBuilder.DropForeignKey(
128  name: "FK_WatchdogReattachInformations_Instances_InstanceId",
129  table: "WatchdogReattachInformations");
130 
131  migrationBuilder.DropIndex(
132  name: "IX_WatchdogReattachInformations_InstanceId",
133  table: "WatchdogReattachInformations");
134 
135  migrationBuilder.DropIndex(
136  name: "IX_TestMerges_PrimaryRevisionInformationId",
137  table: "TestMerges");
138 
139  migrationBuilder.DropIndex(
140  name: "IX_CompileJobs_JobId",
141  table: "CompileJobs");
142 
143  migrationBuilder.DropColumn(
144  name: "InstanceId",
145  table: "WatchdogReattachInformations");
146 
147  migrationBuilder.AlterColumn<long>(
148  name: "PrimaryRevisionInformationId",
149  table: "TestMerges",
150  nullable: true,
151  oldClrType: typeof(long));
152 
153  migrationBuilder.AddColumn<long>(
154  name: "WatchdogReattachInformationId",
155  table: "Instances",
156  nullable: true);
157 
158  migrationBuilder.AlterColumn<string>(
159  name: "Output",
160  table: "CompileJobs",
161  nullable: true,
162  oldClrType: typeof(string));
163 
164  migrationBuilder.AlterColumn<long>(
165  name: "JobId",
166  table: "CompileJobs",
167  nullable: true,
168  oldClrType: typeof(long));
169 
170  migrationBuilder.AlterColumn<string>(
171  name: "DmeName",
172  table: "CompileJobs",
173  nullable: true,
174  oldClrType: typeof(string));
175 
176  migrationBuilder.AlterColumn<Guid>(
177  name: "DirectoryName",
178  table: "CompileJobs",
179  nullable: true,
180  oldClrType: typeof(Guid));
181 
182  migrationBuilder.CreateIndex(
183  name: "IX_TestMerges_PrimaryRevisionInformationId",
184  table: "TestMerges",
185  column: "PrimaryRevisionInformationId",
186  unique: true,
187  filter: "[PrimaryRevisionInformationId] IS NOT NULL");
188 
189  migrationBuilder.CreateIndex(
190  name: "IX_Instances_WatchdogReattachInformationId",
191  table: "Instances",
192  column: "WatchdogReattachInformationId");
193 
194  migrationBuilder.CreateIndex(
195  name: "IX_CompileJobs_JobId",
196  table: "CompileJobs",
197  column: "JobId");
198 
199  migrationBuilder.AddForeignKey(
200  name: "FK_Instances_WatchdogReattachInformations_WatchdogReattachInformationId",
201  table: "Instances",
202  column: "WatchdogReattachInformationId",
203  principalTable: "WatchdogReattachInformations",
204  principalColumn: "Id",
205  onDelete: ReferentialAction.Restrict);
206 
207  migrationBuilder.AddForeignKey(
208  name: "FK_TestMerges_RevisionInformations_PrimaryRevisionInformationId",
209  table: "TestMerges",
210  column: "PrimaryRevisionInformationId",
211  principalTable: "RevisionInformations",
212  principalColumn: "Id",
213  onDelete: ReferentialAction.SetNull);
214  }
215  }
216 }
+
override void Down(MigrationBuilder migrationBuilder)
Unapplies the migration
override void Up(MigrationBuilder migrationBuilder)
Applies the migration
-
diff --git a/_administration_8cs_source.html b/_administration_8cs_source.html index 02b30609a0..28e4f56be2 100644 --- a/_administration_8cs_source.html +++ b/_administration_8cs_source.html @@ -92,7 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Administration.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Models
4 {
6  public sealed class Administration
7  {
11  public bool WindowsHost { get; set; }
12 
16  public Uri TrackedRepositoryUrl { get; set; }
17 
21  public Version LatestVersion { get; set; }
22 
26  public Version NewVersion { get; set; }
27  }
28 }
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Models
4 {
6  public sealed class Administration
7  {
11  public bool WindowsHost { get; set; }
12 
16  public Uri TrackedRepositoryUrl { get; set; }
17 
21  public Version LatestVersion { get; set; }
22 
26  public Version NewVersion { get; set; }
27  }
28 }
+
diff --git a/_administration_client_8cs_source.html b/_administration_client_8cs_source.html index ddfe263784..11eff202ac 100644 --- a/_administration_client_8cs_source.html +++ b/_administration_client_8cs_source.html @@ -92,9 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
AdministrationClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using Tgstation.Server.Api;
6 
7 namespace Tgstation.Server.Client
8 {
11  {
16 
21  public AdministrationClient(IApiClient apiClient)
22  {
23  this.apiClient = apiClient;
24  }
25 
27  public Task<Administration> Read(CancellationToken cancellationToken) => apiClient.Read<Administration>(Routes.Administration, cancellationToken);
28 
30  public Task Update(Administration administration, CancellationToken cancellationToken) => apiClient.Update(Routes.Administration, administration ?? throw new ArgumentNullException(nameof(administration)), cancellationToken);
31 
33  public Task Restart(CancellationToken cancellationToken) => apiClient.Delete(Routes.Administration, cancellationToken);
34  }
35 }
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using Tgstation.Server.Api;
6 
7 namespace Tgstation.Server.Client
8 {
11  {
16 
21  public AdministrationClient(IApiClient apiClient)
22  {
23  this.apiClient = apiClient;
24  }
25 
27  public Task<Administration> Read(CancellationToken cancellationToken) => apiClient.Read<Administration>(Routes.Administration, cancellationToken);
28 
30  public Task Update(Administration administration, CancellationToken cancellationToken) => apiClient.Update(Routes.Administration, administration ?? throw new ArgumentNullException(nameof(administration)), cancellationToken);
31 
33  public Task Restart(CancellationToken cancellationToken) => apiClient.Delete(Routes.Administration, cancellationToken);
34  }
35 }
AdministrationClient(IApiClient apiClient)
Construct an AdministrationClient
+
const string Administration
The Models.Administration controller
Definition: Routes.cs:19
diff --git a/_api_client_8cs_source.html b/_api_client_8cs_source.html index e2ab35a666..7494ca5a8b 100644 --- a/_api_client_8cs_source.html +++ b/_api_client_8cs_source.html @@ -92,9 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ApiClient.cs
-Go to the documentation of this file.
1 using Newtonsoft.Json;
2 using Newtonsoft.Json.Converters;
3 using Newtonsoft.Json.Serialization;
4 using System;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Net;
8 using System.Net.Http;
9 using System.Text;
10 using System.Threading;
11 using System.Threading.Tasks;
12 using Tgstation.Server.Api;
14 
15 namespace Tgstation.Server.Client
16 {
18  sealed class ApiClient : IApiClient
19  {
21  public Uri Url { get; }
22 
24  public ApiHeaders Headers { get; }
25 
27  public TimeSpan Timeout
28  {
29  get => httpClient.Timeout;
30  set => httpClient.Timeout = value;
31  }
32 
37 
41  readonly List<IRequestLogger> requestLoggers;
42 
49  public ApiClient(IHttpClient httpClient, Uri url, ApiHeaders apiHeaders)
50  {
51  this.httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
52  Url = url ?? throw new ArgumentNullException(nameof(url));
53  Headers = apiHeaders ?? throw new ArgumentNullException(nameof(apiHeaders));
54 
55  requestLoggers = new List<IRequestLogger>();
56  }
57 
59  public void Dispose() => httpClient.Dispose();
60 
70  async Task<TResult> RunRequest<TResult>(string route, object body, HttpMethod method, long? instanceId, CancellationToken cancellationToken)
71  {
72  if (route == null)
73  throw new ArgumentNullException(nameof(route));
74  if (method == null)
75  throw new ArgumentNullException(nameof(method));
76  if (body == null && (method == HttpMethod.Post || method == HttpMethod.Put))
77  throw new InvalidOperationException("Body cannot be null for POST or PUT!");
78 
79  var fullUri = new Uri(Url, route);
80 
81  var message = new HttpRequestMessage(method, fullUri);
82 
83  var serializerSettings = new JsonSerializerSettings
84  {
85  ContractResolver = new CamelCasePropertyNamesContractResolver(),
86  Converters = new[] { new VersionConverter() }
87  };
88 
89  if (body != null)
90  message.Content = new StringContent(JsonConvert.SerializeObject(body, serializerSettings), Encoding.UTF8, ApiHeaders.ApplicationJson);
91 
92  Headers.SetRequestHeaders(message.Headers, instanceId);
93 
94  await Task.WhenAll(requestLoggers.Select(x => x.LogRequest(message, cancellationToken))).ConfigureAwait(false);
95 
96  var response = await httpClient.SendAsync(message, cancellationToken).ConfigureAwait(false);
97 
98  await Task.WhenAll(requestLoggers.Select(x => x.LogResponse(response, cancellationToken))).ConfigureAwait(false);
99 
100  var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
101 
102  if (!response.IsSuccessStatusCode)
103  {
104  ErrorMessage errorMessage = null;
105  try
106  {
107  //check if json serializes to an error message
108  errorMessage = JsonConvert.DeserializeObject<ErrorMessage>(json, serializerSettings);
109  }
110  catch (JsonException) { }
111 
112  const string BadSpecExtension = " This is not part of TGS4 communication specification and should be reported if it was returned from a TGS4 server!";
113 
114  switch (response.StatusCode)
115  {
116  case HttpStatusCode.UpgradeRequired:
117  throw new ApiMismatchException(errorMessage ?? new ErrorMessage
118  {
119  Message = "API Mismatch but no current API version provided!" + BadSpecExtension,
120  SeverApiVersion = null
121  });
122  case HttpStatusCode.Unauthorized:
123  throw new UnauthorizedException();
124  case HttpStatusCode.RequestTimeout:
125  throw new RequestTimeoutException();
126  case HttpStatusCode.Forbidden:
128  case HttpStatusCode.ServiceUnavailable:
129  throw new ServiceUnavailableException();
130  case HttpStatusCode.Gone:
131  errorMessage = errorMessage ?? new ErrorMessage
132  {
133  Message = "The requested resource could not be found!",
134  SeverApiVersion = null
135  };
136  goto case HttpStatusCode.Conflict;
137  case HttpStatusCode.NotFound: //our fault somehow
138  errorMessage = errorMessage ?? new ErrorMessage
139  {
140  Message = "This is not a valid route!" + BadSpecExtension,
141  SeverApiVersion = null
142  };
143  goto case HttpStatusCode.Conflict;
144  case HttpStatusCode.Conflict:
145  throw new ConflictException(errorMessage ?? new ErrorMessage
146  {
147  Message = "An undescribed conflict occurred!" + BadSpecExtension,
148  SeverApiVersion = null
149  }, response.StatusCode);
150  case HttpStatusCode.NotImplemented:
151  case (HttpStatusCode)422: //unprocessable entity
152  throw new MethodNotSupportedException();
153  case HttpStatusCode.InternalServerError:
154  //response
155  throw new ServerErrorException(json); //json is html
156  case (HttpStatusCode)429: //rate limited
157  response.Headers.TryGetValues("Retry-After", out var values);
158  throw new RateLimitException(values?.FirstOrDefault());
159  default:
160  throw new ApiConflictException(errorMessage, response.StatusCode);
161  }
162  }
163 
164  if (String.IsNullOrWhiteSpace(json))
165  json = JsonConvert.SerializeObject(new object());
166 
167  try
168  {
169  return JsonConvert.DeserializeObject<TResult>(json, serializerSettings);
170  }
171  catch (JsonException)
172  {
173  throw new UnrecognizedResponseException(json, response.StatusCode);
174  }
175  }
176 
178  public Task<TResult> Create<TResult>(string route, CancellationToken cancellationToken) => RunRequest<TResult>(route, new object(), HttpMethod.Put, null, cancellationToken);
179 
181  public Task<TResult> Read<TResult>(string route, CancellationToken cancellationToken) => RunRequest<TResult>(route, null, HttpMethod.Get, null, cancellationToken);
182 
184  public Task<TResult> Update<TResult>(string route, CancellationToken cancellationToken) => RunRequest<TResult>(route, new object(), HttpMethod.Post, null, cancellationToken);
185 
187  public Task<TResult> Update<TBody, TResult>(string route, TBody body, CancellationToken cancellationToken) => RunRequest<TResult>(route, body, HttpMethod.Post, null, cancellationToken);
188 
190  public Task Update<TBody>(string route, TBody body, CancellationToken cancellationToken) => RunRequest<object>(route, body, HttpMethod.Post, null, cancellationToken);
191 
193  public Task<TResult> Create<TBody, TResult>(string route, TBody body, CancellationToken cancellationToken) => RunRequest<TResult>(route, body, HttpMethod.Put, null, cancellationToken);
194 
196  public Task Delete(string route, CancellationToken cancellationToken) => RunRequest<object>(route, null, HttpMethod.Delete, null, cancellationToken);
197 
199  public Task<TResult> Create<TBody, TResult>(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, body, HttpMethod.Put, instanceId, cancellationToken);
200 
202  public Task<TResult> Read<TResult>(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, null, HttpMethod.Get, instanceId, cancellationToken);
203 
205  public Task<TResult> Update<TBody, TResult>(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, body, HttpMethod.Post, instanceId, cancellationToken);
206 
208  public Task Delete(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<object>(route, null, HttpMethod.Delete, instanceId, cancellationToken);
209 
211  public Task Delete<TBody>(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest<object>(route, body, HttpMethod.Delete, instanceId, cancellationToken);
212 
214  public Task<TResult> Delete<TResult>(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, null, HttpMethod.Delete, instanceId, cancellationToken);
215 
217  public Task<TResult> Create<TResult>(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, new object(), HttpMethod.Put, instanceId, cancellationToken);
218 
220  public Task<TResult> Patch<TResult>(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, new object(), new HttpMethod("PATCH"), instanceId, cancellationToken);
221 
223  public void AddRequestLogger(IRequestLogger requestLogger) => requestLoggers.Add(requestLogger ?? throw new ArgumentNullException(nameof(requestLogger)));
224  }
225 }
+Go to the documentation of this file.
1 using Newtonsoft.Json;
2 using Newtonsoft.Json.Converters;
3 using Newtonsoft.Json.Serialization;
4 using System;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Net;
8 using System.Net.Http;
9 using System.Text;
10 using System.Threading;
11 using System.Threading.Tasks;
12 using Tgstation.Server.Api;
14 
15 namespace Tgstation.Server.Client
16 {
18  sealed class ApiClient : IApiClient
19  {
21  public Uri Url { get; }
22 
24  public ApiHeaders Headers { get; }
25 
27  public TimeSpan Timeout
28  {
29  get => httpClient.Timeout;
30  set => httpClient.Timeout = value;
31  }
32 
37 
41  readonly List<IRequestLogger> requestLoggers;
42 
49  public ApiClient(IHttpClient httpClient, Uri url, ApiHeaders apiHeaders)
50  {
51  this.httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
52  Url = url ?? throw new ArgumentNullException(nameof(url));
53  Headers = apiHeaders ?? throw new ArgumentNullException(nameof(apiHeaders));
54 
55  requestLoggers = new List<IRequestLogger>();
56  }
57 
59  public void Dispose() => httpClient.Dispose();
60 
70  async Task<TResult> RunRequest<TResult>(string route, object body, HttpMethod method, long? instanceId, CancellationToken cancellationToken)
71  {
72  if (route == null)
73  throw new ArgumentNullException(nameof(route));
74  if (method == null)
75  throw new ArgumentNullException(nameof(method));
76  if (body == null && (method == HttpMethod.Post || method == HttpMethod.Put))
77  throw new InvalidOperationException("Body cannot be null for POST or PUT!");
78 
79  var fullUri = new Uri(Url, route);
80 
81  var message = new HttpRequestMessage(method, fullUri);
82 
83  var serializerSettings = new JsonSerializerSettings
84  {
85  ContractResolver = new CamelCasePropertyNamesContractResolver(),
86  Converters = new[] { new VersionConverter() }
87  };
88 
89  if (body != null)
90  message.Content = new StringContent(JsonConvert.SerializeObject(body, serializerSettings), Encoding.UTF8, ApiHeaders.ApplicationJson);
91 
92  Headers.SetRequestHeaders(message.Headers, instanceId);
93 
94  await Task.WhenAll(requestLoggers.Select(x => x.LogRequest(message, cancellationToken))).ConfigureAwait(false);
95 
96  var response = await httpClient.SendAsync(message, cancellationToken).ConfigureAwait(false);
97 
98  await Task.WhenAll(requestLoggers.Select(x => x.LogResponse(response, cancellationToken))).ConfigureAwait(false);
99 
100  var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
101 
102  if (!response.IsSuccessStatusCode)
103  {
104  ErrorMessage errorMessage = null;
105  try
106  {
107  //check if json serializes to an error message
108  errorMessage = JsonConvert.DeserializeObject<ErrorMessage>(json, serializerSettings);
109  }
110  catch (JsonException) { }
111 
112  const string BadSpecExtension = " This is not part of TGS4 communication specification and should be reported if it was returned from a TGS4 server!";
113 
114  switch (response.StatusCode)
115  {
116  case HttpStatusCode.UpgradeRequired:
117  throw new ApiMismatchException(errorMessage ?? new ErrorMessage
118  {
119  Message = "API Mismatch but no current API version provided!" + BadSpecExtension,
120  SeverApiVersion = null
121  });
122  case HttpStatusCode.Unauthorized:
123  throw new UnauthorizedException();
124  case HttpStatusCode.RequestTimeout:
125  throw new RequestTimeoutException();
126  case HttpStatusCode.Forbidden:
128  case HttpStatusCode.ServiceUnavailable:
129  throw new ServiceUnavailableException();
130  case HttpStatusCode.Gone:
131  errorMessage = errorMessage ?? new ErrorMessage
132  {
133  Message = "The requested resource could not be found!",
134  SeverApiVersion = null
135  };
136  goto case HttpStatusCode.Conflict;
137  case HttpStatusCode.NotFound: //our fault somehow
138  errorMessage = errorMessage ?? new ErrorMessage
139  {
140  Message = "This is not a valid route!" + BadSpecExtension,
141  SeverApiVersion = null
142  };
143  goto case HttpStatusCode.Conflict;
144  case HttpStatusCode.Conflict:
145  throw new ConflictException(errorMessage ?? new ErrorMessage
146  {
147  Message = "An undescribed conflict occurred!" + BadSpecExtension,
148  SeverApiVersion = null
149  }, response.StatusCode);
150  case HttpStatusCode.NotImplemented:
151  case (HttpStatusCode)422: //unprocessable entity
152  throw new MethodNotSupportedException();
153  case HttpStatusCode.InternalServerError:
154  //response
155  throw new ServerErrorException(json); //json is html
156  case (HttpStatusCode)429: //rate limited
157  response.Headers.TryGetValues("Retry-After", out var values);
158  throw new RateLimitException(values?.FirstOrDefault());
159  default:
160  throw new ApiConflictException(errorMessage, response.StatusCode);
161  }
162  }
163 
164  if (String.IsNullOrWhiteSpace(json))
165  json = JsonConvert.SerializeObject(new object());
166 
167  try
168  {
169  return JsonConvert.DeserializeObject<TResult>(json, serializerSettings);
170  }
171  catch (JsonException)
172  {
173  throw new UnrecognizedResponseException(json, response.StatusCode);
174  }
175  }
176 
178  public Task<TResult> Create<TResult>(string route, CancellationToken cancellationToken) => RunRequest<TResult>(route, new object(), HttpMethod.Put, null, cancellationToken);
179 
181  public Task<TResult> Read<TResult>(string route, CancellationToken cancellationToken) => RunRequest<TResult>(route, null, HttpMethod.Get, null, cancellationToken);
182 
184  public Task<TResult> Update<TResult>(string route, CancellationToken cancellationToken) => RunRequest<TResult>(route, new object(), HttpMethod.Post, null, cancellationToken);
185 
187  public Task<TResult> Update<TBody, TResult>(string route, TBody body, CancellationToken cancellationToken) => RunRequest<TResult>(route, body, HttpMethod.Post, null, cancellationToken);
188 
190  public Task Update<TBody>(string route, TBody body, CancellationToken cancellationToken) => RunRequest<object>(route, body, HttpMethod.Post, null, cancellationToken);
191 
193  public Task<TResult> Create<TBody, TResult>(string route, TBody body, CancellationToken cancellationToken) => RunRequest<TResult>(route, body, HttpMethod.Put, null, cancellationToken);
194 
196  public Task Delete(string route, CancellationToken cancellationToken) => RunRequest<object>(route, null, HttpMethod.Delete, null, cancellationToken);
197 
199  public Task<TResult> Create<TBody, TResult>(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, body, HttpMethod.Put, instanceId, cancellationToken);
200 
202  public Task<TResult> Read<TResult>(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, null, HttpMethod.Get, instanceId, cancellationToken);
203 
205  public Task<TResult> Update<TBody, TResult>(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, body, HttpMethod.Post, instanceId, cancellationToken);
206 
208  public Task Delete(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<object>(route, null, HttpMethod.Delete, instanceId, cancellationToken);
209 
211  public Task Delete<TBody>(string route, TBody body, long instanceId, CancellationToken cancellationToken) => RunRequest<object>(route, body, HttpMethod.Delete, instanceId, cancellationToken);
212 
214  public Task<TResult> Delete<TResult>(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, null, HttpMethod.Delete, instanceId, cancellationToken);
215 
217  public Task<TResult> Create<TResult>(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, new object(), HttpMethod.Put, instanceId, cancellationToken);
218 
220  public Task<TResult> Patch<TResult>(string route, long instanceId, CancellationToken cancellationToken) => RunRequest<TResult>(route, new object(), new HttpMethod("PATCH"), instanceId, cancellationToken);
221 
223  public void AddRequestLogger(IRequestLogger requestLogger) => requestLoggers.Add(requestLogger ?? throw new ArgumentNullException(nameof(requestLogger)));
224  }
225 }
+
const string ApplicationJson
TODO: Remove this when https://github.com/dotnet/corefx/pull/26701 makes it into the sdk ...
Definition: ApiHeaders.cs:21
Occurs when a GitHub rate limit occurs
@@ -104,7 +105,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Represents the header that must be present for every server request
Definition: ApiHeaders.cs:16
Occurs when the client makes a request while the server is starting or stopping
For sending HTTP requests
Definition: IHttpClient.cs:11
-
readonly List< IRequestLogger > requestLoggers
The IRequestLoggers used by the ApiClient
Definition: ApiClient.cs:41
Occurs when the client provides invalid credentials
ApiClient(IHttpClient httpClient, Uri url, ApiHeaders apiHeaders)
Construct an ApiClient
Definition: ApiClient.cs:49
diff --git a/_api_client_factory_8cs_source.html b/_api_client_factory_8cs_source.html index 6b3212ea22..55aacd880e 100644 --- a/_api_client_factory_8cs_source.html +++ b/_api_client_factory_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ApiClientFactory.cs
-Go to the documentation of this file.
1 using System;
2 using Tgstation.Server.Api;
3 
4 namespace Tgstation.Server.Client
5 {
8  {
10  public IApiClient CreateApiClient(Uri url, ApiHeaders apiHeaders) => new ApiClient(new HttpClient(), url, apiHeaders);
11  }
12 }
+Go to the documentation of this file.
1 using System;
2 using Tgstation.Server.Api;
3 
4 namespace Tgstation.Server.Client
5 {
8  {
10  public IApiClient CreateApiClient(Uri url, ApiHeaders apiHeaders) => new ApiClient(new HttpClient(), url, apiHeaders);
11  }
12 }
+
Represents the header that must be present for every server request
Definition: ApiHeaders.cs:16
- diff --git a/_api_conflict_exception_8cs_source.html b/_api_conflict_exception_8cs_source.html index e1e2e1cea6..42aa827bda 100644 --- a/_api_conflict_exception_8cs_source.html +++ b/_api_conflict_exception_8cs_source.html @@ -92,15 +92,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ApiConflictException.cs
-Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
10  public sealed class ApiConflictException : ClientException
11  {
17  public ApiConflictException(ErrorMessage errorMessage, HttpStatusCode statusCode) : base(errorMessage ?? new ErrorMessage
18  {
19  Message = "An unknown API error occurred!",
20  SeverApiVersion = null
21  }, statusCode)
22  { }
23 
27  public ApiConflictException() { }
28 
33  public ApiConflictException(string message) : base(message) { }
34 
40  public ApiConflictException(string message, Exception innerException) : base(message, innerException) { }
41  }
42 }
+Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
10  public sealed class ApiConflictException : ClientException
11  {
17  public ApiConflictException(ErrorMessage errorMessage, HttpStatusCode statusCode) : base(errorMessage ?? new ErrorMessage
18  {
19  Message = "An unknown API error occurred!",
20  SeverApiVersion = null
21  }, statusCode)
22  { }
23 
27  public ApiConflictException() { }
28 
33  public ApiConflictException(string message) : base(message) { }
34 
40  public ApiConflictException(string message, Exception innerException) : base(message, innerException) { }
41  }
42 }
+
ApiConflictException(ErrorMessage errorMessage, HttpStatusCode statusCode)
Construct an ApiConflictException using an errorMessage
Occurs when the server returns an unknown response
ApiConflictException(string message)
Construct an ApiConflictException with a message
ApiConflictException(string message, Exception innerException)
Construct an ApiConflictException with a message and innerException
-
ApiConflictException()
Construct an ApiConflictException
Represents an error message returned by the server
Definition: ErrorMessage.cs:8
diff --git a/_api_controller_8cs_source.html b/_api_controller_8cs_source.html index f0e148b1a6..f85581ee5f 100644 --- a/_api_controller_8cs_source.html +++ b/_api_controller_8cs_source.html @@ -92,12 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ApiController.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Http;
2 using Microsoft.AspNetCore.Mvc;
3 using Microsoft.AspNetCore.Mvc.Filters;
4 using Microsoft.Extensions.Logging;
5 using System;
6 using System.Globalization;
7 using System.Linq;
8 using System.Net;
9 using System.Threading.Tasks;
10 using Tgstation.Server.Api;
14 
15 namespace Tgstation.Server.Host.Controllers
16 {
20  [Produces(ApiHeaders.ApplicationJson)]
21  [Consumes(ApiHeaders.ApplicationJson)]
22  public abstract class ApiController : Controller
23  {
27  protected ApiHeaders ApiHeaders { get; private set; }
28 
32  protected IDatabaseContext DatabaseContext { get; }
33 
38 
42  protected ILogger Logger { get; }
43 
47  protected Models.Instance Instance { get; }
48 
52  readonly bool requireInstance;
53 
61  public ApiController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger logger, bool requireInstance)
62  {
63  DatabaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext));
64  if (authenticationContextFactory == null)
65  throw new ArgumentNullException(nameof(authenticationContextFactory));
66  Logger = logger ?? throw new ArgumentNullException(nameof(logger));
67  AuthenticationContext = authenticationContextFactory.CurrentAuthenticationContext;
69  this.requireInstance = requireInstance;
70  }
71 
73  public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
74  {
75  //ALL valid token and login requests that match a route go through this function
76  //404 is returned before
77 
78  if (AuthenticationContext != null && AuthenticationContext.User == null)
79  {
80  //valid token, expired password
81  await Unauthorized().ExecuteResultAsync(context).ConfigureAwait(false);
82  return;
83  }
84 
85  //validate the headers
86  try
87  {
88  ApiHeaders = new ApiHeaders(Request.GetTypedHeaders());
89 
90  if (!ApiHeaders.Compatible())
91  {
92  await StatusCode((int)HttpStatusCode.UpgradeRequired, new ErrorMessage
93  {
94  Message = "Provided API version is incompatible with server version!"
95  }).ExecuteResultAsync(context).ConfigureAwait(false);
96  return;
97  }
98 
99  if (requireInstance)
100  {
101  if (!ApiHeaders.InstanceId.HasValue)
102  {
103  await BadRequest(new ErrorMessage { Message = "Missing Instance header!" }).ExecuteResultAsync(context).ConfigureAwait(false);
104  return;
105  }
107  {
108  //accessing an instance they don't have access to or one that's disabled
109  await Forbid().ExecuteResultAsync(context).ConfigureAwait(false);
110  return;
111  }
112  }
113  }
114  catch (InvalidOperationException e)
115  {
116  await BadRequest(new ErrorMessage { Message = e.Message }).ExecuteResultAsync(context).ConfigureAwait(false);
117  return;
118  }
119 
120  if (ModelState?.IsValid == false)
121  {
122  var errorMessages = ModelState.SelectMany(x => x.Value.Errors).Select(x => x.ErrorMessage).ToList();
123  //do some fuckery to remove RequiredAttribute errors
124  for (var I = 0; I < errorMessages.Count; ++I)
125  {
126  var message = errorMessages[I];
127  if (message.StartsWith("The ", StringComparison.Ordinal) && message.EndsWith(" field is required.", StringComparison.Ordinal))
128  {
129  errorMessages.RemoveAt(I);
130  --I;
131  }
132  }
133  if (errorMessages.Count > 0)
134  {
135  await BadRequest(new ErrorMessage { Message = String.Join(Environment.NewLine, errorMessages) }).ExecuteResultAsync(context).ConfigureAwait(false);
136  return;
137  }
138  }
139 
140  Logger.LogDebug("Request made by User ID {0}. Api version: {1}. User-Agent: {2}. Type: {3}. Route {4}{5} to Instance {6}", AuthenticationContext?.User.Id.ToString(CultureInfo.InvariantCulture), ApiHeaders.ApiVersion, ApiHeaders.UserAgent, Request.Method, Request.Path, Request.QueryString, ApiHeaders.InstanceId);
141 
142  try
143  {
144  await base.OnActionExecutionAsync(context, next).ConfigureAwait(false);
145  }
146  catch (OperationCanceledException e)
147  {
148  Logger.LogDebug("Request cancelled! Exception: {0}", e);
149  throw;
150  }
151  }
152  }
153 }
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Http;
2 using Microsoft.AspNetCore.Mvc;
3 using Microsoft.AspNetCore.Mvc.Filters;
4 using Microsoft.Extensions.Logging;
5 using System;
6 using System.Globalization;
7 using System.Linq;
8 using System.Net;
9 using System.Threading.Tasks;
10 using Tgstation.Server.Api;
14 
15 namespace Tgstation.Server.Host.Controllers
16 {
20  [Produces(ApiHeaders.ApplicationJson)]
21  [Consumes(ApiHeaders.ApplicationJson)]
22  public abstract class ApiController : Controller
23  {
27  protected ApiHeaders ApiHeaders { get; private set; }
28 
32  protected IDatabaseContext DatabaseContext { get; }
33 
38 
42  protected ILogger Logger { get; }
43 
47  protected Models.Instance Instance { get; }
48 
52  readonly bool requireInstance;
53 
61  public ApiController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger logger, bool requireInstance)
62  {
63  DatabaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext));
64  if (authenticationContextFactory == null)
65  throw new ArgumentNullException(nameof(authenticationContextFactory));
66  Logger = logger ?? throw new ArgumentNullException(nameof(logger));
67  AuthenticationContext = authenticationContextFactory.CurrentAuthenticationContext;
69  this.requireInstance = requireInstance;
70  }
71 
73  public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
74  {
75  //ALL valid token and login requests that match a route go through this function
76  //404 is returned before
77 
78  if (AuthenticationContext != null && AuthenticationContext.User == null)
79  {
80  //valid token, expired password
81  await Unauthorized().ExecuteResultAsync(context).ConfigureAwait(false);
82  return;
83  }
84 
85  //validate the headers
86  try
87  {
88  ApiHeaders = new ApiHeaders(Request.GetTypedHeaders());
89 
90  if (!ApiHeaders.Compatible())
91  {
92  await StatusCode((int)HttpStatusCode.UpgradeRequired, new ErrorMessage
93  {
94  Message = "Provided API version is incompatible with server version!"
95  }).ExecuteResultAsync(context).ConfigureAwait(false);
96  return;
97  }
98 
99  if (requireInstance)
100  {
101  if (!ApiHeaders.InstanceId.HasValue)
102  {
103  await BadRequest(new ErrorMessage { Message = "Missing Instance header!" }).ExecuteResultAsync(context).ConfigureAwait(false);
104  return;
105  }
107  {
108  //accessing an instance they don't have access to or one that's disabled
109  await Forbid().ExecuteResultAsync(context).ConfigureAwait(false);
110  return;
111  }
112  }
113  }
114  catch (InvalidOperationException e)
115  {
116  await BadRequest(new ErrorMessage { Message = e.Message }).ExecuteResultAsync(context).ConfigureAwait(false);
117  return;
118  }
119 
120  if (ModelState?.IsValid == false)
121  {
122  var errorMessages = ModelState.SelectMany(x => x.Value.Errors).Select(x => x.ErrorMessage).ToList();
123  //do some fuckery to remove RequiredAttribute errors
124  for (var I = 0; I < errorMessages.Count; ++I)
125  {
126  var message = errorMessages[I];
127  if (message.StartsWith("The ", StringComparison.Ordinal) && message.EndsWith(" field is required.", StringComparison.Ordinal))
128  {
129  errorMessages.RemoveAt(I);
130  --I;
131  }
132  }
133  if (errorMessages.Count > 0)
134  {
135  await BadRequest(new ErrorMessage { Message = String.Join(Environment.NewLine, errorMessages) }).ExecuteResultAsync(context).ConfigureAwait(false);
136  return;
137  }
138  }
139 
140  Logger.LogDebug("Request made by User ID {0}. Api version: {1}. User-Agent: {2}. Type: {3}. Route {4}{5} to Instance {6}", AuthenticationContext?.User.Id.ToString(CultureInfo.InvariantCulture), ApiHeaders.ApiVersion, ApiHeaders.UserAgent, Request.Method, Request.Path, Request.QueryString, ApiHeaders.InstanceId);
141 
142  try
143  {
144  await base.OnActionExecutionAsync(context, next).ConfigureAwait(false);
145  }
146  catch (OperationCanceledException e)
147  {
148  Logger.LogDebug("Request cancelled! Exception: {0}", e);
149  throw;
150  }
151  }
152  }
153 }
ApiController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger logger, bool requireInstance)
Construct an ApiController
override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
+
const string ApplicationJson
TODO: Remove this when https://github.com/dotnet/corefx/pull/26701 makes it into the sdk ...
Definition: ApiHeaders.cs:21
@@ -111,7 +112,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
string Message
A human readable description of the error
Definition: ErrorMessage.cs:13
- diff --git a/_api_mismatch_exception_8cs_source.html b/_api_mismatch_exception_8cs_source.html index ea72d11f59..f498deea61 100644 --- a/_api_mismatch_exception_8cs_source.html +++ b/_api_mismatch_exception_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ApiMismatchException.cs
-Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
10  public sealed class ApiMismatchException : ClientException
11  {
16  public ApiMismatchException(ErrorMessage errorMessage) : base(errorMessage, HttpStatusCode.BadRequest)
17  {
18  if (errorMessage == null)
19  throw new ArgumentNullException(nameof(errorMessage));
20  }
21 
25  public ApiMismatchException() { }
26 
31  public ApiMismatchException(string message) : base(message) { }
32 
38  public ApiMismatchException(string message, Exception innerException) : base(message, innerException) { }
39  }
40 }
+Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
10  public sealed class ApiMismatchException : ClientException
11  {
16  public ApiMismatchException(ErrorMessage errorMessage) : base(errorMessage, HttpStatusCode.BadRequest)
17  {
18  if (errorMessage == null)
19  throw new ArgumentNullException(nameof(errorMessage));
20  }
21 
25  public ApiMismatchException() { }
26 
31  public ApiMismatchException(string message) : base(message) { }
32 
38  public ApiMismatchException(string message, Exception innerException) : base(message, innerException) { }
39  }
40 }
+ -
Occurs when the API version of the client is not compatible with the server&#39;s
ApiMismatchException(ErrorMessage errorMessage)
Construct an ApiMismatchException using an errorMessage
ApiMismatchException(string message, Exception innerException)
Construct an ApiMismatchException with a message and innerException
diff --git a/_application_builder_extensions_8cs_source.html b/_application_builder_extensions_8cs_source.html index a6d6e9cbe2..8af2760f1e 100644 --- a/_application_builder_extensions_8cs_source.html +++ b/_application_builder_extensions_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ApplicationBuilderExtensions.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Builder;
2 using Microsoft.AspNetCore.Mvc;
3 using Microsoft.EntityFrameworkCore;
4 using System;
5 using System.Globalization;
7 
8 namespace Tgstation.Server.Host.Core
9 {
14  {
19  public static void UseDbConflictHandling(this IApplicationBuilder applicationBuilder)
20  {
21  if (applicationBuilder == null)
22  throw new ArgumentNullException(nameof(applicationBuilder));
23  applicationBuilder.Use(async (context, next) =>
24  {
25  try
26  {
27  await next().ConfigureAwait(false);
28  }
29  catch (DbUpdateException e)
30  {
31  await new ConflictObjectResult(new ErrorMessage { Message = String.Format(CultureInfo.InvariantCulture, "A database conflict has occurred: {0}", (e.InnerException ?? e).Message) }).ExecuteResultAsync(new ActionContext
32  {
33  HttpContext = context
34  }).ConfigureAwait(false);
35  }
36  });
37  }
38 
43  public static void UseCancelledRequestSuppression(this IApplicationBuilder applicationBuilder)
44  {
45  if (applicationBuilder == null)
46  throw new ArgumentNullException(nameof(applicationBuilder));
47  applicationBuilder.Use(async (context, next) =>
48  {
49  try
50  {
51  await next().ConfigureAwait(false);
52  }
53  catch (OperationCanceledException) { }
54  });
55  }
56  }
57 }
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Builder;
2 using Microsoft.AspNetCore.Mvc;
3 using Microsoft.EntityFrameworkCore;
4 using System;
5 using System.Globalization;
7 
8 namespace Tgstation.Server.Host.Core
9 {
14  {
19  public static void UseDbConflictHandling(this IApplicationBuilder applicationBuilder)
20  {
21  if (applicationBuilder == null)
22  throw new ArgumentNullException(nameof(applicationBuilder));
23  applicationBuilder.Use(async (context, next) =>
24  {
25  try
26  {
27  await next().ConfigureAwait(false);
28  }
29  catch (DbUpdateException e)
30  {
31  await new ConflictObjectResult(new ErrorMessage { Message = String.Format(CultureInfo.InvariantCulture, "A database conflict has occurred: {0}", (e.InnerException ?? e).Message) }).ExecuteResultAsync(new ActionContext
32  {
33  HttpContext = context
34  }).ConfigureAwait(false);
35  }
36  });
37  }
38 
43  public static void UseCancelledRequestSuppression(this IApplicationBuilder applicationBuilder)
44  {
45  if (applicationBuilder == null)
46  throw new ArgumentNullException(nameof(applicationBuilder));
47  applicationBuilder.Use(async (context, next) =>
48  {
49  try
50  {
51  await next().ConfigureAwait(false);
52  }
53  catch (OperationCanceledException) { }
54  });
55  }
56  }
57 }
+ -
static void UseDbConflictHandling(this IApplicationBuilder applicationBuilder)
Return a ConflictObjectResult for DbUpdateExceptions
diff --git a/_authentication_context_factory_8cs_source.html b/_authentication_context_factory_8cs_source.html index eb38d803c6..26f5d9a0a8 100644 --- a/_authentication_context_factory_8cs_source.html +++ b/_authentication_context_factory_8cs_source.html @@ -92,10 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
AuthenticationContextFactory.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using System;
3 using System.Linq;
4 using System.Threading;
5 using System.Threading.Tasks;
7 
8 namespace Tgstation.Server.Host.Security
9 {
12  {
14  public IAuthenticationContext CurrentAuthenticationContext { get; private set; }
15 
20 
25 
30 
37  public AuthenticationContextFactory(ISystemIdentityFactory systemIdentityFactory, IDatabaseContext databaseContext, IIdentityCache identityCache)
38  {
39  this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory));
40  this.databaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext));
41  this.identityCache = identityCache ?? throw new ArgumentNullException(nameof(identityCache));
42  }
43 
45  public async Task CreateAuthenticationContext(long userId, long? instanceId, DateTimeOffset validBefore, CancellationToken cancellationToken)
46  {
47  if (CurrentAuthenticationContext != null)
48  throw new InvalidOperationException("Authentication context has already been loaded");
49 
50  var userQuery = databaseContext.Users.Where(x => x.Id == userId).FirstOrDefaultAsync(cancellationToken);
51 
52  var instanceUser = instanceId.HasValue ? (await databaseContext.InstanceUsers
53  .Where(x => x.UserId == userId && x.InstanceId == instanceId && x.Instance.Online.Value)
54  .Include(x => x.Instance)
55  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false)) : null;
56 
57  var user = await userQuery.ConfigureAwait(false);
58  if (user == default)
59  {
60  CurrentAuthenticationContext = new AuthenticationContext();
61  return;
62  }
63 
64  ISystemIdentity systemIdentity;
65  if (user.SystemIdentifier != null)
66  {
67  systemIdentity = identityCache.LoadCachedIdentity(user);
68  if (systemIdentity == null)
69  throw new InvalidOperationException("Cached system identity has expired!");
70  }
71  else
72  {
73  if (user.LastPasswordUpdate.HasValue && user.LastPasswordUpdate > validBefore)
74  {
75  CurrentAuthenticationContext = new AuthenticationContext();
76  return;
77  }
78  systemIdentity = null;
79  }
80 
81  CurrentAuthenticationContext = new AuthenticationContext(systemIdentity, user, instanceUser);
82  }
83  }
84 }
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using System;
3 using System.Linq;
4 using System.Threading;
5 using System.Threading.Tasks;
7 
8 namespace Tgstation.Server.Host.Security
9 {
12  {
14  public IAuthenticationContext CurrentAuthenticationContext { get; private set; }
15 
20 
25 
30 
37  public AuthenticationContextFactory(ISystemIdentityFactory systemIdentityFactory, IDatabaseContext databaseContext, IIdentityCache identityCache)
38  {
39  this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory));
40  this.databaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext));
41  this.identityCache = identityCache ?? throw new ArgumentNullException(nameof(identityCache));
42  }
43 
45  public async Task CreateAuthenticationContext(long userId, long? instanceId, DateTimeOffset validBefore, CancellationToken cancellationToken)
46  {
47  if (CurrentAuthenticationContext != null)
48  throw new InvalidOperationException("Authentication context has already been loaded");
49 
50  var userQuery = databaseContext.Users.Where(x => x.Id == userId).FirstOrDefaultAsync(cancellationToken);
51 
52  var instanceUser = instanceId.HasValue ? (await databaseContext.InstanceUsers
53  .Where(x => x.UserId == userId && x.InstanceId == instanceId && x.Instance.Online.Value)
54  .Include(x => x.Instance)
55  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false)) : null;
56 
57  var user = await userQuery.ConfigureAwait(false);
58  if (user == default)
59  {
60  CurrentAuthenticationContext = new AuthenticationContext();
61  return;
62  }
63 
64  ISystemIdentity systemIdentity;
65  if (user.SystemIdentifier != null)
66  {
67  systemIdentity = identityCache.LoadCachedIdentity(user);
68  if (systemIdentity == null)
69  throw new InvalidOperationException("Cached system identity has expired!");
70  }
71  else
72  {
73  if (user.LastPasswordUpdate.HasValue && user.LastPasswordUpdate > validBefore)
74  {
75  CurrentAuthenticationContext = new AuthenticationContext();
76  return;
77  }
78  systemIdentity = null;
79  }
80 
81  CurrentAuthenticationContext = new AuthenticationContext(systemIdentity, user, instanceUser);
82  }
83  }
84 }
AuthenticationContextFactory(ISystemIdentityFactory systemIdentityFactory, IDatabaseContext databaseContext, IIdentityCache identityCache)
Construct an AuthenticationContextFactory
+
Represents a user on the current System.Runtime.InteropServices.OSPlatform
@@ -103,7 +104,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
async Task CreateAuthenticationContext(long userId, long?instanceId, DateTimeOffset validBefore, CancellationToken cancellationToken)
Create an IAuthenticationContext to populate CurrentAuthenticationContext
readonly IIdentityCache identityCache
The IIdentityCache for the AuthenticationContextFactory
-
readonly ISystemIdentityFactory systemIdentityFactory
The ISystemIdentityFactory for the AuthenticationContextFactory
readonly IDatabaseContext databaseContext
The IDatabaseContext for the AuthenticationContextFactory
diff --git a/_byond_8cs_source.html b/_byond_8cs_source.html index bb6b6b92a5..3c990821fc 100644 --- a/_byond_8cs_source.html +++ b/_byond_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Byond.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public sealed class Byond
9  {
13  public Version Version { get; set; }
14 
18  public Job InstallJob { get; set; }
19  }
20 }
- +Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public sealed class Byond
9  {
13  public Version Version { get; set; }
14 
18  public Job InstallJob { get; set; }
19  }
20 }
+
Represents a BYOND installation
Definition: Byond.cs:8
Represents a long running job on the server. Model is read-only, updates attempt to cancel the job ...
Definition: Job.cs:6
diff --git a/_byond_client_8cs_source.html b/_byond_client_8cs_source.html index d7542d489f..6103aacb4f 100644 --- a/_byond_client_8cs_source.html +++ b/_byond_client_8cs_source.html @@ -92,8 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ByondClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client.Components
9 {
11  sealed class ByondClient : IByondClient
12  {
20  readonly Instance instance;
21 
27  public ByondClient(IApiClient apiClient, Instance instance)
28  {
29  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
30  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
31  }
32 
34  public Task<Byond> ActiveVersion(CancellationToken cancellationToken) => apiClient.Read<Byond>(Routes.Byond, instance.Id, cancellationToken);
35 
37  public Task<IReadOnlyList<Byond>> InstalledVersions(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<Byond>>(Routes.List(Routes.Byond), instance.Id, cancellationToken);
38 
40  public Task<Byond> SetActiveVersion(Byond byond, CancellationToken cancellationToken) => apiClient.Update<Byond, Byond>(Routes.Byond, byond ?? throw new ArgumentNullException(nameof(byond)), instance.Id, cancellationToken);
41  }
42 }
const string Byond
The Models.Byond controller
Definition: Routes.cs:34
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client.Components
9 {
11  sealed class ByondClient : IByondClient
12  {
20  readonly Instance instance;
21 
27  public ByondClient(IApiClient apiClient, Instance instance)
28  {
29  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
30  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
31  }
32 
34  public Task<Byond> ActiveVersion(CancellationToken cancellationToken) => apiClient.Read<Byond>(Routes.Byond, instance.Id, cancellationToken);
35 
37  public Task<IReadOnlyList<Byond>> InstalledVersions(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<Byond>>(Routes.List(Routes.Byond), instance.Id, cancellationToken);
38 
40  public Task<Byond> SetActiveVersion(Byond byond, CancellationToken cancellationToken) => apiClient.Update<Byond, Byond>(Routes.Byond, byond ?? throw new ArgumentNullException(nameof(byond)), instance.Id, cancellationToken);
41  }
42 }
const string Byond
The Models.Byond controller
Definition: Routes.cs:34
+
For managing the Byond installation
Definition: IByondClient.cs:11
ByondClient(IApiClient apiClient, Instance instance)
Construct a ByondClient
Definition: ByondClient.cs:27
diff --git a/_byond_command_8cs_source.html b/_byond_command_8cs_source.html index 24d3eeb301..3f884690b9 100644 --- a/_byond_command_8cs_source.html +++ b/_byond_command_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ByondCommand.cs
-Go to the documentation of this file.
1 using System;
2 using System.Globalization;
3 using System.Threading;
4 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Components.Chat.Commands
8 {
12  sealed class ByondCommand : ICommand
13  {
15  public string Name => "byond";
16 
18  public string HelpText => "Displays the active Byond version";
19 
21  public bool AdminOnly => false;
22 
27 
32  public ByondCommand(IByondManager byondManager)
33  {
34  this.byondManager = byondManager ?? throw new ArgumentNullException(nameof(byondManager));
35  }
36 
38  public Task<string> Invoke(string arguments, User user, CancellationToken cancellationToken) => Task.FromResult(byondManager.ActiveVersion == null ? "None!" : String.Format(CultureInfo.InvariantCulture, "{0}.{1}", byondManager.ActiveVersion.Major, byondManager.ActiveVersion.Minor));
39  }
40 }
+Go to the documentation of this file.
1 using System;
2 using System.Globalization;
3 using System.Threading;
4 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Components.Chat.Commands
8 {
12  sealed class ByondCommand : ICommand
13  {
15  public string Name => "byond";
16 
18  public string HelpText => "Displays the active Byond version";
19 
21  public bool AdminOnly => false;
22 
27 
32  public ByondCommand(IByondManager byondManager)
33  {
34  this.byondManager = byondManager ?? throw new ArgumentNullException(nameof(byondManager));
35  }
36 
38  public Task<string> Invoke(string arguments, User user, CancellationToken cancellationToken) => Task.FromResult(byondManager.ActiveVersion == null ? "None!" : String.Format(CultureInfo.InvariantCulture, "{0}.{1}", byondManager.ActiveVersion.Major, byondManager.ActiveVersion.Minor));
39  }
40 }
+
readonly IByondManager byondManager
the IByondManager for the ByondCommand
Definition: ByondCommand.cs:26
-
For managing the BYOND installation
Represents a tgs_chat_user datum
Definition: User.cs:10
ByondCommand(IByondManager byondManager)
Construct a ByondCommand
Definition: ByondCommand.cs:32
diff --git a/_byond_controller_8cs_source.html b/_byond_controller_8cs_source.html index ce95a15a46..5144b8c262 100644 --- a/_byond_controller_8cs_source.html +++ b/_byond_controller_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ByondController.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Globalization;
5 using System.Linq;
6 using System.Threading;
7 using System.Threading.Tasks;
8 using Tgstation.Server.Api;
15 
16 namespace Tgstation.Server.Host.Controllers
17 {
21  [Route(Routes.Byond)]
22  public sealed class ByondController : ModelController<Api.Models.Byond>
23  {
28 
33 
42  public ByondController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, IJobManager jobManager, ILogger<ByondController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
43  {
44  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
45  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
46  }
47 
49  [TgsAuthorize(ByondRights.ReadActive)]
50  public override Task<IActionResult> Read(CancellationToken cancellationToken) => Task.FromResult((IActionResult)
51  Json(new Api.Models.Byond
52  {
53  Version = instanceManager.GetInstance(Instance).ByondManager.ActiveVersion
54  }));
55 
57  [TgsAuthorize(ByondRights.ListInstalled)]
58  public override Task<IActionResult> List(CancellationToken cancellationToken) => Task.FromResult((IActionResult)
59  Json(instanceManager.GetInstance(Instance).ByondManager.InstalledVersions.Select(x => new Api.Models.Byond
60  {
61  Version = x
62  })));
63 
65  [TgsAuthorize(ByondRights.ChangeVersion)]
66  public override async Task<IActionResult> Update([FromBody] Api.Models.Byond model, CancellationToken cancellationToken)
67  {
68  if (model == null)
69  throw new ArgumentNullException(nameof(model));
70 
71  if (model.Version == null)
72  return BadRequest(new ErrorMessage { Message = "Missing version!" });
73 
74  var byondManager = instanceManager.GetInstance(Instance).ByondManager;
75 
76  //remove cruff fields
77  var installingVersion = new Version(model.Version.Major, model.Version.Minor);
78 
79  var result = new Api.Models.Byond();
80 
81  if (byondManager.InstalledVersions.Any(x => x == model.Version))
82  {
83  Logger.LogInformation("User ID {0} changing instance ID {1} BYOND version to {2}", AuthenticationContext.User.Id, Instance.Id, installingVersion);
84  await byondManager.ChangeVersion(model.Version, cancellationToken).ConfigureAwait(false);
85  }
86  else
87  {
88  Logger.LogInformation("User ID {0} installing BYOND version to {2} on instance ID {1}", AuthenticationContext.User.Id, Instance.Id, installingVersion);
89  //run the install through the job manager
90  var job = new Models.Job
91  {
92  Description = String.Format(CultureInfo.InvariantCulture, "Install BYOND version {0}", installingVersion),
93  StartedBy = AuthenticationContext.User,
94  CancelRightsType = RightsType.Byond,
95  CancelRight = (ulong)ByondRights.CancelInstall,
97  };
98  await jobManager.RegisterOperation(job, (paramJob, databaseContext, progressHandler, ct) => byondManager.ChangeVersion(installingVersion, ct), cancellationToken).ConfigureAwait(false);
99  result.InstallJob = job.ToApi();
100  }
101  result.Version = byondManager.ActiveVersion;
102  return result.InstallJob != null ? (IActionResult)Accepted(result) : Json(result);
103  }
104  }
105 }
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Globalization;
5 using System.Linq;
6 using System.Threading;
7 using System.Threading.Tasks;
8 using Tgstation.Server.Api;
15 
16 namespace Tgstation.Server.Host.Controllers
17 {
21  [Route(Routes.Byond)]
22  public sealed class ByondController : ModelController<Api.Models.Byond>
23  {
28 
33 
42  public ByondController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, IJobManager jobManager, ILogger<ByondController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
43  {
44  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
45  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
46  }
47 
49  [TgsAuthorize(ByondRights.ReadActive)]
50  public override Task<IActionResult> Read(CancellationToken cancellationToken) => Task.FromResult((IActionResult)
51  Json(new Api.Models.Byond
52  {
53  Version = instanceManager.GetInstance(Instance).ByondManager.ActiveVersion
54  }));
55 
57  [TgsAuthorize(ByondRights.ListInstalled)]
58  public override Task<IActionResult> List(CancellationToken cancellationToken) => Task.FromResult((IActionResult)
59  Json(instanceManager.GetInstance(Instance).ByondManager.InstalledVersions.Select(x => new Api.Models.Byond
60  {
61  Version = x
62  })));
63 
65  [TgsAuthorize(ByondRights.ChangeVersion)]
66  public override async Task<IActionResult> Update([FromBody] Api.Models.Byond model, CancellationToken cancellationToken)
67  {
68  if (model == null)
69  throw new ArgumentNullException(nameof(model));
70 
71  if (model.Version == null)
72  return BadRequest(new ErrorMessage { Message = "Missing version!" });
73 
74  var byondManager = instanceManager.GetInstance(Instance).ByondManager;
75 
76  //remove cruff fields
77  var installingVersion = new Version(model.Version.Major, model.Version.Minor);
78 
79  var result = new Api.Models.Byond();
80 
81  if (byondManager.InstalledVersions.Any(x => x == model.Version))
82  {
83  Logger.LogInformation("User ID {0} changing instance ID {1} BYOND version to {2}", AuthenticationContext.User.Id, Instance.Id, installingVersion);
84  await byondManager.ChangeVersion(model.Version, cancellationToken).ConfigureAwait(false);
85  }
86  else
87  {
88  Logger.LogInformation("User ID {0} installing BYOND version to {2} on instance ID {1}", AuthenticationContext.User.Id, Instance.Id, installingVersion);
89  //run the install through the job manager
90  var job = new Models.Job
91  {
92  Description = String.Format(CultureInfo.InvariantCulture, "Install BYOND version {0}", installingVersion),
93  StartedBy = AuthenticationContext.User,
94  CancelRightsType = RightsType.Byond,
95  CancelRight = (ulong)ByondRights.CancelInstall,
97  };
98  await jobManager.RegisterOperation(job, (paramJob, databaseContext, progressHandler, ct) => byondManager.ChangeVersion(installingVersion, ct), cancellationToken).ConfigureAwait(false);
99  result.InstallJob = job.ToApi();
100  }
101  result.Version = byondManager.ActiveVersion;
102  return result.InstallJob != null ? (IActionResult)Accepted(result) : Json(result);
103  }
104  }
105 }
const string Byond
The Models.Byond controller
Definition: Routes.cs:34
Manages the runtime of Jobs
Definition: IJobManager.cs:12
@@ -100,6 +100,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ByondRights
Rights for Models.Byond
Definition: ByondRights.cs:9
RightsType
The type of rights a model uses
Definition: RightsType.cs:6
+ @@ -108,7 +109,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly IJobManager jobManager
The IJobManager for the ByondController
Routes to a server actions
Definition: Routes.cs:9
-
long Id
The id of the Instance. Not modifiable
Definition: Instance.cs:14
ByondController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, IJobManager jobManager, ILogger< ByondController > logger)
Construct a ByondController
readonly IInstanceManager instanceManager
The IInstanceManager for the ByondController
diff --git a/_byond_manager_8cs_source.html b/_byond_manager_8cs_source.html index 4f8ca535e1..530f8b3a51 100644 --- a/_byond_manager_8cs_source.html +++ b/_byond_manager_8cs_source.html @@ -92,9 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ByondManager.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Collections.Generic;
4 using System.Globalization;
5 using System.Linq;
6 using System.Net;
7 using System.Text;
8 using System.Threading;
9 using System.Threading.Tasks;
11 using Tgstation.Server.Host.IO;
12 
13 namespace Tgstation.Server.Host.Components.Byond
14 {
16  sealed class ByondManager : IByondManager
17  {
21  public const string BinPath = "byond/bin";
22 
23  const string VersionFileName = "Version.txt";
24  const string ActiveVersionFileName = "ActiveVersion.txt";
25 
27  public Version ActiveVersion { get; private set; }
28 
30  public IReadOnlyList<Version> InstalledVersions
31  {
32  get
33  {
34  lock (installedVersions)
35  return installedVersions.Select(x => Version.Parse(x.Key)).ToList();
36  }
37  }
38 
43 
48 
52  readonly ILogger<ByondManager> logger;
53 
57  readonly Dictionary<string, Task> installedVersions;
58 
62  readonly SemaphoreSlim semaphore;
63 
64  static string VersionKey(Version version) => new Version(version.Major, version.Minor).ToString();
65 
72  public ByondManager(IIOManager ioManager, IByondInstaller byondInstaller, ILogger<ByondManager> logger)
73  {
74  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
75  this.byondInstaller = byondInstaller ?? throw new ArgumentNullException(nameof(byondInstaller));
76  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
77 
78  installedVersions = new Dictionary<string, Task>();
79  semaphore = new SemaphoreSlim(1);
80  }
81 
83  public void Dispose() => semaphore.Dispose();
84 
91  async Task InstallVersion(Version version, CancellationToken cancellationToken)
92  {
93  var ourTcs = new TaskCompletionSource<object>();
94  Task inProgressTask;
95 
96  var versionKey = VersionKey(version);
97  bool installed;
98  lock (installedVersions)
99  {
100  installed = installedVersions.TryGetValue(versionKey, out inProgressTask);
101  if (!installed)
102  installedVersions.Add(versionKey, ourTcs.Task);
103  }
104  if (installed)
105  using (cancellationToken.Register(() => ourTcs.SetCanceled()))
106  {
107  await Task.WhenAny(ourTcs.Task, inProgressTask).ConfigureAwait(false);
108  cancellationToken.ThrowIfCancellationRequested();
109  return;
110  }
111  try
112  {
113  var downloadTask = byondInstaller.DownloadVersion(version, cancellationToken);
114 
115  //okay up to us to install it then
116  await ioManager.DeleteDirectory(versionKey, cancellationToken).ConfigureAwait(false);
117  await ioManager.CreateDirectory(versionKey, cancellationToken).ConfigureAwait(false);
118 
119  try
120  {
121  var download = await downloadTask.ConfigureAwait(false);
122  await ioManager.ZipToDirectory(versionKey, download, cancellationToken).ConfigureAwait(false);
123  await byondInstaller.InstallByond(ioManager.ResolvePath(versionKey), version, cancellationToken).ConfigureAwait(false);
124 
125  //make sure to do this last because this is what tells us we have a valid version in the future
126  await ioManager.WriteAllBytes(ioManager.ConcatPath(versionKey, VersionFileName), Encoding.UTF8.GetBytes(version.ToString()), cancellationToken).ConfigureAwait(false);
127  }
128  catch (WebException e)
129  {
130  //since the user can easily provide non-exitent version numbers, we'll turn this into a JobException
131  throw new JobException(String.Format(CultureInfo.InvariantCulture, "Error downloading BYOND version: {0}", e.Message));
132  }
133  catch (OperationCanceledException)
134  {
135  throw;
136  }
137  catch
138  {
139  await ioManager.DeleteDirectory(versionKey, cancellationToken).ConfigureAwait(false);
140  throw;
141  }
142 
143  ourTcs.SetResult(null);
144  }
145  catch (Exception e)
146  {
147  lock (installedVersions)
148  installedVersions.Remove(versionKey);
149  ourTcs.SetException(e);
150  throw;
151  }
152  }
153 
155  public async Task ChangeVersion(Version version, CancellationToken cancellationToken)
156  {
157  await InstallVersion(version, cancellationToken).ConfigureAwait(false);
158  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
159  {
160  await ioManager.WriteAllBytes(ActiveVersionFileName, Encoding.UTF8.GetBytes(version.ToString()), cancellationToken).ConfigureAwait(false);
161  ActiveVersion = version;
162  }
163  }
164 
166  public async Task<IByondExecutableLock> UseExecutables(Version requiredVersion, CancellationToken cancellationToken)
167  {
168  var versionToUse = requiredVersion ?? ActiveVersion;
169  if (versionToUse == null)
170  throw new InvalidOperationException("No BYOND versions installed!");
171  await InstallVersion(versionToUse, cancellationToken).ConfigureAwait(false);
172 
173  var versionKey = VersionKey(versionToUse);
174 
175  return new ByondExecutableLock
176  {
177  DreamDaemonPath = ioManager.ResolvePath(ioManager.ConcatPath(versionKey, BinPath, byondInstaller.DreamDaemonName)),
178  DreamMakerPath = ioManager.ResolvePath(ioManager.ConcatPath(versionKey, BinPath, byondInstaller.DreamMakerName)),
179  Version = versionToUse
180  };
181  }
182 
184  public async Task StartAsync(CancellationToken cancellationToken)
185  {
186  async Task<byte[]> GetActiveVersion()
187  {
188  if (!await ioManager.FileExists(ActiveVersionFileName, cancellationToken).ConfigureAwait(false))
189  return null;
190  return await ioManager.ReadAllBytes(ActiveVersionFileName, cancellationToken).ConfigureAwait(false);
191  }
192 
193  var activeVersionBytesTask = GetActiveVersion();
194 
195  await ioManager.CreateDirectory(".", cancellationToken).ConfigureAwait(false);
196  var directories = await ioManager.GetDirectories(".", cancellationToken).ConfigureAwait(false);
197 
198  async Task ReadVersion(string path)
199  {
200  var versionFile = ioManager.ConcatPath(path, VersionFileName);
201  if (!await ioManager.FileExists(versionFile, cancellationToken).ConfigureAwait(false))
202  {
203  logger.LogInformation("Cleaning unparsable version path: {0}", ioManager.ResolvePath(path));
204  await ioManager.DeleteDirectory(path, cancellationToken).ConfigureAwait(false); //cleanup
205  return;
206  }
207  var bytes = await ioManager.ReadAllBytes(versionFile, cancellationToken).ConfigureAwait(false);
208  var text = Encoding.UTF8.GetString(bytes);
209  if (Version.TryParse(text, out var version))
210  {
211  var key = VersionKey(version);
212  lock (installedVersions)
213  if (!installedVersions.ContainsKey(key))
214  {
215  installedVersions.Add(key, Task.CompletedTask);
216  return;
217  }
218  }
219  await ioManager.DeleteDirectory(path, cancellationToken).ConfigureAwait(false);
220  };
221 
222  await Task.WhenAll(directories.Select(x => ReadVersion(x))).ConfigureAwait(false);
223 
224  var activeVersionBytes = await activeVersionBytesTask.ConfigureAwait(false);
225  if (activeVersionBytes != null)
226  {
227  var activeVersionString = Encoding.UTF8.GetString(activeVersionBytes);
228  if (Version.TryParse(activeVersionString, out var activeVersion))
229  ActiveVersion = activeVersion;
230  else
231  await ioManager.DeleteFile(ActiveVersionFileName, cancellationToken).ConfigureAwait(false);
232  }
233  }
234 
236  public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
237  }
238 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Collections.Generic;
4 using System.Globalization;
5 using System.Linq;
6 using System.Net;
7 using System.Text;
8 using System.Threading;
9 using System.Threading.Tasks;
11 using Tgstation.Server.Host.IO;
12 
13 namespace Tgstation.Server.Host.Components.Byond
14 {
16  sealed class ByondManager : IByondManager
17  {
21  public const string BinPath = "byond/bin";
22 
23  const string VersionFileName = "Version.txt";
24  const string ActiveVersionFileName = "ActiveVersion.txt";
25 
27  public Version ActiveVersion { get; private set; }
28 
30  public IReadOnlyList<Version> InstalledVersions
31  {
32  get
33  {
34  lock (installedVersions)
35  return installedVersions.Select(x => Version.Parse(x.Key)).ToList();
36  }
37  }
38 
43 
48 
52  readonly ILogger<ByondManager> logger;
53 
57  readonly Dictionary<string, Task> installedVersions;
58 
62  readonly SemaphoreSlim semaphore;
63 
64  static string VersionKey(Version version) => new Version(version.Major, version.Minor).ToString();
65 
72  public ByondManager(IIOManager ioManager, IByondInstaller byondInstaller, ILogger<ByondManager> logger)
73  {
74  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
75  this.byondInstaller = byondInstaller ?? throw new ArgumentNullException(nameof(byondInstaller));
76  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
77 
78  installedVersions = new Dictionary<string, Task>();
79  semaphore = new SemaphoreSlim(1);
80  }
81 
83  public void Dispose() => semaphore.Dispose();
84 
91  async Task InstallVersion(Version version, CancellationToken cancellationToken)
92  {
93  var ourTcs = new TaskCompletionSource<object>();
94  Task inProgressTask;
95 
96  var versionKey = VersionKey(version);
97  bool installed;
98  lock (installedVersions)
99  {
100  installed = installedVersions.TryGetValue(versionKey, out inProgressTask);
101  if (!installed)
102  installedVersions.Add(versionKey, ourTcs.Task);
103  }
104  if (installed)
105  using (cancellationToken.Register(() => ourTcs.SetCanceled()))
106  {
107  await Task.WhenAny(ourTcs.Task, inProgressTask).ConfigureAwait(false);
108  cancellationToken.ThrowIfCancellationRequested();
109  return;
110  }
111  try
112  {
113  var downloadTask = byondInstaller.DownloadVersion(version, cancellationToken);
114 
115  //okay up to us to install it then
116  await ioManager.DeleteDirectory(versionKey, cancellationToken).ConfigureAwait(false);
117  await ioManager.CreateDirectory(versionKey, cancellationToken).ConfigureAwait(false);
118 
119  try
120  {
121  var download = await downloadTask.ConfigureAwait(false);
122  await ioManager.ZipToDirectory(versionKey, download, cancellationToken).ConfigureAwait(false);
123  await byondInstaller.InstallByond(ioManager.ResolvePath(versionKey), version, cancellationToken).ConfigureAwait(false);
124 
125  //make sure to do this last because this is what tells us we have a valid version in the future
126  await ioManager.WriteAllBytes(ioManager.ConcatPath(versionKey, VersionFileName), Encoding.UTF8.GetBytes(version.ToString()), cancellationToken).ConfigureAwait(false);
127  }
128  catch (WebException e)
129  {
130  //since the user can easily provide non-exitent version numbers, we'll turn this into a JobException
131  throw new JobException(String.Format(CultureInfo.InvariantCulture, "Error downloading BYOND version: {0}", e.Message));
132  }
133  catch (OperationCanceledException)
134  {
135  throw;
136  }
137  catch
138  {
139  await ioManager.DeleteDirectory(versionKey, cancellationToken).ConfigureAwait(false);
140  throw;
141  }
142 
143  ourTcs.SetResult(null);
144  }
145  catch (Exception e)
146  {
147  lock (installedVersions)
148  installedVersions.Remove(versionKey);
149  ourTcs.SetException(e);
150  throw;
151  }
152  }
153 
155  public async Task ChangeVersion(Version version, CancellationToken cancellationToken)
156  {
157  await InstallVersion(version, cancellationToken).ConfigureAwait(false);
158  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
159  {
160  await ioManager.WriteAllBytes(ActiveVersionFileName, Encoding.UTF8.GetBytes(version.ToString()), cancellationToken).ConfigureAwait(false);
161  ActiveVersion = version;
162  }
163  }
164 
166  public async Task<IByondExecutableLock> UseExecutables(Version requiredVersion, CancellationToken cancellationToken)
167  {
168  var versionToUse = requiredVersion ?? ActiveVersion;
169  if (versionToUse == null)
170  throw new InvalidOperationException("No BYOND versions installed!");
171  await InstallVersion(versionToUse, cancellationToken).ConfigureAwait(false);
172 
173  var versionKey = VersionKey(versionToUse);
174 
175  return new ByondExecutableLock
176  {
177  DreamDaemonPath = ioManager.ResolvePath(ioManager.ConcatPath(versionKey, BinPath, byondInstaller.DreamDaemonName)),
178  DreamMakerPath = ioManager.ResolvePath(ioManager.ConcatPath(versionKey, BinPath, byondInstaller.DreamMakerName)),
179  Version = versionToUse
180  };
181  }
182 
184  public async Task StartAsync(CancellationToken cancellationToken)
185  {
186  async Task<byte[]> GetActiveVersion()
187  {
188  if (!await ioManager.FileExists(ActiveVersionFileName, cancellationToken).ConfigureAwait(false))
189  return null;
190  return await ioManager.ReadAllBytes(ActiveVersionFileName, cancellationToken).ConfigureAwait(false);
191  }
192 
193  var activeVersionBytesTask = GetActiveVersion();
194 
195  await ioManager.CreateDirectory(".", cancellationToken).ConfigureAwait(false);
196  var directories = await ioManager.GetDirectories(".", cancellationToken).ConfigureAwait(false);
197 
198  async Task ReadVersion(string path)
199  {
200  var versionFile = ioManager.ConcatPath(path, VersionFileName);
201  if (!await ioManager.FileExists(versionFile, cancellationToken).ConfigureAwait(false))
202  {
203  logger.LogInformation("Cleaning unparsable version path: {0}", ioManager.ResolvePath(path));
204  await ioManager.DeleteDirectory(path, cancellationToken).ConfigureAwait(false); //cleanup
205  return;
206  }
207  var bytes = await ioManager.ReadAllBytes(versionFile, cancellationToken).ConfigureAwait(false);
208  var text = Encoding.UTF8.GetString(bytes);
209  if (Version.TryParse(text, out var version))
210  {
211  var key = VersionKey(version);
212  lock (installedVersions)
213  if (!installedVersions.ContainsKey(key))
214  {
215  installedVersions.Add(key, Task.CompletedTask);
216  return;
217  }
218  }
219  await ioManager.DeleteDirectory(path, cancellationToken).ConfigureAwait(false);
220  };
221 
222  await Task.WhenAll(directories.Select(x => ReadVersion(x))).ConfigureAwait(false);
223 
224  var activeVersionBytes = await activeVersionBytesTask.ConfigureAwait(false);
225  if (activeVersionBytes != null)
226  {
227  var activeVersionString = Encoding.UTF8.GetString(activeVersionBytes);
228  if (Version.TryParse(activeVersionString, out var activeVersion))
229  ActiveVersion = activeVersion;
230  else
231  await ioManager.DeleteFile(ActiveVersionFileName, cancellationToken).ConfigureAwait(false);
232  }
233  }
234 
236  public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
237  }
238 }
readonly IIOManager ioManager
The IIOManager for the ByondManager
Definition: ByondManager.cs:42
Operation exceptions thrown from the context of a Models.Job
Definition: JobException.cs:8
+
ByondManager(IIOManager ioManager, IByondInstaller byondInstaller, ILogger< ByondManager > logger)
Construct a ByondManager
Definition: ByondManager.cs:72
async Task StartAsync(CancellationToken cancellationToken)
@@ -103,7 +104,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
For downloading and installing BYOND extractions for a given system
async Task InstallVersion(Version version, CancellationToken cancellationToken)
Installs a BYOND version if it isn&#39;t already
Definition: ByondManager.cs:91
-
For managing the BYOND installation
readonly Dictionary< string, Task > installedVersions
Map of byond Versions to Tasks that complete when they are installed
Definition: ByondManager.cs:57
readonly ILogger< ByondManager > logger
The ILogger for the ByondManager
Definition: ByondManager.cs:52
diff --git a/_byond_rights_8cs_source.html b/_byond_rights_8cs_source.html index a2f2c7c605..9fa07a00b3 100644 --- a/_byond_rights_8cs_source.html +++ b/_byond_rights_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ByondRights.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum ByondRights : ulong
10  {
14  None = 0,
18  ReadActive = 1,
22  ListInstalled = 2,
26  ChangeVersion = 4,
30  CancelInstall = 8
31  }
32 }
ByondRights
Rights for Models.Byond
Definition: ByondRights.cs:9
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum ByondRights : ulong
10  {
14  None = 0,
18  ReadActive = 1,
22  ListInstalled = 2,
26  ChangeVersion = 4,
30  CancelInstall = 8
31  }
32 }
ByondRights
Rights for Models.Byond
Definition: ByondRights.cs:9
+
User may list all installed BYOND versions
User may check the active installed BYOND version
User may cancel version installations
-
User can change Models.Administration.NewVersion
diff --git a/_channel_mapping_8cs_source.html b/_channel_mapping_8cs_source.html index 9ed885cbca..ecc72901c9 100644 --- a/_channel_mapping_8cs_source.html +++ b/_channel_mapping_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ChannelMapping.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Components.Chat
2 {
6  sealed class ChannelMapping
7  {
11  public long ProviderId { get; set; }
12 
16  public ulong ProviderChannelId { get; set; }
17 
21  public bool IsWatchdogChannel { get; set; }
22 
26  public bool IsUpdatesChannel { get; set; }
27 
31  public Channel Channel { get; set; }
32  }
33 }
Represents a mapping of a Channel.RealId
- +Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Components.Chat
2 {
6  sealed class ChannelMapping
7  {
11  public long ProviderId { get; set; }
12 
16  public ulong ProviderChannelId { get; set; }
17 
21  public bool IsWatchdogChannel { get; set; }
22 
26  public bool IsUpdatesChannel { get; set; }
27 
31  public Channel Channel { get; set; }
32  }
33 }
Represents a mapping of a Channel.RealId
+
Represents a Providers.IProvider channel
Definition: Channel.cs:10
diff --git a/_chat_8cs_source.html b/_chat_8cs_source.html index ea96dd6b68..796437bf23 100644 --- a/_chat_8cs_source.html +++ b/_chat_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Chat.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using Newtonsoft.Json;
3 using System;
4 using System.Collections.Generic;
5 using System.Globalization;
6 using System.Linq;
7 using System.Threading;
8 using System.Threading.Tasks;
12 using Tgstation.Server.Host.IO;
13 
14 namespace Tgstation.Server.Host.Components.Chat
15 {
17  sealed class Chat : IChat
18  {
19  const string CommonMention = "!tgs";
20 
25 
30 
35 
39  readonly ILogger<Chat> logger;
40 
44  readonly Dictionary<string, ICommand> builtinCommands;
45 
49  readonly Dictionary<long, IProvider> providers;
50 
54  readonly Dictionary<ulong, ChannelMapping> mappedChannels;
55 
59  readonly List<IJsonTrackingContext> trackingContexts;
60 
64  readonly CancellationTokenSource handlerCts;
65 
69  readonly List<Models.ChatBot> initialChatBots;
70 
75 
80 
84  TaskCompletionSource<object> connectionsUpdated;
85 
90 
94  bool started;
95 
104  public Chat(IProviderFactory providerFactory, IIOManager ioManager, ICommandFactory commandFactory, ILogger<Chat> logger, IEnumerable<Models.ChatBot> initialChatBots)
105  {
106  this.providerFactory = providerFactory ?? throw new ArgumentNullException(nameof(providerFactory));
107  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
108  this.commandFactory = commandFactory ?? throw new ArgumentNullException(nameof(commandFactory));
109  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
110  this.initialChatBots = initialChatBots?.ToList() ?? throw new ArgumentNullException(nameof(initialChatBots));
111 
112  builtinCommands = new Dictionary<string, ICommand>();
113  providers = new Dictionary<long, IProvider>();
114  mappedChannels = new Dictionary<ulong, ChannelMapping>();
115  trackingContexts = new List<IJsonTrackingContext>();
116  handlerCts = new CancellationTokenSource();
117  connectionsUpdated = new TaskCompletionSource<object>();
118  channelIdCounter = 1;
119  }
120 
122  public void Dispose()
123  {
124  handlerCts.Dispose();
125  foreach (var I in providers)
126  I.Value.Dispose();
127  }
128 
136  async Task<IProvider> RemoveProvider(long connectionId, bool updateTrackings, CancellationToken cancellationToken)
137  {
138  IProvider provider;
139  lock (providers)
140  if (!providers.TryGetValue(connectionId, out provider))
141  return null;
142  Task task;
143  lock (mappedChannels)
144  {
145  foreach (var I in mappedChannels.Where(x => x.Value.ProviderId == connectionId).Select(x => x.Key).ToList())
146  mappedChannels.Remove(I);
147 
148  if (updateTrackings)
149  lock (trackingContexts)
150  task = Task.WhenAll(trackingContexts.Select(x => x.SetChannels(mappedChannels.Select(y => y.Value.Channel), cancellationToken)));
151  else
152  task = Task.CompletedTask;
153  }
154  await task.ConfigureAwait(false);
155  return provider;
156  }
157 
165  async Task ProcessMessage(IProvider provider, Message message, CancellationToken cancellationToken)
166  {
167  //map the channel if it's private and we haven't seen it
168  lock (providers)
169  {
170  var providerId = providers.Where(x => x.Value == provider).Select(x => x.Key).First();
171  var enumerable = mappedChannels.Where(x => x.Value.ProviderId == providerId && x.Value.ProviderChannelId == message.User.Channel.RealId);
172  if (message.User.Channel.IsPrivate)
173  lock (mappedChannels)
174  {
175  if (!provider.Connected)
176  return;
177  if (!enumerable.Any())
178  {
179  ulong newId;
180  lock (this)
181  newId = channelIdCounter++;
182  mappedChannels.Add(newId, new ChannelMapping
183  {
184  IsWatchdogChannel = false,
185  ProviderChannelId = message.User.Channel.RealId,
186  ProviderId = providerId,
187  Channel = message.User.Channel
188  });
189  message.User.Channel.RealId = newId;
190  }
191  else
192  message.User.Channel.RealId = enumerable.First().Key;
193  }
194  else
195  {
196  //need to add tag and isAdminChannel
197  var mapping = enumerable.First().Value;
198  message.User.Channel.Tag = mapping.Channel.Tag;
199  message.User.Channel.IsAdmin = mapping.Channel.IsAdmin;
200  }
201  }
202 
203  var splits = new List<string>(message.Content.Trim().Split(' '));
204  var address = splits[0];
205  if (address.Length > 1 && (address[address.Length - 1] == ':' || address[address.Length - 1] == ','))
206  address = address.Substring(0, address.Length - 1);
207 
208  address = address.ToUpperInvariant();
209 
210  var addressed = address == CommonMention.ToUpperInvariant() || address == provider.BotMention.ToUpperInvariant();
211 
212  if (!addressed && !message.User.Channel.IsPrivate)
213  //no mention
214  return;
215 
216  logger.LogTrace("Chat command: {0}. User (True provider Id): {1}", message.Content, JsonConvert.SerializeObject(message.User));
217 
218  if (addressed)
219  splits.RemoveAt(0);
220 
221  if (splits.Count == 0)
222  {
223  //just a mention
224  await SendMessage("Hi!", new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
225  return;
226  }
227 
228  var command = splits[0].ToUpperInvariant();
229  splits.RemoveAt(0);
230  var arguments = String.Join(" ", splits);
231 
232  try
233  {
234  async Task<ICommand> GetCommand(string commandName)
235  {
236  if (!builtinCommands.TryGetValue(commandName, out var handler))
237  {
238  var tasks = trackingContexts.Select(x => x.GetCustomCommands(cancellationToken));
239  await Task.WhenAll(tasks).ConfigureAwait(false);
240  handler = tasks.SelectMany(x => x.Result).Where(x => x.Name.ToUpperInvariant() == commandName).FirstOrDefault();
241  }
242  return handler;
243  };
244 
245  const string UnknownCommandMessage = "Unknown command! Type '?' or 'help' for available commands.";
246 
247  if (command == "HELP" || command == "?")
248  {
249  string helpText;
250  if (splits.Count == 0)
251  {
252  var allCommands = builtinCommands.Select(x => x.Value).ToList();
253  var tasks = trackingContexts.Select(x => x.GetCustomCommands(cancellationToken));
254  await Task.WhenAll(tasks).ConfigureAwait(false);
255  allCommands.AddRange(tasks.SelectMany(x => x.Result));
256  helpText = String.Format(CultureInfo.InvariantCulture, "Available commands (Type '?' or 'help' and then a command name for more details): {0}", String.Join(", ", allCommands.Select(x => x.Name)));
257  }
258  else
259  {
260  var helpHandler = await GetCommand(splits[0].ToUpperInvariant()).ConfigureAwait(false);
261  if (helpHandler != default)
262  helpText = String.Format(CultureInfo.InvariantCulture, "{0}: {1}{2}", helpHandler.Name, helpHandler.HelpText, helpHandler.AdminOnly ? " - May only be used in admin channels" : String.Empty);
263  else
264  helpText = UnknownCommandMessage;
265  }
266  await SendMessage(helpText, new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
267  return;
268  }
269 
270  var commandHandler = await GetCommand(command).ConfigureAwait(false);
271 
272  if (commandHandler == default)
273  {
274  await SendMessage(UnknownCommandMessage, new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
275  return;
276  }
277 
278  if (commandHandler.AdminOnly && !message.User.Channel.IsAdmin)
279  {
280  await SendMessage("Use this command in an admin channel!", new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
281  return;
282  }
283 
284  var result = await commandHandler.Invoke(arguments, message.User, cancellationToken).ConfigureAwait(false);
285  if (result != null)
286  await SendMessage(result, new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
287  }
288  catch (Exception e)
289  {
290  //error bc custom commands should reply about why it failed
291  logger.LogError("Error processing chat command: {0}", e);
292  await SendMessage("Internal error processing command!", new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
293  }
294  }
295 
301  async Task MonitorMessages(CancellationToken cancellationToken)
302  {
303  var messageTasks = new Dictionary<IProvider, Task<Message>>();
304  try
305  {
306  while (!cancellationToken.IsCancellationRequested)
307  {
308  //prune disconnected providers
309  foreach (var I in messageTasks.Where(x => !x.Key.Connected).ToList())
310  messageTasks.Remove(I.Key);
311 
312  //add new ones
313  Task updatedTask;
314  lock (this)
315  updatedTask = connectionsUpdated.Task;
316  lock (providers)
317  foreach (var I in providers)
318  if (I.Value.Connected && !messageTasks.ContainsKey(I.Value))
319  messageTasks.Add(I.Value, I.Value.NextMessage(cancellationToken));
320 
321  if (messageTasks.Count == 0)
322  {
323  await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
324  continue;
325  }
326 
327  //wait for a message
328  await Task.WhenAny(updatedTask, Task.WhenAny(messageTasks.Select(x => x.Value))).ConfigureAwait(false);
329 
330  //process completed ones
331  foreach (var I in messageTasks.Where(x => x.Value.IsCompleted).ToList())
332  {
333  var message = await I.Value.ConfigureAwait(false);
334 
335  await ProcessMessage(I.Key, message, cancellationToken).ConfigureAwait(false);
336 
337  messageTasks.Remove(I.Key);
338  }
339  }
340  }
341  catch (OperationCanceledException) { }
342  catch (Exception e)
343  {
344  logger.LogError("Message monitor crashed!: Exception: {0}", e);
345  }
346  }
347 
349  public async Task ChangeChannels(long connectionId, IEnumerable<Api.Models.ChatChannel> newChannels, CancellationToken cancellationToken)
350  {
351  if (newChannels == null)
352  throw new ArgumentNullException(nameof(newChannels));
353  var provider = await RemoveProvider(connectionId, false, cancellationToken).ConfigureAwait(false);
354  if (provider == null)
355  return;
356  var results = await provider.MapChannels(newChannels, cancellationToken).ConfigureAwait(false);
357  if (results == null) //aborted
358  return;
359  var mappings = Enumerable.Zip(newChannels, results, (x, y) => new ChannelMapping
360  {
361  IsWatchdogChannel = x.IsWatchdogChannel == true,
362  IsUpdatesChannel = x.IsUpdatesChannel == true,
363  ProviderChannelId = y.RealId,
364  ProviderId = connectionId,
365  Channel = y
366  });
367 
368  ulong baseId;
369  lock (this)
370  {
371  baseId = channelIdCounter;
372  channelIdCounter += (ulong)results.Count;
373  }
374 
375  Task task;
376  lock (mappedChannels)
377  {
378  lock (providers)
379  if (!providers.TryGetValue(connectionId, out IProvider verify) || verify != provider) //aborted again
380  return;
381  foreach (var I in mappings)
382  {
383  var newId = baseId++;
384  mappedChannels.Add(newId, I);
385  I.Channel.RealId = newId;
386  }
387 
388  lock (trackingContexts)
389  task = Task.WhenAll(trackingContexts.Select(x => x.SetChannels(mappedChannels.Select(y => y.Value.Channel), cancellationToken)));
390  }
391  await task.ConfigureAwait(false);
392  }
393 
395  public async Task ChangeSettings(ChatBot newSettings, CancellationToken cancellationToken)
396  {
397  if (newSettings == null)
398  throw new ArgumentNullException(nameof(newSettings));
399  IProvider provider;
400 
401  async Task DisconnectProvider(IProvider p)
402  {
403  try
404  {
405  await p.Disconnect(cancellationToken).ConfigureAwait(false);
406  }
407  finally
408  {
409  p.Dispose();
410  }
411  }
412 
413  Task disconnectTask;
414  lock (providers)
415  {
416  //raw settings changes forces a rebuild of the provider
417  if (providers.TryGetValue(newSettings.Id, out provider))
418  {
419  providers.Remove(newSettings.Id);
420  disconnectTask = DisconnectProvider(provider);
421  }
422  else
423  disconnectTask = Task.CompletedTask;
424  if (newSettings.Enabled.Value)
425  {
426  provider = providerFactory.CreateProvider(newSettings);
427  providers.Add(newSettings.Id, provider);
428  }
429  }
430 
431  lock (mappedChannels)
432  foreach (var I in mappedChannels.Where(x => x.Value.ProviderId == newSettings.Id).Select(x => x.Key).ToList())
433  mappedChannels.Remove(I);
434 
435  await disconnectTask.ConfigureAwait(false);
436 
437  if (started)
438  {
439  if (newSettings.Enabled.Value)
440  await provider.Connect(cancellationToken).ConfigureAwait(false);
441  lock (this)
442  {
443  //same thread shennanigans
444  var oldOne = connectionsUpdated;
445  connectionsUpdated = new TaskCompletionSource<object>();
446  oldOne.SetResult(null);
447  }
448  }
449  }
450 
452  public Task SendMessage(string message, IEnumerable<ulong> channelIds, CancellationToken cancellationToken)
453  {
454  if (message == null)
455  throw new ArgumentNullException(nameof(message));
456  if (channelIds == null)
457  throw new ArgumentNullException(nameof(channelIds));
458 
459  return Task.WhenAll(channelIds.Select(x =>
460  {
461  ChannelMapping channelMapping;
462  lock (mappedChannels)
463  if (!mappedChannels.TryGetValue(x, out channelMapping))
464  return Task.CompletedTask;
465  IProvider provider;
466  lock (providers)
467  if (!providers.TryGetValue(channelMapping.ProviderId, out provider))
468  return Task.CompletedTask;
469  return provider.SendMessage(channelMapping.ProviderChannelId, message, cancellationToken);
470  }));
471  }
472 
474  public Task SendWatchdogMessage(string message, CancellationToken cancellationToken)
475  {
476  List<ulong> wdChannels;
477  message = String.Format(CultureInfo.InvariantCulture, "WD: {0}", message);
478  lock (mappedChannels) //so it doesn't change while we're using it
479  wdChannels = mappedChannels.Where(x => x.Value.IsWatchdogChannel).Select(x => x.Key).ToList();
480  return SendMessage(message, wdChannels, cancellationToken);
481  }
482 
484  public Task SendUpdateMessage(string message, CancellationToken cancellationToken)
485  {
486  List<ulong> wdChannels;
487  message = String.Format(CultureInfo.InvariantCulture, "DM: {0}", message);
488  lock (mappedChannels) //so it doesn't change while we're using it
489  wdChannels = mappedChannels.Where(x => x.Value.IsUpdatesChannel).Select(x => x.Key).ToList();
490  return SendMessage(message, wdChannels, cancellationToken);
491  }
492 
494  public async Task StartAsync(CancellationToken cancellationToken)
495  {
496  foreach (var I in commandFactory.GenerateCommands())
497  builtinCommands.Add(I.Name.ToUpperInvariant(), I);
498  await Task.WhenAll(initialChatBots.Select(x => ChangeSettings(x, cancellationToken))).ConfigureAwait(false);
499  await Task.WhenAll(providers.Select(x => x.Value).Select(x => x.Connect(cancellationToken))).ConfigureAwait(false);
500  await Task.WhenAll(initialChatBots.Select(x => ChangeChannels(x.Id, x.Channels, cancellationToken))).ConfigureAwait(false);
501  chatHandler = MonitorMessages(handlerCts.Token);
502  started = true;
503  }
504 
506  public async Task StopAsync(CancellationToken cancellationToken)
507  {
508  handlerCts.Cancel();
509  await chatHandler.ConfigureAwait(false);
510  await Task.WhenAll(providers.Select(x => x.Value).Select(x => x.Disconnect(cancellationToken))).ConfigureAwait(false);
511  }
512 
514  public async Task<IJsonTrackingContext> TrackJsons(string basePath, string channelsJsonName, string commandsJsonName, CancellationToken cancellationToken)
515  {
516  if (customCommandHandler == null)
517  throw new InvalidOperationException("RegisterCommandHandler() hasn't been called!");
518  JsonTrackingContext context = null;
519  context = new JsonTrackingContext(ioManager, customCommandHandler, () =>
520  {
521  lock (trackingContexts)
522  trackingContexts.Remove(context);
523  }, ioManager.ConcatPath(basePath, commandsJsonName), ioManager.ConcatPath(basePath, channelsJsonName));
524  Task task;
525  lock (trackingContexts)
526  {
527  trackingContexts.Add(context);
528  lock (mappedChannels)
529  task = Task.WhenAll(trackingContexts.Select(x => x.SetChannels(mappedChannels.Select(y => y.Value.Channel), cancellationToken)));
530  }
531  await task.ConfigureAwait(false);
532  return context;
533  }
534 
536  public bool Connected(long connectionId)
537  {
538  lock (providers)
539  return providers.TryGetValue(connectionId, out var provider) && provider.Connected;
540  }
541 
543  public void RegisterCommandHandler(ICustomCommandHandler customCommandHandler)
544  {
545  if (this.customCommandHandler != null)
546  throw new InvalidOperationException("RegisterCommandHandler() already called!");
547  this.customCommandHandler = customCommandHandler ?? throw new ArgumentNullException(nameof(customCommandHandler));
548  }
549 
551  public async Task DeleteConnection(long connectionId, CancellationToken cancellationToken)
552  {
553  var provider = await RemoveProvider(connectionId, true, cancellationToken).ConfigureAwait(false);
554  if (provider != null)
555  try
556  {
557  await provider.Disconnect(cancellationToken).ConfigureAwait(false);
558  }
559  finally
560  {
561  provider.Dispose();
562  }
563  }
564 
566  public Task SendBroadcast(string message, CancellationToken cancellationToken)
567  {
568  List<ulong> wdChannels;
569  lock (mappedChannels) //so it doesn't change while we're using it
570  wdChannels = mappedChannels.Select(x => x.Key).ToList();
571  return SendMessage(message, wdChannels, cancellationToken);
572  }
573  }
574 }
void RegisterCommandHandler(ICustomCommandHandler customCommandHandler)
Registers a customCommandHandler to use
Definition: Chat.cs:543
+Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using Newtonsoft.Json;
3 using System;
4 using System.Collections.Generic;
5 using System.Globalization;
6 using System.Linq;
7 using System.Threading;
8 using System.Threading.Tasks;
12 using Tgstation.Server.Host.IO;
13 
14 namespace Tgstation.Server.Host.Components.Chat
15 {
17  sealed class Chat : IChat
18  {
19  const string CommonMention = "!tgs";
20 
25 
30 
35 
39  readonly ILogger<Chat> logger;
40 
44  readonly Dictionary<string, ICommand> builtinCommands;
45 
49  readonly Dictionary<long, IProvider> providers;
50 
54  readonly Dictionary<ulong, ChannelMapping> mappedChannels;
55 
59  readonly List<IJsonTrackingContext> trackingContexts;
60 
64  readonly CancellationTokenSource handlerCts;
65 
69  readonly List<Models.ChatBot> initialChatBots;
70 
75 
80 
84  TaskCompletionSource<object> connectionsUpdated;
85 
90 
94  bool started;
95 
104  public Chat(IProviderFactory providerFactory, IIOManager ioManager, ICommandFactory commandFactory, ILogger<Chat> logger, IEnumerable<Models.ChatBot> initialChatBots)
105  {
106  this.providerFactory = providerFactory ?? throw new ArgumentNullException(nameof(providerFactory));
107  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
108  this.commandFactory = commandFactory ?? throw new ArgumentNullException(nameof(commandFactory));
109  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
110  this.initialChatBots = initialChatBots?.ToList() ?? throw new ArgumentNullException(nameof(initialChatBots));
111 
112  builtinCommands = new Dictionary<string, ICommand>();
113  providers = new Dictionary<long, IProvider>();
114  mappedChannels = new Dictionary<ulong, ChannelMapping>();
115  trackingContexts = new List<IJsonTrackingContext>();
116  handlerCts = new CancellationTokenSource();
117  connectionsUpdated = new TaskCompletionSource<object>();
118  channelIdCounter = 1;
119  }
120 
122  public void Dispose()
123  {
124  handlerCts.Dispose();
125  foreach (var I in providers)
126  I.Value.Dispose();
127  }
128 
136  async Task<IProvider> RemoveProvider(long connectionId, bool updateTrackings, CancellationToken cancellationToken)
137  {
138  IProvider provider;
139  lock (providers)
140  if (!providers.TryGetValue(connectionId, out provider))
141  return null;
142  Task task;
143  lock (mappedChannels)
144  {
145  foreach (var I in mappedChannels.Where(x => x.Value.ProviderId == connectionId).Select(x => x.Key).ToList())
146  mappedChannels.Remove(I);
147 
148  if (updateTrackings)
149  lock (trackingContexts)
150  task = Task.WhenAll(trackingContexts.Select(x => x.SetChannels(mappedChannels.Select(y => y.Value.Channel), cancellationToken)));
151  else
152  task = Task.CompletedTask;
153  }
154  await task.ConfigureAwait(false);
155  return provider;
156  }
157 
165  async Task ProcessMessage(IProvider provider, Message message, CancellationToken cancellationToken)
166  {
167  //map the channel if it's private and we haven't seen it
168  lock (providers)
169  {
170  var providerId = providers.Where(x => x.Value == provider).Select(x => x.Key).First();
171  var enumerable = mappedChannels.Where(x => x.Value.ProviderId == providerId && x.Value.ProviderChannelId == message.User.Channel.RealId);
172  if (message.User.Channel.IsPrivate)
173  lock (mappedChannels)
174  {
175  if (!provider.Connected)
176  return;
177  if (!enumerable.Any())
178  {
179  ulong newId;
180  lock (this)
181  newId = channelIdCounter++;
182  mappedChannels.Add(newId, new ChannelMapping
183  {
184  IsWatchdogChannel = false,
185  ProviderChannelId = message.User.Channel.RealId,
186  ProviderId = providerId,
187  Channel = message.User.Channel
188  });
189  message.User.Channel.RealId = newId;
190  }
191  else
192  message.User.Channel.RealId = enumerable.First().Key;
193  }
194  else
195  {
196  //need to add tag and isAdminChannel
197  var mapping = enumerable.First().Value;
198  message.User.Channel.Tag = mapping.Channel.Tag;
199  message.User.Channel.IsAdmin = mapping.Channel.IsAdmin;
200  }
201  }
202 
203  var splits = new List<string>(message.Content.Trim().Split(' '));
204  var address = splits[0];
205  if (address.Length > 1 && (address[address.Length - 1] == ':' || address[address.Length - 1] == ','))
206  address = address.Substring(0, address.Length - 1);
207 
208  address = address.ToUpperInvariant();
209 
210  var addressed = address == CommonMention.ToUpperInvariant() || address == provider.BotMention.ToUpperInvariant();
211 
212  if (!addressed && !message.User.Channel.IsPrivate)
213  //no mention
214  return;
215 
216  logger.LogTrace("Chat command: {0}. User (True provider Id): {1}", message.Content, JsonConvert.SerializeObject(message.User));
217 
218  if (addressed)
219  splits.RemoveAt(0);
220 
221  if (splits.Count == 0)
222  {
223  //just a mention
224  await SendMessage("Hi!", new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
225  return;
226  }
227 
228  var command = splits[0].ToUpperInvariant();
229  splits.RemoveAt(0);
230  var arguments = String.Join(" ", splits);
231 
232  try
233  {
234  async Task<ICommand> GetCommand(string commandName)
235  {
236  if (!builtinCommands.TryGetValue(commandName, out var handler))
237  {
238  var tasks = trackingContexts.Select(x => x.GetCustomCommands(cancellationToken));
239  await Task.WhenAll(tasks).ConfigureAwait(false);
240  handler = tasks.SelectMany(x => x.Result).Where(x => x.Name.ToUpperInvariant() == commandName).FirstOrDefault();
241  }
242  return handler;
243  };
244 
245  const string UnknownCommandMessage = "Unknown command! Type '?' or 'help' for available commands.";
246 
247  if (command == "HELP" || command == "?")
248  {
249  string helpText;
250  if (splits.Count == 0)
251  {
252  var allCommands = builtinCommands.Select(x => x.Value).ToList();
253  var tasks = trackingContexts.Select(x => x.GetCustomCommands(cancellationToken));
254  await Task.WhenAll(tasks).ConfigureAwait(false);
255  allCommands.AddRange(tasks.SelectMany(x => x.Result));
256  helpText = String.Format(CultureInfo.InvariantCulture, "Available commands (Type '?' or 'help' and then a command name for more details): {0}", String.Join(", ", allCommands.Select(x => x.Name)));
257  }
258  else
259  {
260  var helpHandler = await GetCommand(splits[0].ToUpperInvariant()).ConfigureAwait(false);
261  if (helpHandler != default)
262  helpText = String.Format(CultureInfo.InvariantCulture, "{0}: {1}{2}", helpHandler.Name, helpHandler.HelpText, helpHandler.AdminOnly ? " - May only be used in admin channels" : String.Empty);
263  else
264  helpText = UnknownCommandMessage;
265  }
266  await SendMessage(helpText, new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
267  return;
268  }
269 
270  var commandHandler = await GetCommand(command).ConfigureAwait(false);
271 
272  if (commandHandler == default)
273  {
274  await SendMessage(UnknownCommandMessage, new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
275  return;
276  }
277 
278  if (commandHandler.AdminOnly && !message.User.Channel.IsAdmin)
279  {
280  await SendMessage("Use this command in an admin channel!", new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
281  return;
282  }
283 
284  var result = await commandHandler.Invoke(arguments, message.User, cancellationToken).ConfigureAwait(false);
285  if (result != null)
286  await SendMessage(result, new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
287  }
288  catch (Exception e)
289  {
290  //error bc custom commands should reply about why it failed
291  logger.LogError("Error processing chat command: {0}", e);
292  await SendMessage("Internal error processing command!", new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
293  }
294  }
295 
301  async Task MonitorMessages(CancellationToken cancellationToken)
302  {
303  var messageTasks = new Dictionary<IProvider, Task<Message>>();
304  try
305  {
306  while (!cancellationToken.IsCancellationRequested)
307  {
308  //prune disconnected providers
309  foreach (var I in messageTasks.Where(x => !x.Key.Connected).ToList())
310  messageTasks.Remove(I.Key);
311 
312  //add new ones
313  Task updatedTask;
314  lock (this)
315  updatedTask = connectionsUpdated.Task;
316  lock (providers)
317  foreach (var I in providers)
318  if (I.Value.Connected && !messageTasks.ContainsKey(I.Value))
319  messageTasks.Add(I.Value, I.Value.NextMessage(cancellationToken));
320 
321  if (messageTasks.Count == 0)
322  {
323  await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
324  continue;
325  }
326 
327  //wait for a message
328  await Task.WhenAny(updatedTask, Task.WhenAny(messageTasks.Select(x => x.Value))).ConfigureAwait(false);
329 
330  //process completed ones
331  foreach (var I in messageTasks.Where(x => x.Value.IsCompleted).ToList())
332  {
333  var message = await I.Value.ConfigureAwait(false);
334 
335  await ProcessMessage(I.Key, message, cancellationToken).ConfigureAwait(false);
336 
337  messageTasks.Remove(I.Key);
338  }
339  }
340  }
341  catch (OperationCanceledException) { }
342  catch (Exception e)
343  {
344  logger.LogError("Message monitor crashed!: Exception: {0}", e);
345  }
346  }
347 
349  public async Task ChangeChannels(long connectionId, IEnumerable<Api.Models.ChatChannel> newChannels, CancellationToken cancellationToken)
350  {
351  if (newChannels == null)
352  throw new ArgumentNullException(nameof(newChannels));
353  var provider = await RemoveProvider(connectionId, false, cancellationToken).ConfigureAwait(false);
354  if (provider == null)
355  return;
356  var results = await provider.MapChannels(newChannels, cancellationToken).ConfigureAwait(false);
357  if (results == null) //aborted
358  return;
359  var mappings = Enumerable.Zip(newChannels, results, (x, y) => new ChannelMapping
360  {
361  IsWatchdogChannel = x.IsWatchdogChannel == true,
362  IsUpdatesChannel = x.IsUpdatesChannel == true,
363  ProviderChannelId = y.RealId,
364  ProviderId = connectionId,
365  Channel = y
366  });
367 
368  ulong baseId;
369  lock (this)
370  {
371  baseId = channelIdCounter;
372  channelIdCounter += (ulong)results.Count;
373  }
374 
375  Task task;
376  lock (mappedChannels)
377  {
378  lock (providers)
379  if (!providers.TryGetValue(connectionId, out IProvider verify) || verify != provider) //aborted again
380  return;
381  foreach (var I in mappings)
382  {
383  var newId = baseId++;
384  mappedChannels.Add(newId, I);
385  I.Channel.RealId = newId;
386  }
387 
388  lock (trackingContexts)
389  task = Task.WhenAll(trackingContexts.Select(x => x.SetChannels(mappedChannels.Select(y => y.Value.Channel), cancellationToken)));
390  }
391  await task.ConfigureAwait(false);
392  }
393 
395  public async Task ChangeSettings(ChatBot newSettings, CancellationToken cancellationToken)
396  {
397  if (newSettings == null)
398  throw new ArgumentNullException(nameof(newSettings));
399  IProvider provider;
400 
401  async Task DisconnectProvider(IProvider p)
402  {
403  try
404  {
405  await p.Disconnect(cancellationToken).ConfigureAwait(false);
406  }
407  finally
408  {
409  p.Dispose();
410  }
411  }
412 
413  Task disconnectTask;
414  lock (providers)
415  {
416  //raw settings changes forces a rebuild of the provider
417  if (providers.TryGetValue(newSettings.Id, out provider))
418  {
419  providers.Remove(newSettings.Id);
420  disconnectTask = DisconnectProvider(provider);
421  }
422  else
423  disconnectTask = Task.CompletedTask;
424  if (newSettings.Enabled.Value)
425  {
426  provider = providerFactory.CreateProvider(newSettings);
427  providers.Add(newSettings.Id, provider);
428  }
429  }
430 
431  lock (mappedChannels)
432  foreach (var I in mappedChannels.Where(x => x.Value.ProviderId == newSettings.Id).Select(x => x.Key).ToList())
433  mappedChannels.Remove(I);
434 
435  await disconnectTask.ConfigureAwait(false);
436 
437  if (started)
438  {
439  if (newSettings.Enabled.Value)
440  await provider.Connect(cancellationToken).ConfigureAwait(false);
441  lock (this)
442  {
443  //same thread shennanigans
444  var oldOne = connectionsUpdated;
445  connectionsUpdated = new TaskCompletionSource<object>();
446  oldOne.SetResult(null);
447  }
448  }
449  }
450 
452  public Task SendMessage(string message, IEnumerable<ulong> channelIds, CancellationToken cancellationToken)
453  {
454  if (message == null)
455  throw new ArgumentNullException(nameof(message));
456  if (channelIds == null)
457  throw new ArgumentNullException(nameof(channelIds));
458 
459  return Task.WhenAll(channelIds.Select(x =>
460  {
461  ChannelMapping channelMapping;
462  lock (mappedChannels)
463  if (!mappedChannels.TryGetValue(x, out channelMapping))
464  return Task.CompletedTask;
465  IProvider provider;
466  lock (providers)
467  if (!providers.TryGetValue(channelMapping.ProviderId, out provider))
468  return Task.CompletedTask;
469  return provider.SendMessage(channelMapping.ProviderChannelId, message, cancellationToken);
470  }));
471  }
472 
474  public Task SendWatchdogMessage(string message, CancellationToken cancellationToken)
475  {
476  List<ulong> wdChannels;
477  message = String.Format(CultureInfo.InvariantCulture, "WD: {0}", message);
478  lock (mappedChannels) //so it doesn't change while we're using it
479  wdChannels = mappedChannels.Where(x => x.Value.IsWatchdogChannel).Select(x => x.Key).ToList();
480  return SendMessage(message, wdChannels, cancellationToken);
481  }
482 
484  public Task SendUpdateMessage(string message, CancellationToken cancellationToken)
485  {
486  List<ulong> wdChannels;
487  message = String.Format(CultureInfo.InvariantCulture, "DM: {0}", message);
488  lock (mappedChannels) //so it doesn't change while we're using it
489  wdChannels = mappedChannels.Where(x => x.Value.IsUpdatesChannel).Select(x => x.Key).ToList();
490  return SendMessage(message, wdChannels, cancellationToken);
491  }
492 
494  public async Task StartAsync(CancellationToken cancellationToken)
495  {
496  foreach (var I in commandFactory.GenerateCommands())
497  builtinCommands.Add(I.Name.ToUpperInvariant(), I);
498  await Task.WhenAll(initialChatBots.Select(x => ChangeSettings(x, cancellationToken))).ConfigureAwait(false);
499  await Task.WhenAll(providers.Select(x => x.Value).Select(x => x.Connect(cancellationToken))).ConfigureAwait(false);
500  await Task.WhenAll(initialChatBots.Select(x => ChangeChannels(x.Id, x.Channels, cancellationToken))).ConfigureAwait(false);
501  chatHandler = MonitorMessages(handlerCts.Token);
502  started = true;
503  }
504 
506  public async Task StopAsync(CancellationToken cancellationToken)
507  {
508  handlerCts.Cancel();
509  await chatHandler.ConfigureAwait(false);
510  await Task.WhenAll(providers.Select(x => x.Value).Select(x => x.Disconnect(cancellationToken))).ConfigureAwait(false);
511  }
512 
514  public async Task<IJsonTrackingContext> TrackJsons(string basePath, string channelsJsonName, string commandsJsonName, CancellationToken cancellationToken)
515  {
516  if (customCommandHandler == null)
517  throw new InvalidOperationException("RegisterCommandHandler() hasn't been called!");
518  JsonTrackingContext context = null;
519  context = new JsonTrackingContext(ioManager, customCommandHandler, () =>
520  {
521  lock (trackingContexts)
522  trackingContexts.Remove(context);
523  }, ioManager.ConcatPath(basePath, commandsJsonName), ioManager.ConcatPath(basePath, channelsJsonName));
524  Task task;
525  lock (trackingContexts)
526  {
527  trackingContexts.Add(context);
528  lock (mappedChannels)
529  task = Task.WhenAll(trackingContexts.Select(x => x.SetChannels(mappedChannels.Select(y => y.Value.Channel), cancellationToken)));
530  }
531  await task.ConfigureAwait(false);
532  return context;
533  }
534 
536  public bool Connected(long connectionId)
537  {
538  lock (providers)
539  return providers.TryGetValue(connectionId, out var provider) && provider.Connected;
540  }
541 
543  public void RegisterCommandHandler(ICustomCommandHandler customCommandHandler)
544  {
545  if (this.customCommandHandler != null)
546  throw new InvalidOperationException("RegisterCommandHandler() already called!");
547  this.customCommandHandler = customCommandHandler ?? throw new ArgumentNullException(nameof(customCommandHandler));
548  }
549 
551  public async Task DeleteConnection(long connectionId, CancellationToken cancellationToken)
552  {
553  var provider = await RemoveProvider(connectionId, true, cancellationToken).ConfigureAwait(false);
554  if (provider != null)
555  try
556  {
557  await provider.Disconnect(cancellationToken).ConfigureAwait(false);
558  }
559  finally
560  {
561  provider.Dispose();
562  }
563  }
564 
566  public Task SendBroadcast(string message, CancellationToken cancellationToken)
567  {
568  List<ulong> wdChannels;
569  lock (mappedChannels) //so it doesn't change while we're using it
570  wdChannels = mappedChannels.Select(x => x.Key).ToList();
571  return SendMessage(message, wdChannels, cancellationToken);
572  }
573  }
574 }
void RegisterCommandHandler(ICustomCommandHandler customCommandHandler)
Registers a customCommandHandler to use
Definition: Chat.cs:543
bool IsWatchdogChannel
If Channel is a watchdog channel
Represents a mapping of a Channel.RealId
@@ -103,6 +103,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Task SendBroadcast(string message, CancellationToken cancellationToken)
Send a chat to all channels
Definition: Chat.cs:566
+
Task Disconnect(CancellationToken cancellationToken)
Gracefully disconnects the provider. Implies a call to IDisposable.Dispose
Channel Channel
The Components.Chat.Channel the user spoke from
Definition: User.cs:40
@@ -114,7 +115,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly List< Models.ChatBot > initialChatBots
The initial Models.ChatBot for the Chat
Definition: Chat.cs:69
For interacting with a chat service
Definition: IProvider.cs:11
-
Manage the server chat bots
Definition: ChatBot.cs:8
+
Manage the server chat bots
Definition: ChatBot.cs:10
bool Connected(long connectionId)
If a given set of ChatBot is connected
Definition: Chat.cs:536
@@ -129,13 +130,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly ILogger< Chat > logger
The ILogger for the Chat
Definition: Chat.cs:39
readonly ICommandFactory commandFactory
The ICommandFactory for the Chat
Definition: Chat.cs:34
-
Task SendUpdateMessage(string message, CancellationToken cancellationToken)
Send a chat message to configured update channels
Definition: Chat.cs:484
- +
async Task ProcessMessage(IProvider provider, Message message, CancellationToken cancellationToken)
Processes a message
Definition: Chat.cs:165
async Task StopAsync(CancellationToken cancellationToken)
Definition: Chat.cs:506
Represents a Providers.IProvider channel
Definition: Channel.cs:10
- +
async Task MonitorMessages(CancellationToken cancellationToken)
Monitors active providers for new Messages
Definition: Chat.cs:301
bool IsAdmin
If this is considered a channel for admin commands
Definition: Channel.cs:41
@@ -156,7 +156,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Interface for using filesystems
Definition: IIOManager.cs:11
Task SendMessage(string message, IEnumerable< ulong > channelIds, CancellationToken cancellationToken)
Send a chat message to a given set of channelIds
Definition: Chat.cs:452
readonly List< IJsonTrackingContext > trackingContexts
The active IJsonTrackingContexts for the Chat
Definition: Chat.cs:59
-
bool Enabled
If the connection is enabled
Definition: ChatBot.cs:24
+
bool Enabled
If the connection is enabled
Definition: ChatBot.cs:26
async Task ChangeChannels(long connectionId, IEnumerable< Api.Models.ChatChannel > newChannels, CancellationToken cancellationToken)
Change chat channels
Definition: Chat.cs:349
ICustomCommandHandler customCommandHandler
The ICustomCommandHandler for the ChangeChannels(long, IEnumerable<Api.Models.ChatChannel>, CancellationToken)
Definition: Chat.cs:74
diff --git a/_chat_bot_rights_8cs_source.html b/_chat_bot_rights_8cs_source.html index d22c5409ce..62ea8fde09 100644 --- a/_chat_bot_rights_8cs_source.html +++ b/_chat_bot_rights_8cs_source.html @@ -92,15 +92,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ChatBotRights.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum ChatBotRights : ulong
10  {
14  None = 0,
18  WriteEnabled = 1,
22  WriteProvider = 2,
26  WriteChannels = 4,
38  Read = 32,
42  Create = 64,
46  Delete = 128,
50  WriteName = 256,
51  }
52 }
User can read Models.Internal.ChatBot.ConnectionString requires Read
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum ChatBotRights : ulong
10  {
14  None = 0,
18  WriteEnabled = 1,
22  WriteProvider = 2,
26  WriteChannels = 4,
38  Read = 32,
42  Create = 64,
46  Delete = 128,
50  WriteName = 256,
51  }
52 }
User can read Models.Internal.ChatBot.ConnectionString requires Read
User can change Models.Internal.ChatBot.Name
+
User can change Models.Internal.ChatBot.Provider
User can change Models.Internal.ChatBot.ConnectionString
User can create new Models.ChatBot
-
User can change Models.Internal.ChatBot.Enabled
User can change Models.ChatBot.Channels
User can read all chat settings except Models.Internal.ChatBot.ConnectionString
diff --git a/_chat_bots_client_8cs_source.html b/_chat_bots_client_8cs_source.html index d7c87dc155..a4c0f9cc8e 100644 --- a/_chat_bots_client_8cs_source.html +++ b/_chat_bots_client_8cs_source.html @@ -92,16 +92,16 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ChatBotsClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client.Components
9 {
12  {
20  readonly Instance instance;
21 
27  public ChatBotsClient(IApiClient apiClient, Instance instance)
28  {
29  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
30  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
31  }
32 
34  public Task<ChatBot> Create(ChatBot settings, CancellationToken cancellationToken) => apiClient.Create<ChatBot, ChatBot>(Routes.Chat, settings ?? throw new ArgumentNullException(nameof(settings)), instance.Id, cancellationToken);
35 
37  public Task Delete(ChatBot settings, CancellationToken cancellationToken) => apiClient.Delete(Routes.SetID(Routes.Chat, settings?.Id ?? throw new ArgumentNullException(nameof(settings))), instance.Id, cancellationToken);
38 
40  public Task<IReadOnlyList<ChatBot>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<ChatBot>>(Routes.List(Routes.Chat), instance.Id, cancellationToken);
41 
43  public Task<ChatBot> Update(ChatBot settings, CancellationToken cancellationToken) => apiClient.Update<ChatBot, ChatBot>(Routes.Chat, settings ?? throw new ArgumentNullException(nameof(settings)), instance.Id, cancellationToken);
44 
46  public Task<ChatBot> GetId(ChatBot settings, CancellationToken cancellationToken) => apiClient.Read<ChatBot>(Routes.SetID(Routes.Chat, (settings ?? throw new ArgumentNullException(nameof(settings))).Id), instance.Id, cancellationToken);
47  }
48 }
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client.Components
9 {
12  {
20  readonly Instance instance;
21 
27  public ChatBotsClient(IApiClient apiClient, Instance instance)
28  {
29  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
30  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
31  }
32 
34  public Task<ChatBot> Create(ChatBot settings, CancellationToken cancellationToken) => apiClient.Create<ChatBot, ChatBot>(Routes.Chat, settings ?? throw new ArgumentNullException(nameof(settings)), instance.Id, cancellationToken);
35 
37  public Task Delete(ChatBot settings, CancellationToken cancellationToken) => apiClient.Delete(Routes.SetID(Routes.Chat, settings?.Id ?? throw new ArgumentNullException(nameof(settings))), instance.Id, cancellationToken);
38 
40  public Task<IReadOnlyList<ChatBot>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<ChatBot>>(Routes.List(Routes.Chat), instance.Id, cancellationToken);
41 
43  public Task<ChatBot> Update(ChatBot settings, CancellationToken cancellationToken) => apiClient.Update<ChatBot, ChatBot>(Routes.Chat, settings ?? throw new ArgumentNullException(nameof(settings)), instance.Id, cancellationToken);
44 
46  public Task<ChatBot> GetId(ChatBot settings, CancellationToken cancellationToken) => apiClient.Read<ChatBot>(Routes.SetID(Routes.Chat, (settings ?? throw new ArgumentNullException(nameof(settings))).Id), instance.Id, cancellationToken);
47  }
48 }
+
Routes to a server actions
Definition: Routes.cs:9
const string Chat
The Models.ChatBot controller
Definition: Routes.cs:59
- - +
Metadata about a server instance
Definition: Instance.cs:9
static string SetID(string route, long id)
Apply an id postfix to a route
static string List(string route)
Get the /List postfix for a route
diff --git a/_chat_connection_string_builder_8cs.html b/_chat_connection_string_builder_8cs.html new file mode 100644 index 0000000000..fd4075e329 --- /dev/null +++ b/_chat_connection_string_builder_8cs.html @@ -0,0 +1,120 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Api/Models/Internal/ChatConnectionStringBuilder.cs File Reference + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+ +
+
ChatConnectionStringBuilder.cs File Reference
+
+ + + + + diff --git a/_chat_connection_string_builder_8cs_source.html b/_chat_connection_string_builder_8cs_source.html new file mode 100644 index 0000000000..ca58c11477 --- /dev/null +++ b/_chat_connection_string_builder_8cs_source.html @@ -0,0 +1,106 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Api/Models/Internal/ChatConnectionStringBuilder.cs Source File + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+
ChatConnectionStringBuilder.cs
+
+
+Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models.Internal
2 {
6  public abstract class ChatConnectionStringBuilder
7  {
11  public abstract bool Valid { get; }
12 
17  public abstract override string ToString();
18  }
19 }
+ + +
+ + + + diff --git a/_chat_controller_8cs_source.html b/_chat_controller_8cs_source.html index 716b689e38..b719932e95 100644 --- a/_chat_controller_8cs_source.html +++ b/_chat_controller_8cs_source.html @@ -92,27 +92,27 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ChatController.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Linq.Expressions;
5 using System.Net;
6 using System.Reflection;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Microsoft.AspNetCore.Mvc;
10 using Microsoft.EntityFrameworkCore;
11 using Microsoft.Extensions.Logging;
12 using Tgstation.Server.Api;
18 using Z.EntityFramework.Plus;
19 
20 namespace Tgstation.Server.Host.Controllers
21 {
25  [Route(Routes.Chat)]
26  public sealed class ChatController : ModelController<Api.Models.ChatBot>
27  {
32 
40  public ChatController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, ILogger<ChatController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
41  {
42  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
43  }
44 
50  static Models.ChatChannel ConvertApiChatChannel(Api.Models.ChatChannel api) => new Models.ChatChannel
51  {
52  DiscordChannelId = api.DiscordChannelId,
53  IrcChannel = api.IrcChannel,
54  IsAdminChannel = api.IsAdminChannel ?? false,
55  IsWatchdogChannel = api.IsWatchdogChannel ?? false,
56  IsUpdatesChannel = api.IsUpdatesChannel ?? false
57  };
58 
60  [TgsAuthorize(ChatBotRights.Create)]
61  public override async Task<IActionResult> Create([FromBody] Api.Models.ChatBot model, CancellationToken cancellationToken)
62  {
63  if (model == null)
64  throw new ArgumentNullException(nameof(model));
65 
66  if (String.IsNullOrWhiteSpace(model.Name))
67  return BadRequest(new ErrorMessage { Message = "name cannot be null or whitespace!" });
68 
69  if (String.IsNullOrWhiteSpace(model.ConnectionString))
70  return BadRequest(new ErrorMessage { Message = "connection_string cannot be null or whitespace!" });
71 
72  if (!model.Provider.HasValue)
73  return BadRequest(new ErrorMessage { Message = "provider cannot be null!" });
74 
75  switch (model.Provider)
76  {
77  case ChatProvider.Discord:
78  case ChatProvider.Irc:
79  break;
80  default:
81  return BadRequest(new ErrorMessage { Message = "Invalid provider!" });
82  }
83 
84  if (!model.Enabled.HasValue)
85  return BadRequest(new ErrorMessage { Message = "enabled cannot be null!" });
86 
87  if (!model.ValidateProviderChannelTypes())
88  return BadRequest(new ErrorMessage { Message = "One or more of channels aren't formatted correctly for the given provider!" });
89 
90  //try to update das db first
91  var dbModel = new Models.ChatBot
92  {
93  Name = model.Name,
94  ConnectionString = model.ConnectionString,
95  Enabled = model.Enabled,
96  Channels = model.Channels?.Select(x => ConvertApiChatChannel(x)).ToList() ?? new List<Models.ChatChannel>(), //important that this isn't null
97  InstanceId = Instance.Id,
98  Provider = model.Provider,
99  };
100 
101  DatabaseContext.ChatBots.Add(dbModel);
102 
103  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
104 
105  try
106  {
107  try
108  {
109  //try to create it
110  var instance = instanceManager.GetInstance(Instance);
111  await instance.Chat.ChangeSettings(dbModel, cancellationToken).ConfigureAwait(false);
112 
113  if (dbModel.Channels.Count > 0)
114  await instance.Chat.ChangeChannels(dbModel.Id, dbModel.Channels, cancellationToken).ConfigureAwait(false);
115  }
116  catch
117  {
118  //undo the add
119  DatabaseContext.ChatBots.Remove(dbModel);
120  await DatabaseContext.Save(default).ConfigureAwait(false);
121  throw;
122  }
123  }
124  catch (InvalidOperationException e)
125  {
126  return BadRequest(new ErrorMessage { Message = e.Message });
127  }
128  return StatusCode((int)HttpStatusCode.Created, dbModel.ToApi());
129  }
130 
132  [TgsAuthorize(ChatBotRights.Delete)]
133  public override async Task<IActionResult> Delete(long id, CancellationToken cancellationToken)
134  {
135  var instance = instanceManager.GetInstance(Instance);
136  await Task.WhenAll(instance.Chat.DeleteConnection(id, cancellationToken), DatabaseContext.ChatBots.Where(x => x.Id == id).DeleteAsync(cancellationToken)).ConfigureAwait(false);
137 
138  return Ok();
139  }
140 
142  [TgsAuthorize(ChatBotRights.Read)]
143  public override async Task<IActionResult> List(CancellationToken cancellationToken)
144  {
145  var query = DatabaseContext.ChatBots.Where(x => x.InstanceId == Instance.Id).Include(x => x.Channels);
146 
147  var results = await query.ToListAsync(cancellationToken).ConfigureAwait(false);
148 
149  var connectionStrings = (AuthenticationContext.GetRight(RightsType.ChatBots) & (ulong)ChatBotRights.ReadConnectionString) != 0;
150 
151  if (!connectionStrings)
152  foreach (var I in results)
153  I.ConnectionString = null;
154 
155  return Json(results.Select(x => x.ToApi()));
156  }
157 
159  [TgsAuthorize(ChatBotRights.Read)]
160  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
161  {
162  var query = DatabaseContext.ChatBots.Where(x => x.Id == id).Include(x => x.Channels);
163 
164  var results = await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
165  if (results == default)
166  return StatusCode((int)HttpStatusCode.Gone);
167 
168  var connectionStrings = (AuthenticationContext.GetRight(RightsType.ChatBots) & (ulong)ChatBotRights.ReadConnectionString) != 0;
169 
170  if (!connectionStrings)
171  results.ConnectionString = null;
172 
173  return Json(results.ToApi());
174  }
175 
177  [TgsAuthorize(ChatBotRights.WriteChannels | ChatBotRights.WriteConnectionString | ChatBotRights.WriteEnabled | ChatBotRights.WriteName | ChatBotRights.WriteProvider)]
178  public override async Task<IActionResult> Update([FromBody] Api.Models.ChatBot model, CancellationToken cancellationToken)
179  {
180  if (model == null)
181  throw new ArgumentNullException(nameof(model));
182 
183  if (model.Provider.HasValue && !model.ValidateProviderChannelTypes())
184  return BadRequest(new ErrorMessage { Message = "One or more of channels aren't formatted correctly for the given provider!" });
185 
186  var query = DatabaseContext.ChatBots.Where(x => x.InstanceId == Instance.Id && x.Id == model.Id).Include(x => x.Channels);
187 
188  var current = await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
189 
190  if (current == default)
191  return StatusCode((int)HttpStatusCode.Gone);
192 
193  var userRights = (ChatBotRights)AuthenticationContext.GetRight(RightsType.ChatBots);
194 
195  bool anySettingsModified = false;
196 
197  bool CheckModified<T>(Expression<Func<Api.Models.Internal.ChatBot, T>> expression, ChatBotRights requiredRight)
198  {
199  var memberSelectorExpression = (MemberExpression)expression.Body;
200  var property = (PropertyInfo)memberSelectorExpression.Member;
201 
202  var newVal = property.GetValue(model);
203  if (newVal == null)
204  return false;
205  if (!userRights.HasFlag(requiredRight) && property.GetValue(current) != newVal)
206  return true;
207 
208  property.SetValue(current, newVal);
209  anySettingsModified = true;
210  return false;
211  };
212 
213  var oldProvider = current.Provider;
214 
215  if (CheckModified(x => x.ConnectionString, ChatBotRights.WriteConnectionString)
216  || CheckModified(x => x.Enabled, ChatBotRights.WriteEnabled)
217  || CheckModified(x => x.Name, ChatBotRights.WriteName)
218  || CheckModified(x => x.Provider, ChatBotRights.WriteProvider)
219  || (model.Channels != null && !userRights.HasFlag(ChatBotRights.WriteChannels)))
220  return Forbid();
221 
222  var hasChannels = model.Channels != null;
223  if (hasChannels || (model.Provider.HasValue && model.Provider != oldProvider))
224  {
225  DatabaseContext.ChatChannels.RemoveRange(current.Channels);
226  if (hasChannels)
227  {
228  var dbChannels = model.Channels.Select(x => ConvertApiChatChannel(x)).ToList();
229  DatabaseContext.ChatChannels.AddRange(dbChannels);
230  current.Channels = dbChannels;
231  }
232  else
233  current.Channels.Clear();
234  }
235 
236  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
237 
238  var chat = instanceManager.GetInstance(Instance).Chat;
239 
240  if (anySettingsModified)
241  //have to rebuild the thing first
242  await chat.ChangeSettings(current, cancellationToken).ConfigureAwait(false);
243 
244  if (model.Channels != null || anySettingsModified)
245  await chat.ChangeChannels(current.Id, current.Channels, cancellationToken).ConfigureAwait(false);
246 
247  if (userRights.HasFlag(ChatBotRights.Read))
248  {
249  if (!userRights.HasFlag(ChatBotRights.ReadConnectionString))
250  current.ConnectionString = null;
251  return Json(current.ToApi());
252  }
253  return Ok();
254  }
255  }
256 }
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Linq.Expressions;
5 using System.Net;
6 using System.Reflection;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Microsoft.AspNetCore.Mvc;
10 using Microsoft.EntityFrameworkCore;
11 using Microsoft.Extensions.Logging;
12 using Tgstation.Server.Api;
18 using Z.EntityFramework.Plus;
19 
20 namespace Tgstation.Server.Host.Controllers
21 {
25  [Route(Routes.Chat)]
26  public sealed class ChatController : ModelController<Api.Models.ChatBot>
27  {
32 
40  public ChatController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, ILogger<ChatController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
41  {
42  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
43  }
44 
50  static Models.ChatChannel ConvertApiChatChannel(Api.Models.ChatChannel api) => new Models.ChatChannel
51  {
52  DiscordChannelId = api.DiscordChannelId,
53  IrcChannel = api.IrcChannel,
54  IsAdminChannel = api.IsAdminChannel ?? false,
55  IsWatchdogChannel = api.IsWatchdogChannel ?? false,
56  IsUpdatesChannel = api.IsUpdatesChannel ?? false
57  };
58 
60  [TgsAuthorize(ChatBotRights.Create)]
61  public override async Task<IActionResult> Create([FromBody] Api.Models.ChatBot model, CancellationToken cancellationToken)
62  {
63  if (model == null)
64  throw new ArgumentNullException(nameof(model));
65 
66  if (String.IsNullOrWhiteSpace(model.Name))
67  return BadRequest(new ErrorMessage { Message = "name cannot be null or whitespace!" });
68 
69  if (String.IsNullOrWhiteSpace(model.ConnectionString))
70  return BadRequest(new ErrorMessage { Message = "connection_string cannot be null or whitespace!" });
71 
72  if (!model.Provider.HasValue)
73  return BadRequest(new ErrorMessage { Message = "provider cannot be null!" });
74 
75  switch (model.Provider)
76  {
77  case ChatProvider.Discord:
78  case ChatProvider.Irc:
79  break;
80  default:
81  return BadRequest(new ErrorMessage { Message = "Invalid provider!" });
82  }
83 
84  if (!model.ValidateProviderChannelTypes())
85  return BadRequest(new ErrorMessage { Message = "One or more of channels aren't formatted correctly for the given provider!" });
86 
87  model.Enabled = model.Enabled ?? false;
88 
89  //try to update das db first
90  var dbModel = new Models.ChatBot
91  {
92  Name = model.Name,
93  ConnectionString = model.ConnectionString,
94  Enabled = model.Enabled,
95  Channels = model.Channels?.Select(x => ConvertApiChatChannel(x)).ToList() ?? new List<Models.ChatChannel>(), //important that this isn't null
96  InstanceId = Instance.Id,
97  Provider = model.Provider,
98  };
99 
100  DatabaseContext.ChatBots.Add(dbModel);
101 
102  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
103 
104  try
105  {
106  try
107  {
108  //try to create it
109  var instance = instanceManager.GetInstance(Instance);
110  await instance.Chat.ChangeSettings(dbModel, cancellationToken).ConfigureAwait(false);
111 
112  if (dbModel.Channels.Count > 0)
113  await instance.Chat.ChangeChannels(dbModel.Id, dbModel.Channels, cancellationToken).ConfigureAwait(false);
114  }
115  catch
116  {
117  //undo the add
118  DatabaseContext.ChatBots.Remove(dbModel);
119  await DatabaseContext.Save(default).ConfigureAwait(false);
120  throw;
121  }
122  }
123  catch (InvalidOperationException e)
124  {
125  return BadRequest(new ErrorMessage { Message = e.Message });
126  }
127  return StatusCode((int)HttpStatusCode.Created, dbModel.ToApi());
128  }
129 
131  [TgsAuthorize(ChatBotRights.Delete)]
132  public override async Task<IActionResult> Delete(long id, CancellationToken cancellationToken)
133  {
134  var instance = instanceManager.GetInstance(Instance);
135  await Task.WhenAll(instance.Chat.DeleteConnection(id, cancellationToken), DatabaseContext.ChatBots.Where(x => x.Id == id).DeleteAsync(cancellationToken)).ConfigureAwait(false);
136 
137  return Ok();
138  }
139 
141  [TgsAuthorize(ChatBotRights.Read)]
142  public override async Task<IActionResult> List(CancellationToken cancellationToken)
143  {
144  var query = DatabaseContext.ChatBots.Where(x => x.InstanceId == Instance.Id).Include(x => x.Channels);
145 
146  var results = await query.ToListAsync(cancellationToken).ConfigureAwait(false);
147 
148  var connectionStrings = (AuthenticationContext.GetRight(RightsType.ChatBots) & (ulong)ChatBotRights.ReadConnectionString) != 0;
149 
150  if (!connectionStrings)
151  foreach (var I in results)
152  I.ConnectionString = null;
153 
154  return Json(results.Select(x => x.ToApi()));
155  }
156 
158  [TgsAuthorize(ChatBotRights.Read)]
159  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
160  {
161  var query = DatabaseContext.ChatBots.Where(x => x.Id == id).Include(x => x.Channels);
162 
163  var results = await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
164  if (results == default)
165  return StatusCode((int)HttpStatusCode.Gone);
166 
167  var connectionStrings = (AuthenticationContext.GetRight(RightsType.ChatBots) & (ulong)ChatBotRights.ReadConnectionString) != 0;
168 
169  if (!connectionStrings)
170  results.ConnectionString = null;
171 
172  return Json(results.ToApi());
173  }
174 
176  [TgsAuthorize(ChatBotRights.WriteChannels | ChatBotRights.WriteConnectionString | ChatBotRights.WriteEnabled | ChatBotRights.WriteName | ChatBotRights.WriteProvider)]
177  public override async Task<IActionResult> Update([FromBody] Api.Models.ChatBot model, CancellationToken cancellationToken)
178  {
179  if (model == null)
180  throw new ArgumentNullException(nameof(model));
181 
182  if (model.Provider.HasValue && !model.ValidateProviderChannelTypes())
183  return BadRequest(new ErrorMessage { Message = "One or more of channels aren't formatted correctly for the given provider!" });
184 
185  var query = DatabaseContext.ChatBots.Where(x => x.InstanceId == Instance.Id && x.Id == model.Id).Include(x => x.Channels);
186 
187  var current = await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
188 
189  if (current == default)
190  return StatusCode((int)HttpStatusCode.Gone);
191 
192  var userRights = (ChatBotRights)AuthenticationContext.GetRight(RightsType.ChatBots);
193 
194  bool anySettingsModified = false;
195 
196  bool CheckModified<T>(Expression<Func<Api.Models.Internal.ChatBot, T>> expression, ChatBotRights requiredRight)
197  {
198  var memberSelectorExpression = (MemberExpression)expression.Body;
199  var property = (PropertyInfo)memberSelectorExpression.Member;
200 
201  var newVal = property.GetValue(model);
202  if (newVal == null)
203  return false;
204  if (!userRights.HasFlag(requiredRight) && property.GetValue(current) != newVal)
205  return true;
206 
207  property.SetValue(current, newVal);
208  anySettingsModified = true;
209  return false;
210  };
211 
212  var oldProvider = current.Provider;
213 
214  if (CheckModified(x => x.ConnectionString, ChatBotRights.WriteConnectionString)
215  || CheckModified(x => x.Enabled, ChatBotRights.WriteEnabled)
216  || CheckModified(x => x.Name, ChatBotRights.WriteName)
217  || CheckModified(x => x.Provider, ChatBotRights.WriteProvider)
218  || (model.Channels != null && !userRights.HasFlag(ChatBotRights.WriteChannels)))
219  return Forbid();
220 
221  var hasChannels = model.Channels != null;
222  if (hasChannels || (model.Provider.HasValue && model.Provider != oldProvider))
223  {
224  DatabaseContext.ChatChannels.RemoveRange(current.Channels);
225  if (hasChannels)
226  {
227  var dbChannels = model.Channels.Select(x => ConvertApiChatChannel(x)).ToList();
228  DatabaseContext.ChatChannels.AddRange(dbChannels);
229  current.Channels = dbChannels;
230  }
231  else
232  current.Channels.Clear();
233  }
234 
235  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
236 
237  var chat = instanceManager.GetInstance(Instance).Chat;
238 
239  if (anySettingsModified)
240  //have to rebuild the thing first
241  await chat.ChangeSettings(current, cancellationToken).ConfigureAwait(false);
242 
243  if (model.Channels != null || anySettingsModified)
244  await chat.ChangeChannels(current.Id, current.Channels, cancellationToken).ConfigureAwait(false);
245 
246  if (userRights.HasFlag(ChatBotRights.Read))
247  {
248  if (!userRights.HasFlag(ChatBotRights.ReadConnectionString))
249  current.ConnectionString = null;
250  return Json(current.ToApi());
251  }
252  return Ok();
253  }
254  }
255 }
RightsType
The type of rights a model uses
Definition: RightsType.cs:6
+ -
override async Task< IActionResult > Delete(long id, CancellationToken cancellationToken)
Attempt to delete a model with a particular id
+
override async Task< IActionResult > Delete(long id, CancellationToken cancellationToken)
Attempt to delete a model with a particular id
Task Save(CancellationToken cancellationToken)
Saves changes made to the IDatabaseContext
ulong GetRight(RightsType rightsType)
Get the value of a given rightsType
Routes to a server actions
Definition: Routes.cs:9
ChatController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, ILogger< ChatController > logger)
Construct a ChatController
-
override async Task< IActionResult > Update([FromBody] Api.Models.ChatBot model, CancellationToken cancellationToken)
+
override async Task< IActionResult > Update([FromBody] Api.Models.ChatBot model, CancellationToken cancellationToken)
string Message
A human readable description of the error
Definition: ErrorMessage.cs:13
const string Chat
The Models.ChatBot controller
Definition: Routes.cs:59
-
override async Task< IActionResult > GetId(long id, CancellationToken cancellationToken)
Attempt to get a specific a TModel
- +
override async Task< IActionResult > GetId(long id, CancellationToken cancellationToken)
Attempt to get a specific a TModel
long Id
The id of the Instance. Not modifiable
Definition: Instance.cs:14
override async Task< IActionResult > Create([FromBody] Api.Models.ChatBot model, CancellationToken cancellationToken)
@@ -120,7 +120,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); -
override async Task< IActionResult > List(CancellationToken cancellationToken)
Attempt to list entries of the TModel
+
override async Task< IActionResult > List(CancellationToken cancellationToken)
Attempt to list entries of the TModel
Metadata about a server instance
Definition: Instance.cs:9
ModelController<TModel> for managing Api.Models.ChatBots
diff --git a/_chat_factory_8cs_source.html b/_chat_factory_8cs_source.html index 9970aef746..924c6daa0e 100644 --- a/_chat_factory_8cs_source.html +++ b/_chat_factory_8cs_source.html @@ -92,15 +92,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ChatFactory.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Collections.Generic;
5 using Tgstation.Server.Host.IO;
6 
7 namespace Tgstation.Server.Host.Components.Chat
8 {
10  sealed class ChatFactory : IChatFactory
11  {
16 
20  readonly ILoggerFactory loggerFactory;
21 
26 
31 
39  public ChatFactory(IIOManager ioManager, ILoggerFactory loggerFactory, ICommandFactory commandFactory, IProviderFactory providerFactory)
40  {
41  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
42  this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
43  this.commandFactory = commandFactory ?? throw new ArgumentNullException(nameof(commandFactory));
44  this.providerFactory = providerFactory ?? throw new ArgumentNullException(nameof(providerFactory));
45  }
46 
48  public IChat CreateChat(IEnumerable<Models.ChatBot> initialChatBots) => new Chat(providerFactory, ioManager, commandFactory, loggerFactory.CreateLogger<Chat>(), initialChatBots);
49  }
50 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Collections.Generic;
5 using Tgstation.Server.Host.IO;
6 
7 namespace Tgstation.Server.Host.Components.Chat
8 {
10  sealed class ChatFactory : IChatFactory
11  {
16 
20  readonly ILoggerFactory loggerFactory;
21 
26 
31 
39  public ChatFactory(IIOManager ioManager, ILoggerFactory loggerFactory, ICommandFactory commandFactory, IProviderFactory providerFactory)
40  {
41  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
42  this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
43  this.commandFactory = commandFactory ?? throw new ArgumentNullException(nameof(commandFactory));
44  this.providerFactory = providerFactory ?? throw new ArgumentNullException(nameof(providerFactory));
45  }
46 
48  public IChat CreateChat(IEnumerable<Models.ChatBot> initialChatBots) => new Chat(providerFactory, ioManager, commandFactory, loggerFactory.CreateLogger<Chat>(), initialChatBots);
49  }
50 }
+
readonly ICommandFactory commandFactory
The ICommandFactory for the ChatFactory
Definition: ChatFactory.cs:25
For managing connected chat services
Definition: IChat.cs:13
-
readonly ILoggerFactory loggerFactory
The ILoggerFactory for the ChatFactory
Definition: ChatFactory.cs:20
diff --git a/_chat_provider_8cs_source.html b/_chat_provider_8cs_source.html index 2ab835d755..e1274ce1b6 100644 --- a/_chat_provider_8cs_source.html +++ b/_chat_provider_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ChatProvider.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
6  public enum ChatProvider
7  {
11  Irc,
15  Discord
16  }
17 }
- +Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
6  public enum ChatProvider
7  {
11  Irc,
15  Discord
16  }
17 }
+
ChatProvider
Represents a chat service provider
Definition: ChatProvider.cs:6
diff --git a/_claims_injector_8cs_source.html b/_claims_injector_8cs_source.html index d8a68757e3..2a5ad7af07 100644 --- a/_claims_injector_8cs_source.html +++ b/_claims_injector_8cs_source.html @@ -92,20 +92,20 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ClaimsInjector.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Authentication.JwtBearer;
2 using Microsoft.AspNetCore.Http;
3 using System;
4 using System.Collections.Generic;
5 using System.Globalization;
6 using System.IdentityModel.Tokens.Jwt;
7 using System.Security.Claims;
8 using System.Threading;
9 using System.Threading.Tasks;
10 using Tgstation.Server.Api;
13 
14 namespace Tgstation.Server.Host.Security
15 {
18  {
23 
28 
34  public ClaimsInjector(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory)
35  {
36  this.databaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext));
37  this.authenticationContextFactory = authenticationContextFactory ?? throw new ArgumentNullException(nameof(authenticationContextFactory));
38  }
39 
41  public async Task InjectClaimsIntoContext(TokenValidatedContext tokenValidatedContext, CancellationToken cancellationToken)
42  {
43  if (tokenValidatedContext == null)
44  throw new ArgumentNullException(nameof(tokenValidatedContext));
45 
46  //Find the user id in the token
47  var userIdClaim = tokenValidatedContext.Principal.FindFirst(JwtRegisteredClaimNames.Sub);
48  if (userIdClaim == default)
49  throw new InvalidOperationException("Missing required claim!");
50 
51  long userId;
52  try
53  {
54  userId = Int64.Parse(userIdClaim.Value, CultureInfo.InvariantCulture);
55  }
56  catch (Exception e)
57  {
58  throw new InvalidOperationException("Failed to parse user ID!", e);
59  }
60 
61  ApiHeaders apiHeaders;
62  try
63  {
64  apiHeaders = new ApiHeaders(tokenValidatedContext.HttpContext.Request.GetTypedHeaders());
65  }
66  catch (InvalidOperationException)
67  {
68  //we are not responsible for handling header validation issues
69  return;
70  }
71 
72  //This populates the CurrentAuthenticationContext field for use by us and subsequent controllers
73  await authenticationContextFactory.CreateAuthenticationContext(userId, apiHeaders.InstanceId, tokenValidatedContext.SecurityToken.ValidFrom, cancellationToken).ConfigureAwait(false);
74 
75  var authenticationContext = authenticationContextFactory.CurrentAuthenticationContext;
76 
77  var enumerator = Enum.GetValues(typeof(RightsType));
78  var claims = new List<Claim>();
79  foreach (RightsType I in enumerator)
80  {
81  //if there's no instance user, do a weird thing and add all the instance roles
82  //we need it so we can get to OnActionExecutionAsync where we can properly decide between BadRequest and Forbid
83  //if user is null that means they got the token with an expired password
84  var rightInt = authenticationContext.User == null || (RightsHelper.IsInstanceRight(I) && authenticationContext.InstanceUser == null) ? ~0U : authenticationContext.GetRight(I);
85  var rightEnum = RightsHelper.RightToType(I);
86  var right = (Enum)Enum.ToObject(rightEnum, rightInt);
87  foreach (Enum J in Enum.GetValues(rightEnum))
88  if (right.HasFlag(J))
89  claims.Add(new Claim(ClaimTypes.Role, RightsHelper.RoleName(I, J)));
90  }
91 
92  tokenValidatedContext.Principal.AddIdentity(new ClaimsIdentity(claims));
93  }
94  }
95 }
static bool IsInstanceRight(RightsType rightsType)
Check if a given rightsType is meant for an Models.Instance
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Authentication.JwtBearer;
2 using Microsoft.AspNetCore.Http;
3 using System;
4 using System.Collections.Generic;
5 using System.Globalization;
6 using System.IdentityModel.Tokens.Jwt;
7 using System.Security.Claims;
8 using System.Threading;
9 using System.Threading.Tasks;
10 using Tgstation.Server.Api;
13 
14 namespace Tgstation.Server.Host.Security
15 {
18  {
23 
28 
34  public ClaimsInjector(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory)
35  {
36  this.databaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext));
37  this.authenticationContextFactory = authenticationContextFactory ?? throw new ArgumentNullException(nameof(authenticationContextFactory));
38  }
39 
41  public async Task InjectClaimsIntoContext(TokenValidatedContext tokenValidatedContext, CancellationToken cancellationToken)
42  {
43  if (tokenValidatedContext == null)
44  throw new ArgumentNullException(nameof(tokenValidatedContext));
45 
46  //Find the user id in the token
47  var userIdClaim = tokenValidatedContext.Principal.FindFirst(JwtRegisteredClaimNames.Sub);
48  if (userIdClaim == default)
49  throw new InvalidOperationException("Missing required claim!");
50 
51  long userId;
52  try
53  {
54  userId = Int64.Parse(userIdClaim.Value, CultureInfo.InvariantCulture);
55  }
56  catch (Exception e)
57  {
58  throw new InvalidOperationException("Failed to parse user ID!", e);
59  }
60 
61  ApiHeaders apiHeaders;
62  try
63  {
64  apiHeaders = new ApiHeaders(tokenValidatedContext.HttpContext.Request.GetTypedHeaders());
65  }
66  catch (InvalidOperationException)
67  {
68  //we are not responsible for handling header validation issues
69  return;
70  }
71 
72  //This populates the CurrentAuthenticationContext field for use by us and subsequent controllers
73  await authenticationContextFactory.CreateAuthenticationContext(userId, apiHeaders.InstanceId, tokenValidatedContext.SecurityToken.ValidFrom, cancellationToken).ConfigureAwait(false);
74 
75  var authenticationContext = authenticationContextFactory.CurrentAuthenticationContext;
76 
77  var enumerator = Enum.GetValues(typeof(RightsType));
78  var claims = new List<Claim>();
79  foreach (RightsType I in enumerator)
80  {
81  //if there's no instance user, do a weird thing and add all the instance roles
82  //we need it so we can get to OnActionExecutionAsync where we can properly decide between BadRequest and Forbid
83  //if user is null that means they got the token with an expired password
84  var rightInt = authenticationContext.User == null || (RightsHelper.IsInstanceRight(I) && authenticationContext.InstanceUser == null) ? ~0U : authenticationContext.GetRight(I);
85  var rightEnum = RightsHelper.RightToType(I);
86  var right = (Enum)Enum.ToObject(rightEnum, rightInt);
87  foreach (Enum J in Enum.GetValues(rightEnum))
88  if (right.HasFlag(J))
89  claims.Add(new Claim(ClaimTypes.Role, RightsHelper.RoleName(I, J)));
90  }
91 
92  tokenValidatedContext.Principal.AddIdentity(new ClaimsIdentity(claims));
93  }
94  }
95 }
static bool IsInstanceRight(RightsType rightsType)
Check if a given rightsType is meant for an Models.Instance
RightsType
The type of rights a model uses
Definition: RightsType.cs:6
+
async Task InjectClaimsIntoContext(TokenValidatedContext tokenValidatedContext, CancellationToken cancellationToken)
Setup the System.Security.Claims.Claims for a given tokenValidatedContext
Represents the header that must be present for every server request
Definition: ApiHeaders.cs:16
ClaimsInjector(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory)
Construct a ClaimsInjector
readonly IAuthenticationContextFactory authenticationContextFactory
The IAuthenticationContextFactory for the ClaimsInjector
-
static Type RightToType(RightsType rightsType)
Map a given rightsType to its respective Enum Type
For injecting System.Security.Claims.Claims that Controllers.TgsAuthorizeAttribute can look for ...
diff --git a/_client_exception_8cs_source.html b/_client_exception_8cs_source.html index a009c605e3..34327e00b6 100644 --- a/_client_exception_8cs_source.html +++ b/_client_exception_8cs_source.html @@ -92,15 +92,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ClientException.cs
-Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
10  public abstract class ClientException : Exception
11  {
15  public HttpStatusCode StatusCode { get; }
16 
20  public Version ServerApiVersion { get; }
21 
27  protected ClientException(ErrorMessage errorMessage, HttpStatusCode statusCode) : base(errorMessage == null ? throw new ArgumentNullException(nameof(errorMessage)) : errorMessage.Message ?? "Unknown Error")
28  {
29  StatusCode = statusCode;
30  ServerApiVersion = errorMessage?.SeverApiVersion;
31  }
32 
36  protected ClientException() { }
37 
42  protected ClientException(string message) : base(message) { }
43 
49  protected ClientException(string message, Exception innerException) : base(message, innerException) { }
50  }
51 }
+Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
10  public abstract class ClientException : Exception
11  {
15  public HttpStatusCode StatusCode { get; }
16 
20  public Version ServerApiVersion { get; }
21 
27  protected ClientException(ErrorMessage errorMessage, HttpStatusCode statusCode) : base(errorMessage == null ? throw new ArgumentNullException(nameof(errorMessage)) : errorMessage.Message ?? "Unknown Error")
28  {
29  StatusCode = statusCode;
30  ServerApiVersion = errorMessage?.SeverApiVersion;
31  }
32 
36  protected ClientException() { }
37 
42  protected ClientException(string message) : base(message) { }
43 
49  protected ClientException(string message, Exception innerException) : base(message, innerException) { }
50  }
51 }
ClientException()
Construct a ClientException
ClientException(ErrorMessage errorMessage, HttpStatusCode statusCode)
Construct a ClientException using an errorMessage and statusCode
+
ClientException(string message, Exception innerException)
Construct a ClientException with a message and innerException
ClientException(string message)
Construct a ClientException with a message
-
Version SeverApiVersion
The version of the API the server is using
Definition: ErrorMessage.cs:18
Represents an error message returned by the server
Definition: ErrorMessage.cs:8
diff --git a/_comm_command_8cs_source.html b/_comm_command_8cs_source.html index 6c902bf414..65ecaa0b8b 100644 --- a/_comm_command_8cs_source.html +++ b/_comm_command_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
CommCommand.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 
3 namespace Tgstation.Server.Host.Components.Interop
4 {
8  sealed class CommCommand
9  {
13  public IReadOnlyDictionary<string, string> Parameters { get; set; }
14  }
15 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 
3 namespace Tgstation.Server.Host.Components.Interop
4 {
8  sealed class CommCommand
9  {
13  public IReadOnlyDictionary<string, string> Parameters { get; set; }
14  }
15 }
+ -
diff --git a/_comm_context_8cs_source.html b/_comm_context_8cs_source.html index 64edd0279a..fd9e4a8c9c 100644 --- a/_comm_context_8cs_source.html +++ b/_comm_context_8cs_source.html @@ -92,7 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
CommContext.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using Newtonsoft.Json;
3 using System;
4 using System.Collections.Generic;
5 using System.IO;
6 using System.Text;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Tgstation.Server.Host.IO;
10 
11 namespace Tgstation.Server.Host.Components.Interop
12 {
14  sealed class CommContext : ICommContext
15  {
20 
24  readonly ILogger<CommContext> logger;
25 
29  readonly FileSystemWatcher fileSystemWatcher;
30 
34  readonly CancellationTokenSource cancellationTokenSource;
35 
39  readonly CancellationToken cancellationToken;
40 
45 
49  bool disposed;
50 
58  public CommContext(IIOManager ioManager, ILogger<CommContext> logger, string directory, string filter)
59  {
60  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
61  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
62 
63  directory = ioManager.ResolvePath(directory) ?? throw new ArgumentNullException(nameof(directory));
64  if (filter == null)
65  throw new ArgumentNullException(nameof(filter));
66 
67  fileSystemWatcher = new FileSystemWatcher(directory, filter)
68  {
69  EnableRaisingEvents = true,
70  IncludeSubdirectories = false,
71  NotifyFilter = NotifyFilters.LastWrite
72  };
73 
74  fileSystemWatcher.Created += HandleWrite;
75  fileSystemWatcher.Changed += HandleWrite;
76 
77  cancellationTokenSource = new CancellationTokenSource();
78  cancellationToken = cancellationTokenSource.Token;
79  disposed = false;
80  }
81 
83  public void Dispose()
84  {
85  if (disposed)
86  return;
87  disposed = true;
88  fileSystemWatcher.Dispose();
89  cancellationTokenSource.Cancel();
90  cancellationTokenSource.Dispose();
91  }
92 
98  async void HandleWrite(object sender, FileSystemEventArgs e) //this is what async void was made for
99  {
100  try
101  {
102  var fileBytes = await ioManager.ReadAllBytes(e.FullPath, cancellationToken).ConfigureAwait(false);
103  var file = Encoding.UTF8.GetString(fileBytes);
104 
105  logger.LogTrace("Read interop command json: {0}", file);
106 
107  CommCommand command;
108  try
109  {
110  command = new CommCommand
111  {
112  Parameters = JsonConvert.DeserializeObject<IReadOnlyDictionary<string, string>>(file)
113  };
114  }
115  catch (JsonSerializationException ex)
116  {
117  //file not fully written yet
118  logger.LogDebug("Suppressing json convert exception for command file write: {0}", ex);
119  return;
120  }
121 
122  await (handler?.HandleInterop(command, cancellationToken) ?? Task.CompletedTask).ConfigureAwait(false);
123  }
124  catch (Exception ex)
125  {
126  logger.LogDebug("Exception while trying to handle command json write: {0}", ex);
127  }
128  }
129 
131  public void RegisterHandler(ICommHandler handler)
132  {
133  if (this.handler != null)
134  throw new InvalidOperationException("RegisterHandler already called!");
135  this.handler = handler ?? throw new ArgumentNullException(nameof(handler));
136  }
137  }
138 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using Newtonsoft.Json;
3 using System;
4 using System.Collections.Generic;
5 using System.IO;
6 using System.Text;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Tgstation.Server.Host.IO;
10 
11 namespace Tgstation.Server.Host.Components.Interop
12 {
14  sealed class CommContext : ICommContext
15  {
20 
24  readonly ILogger<CommContext> logger;
25 
29  readonly FileSystemWatcher fileSystemWatcher;
30 
34  readonly CancellationTokenSource cancellationTokenSource;
35 
39  readonly CancellationToken cancellationToken;
40 
45 
49  bool disposed;
50 
58  public CommContext(IIOManager ioManager, ILogger<CommContext> logger, string directory, string filter)
59  {
60  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
61  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
62 
63  directory = ioManager.ResolvePath(directory) ?? throw new ArgumentNullException(nameof(directory));
64  if (filter == null)
65  throw new ArgumentNullException(nameof(filter));
66 
67  fileSystemWatcher = new FileSystemWatcher(directory, filter)
68  {
69  EnableRaisingEvents = true,
70  IncludeSubdirectories = false,
71  NotifyFilter = NotifyFilters.LastWrite
72  };
73 
74  fileSystemWatcher.Created += HandleWrite;
75  fileSystemWatcher.Changed += HandleWrite;
76 
77  cancellationTokenSource = new CancellationTokenSource();
78  cancellationToken = cancellationTokenSource.Token;
79  disposed = false;
80  }
81 
83  public void Dispose()
84  {
85  if (disposed)
86  return;
87  disposed = true;
88  fileSystemWatcher.Dispose();
89  cancellationTokenSource.Cancel();
90  cancellationTokenSource.Dispose();
91  }
92 
98  async void HandleWrite(object sender, FileSystemEventArgs e) //this is what async void was made for
99  {
100  try
101  {
102  var fileBytes = await ioManager.ReadAllBytes(e.FullPath, cancellationToken).ConfigureAwait(false);
103  var file = Encoding.UTF8.GetString(fileBytes);
104 
105  logger.LogTrace("Read interop command json: {0}", file);
106 
107  CommCommand command;
108  try
109  {
110  command = new CommCommand
111  {
112  Parameters = JsonConvert.DeserializeObject<IReadOnlyDictionary<string, string>>(file)
113  };
114  }
115  catch (JsonSerializationException ex)
116  {
117  //file not fully written yet
118  logger.LogDebug("Suppressing json convert exception for command file write: {0}", ex);
119  return;
120  }
121 
122  await (handler?.HandleInterop(command, cancellationToken) ?? Task.CompletedTask).ConfigureAwait(false);
123  }
124  catch (Exception ex)
125  {
126  logger.LogDebug("Exception while trying to handle command json write: {0}", ex);
127  }
128  }
129 
131  public void RegisterHandler(ICommHandler handler)
132  {
133  if (this.handler != null)
134  throw new InvalidOperationException("RegisterHandler already called!");
135  this.handler = handler ?? throw new ArgumentNullException(nameof(handler));
136  }
137  }
138 }
+
readonly CancellationToken cancellationToken
The CancellationToken for the CommContext
Definition: CommContext.cs:39
Represents a registration of an interop session
Definition: ICommContext.cs:8
@@ -102,7 +103,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
void RegisterHandler(ICommHandler handler)
Register a handler with the ICommContext
Definition: CommContext.cs:131
ICommHandler handler
The ICommHandler for the CommContext
Definition: CommContext.cs:44
-
CommContext(IIOManager ioManager, ILogger< CommContext > logger, string directory, string filter)
Construct an CommContext
Definition: CommContext.cs:58
diff --git a/_command_factory_8cs_source.html b/_command_factory_8cs_source.html index da18b49e47..ec30d07a06 100644 --- a/_command_factory_8cs_source.html +++ b/_command_factory_8cs_source.html @@ -92,8 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
CommandFactory.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
7 
8 namespace Tgstation.Server.Host.Components.Chat.Commands
9 {
12  {
17 
22 
27 
32 
36  readonly Models.Instance instance;
37 
42 
51  public CommandFactory(IApplication application, IByondManager byondManager, IRepositoryManager repositoryManager, IDatabaseContextFactory databaseContextFactory, Models.Instance instance)
52  {
53  this.application = application ?? throw new ArgumentNullException(nameof(application));
54  this.byondManager = byondManager ?? throw new ArgumentNullException(nameof(byondManager));
55  this.repositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager));
56  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
57  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
58  }
59 
64  public void SetWatchdog(IWatchdog watchdog)
65  {
66  if (this.watchdog != null)
67  throw new InvalidOperationException("SetWatchdog has already been called!");
68  this.watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog));
69  }
70 
72  public IReadOnlyList<ICommand> GenerateCommands()
73  {
74  if (watchdog == null)
75  throw new InvalidOperationException("SetWatchdog has not been called!");
76  return new List<ICommand>
77  {
78  new VersionCommand(application),
79  new ByondCommand(byondManager),
80  new RevisionCommand(watchdog, repositoryManager, instance),
81  new PullRequestsCommand(watchdog, repositoryManager, databaseContextFactory, instance),
82  new KekCommand()
83  };
84  }
85  }
86 }
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
7 
8 namespace Tgstation.Server.Host.Components.Chat.Commands
9 {
12  {
17 
22 
27 
32 
36  readonly Models.Instance instance;
37 
42 
51  public CommandFactory(IApplication application, IByondManager byondManager, IRepositoryManager repositoryManager, IDatabaseContextFactory databaseContextFactory, Models.Instance instance)
52  {
53  this.application = application ?? throw new ArgumentNullException(nameof(application));
54  this.byondManager = byondManager ?? throw new ArgumentNullException(nameof(byondManager));
55  this.repositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager));
56  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
57  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
58  }
59 
64  public void SetWatchdog(IWatchdog watchdog)
65  {
66  if (this.watchdog != null)
67  throw new InvalidOperationException("SetWatchdog has already been called!");
68  this.watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog));
69  }
70 
72  public IReadOnlyList<ICommand> GenerateCommands()
73  {
74  if (watchdog == null)
75  throw new InvalidOperationException("SetWatchdog has not been called!");
76  return new List<ICommand>
77  {
78  new VersionCommand(application),
79  new ByondCommand(byondManager),
80  new RevisionCommand(watchdog, repositoryManager, instance),
81  new PullRequestsCommand(watchdog, repositoryManager, databaseContextFactory, instance),
82  new KekCommand()
83  };
84  }
85  }
86 }
Configures the ASP.NET Core web application
Definition: IApplication.cs:8
+
readonly IByondManager byondManager
The IByondManager for the CommandFactory
Factory for scoping usage of IDatabaseContexts. Meant for use by Components
@@ -106,7 +107,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly IDatabaseContextFactory databaseContextFactory
The IDatabaseContextFactory for the CommandFactory
Command for reading the active Api.Models.TestMerges
-
For managing the BYOND installation
IWatchdog watchdog
The IWatchdog for the CommandFactory
CommandFactory(IApplication application, IByondManager byondManager, IRepositoryManager repositoryManager, IDatabaseContextFactory databaseContextFactory, Models.Instance instance)
Construct a CommandFactory
diff --git a/_components_2_watchdog_2_i_watchdog_8cs_source.html b/_components_2_watchdog_2_i_watchdog_8cs_source.html index 8e9bdc420a..a11c449487 100644 --- a/_components_2_watchdog_2_i_watchdog_8cs_source.html +++ b/_components_2_watchdog_2_i_watchdog_8cs_source.html @@ -92,14 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IWatchdog.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Threading;
4 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Components.Watchdog
8 {
13  {
17  bool Running { get; }
18 
22  bool AlphaIsActive { get; }
23 
27  Models.CompileJob ActiveCompileJob { get; }
28 
32  LaunchResult LastLaunchResult { get; }
33 
37  DreamDaemonLaunchParameters ActiveLaunchParameters { get; }
38 
42  DreamDaemonLaunchParameters LastLaunchParameters { get; }
43 
48 
54  Task<WatchdogLaunchResult> Launch(CancellationToken cancellationToken);
55 
62  Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken);
63 
70  Task<WatchdogLaunchResult> Restart(bool graceful, CancellationToken cancellationToken);
71 
78  Task Terminate(bool graceful, CancellationToken cancellationToken);
79 
85  Task ResetRebootState(CancellationToken cancellationToken);
86  }
87 }
Represents the result of trying to start a DD process
Definition: LaunchResult.cs:9
+Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Threading;
4 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Components.Watchdog
8 {
13  {
17  bool Running { get; }
18 
22  bool AlphaIsActive { get; }
23 
27  Models.CompileJob ActiveCompileJob { get; }
28 
32  LaunchResult LastLaunchResult { get; }
33 
37  DreamDaemonLaunchParameters ActiveLaunchParameters { get; }
38 
42  DreamDaemonLaunchParameters LastLaunchParameters { get; }
43 
48 
54  Task<WatchdogLaunchResult> Launch(CancellationToken cancellationToken);
55 
62  Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken);
63 
70  Task<WatchdogLaunchResult> Restart(bool graceful, CancellationToken cancellationToken);
71 
78  Task Terminate(bool graceful, CancellationToken cancellationToken);
79 
85  Task ResetRebootState(CancellationToken cancellationToken);
86  }
87 }
Represents the result of trying to start a DD process
Definition: LaunchResult.cs:9
Consumes EventTypes and takes the appropriate actions
+ - - + diff --git a/_components_2_watchdog_2_i_watchdog_factory_8cs_source.html b/_components_2_watchdog_2_i_watchdog_factory_8cs_source.html index 0ee0e214dd..6039512a0a 100644 --- a/_components_2_watchdog_2_i_watchdog_factory_8cs_source.html +++ b/_components_2_watchdog_2_i_watchdog_factory_8cs_source.html @@ -92,17 +92,17 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IWatchdogFactory.cs
-Go to the documentation of this file.
4 
5 namespace Tgstation.Server.Host.Components.Watchdog
6 {
10  interface IWatchdogFactory
11  {
23  IWatchdog CreateWatchdog(IChat chat, IDmbFactory dmbFactory, IReattachInfoHandler reattachInfoHandler, IEventConsumer eventConsumer, ISessionControllerFactory sessionControllerFactory, Api.Models.Instance instance, DreamDaemonSettings settings);
24  }
25 }
+Go to the documentation of this file.
4 
5 namespace Tgstation.Server.Host.Components.Watchdog
6 {
10  interface IWatchdogFactory
11  {
23  IWatchdog CreateWatchdog(IChat chat, IDmbFactory dmbFactory, IReattachInfoHandler reattachInfoHandler, IEventConsumer eventConsumer, ISessionControllerFactory sessionControllerFactory, Api.Models.Instance instance, DreamDaemonSettings settings);
24  }
25 }
Consumes EventTypes and takes the appropriate actions
+
For managing connected chat services
Definition: IChat.cs:13
- - + diff --git a/_components_2_watchdog_2_reattach_information_8cs_source.html b/_components_2_watchdog_2_reattach_information_8cs_source.html index 730a4dd883..fad001dea8 100644 --- a/_components_2_watchdog_2_reattach_information_8cs_source.html +++ b/_components_2_watchdog_2_reattach_information_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ReattachInformation.cs
-Go to the documentation of this file.
1 using System;
3 
4 namespace Tgstation.Server.Host.Components.Watchdog
5 {
10  {
14  public IDmbProvider Dmb { get; set; }
15 
19  public ReattachInformation() { }
20 
26  public ReattachInformation(Models.ReattachInformation copy, IDmbProvider dmb) : base(copy)
27  {
28  Dmb = dmb ?? throw new ArgumentNullException(nameof(dmb));
29  }
30  }
31 }
+Go to the documentation of this file.
1 using System;
3 
4 namespace Tgstation.Server.Host.Components.Watchdog
5 {
10  {
14  public IDmbProvider Dmb { get; set; }
15 
19  public ReattachInformation() { }
20 
26  public ReattachInformation(Models.ReattachInformation copy, IDmbProvider dmb) : base(copy)
27  {
28  Dmb = dmb ?? throw new ArgumentNullException(nameof(dmb));
29  }
30  }
31 }
+ -
ReattachInformation(Models.ReattachInformation copy, IDmbProvider dmb)
Construct a ReattachInformation from a given copy and dmb
Provides absolute paths to the latest compiled .dmbs
Definition: IDmbProvider.cs:9
diff --git a/_components_2_watchdog_2_watchdog_8cs_source.html b/_components_2_watchdog_2_watchdog_8cs_source.html index 4ac8eb4c56..5ec5a07397 100644 --- a/_components_2_watchdog_2_watchdog_8cs_source.html +++ b/_components_2_watchdog_2_watchdog_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Watchdog.cs
-Go to the documentation of this file.
1 using Byond.TopicSender;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using Newtonsoft.Json;
5 using Newtonsoft.Json.Serialization;
6 using System;
7 using System.Collections.Generic;
8 using System.Globalization;
9 using System.Linq;
10 using System.Text;
11 using System.Threading;
12 using System.Threading.Tasks;
19 
20 namespace Tgstation.Server.Host.Components.Watchdog
21 {
24  {
28  const int AlphaBravoStartupSeperationInterval = 10;
29 
31  public bool Running { get; private set; }
32 
34  public bool AlphaIsActive { get; private set; }
35 
37  public Models.CompileJob ActiveCompileJob => (AlphaIsActive ? alphaServer : bravoServer)?.Dmb.CompileJob;
38 
40  public LaunchResult LastLaunchResult { get; private set; }
41 
43  public DreamDaemonLaunchParameters ActiveLaunchParameters { get; private set; }
44 
46  public DreamDaemonLaunchParameters LastLaunchParameters { get; private set; }
47 
49  public RebootState? RebootState => Running ? (AlphaIsActive ? alphaServer?.RebootState : bravoServer?.RebootState) : null;
50 
54  readonly IChat chat;
55 
60 
65 
69  readonly ILogger<Watchdog> logger;
70 
75 
80 
84  readonly IByondTopicSender byondTopicSender;
85 
90 
95 
99  readonly SemaphoreSlim semaphore;
100 
104  readonly Api.Models.Instance instance;
105 
109  readonly bool autoStart;
110 
114  CancellationTokenSource monitorCts;
115 
120 
124  TaskCompletionSource<object> activeParametersUpdated;
125 
134 
139 
156  public Watchdog(IChat chat, ISessionControllerFactory sessionControllerFactory, IDmbFactory dmbFactory, IServerControl serverUpdater, ILogger<Watchdog> logger, IReattachInfoHandler reattachInfoHandler, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IEventConsumer eventConsumer, IJobManager jobManager, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, bool autoStart)
157  {
158  this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
159  this.sessionControllerFactory = sessionControllerFactory ?? throw new ArgumentNullException(nameof(sessionControllerFactory));
160  this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory));
161  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
162  this.reattachInfoHandler = reattachInfoHandler ?? throw new ArgumentNullException(nameof(reattachInfoHandler));
163  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
164  this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender));
165  this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
166  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
167  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
168  this.autoStart = autoStart;
169 
170  if (serverUpdater == null)
171  throw new ArgumentNullException(nameof(serverUpdater));
172 
173  serverUpdater.RegisterForRestart(() => releaseServers = true);
174 
175  chat.RegisterCommandHandler(this);
176 
177  AlphaIsActive = true;
178  ActiveLaunchParameters = initialLaunchParameters;
179  releaseServers = false;
180  semaphore = new SemaphoreSlim(1);
181  activeParametersUpdated = new TaskCompletionSource<object>();
182  }
183 
185  public void Dispose()
186  {
187  DisposeAndNullControllers();
188  semaphore.Dispose();
189  }
190 
195  {
196  alphaServer?.Dispose();
197  alphaServer = null;
198  bravoServer?.Dispose();
199  bravoServer = null;
200  Running = false;
201  }
202 
209  async Task TerminateNoLock(bool graceful, bool announce, CancellationToken cancellationToken)
210  {
211  if (!Running)
212  return;
213  if (!graceful)
214  {
215  var chatTask = announce ? chat.SendWatchdogMessage("Terminating...", cancellationToken) : Task.CompletedTask;
216  await StopMonitor().ConfigureAwait(false);
217  DisposeAndNullControllers();
218  LastLaunchParameters = null;
219  await chatTask.ConfigureAwait(false);
220  return;
221  }
222  var toKill = AlphaIsActive ? alphaServer : bravoServer;
223  var other = AlphaIsActive ? bravoServer : alphaServer;
224  if (toKill != null)
225  await toKill.SetRebootState(Components.Watchdog.RebootState.Shutdown, cancellationToken).ConfigureAwait(false);
226  }
227 
235  async Task HandlerMonitorWakeup(MonitorActivationReason activationReason, MonitorState monitorState, CancellationToken cancellationToken)
236  {
237  logger.LogDebug("Monitor activation. Reason: {0}", activationReason);
238 
239  //returns true if the inactive server can't be used immediately
240  bool FullRestartDeadInactive()
241  {
242  if (monitorState.RebootingInactiveServer || monitorState.InactiveServerCritFail)
243  {
244  logger.LogInformation("Inactive server is {0}! Restarting monitor...", monitorState.InactiveServerCritFail ? "critically failed" : "still rebooting");
245  monitorState.NextAction = MonitorAction.Restart; //will dispose server
246  return true;
247  }
248  return false;
249  };
250 
251  //trys to set inactive server's port to the public port
252  //doesn't handle closing active server's port
253  async Task<bool> MakeInactiveActive()
254  {
255  logger.LogDebug("Setting inactive server to port {0}...", ActiveLaunchParameters.PrimaryPort.Value);
256  var result = await monitorState.InactiveServer.SetPort(ActiveLaunchParameters.PrimaryPort.Value, cancellationToken).ConfigureAwait(false);
257 
258  if (!result)
259  {
260  logger.LogWarning("Failed to activate inactive server! Restarting monitor...");
261  monitorState.NextAction = MonitorAction.Restart; //will dispose server
262  return false;
263  }
264 
265  //inactive server should always be using active launch parameters
266  LastLaunchParameters = ActiveLaunchParameters;
267 
268  var tmp = monitorState.ActiveServer;
269  monitorState.ActiveServer = monitorState.InactiveServer;
270  monitorState.InactiveServer = tmp;
271  AlphaIsActive = !AlphaIsActive;
272  return true;
273  }
274 
275  // Tries to load inactive server with latest dmb, falling back to current dmb on failure. Requires a lock on <see cref="semaphore"/>
276  async Task<bool> RestartInactiveServer()
277  {
278  logger.LogInformation("Rebooting inactive server...");
279  var newDmb = dmbFactory.LockNextDmb(1);
280  bool usedMostRecentDmb;
281  try
282  {
283  monitorState.InactiveServer = await sessionControllerFactory.LaunchNew(ActiveLaunchParameters, newDmb, null, false, !monitorState.ActiveServer.IsPrimary, false, cancellationToken).ConfigureAwait(false);
284  usedMostRecentDmb = true;
285  }
286  catch (OperationCanceledException)
287  {
288  throw;
289  }
290  catch (Exception e)
291  {
292  logger.LogError("Error occurred while recreating server! Attempting backup strategy of running DMB of running server! Exception: {0}", e.ToString());
293  //ahh jeez, what do we do here?
294  //this is our fault, so it should never happen but
295  //idk maybe a database error while handling the newest dmb?
296  //either way try to start it using the active server's dmb as a backup
297  try
298  {
299  var dmbBackup = await dmbFactory.FromCompileJob(monitorState.ActiveServer.Dmb.CompileJob, cancellationToken).ConfigureAwait(false);
300 
301  if (dmbBackup == null) //NANI!?
302  //just give up, if THAT compile job is failing then the ActiveServer is gonna crash soon too or already has
303  throw new JobException("Creating backup DMB provider failed!");
304 
305  monitorState.InactiveServer = await sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbBackup, null, false, !monitorState.ActiveServer.IsPrimary, false, cancellationToken).ConfigureAwait(false);
306  usedMostRecentDmb = false;
307  await chat.SendWatchdogMessage("Staging newest DMB on inactive server failed: {0} Falling back to previous dmb...", cancellationToken).ConfigureAwait(false);
308  }
309  catch (OperationCanceledException)
310  {
311  throw;
312  }
313  catch (Exception e2)
314  {
315  //fuuuuucckkk
316  logger.LogError("Backup strategy failed! Monitor will restart when active server reboots! Exception: {0}", e2.ToString());
317  monitorState.InactiveServerCritFail = true;
318  await chat.SendWatchdogMessage("Attempted reboot of inactive server failed. Watchdog will reset when active server fails or exits", cancellationToken).ConfigureAwait(false);
319  return true; //we didn't use the old dmb
320  }
321  }
322 
323  logger.LogInformation("Successfully relaunched inactive server!");
324  monitorState.RebootingInactiveServer = true;
325  return usedMostRecentDmb;
326  }
327 
328  async Task UpdateAndRestartInactiveServer(bool breakAfter)
329  {
330  //replace the notification tcs here so that the next loop will read a fresh one
331  activeParametersUpdated = new TaskCompletionSource<object>();
332  monitorState.InactiveServer.Dispose(); //kill or recycle it
333  monitorState.NextAction = breakAfter ? MonitorAction.Break : MonitorAction.Continue;
334 
335  var usedLatestDmb = await RestartInactiveServer().ConfigureAwait(false);
336 
337  if (monitorState.NextAction == (breakAfter ? MonitorAction.Break : MonitorAction.Continue))
338  {
339  monitorState.ActiveServer.ClosePortOnReboot = false;
340  if (monitorState.InactiveServerHasStagedDmb && !usedLatestDmb)
341  monitorState.InactiveServerHasStagedDmb = false; //don't try to load it again though
342  }
343  };
344 
345  string ExitWord(ISessionController controller) => controller.TerminationWasRequested ? "exited" : "crashed";
346 
347  //reason handling
348  switch (activationReason)
349  {
350  case MonitorActivationReason.ActiveServerCrashed:
351  if (monitorState.ActiveServer.RebootState == Components.Watchdog.RebootState.Shutdown)
352  {
353  await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Active server {0}! Exiting due to graceful termination request...", ExitWord(monitorState.ActiveServer)), cancellationToken).ConfigureAwait(false);
354  monitorState.NextAction = MonitorAction.Exit;
355  break;
356  }
357 
358  if (FullRestartDeadInactive())
359  {
360  await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Active server {0}! Inactive server unable to online!", ExitWord(monitorState.ActiveServer)), cancellationToken).ConfigureAwait(false);
361  break;
362  }
363 
364  await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Active server {0}! Onlining inactive server...", ExitWord(monitorState.ActiveServer)), cancellationToken).ConfigureAwait(false);
365  if (!await MakeInactiveActive().ConfigureAwait(false))
366  break;
367 
368  await UpdateAndRestartInactiveServer(true).ConfigureAwait(false);
369  break;
370  case MonitorActivationReason.InactiveServerCrashed:
371  await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Inactive server {0}! Rebooting...", ExitWord(monitorState.InactiveServer)), cancellationToken).ConfigureAwait(false);
372  await UpdateAndRestartInactiveServer(false).ConfigureAwait(false);
373  break;
374  case MonitorActivationReason.ActiveServerRebooted:
375  if (FullRestartDeadInactive())
376  break;
377 
378  //what matters here is the RebootState
379  bool restartOnceSwapped = false;
380  var rebootState = monitorState.ActiveServer.RebootState;
381  monitorState.ActiveServer.ResetRebootState(); //the DMAPI has already done this internally
382 
383  switch (rebootState)
384  {
385  case Components.Watchdog.RebootState.Normal:
386  break;
387  case Components.Watchdog.RebootState.Restart:
388  restartOnceSwapped = true;
389  break;
390  case Components.Watchdog.RebootState.Shutdown:
391  await chat.SendWatchdogMessage("Active server rebooted! Exiting due to graceful termination request...", cancellationToken).ConfigureAwait(false);
392  DisposeAndNullControllers();
393  monitorState.NextAction = MonitorAction.Exit;
394  return;
395  }
396 
397  var sameCompileJob = monitorState.InactiveServer.Dmb.CompileJob.Id == monitorState.ActiveServer.Dmb.CompileJob.Id;
398  if (sameCompileJob && monitorState.InactiveServerHasStagedDmb)
399  //both servers up to date
400  monitorState.InactiveServerHasStagedDmb = false;
401  if (!sameCompileJob || ActiveLaunchParameters != LastLaunchParameters)
402  //need a new launch in ActiveServer
403  restartOnceSwapped = true;
404 
405  if (restartOnceSwapped && !monitorState.ActiveServer.ClosePortOnReboot)
406  //we need to manually restart active server
407  //it won't listen to us right now so just kill it
408  monitorState.ActiveServer.Dispose();
409 
410  if ((!restartOnceSwapped && !monitorState.ActiveServer.ClosePortOnReboot) || !await MakeInactiveActive().ConfigureAwait(false))
411  break;
412 
413  monitorState.ActiveServer.ClosePortOnReboot = true;
414 
415  if (!restartOnceSwapped)
416  {
417  monitorState.InactiveServer.ClosePortOnReboot = false;
418  //try to reopen inactive server on the private port so it's not pinging all the time
419  //failing that, just reboot it
420  restartOnceSwapped = !await monitorState.InactiveServer.SetPort(ActiveLaunchParameters.SecondaryPort.Value, cancellationToken).ConfigureAwait(false);
421  }
422 
423  if (restartOnceSwapped) //for one reason or another
424  await UpdateAndRestartInactiveServer(true).ConfigureAwait(false); //break because worse case, active server is still booting
425  else
426  {
427  monitorState.InactiveServer.ClosePortOnReboot = false;
428  monitorState.NextAction = MonitorAction.Break;
429  }
430  break;
431  case MonitorActivationReason.InactiveServerRebooted:
432  monitorState.RebootingInactiveServer = true;
433  monitorState.InactiveServer.ResetRebootState();
434  monitorState.ActiveServer.ClosePortOnReboot = false;
435  monitorState.NextAction = MonitorAction.Continue;
436  break;
437  case MonitorActivationReason.InactiveServerStartupComplete:
438  //eziest case of my life
439  monitorState.RebootingInactiveServer = false;
440  monitorState.ActiveServer.ClosePortOnReboot = true;
441  monitorState.NextAction = MonitorAction.Continue;
442  break;
443  case MonitorActivationReason.NewDmbAvailable:
444  monitorState.InactiveServerHasStagedDmb = true;
445  goto case MonitorActivationReason.ActiveLaunchParametersUpdated;
446  case MonitorActivationReason.ActiveLaunchParametersUpdated:
447  await UpdateAndRestartInactiveServer(true).ConfigureAwait(false);
448  break;
449  }
450  }
451 
457  async Task MonitorLifetimes(CancellationToken cancellationToken)
458  {
459  logger.LogTrace("Entered MonitorLifetimes");
460  var iteration = 1;
461  for (var monitorState = new MonitorState(); monitorState.NextAction != MonitorAction.Exit; ++iteration)
462  {
463  monitorState.NextAction = MonitorAction.Continue;
464  logger.LogDebug("Iteration {0} of monitor loop", iteration);
465  try
466  {
467  if (AlphaIsActive)
468  logger.LogDebug("Alpha is the active server");
469  else
470  logger.LogDebug("Bravo is the active server");
471 
472  if (monitorState.InactiveServerHasStagedDmb)
473  logger.LogDebug("Inactive server has staged .dmb");
474  if (monitorState.RebootingInactiveServer)
475  logger.LogDebug("Inactive server is rebooting");
476 
477  monitorState.ActiveServer = AlphaIsActive ? alphaServer : bravoServer;
478  monitorState.InactiveServer = AlphaIsActive ? bravoServer : alphaServer;
479 
480  if (monitorState.ActiveServer.ClosePortOnReboot)
481  logger.LogDebug("Active server will close port on reboot");
482  if (monitorState.InactiveServer.ClosePortOnReboot)
483  logger.LogDebug("Inactive server will close port on reboot");
484 
485  var activeServerLifetime = monitorState.ActiveServer.Lifetime;
486  var inactiveServerLifetime = monitorState.InactiveServer.Lifetime;
487  var activeServerReboot = monitorState.ActiveServer.OnReboot;
488  var inactiveServerReboot = monitorState.InactiveServer.OnReboot;
489  var inactiveServerStartup = monitorState.RebootingInactiveServer ? monitorState.InactiveServer.LaunchResult : null;
490  var activeLaunchParametersChanged = activeParametersUpdated.Task;
491  var newDmbAvailable = dmbFactory.OnNewerDmb;
492 
493  var cancelTcs = new TaskCompletionSource<object>();
494  using (cancellationToken.Register(() => cancelTcs.SetCanceled()))
495  {
496  var toWaitOn = Task.WhenAny(activeServerLifetime, inactiveServerLifetime, activeServerReboot, inactiveServerReboot, newDmbAvailable, cancelTcs.Task, activeLaunchParametersChanged);
497  if (monitorState.RebootingInactiveServer)
498  toWaitOn = Task.WhenAny(toWaitOn, inactiveServerStartup);
499  await toWaitOn.ConfigureAwait(false);
500  cancellationToken.ThrowIfCancellationRequested();
501  }
502 
503  var chatTask = Task.CompletedTask;
504  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
505  {
506  MonitorActivationReason activationReason = default;
507  //multiple things may have happened, handle them one at a time
508  for (var moreActivationsToProcess = true; moreActivationsToProcess && monitorState.NextAction == MonitorAction.Continue;)
509  {
510  if (activeServerLifetime?.IsCompleted == true)
511  {
512  activationReason = MonitorActivationReason.ActiveServerCrashed;
513  activeServerLifetime = null;
514  }
515  else if (inactiveServerLifetime?.IsCompleted == true)
516  {
517  activationReason = MonitorActivationReason.InactiveServerCrashed;
518  inactiveServerLifetime = null;
519  }
520  else if (activeServerReboot?.IsCompleted == true)
521  {
522  activationReason = MonitorActivationReason.ActiveServerRebooted;
523  activeServerReboot = null;
524  }
525  else if (inactiveServerReboot?.IsCompleted == true)
526  {
527  activationReason = MonitorActivationReason.InactiveServerRebooted;
528  inactiveServerReboot = null;
529  }
530  else if (inactiveServerStartup?.IsCompleted == true)
531  {
532  activationReason = MonitorActivationReason.InactiveServerStartupComplete;
533  inactiveServerStartup = null;
534  }
535  else if (newDmbAvailable?.IsCompleted == true)
536  {
537  activationReason = MonitorActivationReason.NewDmbAvailable;
538  newDmbAvailable = null;
539  }
540  else if (activeLaunchParametersChanged?.IsCompleted == true)
541  {
542  activationReason = MonitorActivationReason.ActiveLaunchParametersUpdated;
543  activeLaunchParametersChanged = null;
544  }
545  else
546  moreActivationsToProcess = false;
547 
548  if (moreActivationsToProcess)
549  await HandlerMonitorWakeup(activationReason, monitorState, cancellationToken).ConfigureAwait(false);
550  }
551 
552  //writeback alphaServer and bravoServer
553  alphaServer = AlphaIsActive ? monitorState.ActiveServer : monitorState.InactiveServer;
554  bravoServer = !AlphaIsActive ? monitorState.ActiveServer : monitorState.InactiveServer;
555  }
556 
557  //full reboot required
558  if (monitorState.NextAction == MonitorAction.Restart)
559  {
560  logger.LogDebug("Next state action is to restart");
561  DisposeAndNullControllers();
562  chatTask = chat.SendWatchdogMessage("Restarting entirely due to complications...", cancellationToken);
563 
564  for (var retryAttempts = 1; monitorState.NextAction == MonitorAction.Restart; ++retryAttempts)
565  {
566  WatchdogLaunchResult result;
567  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
568  {
569  result = await LaunchNoLock(false, false, false, cancellationToken).ConfigureAwait(false);
570  if (Running)
571  {
572  logger.LogDebug("Relaunch successful, resetting monitor state...");
573  monitorState = new MonitorState(); //clean the slate
574  }
575  }
576 
577  await chatTask.ConfigureAwait(false);
578  if (!Running)
579  {
580  logger.LogWarning("Failed to automatically restart the watchdog! Alpha: {0}; Bravo: {1}", result.Alpha.ToString(), result.Bravo.ToString());
581  var retryDelay = Math.Min(Math.Pow(2, retryAttempts), 3600); //max of one hour
582  chatTask = chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Failed to restart watchdog (Attempt: {0}), retrying in {1} seconds...", retryAttempts, retryDelay), cancellationToken);
583  await Task.WhenAll(Task.Delay((int)retryDelay, cancellationToken), chatTask).ConfigureAwait(false);
584  }
585  }
586  }
587  }
588  catch (OperationCanceledException)
589  {
590  logger.LogDebug("Monitor cancelled");
591  break;
592  }
593  catch (Exception e)
594  {
595  logger.LogError("Monitor crashed! Iteration: {0}, State: {1}", iteration, JsonConvert.SerializeObject(monitorState));
596  await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Monitor crashed, this should NEVER happen! Please report this, full details in logs! Restarting monitor... Error: {0}", e.Message), cancellationToken).ConfigureAwait(false);
597  }
598  }
599  }
600 
601  async Task<bool> StopMonitor()
602  {
603  logger.LogTrace("StopMonitor");
604  if (monitorTask == null)
605  return false;
606  monitorCts.Cancel();
607  await monitorTask.ConfigureAwait(false);
608  monitorCts.Dispose();
609  monitorTask = null;
610  return true;
611  }
612 
613 
615  public async Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken)
616  {
617  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
618  {
619  if (launchParameters.Match(ActiveLaunchParameters))
620  return;
621  ActiveLaunchParameters = launchParameters;
622  if (Running)
623  //queue an update
624  activeParametersUpdated.TrySetResult(null);
625  }
626  }
627 
628  async Task<WatchdogLaunchResult> LaunchNoLock(bool startMonitor, bool announce, bool doReattach, CancellationToken cancellationToken)
629  {
630  logger.LogTrace("Begin LaunchNoLock");
631  using (var alphaStartCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
632  {
633  if (Running)
634  {
635  logger.LogTrace("Aborted due to already running!");
636  return null;
637  }
638 
639  Task chatTask;
640  //this is necessary, the monitor could be in it's sleep loop trying to restart
641  if (startMonitor && await StopMonitor().ConfigureAwait(false))
642  chatTask = chat.SendWatchdogMessage("Automatic retry sequence cancelled by manual launch. Restarting...", cancellationToken);
643  else if (announce)
644  chatTask = chat.SendWatchdogMessage("Starting...", cancellationToken);
645  else
646  chatTask = Task.CompletedTask;
647  //start both servers
648  LastLaunchParameters = ActiveLaunchParameters;
649  try
650  {
651  //good ole sanity
652  if (alphaServer != null || bravoServer != null)
653  throw new InvalidOperationException("Entered LaunchNoLock with one or more of the servers not being null!");
654 
655  var reattachInfo = doReattach ? await reattachInfoHandler.Load(cancellationToken).ConfigureAwait(false) : null;
656  var doesntNeedNewDmb = doReattach && reattachInfo?.Alpha != null && reattachInfo?.Bravo != null;
657  var dmbToUse = doesntNeedNewDmb ? null : dmbFactory.LockNextDmb(2);
658 
659  try
660  {
661  Task<ISessionController> alphaServerTask;
662  if (!doesntNeedNewDmb)
663  alphaServerTask = sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, null, true, true, false, alphaStartCts.Token);
664  else
665  alphaServerTask = sessionControllerFactory.Reattach(reattachInfo.Alpha, cancellationToken);
666 
667  //wait until this boy officially starts so as not to confuse the servers as to who came first
668  var startTime = DateTimeOffset.Now;
669  alphaServer = await alphaServerTask.ConfigureAwait(false);
670  alphaServer.SetHighPriority();
671 
672  //extra delay for total ordering
673  var now = DateTimeOffset.Now;
674  var delay = now - startTime;
675 
676  if (delay.TotalSeconds < AlphaBravoStartupSeperationInterval)
677  await Task.Delay(startTime.AddSeconds(AlphaBravoStartupSeperationInterval) - now, cancellationToken).ConfigureAwait(false);
678 
679  Task<ISessionController> bravoServerTask;
680  if (!doesntNeedNewDmb)
681  bravoServerTask = sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, null, false, false, false, cancellationToken);
682  else
683  bravoServerTask = sessionControllerFactory.Reattach(reattachInfo.Bravo, cancellationToken);
684 
685  bravoServer = await bravoServerTask.ConfigureAwait(false);
686  bravoServer.SetHighPriority();
687 
688  async Task<LaunchResult> CheckLaunch(ISessionController controller, string serverName)
689  {
690  var launch = await controller.LaunchResult.ConfigureAwait(false);
691  if (launch.ExitCode.HasValue)
692  //you killed us ray...
693  throw new JobException(String.Format(CultureInfo.InvariantCulture, "{1} server failed to start: {0}", launch.ToString(), serverName));
694  if (!launch.StartupTime.HasValue)
695  throw new JobException(String.Format(CultureInfo.InvariantCulture, "{1} server timed out on startup: {0}s", launch.ToString(), ActiveLaunchParameters.StartupTimeout.Value));
696  return launch;
697  }
698 
699  var alphaLrt = CheckLaunch(alphaServer, "Alpha");
700  var bravoLrt = CheckLaunch(bravoServer, "Bravo");
701  //now we have two booting servers, get them up and running
702  var allTask = Task.WhenAll(alphaLrt, bravoLrt);
703 
704  //don't forget about the cancelationToken
705  var cancelTcs = new TaskCompletionSource<object>();
706  using (cancellationToken.Register(() => cancelTcs.SetCanceled()))
707  await Task.WhenAny(allTask, cancelTcs.Task).ConfigureAwait(false);
708  cancellationToken.ThrowIfCancellationRequested();
709 
710  //both servers are now running, alpha is the active server, huzzah
711  AlphaIsActive = doReattach ? reattachInfo?.AlphaIsActive ?? true : true;
712  LastLaunchResult = alphaLrt.Result;
713  logger.LogInformation("Launched servers successfully");
714  Running = true;
715 
716  if (startMonitor)
717  {
718  monitorCts = new CancellationTokenSource();
719  monitorTask = MonitorLifetimes(monitorCts.Token);
720  }
721  return new WatchdogLaunchResult
722  {
723  Alpha = alphaLrt.Result,
724  Bravo = bravoLrt.Result
725  };
726  }
727  catch
728  {
729  if (!doesntNeedNewDmb && (alphaServer == null && bravoServer == null))
730  {
731  dmbToUse.Dispose(); //guaranteed to not be null here
732  dmbToUse.Dispose(); //yes, dispose it twice. See the definition of IDmbFactory.LockNextDmb(), we called it with 2 locks
733  }
734  DisposeAndNullControllers();
735  throw;
736  }
737  }
738  catch (Exception e)
739  {
740  var originalChatTask = chatTask;
741  async Task ChainChatTaskWithErrorMessage()
742  {
743  await originalChatTask.ConfigureAwait(false);
744  await chat.SendWatchdogMessage("Startup failed!", cancellationToken).ConfigureAwait(false);
745  }
746  chatTask = ChainChatTaskWithErrorMessage();
747  logger.LogWarning("Failed to start watchdog: {0}", e.ToString());
748  throw;
749  }
750  finally
751  {
752  try
753  {
754  await chatTask.ConfigureAwait(false);
755  }
756  catch (OperationCanceledException) { }
757  }
758  }
759  }
760 
762  public async Task<WatchdogLaunchResult> Launch(CancellationToken cancellationToken)
763  {
764  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
765  return await LaunchNoLock(true, true, false, cancellationToken).ConfigureAwait(false);
766  }
767 
769  public async Task ResetRebootState(CancellationToken cancellationToken)
770  {
771  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
772  {
773  if (!Running)
774  return;
775  var toClear = AlphaIsActive ? alphaServer : bravoServer;
776  if (toClear != null)
777  toClear.ResetRebootState();
778  }
779  }
780 
782  public async Task<WatchdogLaunchResult> Restart(bool graceful, CancellationToken cancellationToken)
783  {
784  logger.LogTrace("Begin Restart. Graceful: {0}", graceful);
785  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
786  {
787  if (!graceful || !Running)
788  {
789  Task chatTask;
790  if (Running)
791  {
792  chatTask = chat.SendWatchdogMessage("Manual restart triggered...", cancellationToken);
793  await TerminateNoLock(false, false, cancellationToken).ConfigureAwait(false);
794  }
795  else
796  chatTask = Task.CompletedTask;
797  var result = await LaunchNoLock(true, !Running, false, cancellationToken).ConfigureAwait(false);
798  await chatTask.ConfigureAwait(false);
799  return result;
800  }
801  var toReboot = AlphaIsActive ? alphaServer : bravoServer;
802  if (toReboot != null)
803  {
804  if (!await toReboot.SetRebootState(Components.Watchdog.RebootState.Restart, cancellationToken).ConfigureAwait(false))
805  logger.LogWarning("Unable to send reboot state change event!");
806  }
807  return null;
808  }
809  }
810 
812  public async Task Terminate(bool graceful, CancellationToken cancellationToken)
813  {
814  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
815  await TerminateNoLock(graceful, true, cancellationToken).ConfigureAwait(false);
816  }
817 
819  public async Task StartAsync(CancellationToken cancellationToken)
820  {
821  if (!autoStart)
822  return;
823 
824  long? adminUserId = null;
825 
826  await databaseContextFactory.UseContext(async db => adminUserId = await db.Users.Select(x => x.Id).FirstAsync(cancellationToken).ConfigureAwait(false)).ConfigureAwait(false);
827  var job = new Models.Job
828  {
829  StartedBy = new Models.User
830  {
831  Id = adminUserId.Value
832  },
833  Instance = new Models.Instance
834  {
835  Id = instance.Id
836  },
837  Description = "Instance startup watchdog launch",
838  CancelRight = (ulong)DreamDaemonRights.Shutdown,
839  CancelRightsType = RightsType.DreamDaemon
840  };
841  await jobManager.RegisterOperation(job, (j, databaseContext, progressFunction, ct) => Launch(ct), cancellationToken).ConfigureAwait(false);
842  }
843 
845  public async Task StopAsync(CancellationToken cancellationToken)
846  {
847  if (releaseServers && Running)
848  {
849  var reattachInformation = new WatchdogReattachInformation
850  {
851  AlphaIsActive = AlphaIsActive
852  };
853  reattachInformation.Alpha = alphaServer?.Release();
854  reattachInformation.Bravo = bravoServer?.Release();
855  await reattachInfoHandler.Save(reattachInformation, cancellationToken).ConfigureAwait(false);
856  }
857  await Terminate(false, cancellationToken).ConfigureAwait(false);
858  }
859 
861  public async Task<bool> HandleEvent(EventType eventType, IEnumerable<string> parameters, CancellationToken cancellationToken)
862  {
863  string results;
864  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
865  {
866  if (!Running)
867  return true;
868 
869  var builder = new StringBuilder(Constants.DMTopicEvent);
870  builder.Append("&");
871  var notification = new EventNotification
872  {
873  Type = eventType,
874  Parameters = parameters
875  };
876  var json = JsonConvert.SerializeObject(notification);
877  builder.Append(byondTopicSender.SanitizeString(json));
878 
879  var activeServer = AlphaIsActive ? alphaServer : bravoServer;
880  results = await activeServer.SendCommand(builder.ToString(), cancellationToken).ConfigureAwait(false);
881  }
882 
883  if (results == Constants.DMResponseSuccess)
884  return true;
885 
886  List<Response> responses;
887  try
888  {
889  responses = JsonConvert.DeserializeObject<List<Response>>(results);
890  }
891  catch
892  {
893  logger.LogInformation("Recieved invalid response from DD when parsing event {0}:{1}{2}", eventType, Environment.NewLine, results);
894  return true;
895  }
896 
897  await Task.WhenAll(responses.Select(x => chat.SendMessage(x.Message, x.ChannelIds, cancellationToken))).ConfigureAwait(false);
898 
899  return true;
900  }
901 
903  public async Task<string> HandleChatCommand(string commandName, string arguments, Chat.User sender, CancellationToken cancellationToken)
904  {
905  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
906  {
907  if (!Running)
908  return "ERROR: Server offline!";
909 
910  var commandObject = new ChatCommand
911  {
912  Command = commandName,
913  Parameters = arguments,
914  User = sender
915  };
916 
917  var json = JsonConvert.SerializeObject(commandObject, new JsonSerializerSettings
918  {
919  ContractResolver = new CamelCasePropertyNamesContractResolver()
920  });
921 
922  var command = String.Format(CultureInfo.InvariantCulture, "{0}&{1}={2}", byondTopicSender.SanitizeString(Constants.DMTopicChatCommand), byondTopicSender.SanitizeString(Constants.DMParameterData), byondTopicSender.SanitizeString(json));
923 
924  var activeServer = AlphaIsActive ? alphaServer : bravoServer;
925  return await activeServer.SendCommand(command, cancellationToken).ConfigureAwait(false) ?? "ERROR: Bad topic exchange!";
926  }
927  }
928  }
929 }
+Go to the documentation of this file.
1 using Byond.TopicSender;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using Newtonsoft.Json;
5 using Newtonsoft.Json.Serialization;
6 using System;
7 using System.Collections.Generic;
8 using System.Globalization;
9 using System.Linq;
10 using System.Text;
11 using System.Threading;
12 using System.Threading.Tasks;
19 
20 namespace Tgstation.Server.Host.Components.Watchdog
21 {
24  {
28  const int AlphaBravoStartupSeperationInterval = 10;
29 
31  public bool Running { get; private set; }
32 
34  public bool AlphaIsActive { get; private set; }
35 
37  public Models.CompileJob ActiveCompileJob => (AlphaIsActive ? alphaServer : bravoServer)?.Dmb.CompileJob;
38 
40  public LaunchResult LastLaunchResult { get; private set; }
41 
43  public DreamDaemonLaunchParameters ActiveLaunchParameters { get; private set; }
44 
46  public DreamDaemonLaunchParameters LastLaunchParameters { get; private set; }
47 
49  public RebootState? RebootState => Running ? (AlphaIsActive ? alphaServer?.RebootState : bravoServer?.RebootState) : null;
50 
54  readonly IChat chat;
55 
60 
65 
69  readonly ILogger<Watchdog> logger;
70 
75 
80 
84  readonly IByondTopicSender byondTopicSender;
85 
90 
95 
99  readonly SemaphoreSlim semaphore;
100 
104  readonly Api.Models.Instance instance;
105 
109  readonly bool autoStart;
110 
114  CancellationTokenSource monitorCts;
115 
120 
124  TaskCompletionSource<object> activeParametersUpdated;
125 
134 
139 
156  public Watchdog(IChat chat, ISessionControllerFactory sessionControllerFactory, IDmbFactory dmbFactory, IServerControl serverUpdater, ILogger<Watchdog> logger, IReattachInfoHandler reattachInfoHandler, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IEventConsumer eventConsumer, IJobManager jobManager, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, bool autoStart)
157  {
158  this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
159  this.sessionControllerFactory = sessionControllerFactory ?? throw new ArgumentNullException(nameof(sessionControllerFactory));
160  this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory));
161  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
162  this.reattachInfoHandler = reattachInfoHandler ?? throw new ArgumentNullException(nameof(reattachInfoHandler));
163  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
164  this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender));
165  this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
166  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
167  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
168  this.autoStart = autoStart;
169 
170  if (serverUpdater == null)
171  throw new ArgumentNullException(nameof(serverUpdater));
172 
173  serverUpdater.RegisterForRestart(() => releaseServers = true);
174 
175  chat.RegisterCommandHandler(this);
176 
177  AlphaIsActive = true;
178  ActiveLaunchParameters = initialLaunchParameters;
179  releaseServers = false;
180  semaphore = new SemaphoreSlim(1);
181  activeParametersUpdated = new TaskCompletionSource<object>();
182  }
183 
185  public void Dispose()
186  {
187  DisposeAndNullControllers();
188  semaphore.Dispose();
189  }
190 
195  {
196  alphaServer?.Dispose();
197  alphaServer = null;
198  bravoServer?.Dispose();
199  bravoServer = null;
200  Running = false;
201  }
202 
209  async Task TerminateNoLock(bool graceful, bool announce, CancellationToken cancellationToken)
210  {
211  if (!Running)
212  return;
213  if (!graceful)
214  {
215  var chatTask = announce ? chat.SendWatchdogMessage("Terminating...", cancellationToken) : Task.CompletedTask;
216  await StopMonitor().ConfigureAwait(false);
217  DisposeAndNullControllers();
218  LastLaunchParameters = null;
219  await chatTask.ConfigureAwait(false);
220  return;
221  }
222  var toKill = AlphaIsActive ? alphaServer : bravoServer;
223  var other = AlphaIsActive ? bravoServer : alphaServer;
224  if (toKill != null)
225  await toKill.SetRebootState(Components.Watchdog.RebootState.Shutdown, cancellationToken).ConfigureAwait(false);
226  }
227 
235  async Task HandlerMonitorWakeup(MonitorActivationReason activationReason, MonitorState monitorState, CancellationToken cancellationToken)
236  {
237  logger.LogDebug("Monitor activation. Reason: {0}", activationReason);
238 
239  //returns true if the inactive server can't be used immediately
240  bool FullRestartDeadInactive()
241  {
242  if (monitorState.RebootingInactiveServer || monitorState.InactiveServerCritFail)
243  {
244  logger.LogInformation("Inactive server is {0}! Restarting monitor...", monitorState.InactiveServerCritFail ? "critically failed" : "still rebooting");
245  monitorState.NextAction = MonitorAction.Restart; //will dispose server
246  return true;
247  }
248  return false;
249  };
250 
251  //trys to set inactive server's port to the public port
252  //doesn't handle closing active server's port
253  async Task<bool> MakeInactiveActive()
254  {
255  logger.LogDebug("Setting inactive server to port {0}...", ActiveLaunchParameters.PrimaryPort.Value);
256  var result = await monitorState.InactiveServer.SetPort(ActiveLaunchParameters.PrimaryPort.Value, cancellationToken).ConfigureAwait(false);
257 
258  if (!result)
259  {
260  logger.LogWarning("Failed to activate inactive server! Restarting monitor...");
261  monitorState.NextAction = MonitorAction.Restart; //will dispose server
262  return false;
263  }
264 
265  //inactive server should always be using active launch parameters
266  LastLaunchParameters = ActiveLaunchParameters;
267 
268  var tmp = monitorState.ActiveServer;
269  monitorState.ActiveServer = monitorState.InactiveServer;
270  monitorState.InactiveServer = tmp;
271  AlphaIsActive = !AlphaIsActive;
272  return true;
273  }
274 
275  // Tries to load inactive server with latest dmb, falling back to current dmb on failure. Requires a lock on <see cref="semaphore"/>
276  async Task<bool> RestartInactiveServer()
277  {
278  logger.LogInformation("Rebooting inactive server...");
279  var newDmb = dmbFactory.LockNextDmb(1);
280  bool usedMostRecentDmb;
281  try
282  {
283  monitorState.InactiveServer = await sessionControllerFactory.LaunchNew(ActiveLaunchParameters, newDmb, null, false, !monitorState.ActiveServer.IsPrimary, false, cancellationToken).ConfigureAwait(false);
284  usedMostRecentDmb = true;
285  }
286  catch (OperationCanceledException)
287  {
288  throw;
289  }
290  catch (Exception e)
291  {
292  logger.LogError("Error occurred while recreating server! Attempting backup strategy of running DMB of running server! Exception: {0}", e.ToString());
293  //ahh jeez, what do we do here?
294  //this is our fault, so it should never happen but
295  //idk maybe a database error while handling the newest dmb?
296  //either way try to start it using the active server's dmb as a backup
297  try
298  {
299  var dmbBackup = await dmbFactory.FromCompileJob(monitorState.ActiveServer.Dmb.CompileJob, cancellationToken).ConfigureAwait(false);
300 
301  if (dmbBackup == null) //NANI!?
302  //just give up, if THAT compile job is failing then the ActiveServer is gonna crash soon too or already has
303  throw new JobException("Creating backup DMB provider failed!");
304 
305  monitorState.InactiveServer = await sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbBackup, null, false, !monitorState.ActiveServer.IsPrimary, false, cancellationToken).ConfigureAwait(false);
306  usedMostRecentDmb = false;
307  await chat.SendWatchdogMessage("Staging newest DMB on inactive server failed: {0} Falling back to previous dmb...", cancellationToken).ConfigureAwait(false);
308  }
309  catch (OperationCanceledException)
310  {
311  throw;
312  }
313  catch (Exception e2)
314  {
315  //fuuuuucckkk
316  logger.LogError("Backup strategy failed! Monitor will restart when active server reboots! Exception: {0}", e2.ToString());
317  monitorState.InactiveServerCritFail = true;
318  await chat.SendWatchdogMessage("Attempted reboot of inactive server failed. Watchdog will reset when active server fails or exits", cancellationToken).ConfigureAwait(false);
319  return true; //we didn't use the old dmb
320  }
321  }
322 
323  logger.LogInformation("Successfully relaunched inactive server!");
324  monitorState.RebootingInactiveServer = true;
325  return usedMostRecentDmb;
326  }
327 
328  async Task UpdateAndRestartInactiveServer(bool breakAfter)
329  {
330  //replace the notification tcs here so that the next loop will read a fresh one
331  activeParametersUpdated = new TaskCompletionSource<object>();
332  monitorState.InactiveServer.Dispose(); //kill or recycle it
333  monitorState.NextAction = breakAfter ? MonitorAction.Break : MonitorAction.Continue;
334 
335  var usedLatestDmb = await RestartInactiveServer().ConfigureAwait(false);
336 
337  if (monitorState.NextAction == (breakAfter ? MonitorAction.Break : MonitorAction.Continue))
338  {
339  monitorState.ActiveServer.ClosePortOnReboot = false;
340  if (monitorState.InactiveServerHasStagedDmb && !usedLatestDmb)
341  monitorState.InactiveServerHasStagedDmb = false; //don't try to load it again though
342  }
343  };
344 
345  string ExitWord(ISessionController controller) => controller.TerminationWasRequested ? "exited" : "crashed";
346 
347  //reason handling
348  switch (activationReason)
349  {
350  case MonitorActivationReason.ActiveServerCrashed:
351  if (monitorState.ActiveServer.RebootState == Components.Watchdog.RebootState.Shutdown)
352  {
353  await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Active server {0}! Exiting due to graceful termination request...", ExitWord(monitorState.ActiveServer)), cancellationToken).ConfigureAwait(false);
354  monitorState.NextAction = MonitorAction.Exit;
355  break;
356  }
357 
358  if (FullRestartDeadInactive())
359  {
360  await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Active server {0}! Inactive server unable to online!", ExitWord(monitorState.ActiveServer)), cancellationToken).ConfigureAwait(false);
361  break;
362  }
363 
364  await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Active server {0}! Onlining inactive server...", ExitWord(monitorState.ActiveServer)), cancellationToken).ConfigureAwait(false);
365  if (!await MakeInactiveActive().ConfigureAwait(false))
366  break;
367 
368  await UpdateAndRestartInactiveServer(true).ConfigureAwait(false);
369  break;
370  case MonitorActivationReason.InactiveServerCrashed:
371  await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Inactive server {0}! Rebooting...", ExitWord(monitorState.InactiveServer)), cancellationToken).ConfigureAwait(false);
372  await UpdateAndRestartInactiveServer(false).ConfigureAwait(false);
373  break;
374  case MonitorActivationReason.ActiveServerRebooted:
375  if (FullRestartDeadInactive())
376  break;
377 
378  //what matters here is the RebootState
379  bool restartOnceSwapped = false;
380  var rebootState = monitorState.ActiveServer.RebootState;
381  monitorState.ActiveServer.ResetRebootState(); //the DMAPI has already done this internally
382 
383  switch (rebootState)
384  {
385  case Components.Watchdog.RebootState.Normal:
386  break;
387  case Components.Watchdog.RebootState.Restart:
388  restartOnceSwapped = true;
389  break;
390  case Components.Watchdog.RebootState.Shutdown:
391  await chat.SendWatchdogMessage("Active server rebooted! Exiting due to graceful termination request...", cancellationToken).ConfigureAwait(false);
392  DisposeAndNullControllers();
393  monitorState.NextAction = MonitorAction.Exit;
394  return;
395  }
396 
397  var sameCompileJob = monitorState.InactiveServer.Dmb.CompileJob.Id == monitorState.ActiveServer.Dmb.CompileJob.Id;
398  if (sameCompileJob && monitorState.InactiveServerHasStagedDmb)
399  //both servers up to date
400  monitorState.InactiveServerHasStagedDmb = false;
401  if (!sameCompileJob || ActiveLaunchParameters != LastLaunchParameters)
402  //need a new launch in ActiveServer
403  restartOnceSwapped = true;
404 
405  if (restartOnceSwapped && !monitorState.ActiveServer.ClosePortOnReboot)
406  //we need to manually restart active server
407  //it won't listen to us right now so just kill it
408  monitorState.ActiveServer.Dispose();
409 
410  if ((!restartOnceSwapped && !monitorState.ActiveServer.ClosePortOnReboot) || !await MakeInactiveActive().ConfigureAwait(false))
411  break;
412 
413  monitorState.ActiveServer.ClosePortOnReboot = true;
414 
415  if (!restartOnceSwapped)
416  {
417  monitorState.InactiveServer.ClosePortOnReboot = false;
418  //try to reopen inactive server on the private port so it's not pinging all the time
419  //failing that, just reboot it
420  restartOnceSwapped = !await monitorState.InactiveServer.SetPort(ActiveLaunchParameters.SecondaryPort.Value, cancellationToken).ConfigureAwait(false);
421  }
422 
423  if (restartOnceSwapped) //for one reason or another
424  await UpdateAndRestartInactiveServer(true).ConfigureAwait(false); //break because worse case, active server is still booting
425  else
426  {
427  monitorState.InactiveServer.ClosePortOnReboot = false;
428  monitorState.NextAction = MonitorAction.Break;
429  }
430  break;
431  case MonitorActivationReason.InactiveServerRebooted:
432  monitorState.RebootingInactiveServer = true;
433  monitorState.InactiveServer.ResetRebootState();
434  monitorState.ActiveServer.ClosePortOnReboot = false;
435  monitorState.NextAction = MonitorAction.Continue;
436  break;
437  case MonitorActivationReason.InactiveServerStartupComplete:
438  //eziest case of my life
439  monitorState.RebootingInactiveServer = false;
440  monitorState.ActiveServer.ClosePortOnReboot = true;
441  monitorState.NextAction = MonitorAction.Continue;
442  break;
443  case MonitorActivationReason.NewDmbAvailable:
444  monitorState.InactiveServerHasStagedDmb = true;
445  goto case MonitorActivationReason.ActiveLaunchParametersUpdated;
446  case MonitorActivationReason.ActiveLaunchParametersUpdated:
447  await UpdateAndRestartInactiveServer(true).ConfigureAwait(false);
448  break;
449  }
450  }
451 
457  async Task MonitorLifetimes(CancellationToken cancellationToken)
458  {
459  logger.LogTrace("Entered MonitorLifetimes");
460  var iteration = 1;
461  for (var monitorState = new MonitorState(); monitorState.NextAction != MonitorAction.Exit; ++iteration)
462  {
463  monitorState.NextAction = MonitorAction.Continue;
464  logger.LogDebug("Iteration {0} of monitor loop", iteration);
465  try
466  {
467  if (AlphaIsActive)
468  logger.LogDebug("Alpha is the active server");
469  else
470  logger.LogDebug("Bravo is the active server");
471 
472  if (monitorState.InactiveServerHasStagedDmb)
473  logger.LogDebug("Inactive server has staged .dmb");
474  if (monitorState.RebootingInactiveServer)
475  logger.LogDebug("Inactive server is rebooting");
476 
477  monitorState.ActiveServer = AlphaIsActive ? alphaServer : bravoServer;
478  monitorState.InactiveServer = AlphaIsActive ? bravoServer : alphaServer;
479 
480  if (monitorState.ActiveServer.ClosePortOnReboot)
481  logger.LogDebug("Active server will close port on reboot");
482  if (monitorState.InactiveServer.ClosePortOnReboot)
483  logger.LogDebug("Inactive server will close port on reboot");
484 
485  var activeServerLifetime = monitorState.ActiveServer.Lifetime;
486  var inactiveServerLifetime = monitorState.InactiveServer.Lifetime;
487  var activeServerReboot = monitorState.ActiveServer.OnReboot;
488  var inactiveServerReboot = monitorState.InactiveServer.OnReboot;
489  var inactiveServerStartup = monitorState.RebootingInactiveServer ? monitorState.InactiveServer.LaunchResult : null;
490  var activeLaunchParametersChanged = activeParametersUpdated.Task;
491  var newDmbAvailable = dmbFactory.OnNewerDmb;
492 
493  var cancelTcs = new TaskCompletionSource<object>();
494  using (cancellationToken.Register(() => cancelTcs.SetCanceled()))
495  {
496  var toWaitOn = Task.WhenAny(activeServerLifetime, inactiveServerLifetime, activeServerReboot, inactiveServerReboot, newDmbAvailable, cancelTcs.Task, activeLaunchParametersChanged);
497  if (monitorState.RebootingInactiveServer)
498  toWaitOn = Task.WhenAny(toWaitOn, inactiveServerStartup);
499  await toWaitOn.ConfigureAwait(false);
500  cancellationToken.ThrowIfCancellationRequested();
501  }
502 
503  var chatTask = Task.CompletedTask;
504  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
505  {
506  MonitorActivationReason activationReason = default;
507  //multiple things may have happened, handle them one at a time
508  for (var moreActivationsToProcess = true; moreActivationsToProcess && monitorState.NextAction == MonitorAction.Continue;)
509  {
510  if (activeServerLifetime?.IsCompleted == true)
511  {
512  activationReason = MonitorActivationReason.ActiveServerCrashed;
513  activeServerLifetime = null;
514  }
515  else if (inactiveServerLifetime?.IsCompleted == true)
516  {
517  activationReason = MonitorActivationReason.InactiveServerCrashed;
518  inactiveServerLifetime = null;
519  }
520  else if (activeServerReboot?.IsCompleted == true)
521  {
522  activationReason = MonitorActivationReason.ActiveServerRebooted;
523  activeServerReboot = null;
524  }
525  else if (inactiveServerReboot?.IsCompleted == true)
526  {
527  activationReason = MonitorActivationReason.InactiveServerRebooted;
528  inactiveServerReboot = null;
529  }
530  else if (inactiveServerStartup?.IsCompleted == true)
531  {
532  activationReason = MonitorActivationReason.InactiveServerStartupComplete;
533  inactiveServerStartup = null;
534  }
535  else if (newDmbAvailable?.IsCompleted == true)
536  {
537  activationReason = MonitorActivationReason.NewDmbAvailable;
538  newDmbAvailable = null;
539  }
540  else if (activeLaunchParametersChanged?.IsCompleted == true)
541  {
542  activationReason = MonitorActivationReason.ActiveLaunchParametersUpdated;
543  activeLaunchParametersChanged = null;
544  }
545  else
546  moreActivationsToProcess = false;
547 
548  if (moreActivationsToProcess)
549  await HandlerMonitorWakeup(activationReason, monitorState, cancellationToken).ConfigureAwait(false);
550  }
551 
552  //writeback alphaServer and bravoServer
553  alphaServer = AlphaIsActive ? monitorState.ActiveServer : monitorState.InactiveServer;
554  bravoServer = !AlphaIsActive ? monitorState.ActiveServer : monitorState.InactiveServer;
555  }
556 
557  //full reboot required
558  if (monitorState.NextAction == MonitorAction.Restart)
559  {
560  logger.LogDebug("Next state action is to restart");
561  DisposeAndNullControllers();
562  chatTask = chat.SendWatchdogMessage("Restarting entirely due to complications...", cancellationToken);
563 
564  for (var retryAttempts = 1; monitorState.NextAction == MonitorAction.Restart; ++retryAttempts)
565  {
566  WatchdogLaunchResult result;
567  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
568  {
569  result = await LaunchNoLock(false, false, false, cancellationToken).ConfigureAwait(false);
570  if (Running)
571  {
572  logger.LogDebug("Relaunch successful, resetting monitor state...");
573  monitorState = new MonitorState(); //clean the slate
574  }
575  }
576 
577  await chatTask.ConfigureAwait(false);
578  if (!Running)
579  {
580  logger.LogWarning("Failed to automatically restart the watchdog! Alpha: {0}; Bravo: {1}", result.Alpha.ToString(), result.Bravo.ToString());
581  var retryDelay = Math.Min(Math.Pow(2, retryAttempts), 3600); //max of one hour
582  chatTask = chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Failed to restart watchdog (Attempt: {0}), retrying in {1} seconds...", retryAttempts, retryDelay), cancellationToken);
583  await Task.WhenAll(Task.Delay((int)retryDelay, cancellationToken), chatTask).ConfigureAwait(false);
584  }
585  }
586  }
587  }
588  catch (OperationCanceledException)
589  {
590  logger.LogDebug("Monitor cancelled");
591  break;
592  }
593  catch (Exception e)
594  {
595  logger.LogError("Monitor crashed! Iteration: {0}, State: {1}", iteration, JsonConvert.SerializeObject(monitorState));
596  await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Monitor crashed, this should NEVER happen! Please report this, full details in logs! Restarting monitor... Error: {0}", e.Message), cancellationToken).ConfigureAwait(false);
597  }
598  }
599  }
600 
601  async Task<bool> StopMonitor()
602  {
603  logger.LogTrace("StopMonitor");
604  if (monitorTask == null)
605  return false;
606  monitorCts.Cancel();
607  await monitorTask.ConfigureAwait(false);
608  monitorCts.Dispose();
609  monitorTask = null;
610  return true;
611  }
612 
613 
615  public async Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken)
616  {
617  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
618  {
619  if (launchParameters.Match(ActiveLaunchParameters))
620  return;
621  ActiveLaunchParameters = launchParameters;
622  if (Running)
623  //queue an update
624  activeParametersUpdated.TrySetResult(null);
625  }
626  }
627 
628  async Task<WatchdogLaunchResult> LaunchNoLock(bool startMonitor, bool announce, bool doReattach, CancellationToken cancellationToken)
629  {
630  logger.LogTrace("Begin LaunchNoLock");
631  using (var alphaStartCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
632  {
633  if (Running)
634  {
635  logger.LogTrace("Aborted due to already running!");
636  return null;
637  }
638 
639  Task chatTask;
640  //this is necessary, the monitor could be in it's sleep loop trying to restart
641  if (startMonitor && await StopMonitor().ConfigureAwait(false))
642  chatTask = chat.SendWatchdogMessage("Automatic retry sequence cancelled by manual launch. Restarting...", cancellationToken);
643  else if (announce)
644  chatTask = chat.SendWatchdogMessage("Starting...", cancellationToken);
645  else
646  chatTask = Task.CompletedTask;
647  //start both servers
648  LastLaunchParameters = ActiveLaunchParameters;
649  try
650  {
651  //good ole sanity
652  if (alphaServer != null || bravoServer != null)
653  throw new InvalidOperationException("Entered LaunchNoLock with one or more of the servers not being null!");
654 
655  var reattachInfo = doReattach ? await reattachInfoHandler.Load(cancellationToken).ConfigureAwait(false) : null;
656  var doesntNeedNewDmb = doReattach && reattachInfo?.Alpha != null && reattachInfo?.Bravo != null;
657  var dmbToUse = doesntNeedNewDmb ? null : dmbFactory.LockNextDmb(2);
658 
659  try
660  {
661  Task<ISessionController> alphaServerTask;
662  if (!doesntNeedNewDmb)
663  alphaServerTask = sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, null, true, true, false, alphaStartCts.Token);
664  else
665  alphaServerTask = sessionControllerFactory.Reattach(reattachInfo.Alpha, cancellationToken);
666 
667  //wait until this boy officially starts so as not to confuse the servers as to who came first
668  var startTime = DateTimeOffset.Now;
669  alphaServer = await alphaServerTask.ConfigureAwait(false);
670  alphaServer.SetHighPriority();
671 
672  //extra delay for total ordering
673  var now = DateTimeOffset.Now;
674  var delay = now - startTime;
675 
676  if (delay.TotalSeconds < AlphaBravoStartupSeperationInterval)
677  await Task.Delay(startTime.AddSeconds(AlphaBravoStartupSeperationInterval) - now, cancellationToken).ConfigureAwait(false);
678 
679  Task<ISessionController> bravoServerTask;
680  if (!doesntNeedNewDmb)
681  bravoServerTask = sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, null, false, false, false, cancellationToken);
682  else
683  bravoServerTask = sessionControllerFactory.Reattach(reattachInfo.Bravo, cancellationToken);
684 
685  bravoServer = await bravoServerTask.ConfigureAwait(false);
686  bravoServer.SetHighPriority();
687 
688  async Task<LaunchResult> CheckLaunch(ISessionController controller, string serverName)
689  {
690  var launch = await controller.LaunchResult.ConfigureAwait(false);
691  if (launch.ExitCode.HasValue)
692  //you killed us ray...
693  throw new JobException(String.Format(CultureInfo.InvariantCulture, "{1} server failed to start: {0}", launch.ToString(), serverName));
694  if (!launch.StartupTime.HasValue)
695  throw new JobException(String.Format(CultureInfo.InvariantCulture, "{1} server timed out on startup: {0}s", launch.ToString(), ActiveLaunchParameters.StartupTimeout.Value));
696  return launch;
697  }
698 
699  var alphaLrt = CheckLaunch(alphaServer, "Alpha");
700  var bravoLrt = CheckLaunch(bravoServer, "Bravo");
701  //now we have two booting servers, get them up and running
702  var allTask = Task.WhenAll(alphaLrt, bravoLrt);
703 
704  //don't forget about the cancelationToken
705  var cancelTcs = new TaskCompletionSource<object>();
706  using (cancellationToken.Register(() => cancelTcs.SetCanceled()))
707  await Task.WhenAny(allTask, cancelTcs.Task).ConfigureAwait(false);
708  cancellationToken.ThrowIfCancellationRequested();
709 
710  //both servers are now running, alpha is the active server, huzzah
711  AlphaIsActive = doReattach ? reattachInfo?.AlphaIsActive ?? true : true;
712  LastLaunchResult = alphaLrt.Result;
713  logger.LogInformation("Launched servers successfully");
714  Running = true;
715 
716  if (startMonitor)
717  {
718  monitorCts = new CancellationTokenSource();
719  monitorTask = MonitorLifetimes(monitorCts.Token);
720  }
721  return new WatchdogLaunchResult
722  {
723  Alpha = alphaLrt.Result,
724  Bravo = bravoLrt.Result
725  };
726  }
727  catch
728  {
729  if (!doesntNeedNewDmb && (alphaServer == null && bravoServer == null))
730  {
731  dmbToUse.Dispose(); //guaranteed to not be null here
732  dmbToUse.Dispose(); //yes, dispose it twice. See the definition of IDmbFactory.LockNextDmb(), we called it with 2 locks
733  }
734  DisposeAndNullControllers();
735  throw;
736  }
737  }
738  catch (Exception e)
739  {
740  var originalChatTask = chatTask;
741  async Task ChainChatTaskWithErrorMessage()
742  {
743  await originalChatTask.ConfigureAwait(false);
744  await chat.SendWatchdogMessage("Startup failed!", cancellationToken).ConfigureAwait(false);
745  }
746  chatTask = ChainChatTaskWithErrorMessage();
747  logger.LogWarning("Failed to start watchdog: {0}", e.ToString());
748  throw;
749  }
750  finally
751  {
752  try
753  {
754  await chatTask.ConfigureAwait(false);
755  }
756  catch (OperationCanceledException) { }
757  }
758  }
759  }
760 
762  public async Task<WatchdogLaunchResult> Launch(CancellationToken cancellationToken)
763  {
764  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
765  return await LaunchNoLock(true, true, false, cancellationToken).ConfigureAwait(false);
766  }
767 
769  public async Task ResetRebootState(CancellationToken cancellationToken)
770  {
771  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
772  {
773  if (!Running)
774  return;
775  var toClear = AlphaIsActive ? alphaServer : bravoServer;
776  if (toClear != null)
777  toClear.ResetRebootState();
778  }
779  }
780 
782  public async Task<WatchdogLaunchResult> Restart(bool graceful, CancellationToken cancellationToken)
783  {
784  logger.LogTrace("Begin Restart. Graceful: {0}", graceful);
785  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
786  {
787  if (!graceful || !Running)
788  {
789  Task chatTask;
790  if (Running)
791  {
792  chatTask = chat.SendWatchdogMessage("Manual restart triggered...", cancellationToken);
793  await TerminateNoLock(false, false, cancellationToken).ConfigureAwait(false);
794  }
795  else
796  chatTask = Task.CompletedTask;
797  var result = await LaunchNoLock(true, !Running, false, cancellationToken).ConfigureAwait(false);
798  await chatTask.ConfigureAwait(false);
799  return result;
800  }
801  var toReboot = AlphaIsActive ? alphaServer : bravoServer;
802  if (toReboot != null)
803  {
804  if (!await toReboot.SetRebootState(Components.Watchdog.RebootState.Restart, cancellationToken).ConfigureAwait(false))
805  logger.LogWarning("Unable to send reboot state change event!");
806  }
807  return null;
808  }
809  }
810 
812  public async Task Terminate(bool graceful, CancellationToken cancellationToken)
813  {
814  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
815  await TerminateNoLock(graceful, true, cancellationToken).ConfigureAwait(false);
816  }
817 
819  public async Task StartAsync(CancellationToken cancellationToken)
820  {
821  if (!autoStart)
822  return;
823 
824  long? adminUserId = null;
825 
826  await databaseContextFactory.UseContext(async db => adminUserId = await db.Users.Select(x => x.Id).FirstAsync(cancellationToken).ConfigureAwait(false)).ConfigureAwait(false);
827  var job = new Models.Job
828  {
829  StartedBy = new Models.User
830  {
831  Id = adminUserId.Value
832  },
833  Instance = new Models.Instance
834  {
835  Id = instance.Id
836  },
837  Description = "Instance startup watchdog launch",
838  CancelRight = (ulong)DreamDaemonRights.Shutdown,
839  CancelRightsType = RightsType.DreamDaemon
840  };
841  await jobManager.RegisterOperation(job, (j, databaseContext, progressFunction, ct) => Launch(ct), cancellationToken).ConfigureAwait(false);
842  }
843 
845  public async Task StopAsync(CancellationToken cancellationToken)
846  {
847  if (releaseServers && Running)
848  {
849  var reattachInformation = new WatchdogReattachInformation
850  {
851  AlphaIsActive = AlphaIsActive
852  };
853  reattachInformation.Alpha = alphaServer?.Release();
854  reattachInformation.Bravo = bravoServer?.Release();
855  await reattachInfoHandler.Save(reattachInformation, cancellationToken).ConfigureAwait(false);
856  }
857  await Terminate(false, cancellationToken).ConfigureAwait(false);
858  }
859 
861  public async Task<bool> HandleEvent(EventType eventType, IEnumerable<string> parameters, CancellationToken cancellationToken)
862  {
863  string results;
864  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
865  {
866  if (!Running)
867  return true;
868 
869  var builder = new StringBuilder(Constants.DMTopicEvent);
870  builder.Append("&");
871  var notification = new EventNotification
872  {
873  Type = eventType,
874  Parameters = parameters
875  };
876  var json = JsonConvert.SerializeObject(notification);
877  builder.Append(byondTopicSender.SanitizeString(json));
878 
879  var activeServer = AlphaIsActive ? alphaServer : bravoServer;
880  results = await activeServer.SendCommand(builder.ToString(), cancellationToken).ConfigureAwait(false);
881  }
882 
883  if (results == Constants.DMResponseSuccess)
884  return true;
885 
886  List<Response> responses;
887  try
888  {
889  responses = JsonConvert.DeserializeObject<List<Response>>(results);
890  }
891  catch
892  {
893  logger.LogInformation("Recieved invalid response from DD when parsing event {0}:{1}{2}", eventType, Environment.NewLine, results);
894  return true;
895  }
896 
897  await Task.WhenAll(responses.Select(x => chat.SendMessage(x.Message, x.ChannelIds, cancellationToken))).ConfigureAwait(false);
898 
899  return true;
900  }
901 
903  public async Task<string> HandleChatCommand(string commandName, string arguments, Chat.User sender, CancellationToken cancellationToken)
904  {
905  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
906  {
907  if (!Running)
908  return "ERROR: Server offline!";
909 
910  var commandObject = new ChatCommand
911  {
912  Command = commandName,
913  Parameters = arguments,
914  User = sender
915  };
916 
917  var json = JsonConvert.SerializeObject(commandObject, new JsonSerializerSettings
918  {
919  ContractResolver = new CamelCasePropertyNamesContractResolver()
920  });
921 
922  var command = String.Format(CultureInfo.InvariantCulture, "{0}&{1}={2}", byondTopicSender.SanitizeString(Constants.DMTopicChatCommand), byondTopicSender.SanitizeString(Constants.DMParameterData), byondTopicSender.SanitizeString(json));
923 
924  var activeServer = AlphaIsActive ? alphaServer : bravoServer;
925  return await activeServer.SendCommand(command, cancellationToken).ConfigureAwait(false) ?? "ERROR: Bad topic exchange!";
926  }
927  }
928  }
929 }
async Task< WatchdogLaunchResult > Restart(bool graceful, CancellationToken cancellationToken)
Restarts the watchdog
Definition: Watchdog.cs:782
bool InactiveServerCritFail
If the inactive server is in an unrecoverable state
Definition: MonitorState.cs:23
Manages the runtime of Jobs
Definition: IJobManager.cs:12
@@ -116,6 +116,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Consumes EventTypes and takes the appropriate actions
Task< LaunchResult > LaunchResult
A Task that completes when DreamDaemon starts pumping the windows message queue after loading a ...
+
LaunchResult Bravo
The LaunchResult for the bravo process
bool InactiveServerHasStagedDmb
If the inactive server has a .dmb and needs to be swapped in
Definition: MonitorState.cs:18
@@ -160,9 +161,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
async Task StopAsync(CancellationToken cancellationToken)
Definition: Watchdog.cs:845
Task< bool > SetPort(ushort newPort, CancellationToken cancellatonToken)
Causes the world to start listening on a newPort
-
ReattachInformation Release()
Releases the IProcess without terminating it. Also calls System.IDisposable.Dispose ...
- +
readonly IChat chat
The IChat for the Watchdog
Definition: Watchdog.cs:54
diff --git a/_components_2_watchdog_2_watchdog_factory_8cs_source.html b/_components_2_watchdog_2_watchdog_factory_8cs_source.html index c4f89a81a5..275a292483 100644 --- a/_components_2_watchdog_2_watchdog_factory_8cs_source.html +++ b/_components_2_watchdog_2_watchdog_factory_8cs_source.html @@ -92,12 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
WatchdogFactory.cs
-Go to the documentation of this file.
1 using Byond.TopicSender;
2 using Microsoft.Extensions.Logging;
3 using System;
8 
9 namespace Tgstation.Server.Host.Components.Watchdog
10 {
13  {
18 
22  readonly ILoggerFactory loggerFactory;
23 
28 
32  readonly IByondTopicSender byondTopicSender;
33 
38 
47  public WatchdogFactory(IServerControl serverUpdater, ILoggerFactory loggerFactory, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IJobManager jobManager)
48  {
49  this.serverUpdater = serverUpdater ?? throw new ArgumentNullException(nameof(serverUpdater));
50  this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
51  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
52  this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender));
53  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
54  }
55 
57  public IWatchdog CreateWatchdog(IChat chat, IDmbFactory dmbFactory, IReattachInfoHandler reattachInfoHandler, IEventConsumer eventConsumer, ISessionControllerFactory sessionControllerFactory, Api.Models.Instance instance, DreamDaemonSettings settings) => new Watchdog(chat, sessionControllerFactory, dmbFactory, serverUpdater, loggerFactory.CreateLogger<Watchdog>(), reattachInfoHandler, databaseContextFactory, byondTopicSender, eventConsumer, jobManager, settings, instance, settings.AutoStart.Value);
58  }
59 }
Manages the runtime of Jobs
Definition: IJobManager.cs:12
+Go to the documentation of this file.
1 using Byond.TopicSender;
2 using Microsoft.Extensions.Logging;
3 using System;
8 
9 namespace Tgstation.Server.Host.Components.Watchdog
10 {
13  {
18 
22  readonly ILoggerFactory loggerFactory;
23 
28 
32  readonly IByondTopicSender byondTopicSender;
33 
38 
47  public WatchdogFactory(IServerControl serverUpdater, ILoggerFactory loggerFactory, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IJobManager jobManager)
48  {
49  this.serverUpdater = serverUpdater ?? throw new ArgumentNullException(nameof(serverUpdater));
50  this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
51  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
52  this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender));
53  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
54  }
55 
57  public IWatchdog CreateWatchdog(IChat chat, IDmbFactory dmbFactory, IReattachInfoHandler reattachInfoHandler, IEventConsumer eventConsumer, ISessionControllerFactory sessionControllerFactory, Api.Models.Instance instance, DreamDaemonSettings settings) => new Watchdog(chat, sessionControllerFactory, dmbFactory, serverUpdater, loggerFactory.CreateLogger<Watchdog>(), reattachInfoHandler, databaseContextFactory, byondTopicSender, eventConsumer, jobManager, settings, instance, settings.AutoStart.Value);
58  }
59 }
Manages the runtime of Jobs
Definition: IJobManager.cs:12
readonly IByondTopicSender byondTopicSender
The IByondTopicSender for the WatchdogFactory
Consumes EventTypes and takes the appropriate actions
+
Factory for scoping usage of IDatabaseContexts. Meant for use by Components
readonly IJobManager jobManager
The IJobManager for the WatchdogFactory
@@ -111,8 +112,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
WatchdogFactory(IServerControl serverUpdater, ILoggerFactory loggerFactory, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IJobManager jobManager)
Construct a WatchdogFactory
- - +
readonly IDatabaseContextFactory databaseContextFactory
The IDatabaseContextFactory for the WatchdogFactory
diff --git a/_components_2_watchdog_2_watchdog_reattach_information_8cs_source.html b/_components_2_watchdog_2_watchdog_reattach_information_8cs_source.html index 408d6c1cda..2434028e56 100644 --- a/_components_2_watchdog_2_watchdog_reattach_information_8cs_source.html +++ b/_components_2_watchdog_2_watchdog_reattach_information_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
WatchdogReattachInformation.cs
-Go to the documentation of this file.
1 using System;
2 using System.Globalization;
4 
5 namespace Tgstation.Server.Host.Components.Watchdog
6 {
11  {
15  public ReattachInformation Alpha { get; set; }
16 
20  public ReattachInformation Bravo { get; set; }
21 
26 
33  public WatchdogReattachInformation(Models.WatchdogReattachInformation copy, IDmbProvider dmbAlpha, IDmbProvider dmbBravo) : base(copy)
34  {
35  if (copy.Alpha != null)
36  Alpha = new ReattachInformation(copy.Alpha, dmbAlpha);
37  if (copy.Bravo != null)
38  Bravo = new ReattachInformation(copy.Bravo, dmbBravo);
39  }
40 
42  public override string ToString() => String.Format(CultureInfo.InvariantCulture, "Alpha: {0}, Bravo {1}", Alpha, Bravo);
43  }
44 }
+Go to the documentation of this file.
1 using System;
2 using System.Globalization;
4 
5 namespace Tgstation.Server.Host.Components.Watchdog
6 {
11  {
15  public ReattachInformation Alpha { get; set; }
16 
20  public ReattachInformation Bravo { get; set; }
21 
26 
33  public WatchdogReattachInformation(Models.WatchdogReattachInformation copy, IDmbProvider dmbAlpha, IDmbProvider dmbBravo) : base(copy)
34  {
35  if (copy.Alpha != null)
36  Alpha = new ReattachInformation(copy.Alpha, dmbAlpha);
37  if (copy.Bravo != null)
38  Bravo = new ReattachInformation(copy.Bravo, dmbBravo);
39  }
40 
42  public override string ToString() => String.Format(CultureInfo.InvariantCulture, "Alpha: {0}, Bravo {1}", Alpha, Bravo);
43  }
44 }
+ -
WatchdogReattachInformation(Models.WatchdogReattachInformation copy, IDmbProvider dmbAlpha, IDmbProvider dmbBravo)
Construct a WatchdogReattachInformation from a given copy with a given dmbAlpha and dmbBravo ...
diff --git a/_configuration_client_8cs_source.html b/_configuration_client_8cs_source.html index ea29ce0bcb..b59d458b13 100644 --- a/_configuration_client_8cs_source.html +++ b/_configuration_client_8cs_source.html @@ -92,15 +92,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ConfigurationClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client.Components
9 {
12  {
20  readonly Instance instance;
21 
27  static string SanitizeGetPath(string path)
28  {
29  if (path == null)
30  path = String.Empty;
31  if (path.Length == 0 || path[0] != '/')
32  path = '/' + path;
33  return path;
34  }
35 
41  public ConfigurationClient(IApiClient apiClient, Instance instance)
42  {
43  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
44  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
45  }
46 
48  public Task DeleteEmptyDirectory(ConfigurationFile directory, CancellationToken cancellationToken) => apiClient.Delete(Routes.Configuration, directory, instance.Id, cancellationToken);
49 
51  public Task<IReadOnlyList<ConfigurationFile>> List(string directory, CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<ConfigurationFile>>(Routes.List(Routes.Configuration) + SanitizeGetPath(directory), instance.Id, cancellationToken);
52 
54  public Task<ConfigurationFile> Read(ConfigurationFile file, CancellationToken cancellationToken)
55  {
56  if (file == null)
57  throw new ArgumentNullException(nameof(file));
58  return apiClient.Read<ConfigurationFile>(Routes.Configuration + SanitizeGetPath(file.Path), instance.Id, cancellationToken);
59  }
60 
62  public Task<ConfigurationFile> Write(ConfigurationFile file, CancellationToken cancellationToken) => apiClient.Update<ConfigurationFile, ConfigurationFile>(Routes.Configuration, file ?? throw new ArgumentNullException(nameof(file)), instance.Id, cancellationToken);
63  }
64 }
const string Configuration
The Models.ConfigurationFile controller
Definition: Routes.cs:49
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client.Components
9 {
12  {
20  readonly Instance instance;
21 
27  static string SanitizeGetPath(string path)
28  {
29  if (path == null)
30  path = String.Empty;
31  if (path.Length == 0 || path[0] != '/')
32  path = '/' + path;
33  return path;
34  }
35 
41  public ConfigurationClient(IApiClient apiClient, Instance instance)
42  {
43  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
44  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
45  }
46 
48  public Task DeleteEmptyDirectory(ConfigurationFile directory, CancellationToken cancellationToken) => apiClient.Delete(Routes.Configuration, directory, instance.Id, cancellationToken);
49 
51  public Task<IReadOnlyList<ConfigurationFile>> List(string directory, CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<ConfigurationFile>>(Routes.List(Routes.Configuration) + SanitizeGetPath(directory), instance.Id, cancellationToken);
52 
54  public Task<ConfigurationFile> Read(ConfigurationFile file, CancellationToken cancellationToken)
55  {
56  if (file == null)
57  throw new ArgumentNullException(nameof(file));
58  return apiClient.Read<ConfigurationFile>(Routes.Configuration + SanitizeGetPath(file.Path), instance.Id, cancellationToken);
59  }
60 
62  public Task<ConfigurationFile> Write(ConfigurationFile file, CancellationToken cancellationToken) => apiClient.Update<ConfigurationFile, ConfigurationFile>(Routes.Configuration, file ?? throw new ArgumentNullException(nameof(file)), instance.Id, cancellationToken);
63  }
64 }
const string Configuration
The Models.ConfigurationFile controller
Definition: Routes.cs:49
+
readonly IApiClient apiClient
The IApiClient for the ConfigurationClient
Task< ConfigurationFile > Read(ConfigurationFile file, CancellationToken cancellationToken)
Read a ConfigurationFile file
string Path
The path to the ConfigurationFile file
Routes to a server actions
Definition: Routes.cs:9
-
readonly Instance instance
The Instance for the ConfigurationClient
static string SanitizeGetPath(string path)
Sanitize a ConfigurationFile path for use in a GET Uri
Represents a game configuration file. Create and delete actions uncerimonuously overwrite/delete file...
diff --git a/_configuration_controller_8cs_source.html b/_configuration_controller_8cs_source.html index 801175b63e..0a44586c96 100644 --- a/_configuration_controller_8cs_source.html +++ b/_configuration_controller_8cs_source.html @@ -92,12 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ConfigurationController.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.IO;
5 using System.Net;
6 using System.Threading;
7 using System.Threading.Tasks;
8 using Tgstation.Server.Api;
12 using Tgstation.Server.Host.IO;
15 
16 namespace Tgstation.Server.Host.Controllers
17 {
21  [Route(Routes.Configuration)]
22  public sealed class ConfigurationController : ModelController<ConfigurationFile>
23  {
28 
33 
42  public ConfigurationController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, IIOManager ioManager, ILogger<ConfigurationController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
43  {
44  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
45  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
46  }
47 
53  bool ForbidDueToModeConflicts(string path) => Instance.ConfigurationType == ConfigurationType.Disallowed || (Instance.ConfigurationType == ConfigurationType.SystemIdentityWrite && AuthenticationContext.SystemIdentity == null) || (path != null && ioManager.PathContainsParentAccess(path));
54 
56  [TgsAuthorize(ConfigurationRights.Write)]
57  public override async Task<IActionResult> Update([FromBody] ConfigurationFile model, CancellationToken cancellationToken)
58  {
59  if (model == null)
60  throw new ArgumentNullException(nameof(model));
61  if (ForbidDueToModeConflicts(model.Path))
62  return Forbid();
63 
64  var config = instanceManager.GetInstance(Instance).Configuration;
65  try
66  {
67  var newFile = await config.Write(model.Path, AuthenticationContext.SystemIdentity, model.Content, model.LastReadHash, cancellationToken).ConfigureAwait(false);
68  if (newFile == null)
69  return Conflict(new ErrorMessage
70  {
71  Message = ""
72  });
73 
74  newFile.Content = null;
75 
76  return model.LastReadHash == null ? (IActionResult)StatusCode((int)HttpStatusCode.Created, newFile) : Json(newFile);
77  }
78  catch(IOException e)
79  {
80  Logger.LogInformation("IOException while updating file {0}: {1}", model.Path, e);
81  return Conflict(new ErrorMessage
82  {
83  Message = e.Message
84  });
85  }
86  catch (NotImplementedException)
87  {
88  return StatusCode((int)HttpStatusCode.NotImplemented);
89  }
90  }
91 
98  [HttpGet("File/{*filePath}")]
99  [TgsAuthorize(ConfigurationRights.Read)]
100  public async Task<IActionResult> File(string filePath, CancellationToken cancellationToken)
101  {
102  if (ForbidDueToModeConflicts(filePath))
103  return Forbid();
104 
105  try
106  {
107  var result = await instanceManager.GetInstance(Instance).Configuration.Read(filePath, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false);
108  if (result == null)
109  return StatusCode((int)HttpStatusCode.Gone);
110 
111  return Json(result);
112  }
113  catch (IOException e)
114  {
115  Logger.LogInformation("IOException while reading file {0}: {1}", filePath, e);
116  return Conflict(new ErrorMessage
117  {
118  Message = e.Message
119  });
120  }
121  catch (NotImplementedException)
122  {
123  return StatusCode((int)HttpStatusCode.NotImplemented);
124  }
125  }
126 
133  [HttpGet("List/{*directoryPath}")]
134  [TgsAuthorize(ConfigurationRights.List)]
135  public async Task<IActionResult> Directory(string directoryPath, CancellationToken cancellationToken)
136  {
137  if (ForbidDueToModeConflicts(directoryPath))
138  return Forbid();
139 
140  try
141  {
142  var result = await instanceManager.GetInstance(Instance).Configuration.ListDirectory(directoryPath, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false);
143  if (result == null)
144  return StatusCode((int)HttpStatusCode.Gone);
145 
146  return Json(result);
147  }
148  catch (NotImplementedException)
149  {
150  return StatusCode((int)HttpStatusCode.NotImplemented);
151  }
152  catch (UnauthorizedAccessException)
153  {
154  return Forbid();
155  }
156  }
157 
159  [TgsAuthorize(ConfigurationRights.List)]
160  public override Task<IActionResult> List(CancellationToken cancellationToken) => Directory(null, cancellationToken);
161 
163  [TgsAuthorize(ConfigurationRights.Write)]
164  public override async Task<IActionResult> Create([FromBody] ConfigurationFile model, CancellationToken cancellationToken)
165  {
166  if (model == null)
167  throw new ArgumentNullException(nameof(model));
168 
169  if (ForbidDueToModeConflicts(model.Path))
170  return Forbid();
171 
172  try
173  {
174  return await instanceManager.GetInstance(Instance).Configuration.CreateDirectory(model.Path, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false) ? (IActionResult)Json(model) : StatusCode((int)HttpStatusCode.Created, model);
175  }
176  catch (NotImplementedException)
177  {
178  return StatusCode((int)HttpStatusCode.NotImplemented);
179  }
180  catch (UnauthorizedAccessException)
181  {
182  return Forbid();
183  }
184  }
185 
192  [HttpDelete]
193  [TgsAuthorize(ConfigurationRights.Delete)]
194  public async Task<IActionResult> Delete([FromBody] ConfigurationFile directory, CancellationToken cancellationToken)
195  {
196  if (directory == null)
197  throw new ArgumentNullException(nameof(directory));
198 
199  if (ForbidDueToModeConflicts(directory.Path))
200  return Forbid();
201 
202  try
203  {
204  return await instanceManager.GetInstance(Instance).Configuration.DeleteDirectory(directory.Path, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false) ? (IActionResult)Ok() : Conflict(new ErrorMessage
205  {
206  Message = "Directory not empty!"
207  });
208  }
209  catch (NotImplementedException)
210  {
211  return StatusCode((int)HttpStatusCode.NotImplemented);
212  }
213  catch (UnauthorizedAccessException)
214  {
215  return Forbid();
216  }
217  }
218  }
219 }
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.IO;
5 using System.Net;
6 using System.Threading;
7 using System.Threading.Tasks;
8 using Tgstation.Server.Api;
12 using Tgstation.Server.Host.IO;
15 
16 namespace Tgstation.Server.Host.Controllers
17 {
21  [Route(Routes.Configuration)]
22  public sealed class ConfigurationController : ModelController<ConfigurationFile>
23  {
28 
33 
42  public ConfigurationController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, IIOManager ioManager, ILogger<ConfigurationController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
43  {
44  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
45  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
46  }
47 
53  bool ForbidDueToModeConflicts(string path) => Instance.ConfigurationType == ConfigurationType.Disallowed || (Instance.ConfigurationType == ConfigurationType.SystemIdentityWrite && AuthenticationContext.SystemIdentity == null) || (path != null && ioManager.PathContainsParentAccess(path));
54 
56  [TgsAuthorize(ConfigurationRights.Write)]
57  public override async Task<IActionResult> Update([FromBody] ConfigurationFile model, CancellationToken cancellationToken)
58  {
59  if (model == null)
60  throw new ArgumentNullException(nameof(model));
61  if (ForbidDueToModeConflicts(model.Path))
62  return Forbid();
63 
64  var config = instanceManager.GetInstance(Instance).Configuration;
65  try
66  {
67  var newFile = await config.Write(model.Path, AuthenticationContext.SystemIdentity, model.Content, model.LastReadHash, cancellationToken).ConfigureAwait(false);
68  if (newFile == null)
69  return Conflict(new ErrorMessage
70  {
71  Message = ""
72  });
73 
74  newFile.Content = null;
75 
76  return model.LastReadHash == null ? (IActionResult)StatusCode((int)HttpStatusCode.Created, newFile) : Json(newFile);
77  }
78  catch(IOException e)
79  {
80  Logger.LogInformation("IOException while updating file {0}: {1}", model.Path, e);
81  return Conflict(new ErrorMessage
82  {
83  Message = e.Message
84  });
85  }
86  catch (NotImplementedException)
87  {
88  return StatusCode((int)HttpStatusCode.NotImplemented);
89  }
90  }
91 
98  [HttpGet("File/{*filePath}")]
99  [TgsAuthorize(ConfigurationRights.Read)]
100  public async Task<IActionResult> File(string filePath, CancellationToken cancellationToken)
101  {
102  if (ForbidDueToModeConflicts(filePath))
103  return Forbid();
104 
105  try
106  {
107  var result = await instanceManager.GetInstance(Instance).Configuration.Read(filePath, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false);
108  if (result == null)
109  return StatusCode((int)HttpStatusCode.Gone);
110 
111  return Json(result);
112  }
113  catch (IOException e)
114  {
115  Logger.LogInformation("IOException while reading file {0}: {1}", filePath, e);
116  return Conflict(new ErrorMessage
117  {
118  Message = e.Message
119  });
120  }
121  catch (NotImplementedException)
122  {
123  return StatusCode((int)HttpStatusCode.NotImplemented);
124  }
125  }
126 
133  [HttpGet("List/{*directoryPath}")]
134  [TgsAuthorize(ConfigurationRights.List)]
135  public async Task<IActionResult> Directory(string directoryPath, CancellationToken cancellationToken)
136  {
137  if (ForbidDueToModeConflicts(directoryPath))
138  return Forbid();
139 
140  try
141  {
142  var result = await instanceManager.GetInstance(Instance).Configuration.ListDirectory(directoryPath, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false);
143  if (result == null)
144  return StatusCode((int)HttpStatusCode.Gone);
145 
146  return Json(result);
147  }
148  catch (NotImplementedException)
149  {
150  return StatusCode((int)HttpStatusCode.NotImplemented);
151  }
152  catch (UnauthorizedAccessException)
153  {
154  return Forbid();
155  }
156  }
157 
159  [TgsAuthorize(ConfigurationRights.List)]
160  public override Task<IActionResult> List(CancellationToken cancellationToken) => Directory(null, cancellationToken);
161 
163  [TgsAuthorize(ConfigurationRights.Write)]
164  public override async Task<IActionResult> Create([FromBody] ConfigurationFile model, CancellationToken cancellationToken)
165  {
166  if (model == null)
167  throw new ArgumentNullException(nameof(model));
168 
169  if (ForbidDueToModeConflicts(model.Path))
170  return Forbid();
171 
172  try
173  {
174  return await instanceManager.GetInstance(Instance).Configuration.CreateDirectory(model.Path, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false) ? (IActionResult)Json(model) : StatusCode((int)HttpStatusCode.Created, model);
175  }
176  catch (NotImplementedException)
177  {
178  return StatusCode((int)HttpStatusCode.NotImplemented);
179  }
180  catch (UnauthorizedAccessException)
181  {
182  return Forbid();
183  }
184  }
185 
192  [HttpDelete]
193  [TgsAuthorize(ConfigurationRights.Delete)]
194  public async Task<IActionResult> Delete([FromBody] ConfigurationFile directory, CancellationToken cancellationToken)
195  {
196  if (directory == null)
197  throw new ArgumentNullException(nameof(directory));
198 
199  if (ForbidDueToModeConflicts(directory.Path))
200  return Forbid();
201 
202  try
203  {
204  return await instanceManager.GetInstance(Instance).Configuration.DeleteDirectory(directory.Path, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false) ? (IActionResult)Ok() : Conflict(new ErrorMessage
205  {
206  Message = "Directory not empty!"
207  });
208  }
209  catch (NotImplementedException)
210  {
211  return StatusCode((int)HttpStatusCode.NotImplemented);
212  }
213  catch (UnauthorizedAccessException)
214  {
215  return Forbid();
216  }
217  }
218  }
219 }
ConfigurationRights
Rights for Models.ConfigurationFile
const string Configuration
The Models.ConfigurationFile controller
Definition: Routes.cs:49
+ @@ -111,7 +112,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Routes to a server actions
Definition: Routes.cs:9
readonly IIOManager ioManager
The IIOManager for the ConfigurationController
string Message
A human readable description of the error
Definition: ErrorMessage.cs:13
-
Represents a game configuration file. Create and delete actions uncerimonuously overwrite/delete file...
An ApiController representing a TModel
diff --git a/_configuration_file_8cs_source.html b/_configuration_file_8cs_source.html index 43d0fb6389..a2b122dded 100644 --- a/_configuration_file_8cs_source.html +++ b/_configuration_file_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ConfigurationFile.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
6  public sealed class ConfigurationFile
7  {
11  public string Path { get; set; }
12 
16  public bool? AccessDenied { get; set; }
17 
21  public bool? IsDirectory { get; set; }
22 
26  public string LastReadHash { get; set; }
27 
31  public byte[] Content { get; set; }
32  }
33 }
+Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
6  public sealed class ConfigurationFile
7  {
11  public string Path { get; set; }
12 
16  public bool? AccessDenied { get; set; }
17 
21  public bool? IsDirectory { get; set; }
22 
26  public string LastReadHash { get; set; }
27 
31  public byte[] Content { get; set; }
32  }
33 }
Represents a game configuration file. Create and delete actions uncerimonuously overwrite/delete file...
diff --git a/_configuration_rights_8cs_source.html b/_configuration_rights_8cs_source.html index a9fcef01e4..61c6172dd7 100644 --- a/_configuration_rights_8cs_source.html +++ b/_configuration_rights_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ConfigurationRights.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum ConfigurationRights : ulong
10  {
14  None = 0,
18  Read = 1,
22  Write = 2,
26  List = 4,
30  Delete = 8
31  }
32 }
ConfigurationRights
Rights for Models.ConfigurationFile
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum ConfigurationRights : ulong
10  {
14  None = 0,
18  Read = 1,
22  Write = 2,
26  List = 4,
30  Delete = 8
31  }
32 }
ConfigurationRights
Rights for Models.ConfigurationFile
+ -
User can read all chat settings except Models.Internal.ChatBot.ConnectionString
diff --git a/_configuration_test_8cs_source.html b/_configuration_test_8cs_source.html index d4fe5f6f24..c4e78e34cc 100644 --- a/_configuration_test_8cs_source.html +++ b/_configuration_test_8cs_source.html @@ -92,7 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ConfigurationTest.cs
-Go to the documentation of this file.
1 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using System;
3 using System.IO;
4 using System.Text;
5 using System.Threading;
6 using System.Threading.Tasks;
10 
11 namespace Tgstation.Server.Tests.Instance
12 {
13  sealed class ConfigurationTest
14  {
16  readonly Api.Models.Instance instance;
17 
18  public ConfigurationTest(IConfigurationClient configurationClient, Api.Models.Instance instance)
19  {
20  this.configurationClient = configurationClient ?? throw new ArgumentNullException(nameof(configurationClient));
21  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
22  }
23 
25  {
26  var tmp = (file.Path?.StartsWith('/') ?? false) ? '.' + file.Path : file.Path;
27  var path = Path.Combine(instance.Path, "Configuration", tmp);
28  return File.Exists(path);
29  }
30 
31  async Task TestDeleteDirectory(CancellationToken cancellationToken)
32  {
33  //try to delete non-existent
34  var TestDir = new ConfigurationFile
35  {
36  Path = "/TestDeleteDir"
37  };
38 
39  await configurationClient.DeleteEmptyDirectory(TestDir, cancellationToken).ConfigureAwait(false);
40 
41  //try to delete non-empty
42  var file = await configurationClient.Write(new ConfigurationFile
43  {
44  Content = Encoding.UTF8.GetBytes("Hello world!"),
45  Path = TestDir.Path + "/test.txt"
46  }, cancellationToken).ConfigureAwait(false);
47 
48  Assert.IsTrue(FileExists(file));
49 
50  await Assert.ThrowsExceptionAsync<ConflictException>(() => configurationClient.DeleteEmptyDirectory(TestDir, cancellationToken)).ConfigureAwait(false);
51 
52  file.Content = null;
53  await configurationClient.Write(file, cancellationToken).ConfigureAwait(false);
54 
55  await configurationClient.DeleteEmptyDirectory(TestDir, cancellationToken).ConfigureAwait(false);
56  }
57 
58  public async Task Run(CancellationToken cancellationToken)
59  {
60  await TestDeleteDirectory(cancellationToken).ConfigureAwait(false);
61  }
62  }
63 }
+Go to the documentation of this file.
1 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using System;
3 using System.IO;
4 using System.Text;
5 using System.Threading;
6 using System.Threading.Tasks;
10 
11 namespace Tgstation.Server.Tests.Instance
12 {
13  sealed class ConfigurationTest
14  {
16  readonly Api.Models.Instance instance;
17 
18  public ConfigurationTest(IConfigurationClient configurationClient, Api.Models.Instance instance)
19  {
20  this.configurationClient = configurationClient ?? throw new ArgumentNullException(nameof(configurationClient));
21  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
22  }
23 
25  {
26  var tmp = (file.Path?.StartsWith('/') ?? false) ? '.' + file.Path : file.Path;
27  var path = Path.Combine(instance.Path, "Configuration", tmp);
28  return File.Exists(path);
29  }
30 
31  async Task TestDeleteDirectory(CancellationToken cancellationToken)
32  {
33  //try to delete non-existent
34  var TestDir = new ConfigurationFile
35  {
36  Path = "/TestDeleteDir"
37  };
38 
39  await configurationClient.DeleteEmptyDirectory(TestDir, cancellationToken).ConfigureAwait(false);
40 
41  //try to delete non-empty
42  var file = await configurationClient.Write(new ConfigurationFile
43  {
44  Content = Encoding.UTF8.GetBytes("Hello world!"),
45  Path = TestDir.Path + "/test.txt"
46  }, cancellationToken).ConfigureAwait(false);
47 
48  Assert.IsTrue(FileExists(file));
49 
50  await Assert.ThrowsExceptionAsync<ConflictException>(() => configurationClient.DeleteEmptyDirectory(TestDir, cancellationToken)).ConfigureAwait(false);
51 
52  file.Content = null;
53  await configurationClient.Write(file, cancellationToken).ConfigureAwait(false);
54 
55  await configurationClient.DeleteEmptyDirectory(TestDir, cancellationToken).ConfigureAwait(false);
56  }
57 
58  public async Task Run(CancellationToken cancellationToken)
59  {
60  await TestDeleteDirectory(cancellationToken).ConfigureAwait(false);
61  }
62  }
63 }
+ @@ -102,7 +103,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
string Path
The path to the ConfigurationFile file
async Task TestDeleteDirectory(CancellationToken cancellationToken)
-
Represents a game configuration file. Create and delete actions uncerimonuously overwrite/delete file...
Occurs when the client performs an action that would result in data conflict
Metadata about a server instance
Definition: Instance.cs:9
diff --git a/_configuration_type_8cs_source.html b/_configuration_type_8cs_source.html index 51aae93987..4698efdedf 100644 --- a/_configuration_type_8cs_source.html +++ b/_configuration_type_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ConfigurationType.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
6  public enum ConfigurationType
7  {
11  Disallowed,
15  HostWrite,
20  }
21 }
ConfigurationType
The type of configuration allowed on an Instance
+Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
6  public enum ConfigurationType
7  {
11  Disallowed,
15  HostWrite,
20  }
21 }
+
ConfigurationType
The type of configuration allowed on an Instance
-
Configuration editing is allowed by all users on all files
diff --git a/_conflict_exception_8cs_source.html b/_conflict_exception_8cs_source.html index d8d13ba43f..0539cedea4 100644 --- a/_conflict_exception_8cs_source.html +++ b/_conflict_exception_8cs_source.html @@ -92,15 +92,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ConflictException.cs
-Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
10  public sealed class ConflictException : ClientException
11  {
17  public ConflictException(ErrorMessage errorMessage, HttpStatusCode statusCode) : base(errorMessage, statusCode)
18  { }
19 
23  public ConflictException() { }
24 
29  public ConflictException(string message) : base(message) { }
30 
36  public ConflictException(string message, Exception innerException) : base(message, innerException) { }
37  }
38 }
+Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
10  public sealed class ConflictException : ClientException
11  {
17  public ConflictException(ErrorMessage errorMessage, HttpStatusCode statusCode) : base(errorMessage, statusCode)
18  { }
19 
23  public ConflictException() { }
24 
29  public ConflictException(string message) : base(message) { }
30 
36  public ConflictException(string message, Exception innerException) : base(message, innerException) { }
37  }
38 }
ConflictException()
Construct a ConflictException
ConflictException(string message, Exception innerException)
Construct an ConflictException with a message and innerException
ConflictException(string message)
Construct an ConflictException with a message
+
ConflictException(ErrorMessage errorMessage, HttpStatusCode statusCode)
Construct an ConflictException with a errorMessage and statusCode
-
Occurs when the client performs an action that would result in data conflict
Represents an error message returned by the server
Definition: ErrorMessage.cs:8
diff --git a/_constants_8cs_source.html b/_constants_8cs_source.html index 6e6cb85471..3f8bdfd448 100644 --- a/_constants_8cs_source.html +++ b/_constants_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Constants.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Components.Interop
2 {
3  static class Constants
4  {
5  //interop values, match them up with the appropriate api.dm
6 
7  //api version 4.0.0.0
8  public const string DMParamHostVersion = "server_service_version";
9  public const string DMParamInfoJson = "tgs_json";
10 
11  public const string DMInteropAccessIdentifier = "tgs_tok";
12 
13  public const string DMResponseSuccess = "tgs_succ";
14 
15  public const string DMTopicChangePort = "tgs_port";
16  public const string DMTopicChangeReboot = "tgs_rmode";
17  public const string DMTopicChatCommand = "tgs_chat_comm";
18  public const string DMTopicEvent = "tgs_event";
19  public const string DMTopicInteropResponse = "tgs_interop";
20 
21  public const string DMCommandNewPort = "tgs_new_port";
22  public const string DMCommandApiValidate = "tgs_validate";
23  public const string DMCommandServerPrimed = "tgs_prime";
24  public const string DMCommandWorldReboot = "tgs_reboot";
25  public const string DMCommandEndProcess = "tgs_kill";
26  public const string DMCommandChat = "tgs_chat_send";
27 
28  public const string DMParameterCommand = "tgs_com";
29  public const string DMParameterData = "tgs_data";
30  }
31 }
+Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Components.Interop
2 {
3  static class Constants
4  {
5  //interop values, match them up with the appropriate api.dm
6 
7  //api version 4.0.0.0
8  public const string DMParamHostVersion = "server_service_version";
9  public const string DMParamInfoJson = "tgs_json";
10 
11  public const string DMInteropAccessIdentifier = "tgs_tok";
12 
13  public const string DMResponseSuccess = "tgs_succ";
14 
15  public const string DMTopicChangePort = "tgs_port";
16  public const string DMTopicChangeReboot = "tgs_rmode";
17  public const string DMTopicChatCommand = "tgs_chat_comm";
18  public const string DMTopicEvent = "tgs_event";
19  public const string DMTopicInteropResponse = "tgs_interop";
20 
21  public const string DMCommandNewPort = "tgs_new_port";
22  public const string DMCommandApiValidate = "tgs_validate";
23  public const string DMCommandServerPrimed = "tgs_prime";
24  public const string DMCommandWorldReboot = "tgs_reboot";
25  public const string DMCommandEndProcess = "tgs_kill";
26  public const string DMCommandChat = "tgs_chat_send";
27 
28  public const string DMParameterCommand = "tgs_com";
29  public const string DMParameterData = "tgs_data";
30  }
31 }
diff --git a/_cryptography_suite_8cs_source.html b/_cryptography_suite_8cs_source.html index 207c983117..baec7e650d 100644 --- a/_cryptography_suite_8cs_source.html +++ b/_cryptography_suite_8cs_source.html @@ -92,7 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
CryptographySuite.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Identity;
2 using System;
3 using System.Security.Cryptography;
5 
6 namespace Tgstation.Server.Host.Security
7 {
10  {
15  public static byte[] GetSecureBytes(int amount)
16  {
17  using (var rng = new RNGCryptoServiceProvider())
18  {
19  var byt = new byte[amount];
20  rng.GetBytes(byt);
21  return byt;
22  }
23  }
24 
28  readonly IPasswordHasher<User> passwordHasher;
29 
34  public CryptographySuite(IPasswordHasher<User> passwordHasher) => this.passwordHasher = passwordHasher ?? throw new ArgumentNullException(nameof(passwordHasher));
35 
37  public void SetUserPassword(User user, string newPassword)
38  {
39  if (user == null)
40  throw new ArgumentNullException(nameof(user));
41  if (String.IsNullOrEmpty(newPassword))
42  throw new ArgumentNullException(nameof(newPassword));
43  user.PasswordHash = passwordHasher.HashPassword(user, newPassword);
44  user.LastPasswordUpdate = DateTimeOffset.Now;
45  }
46 
48  public bool CheckUserPassword(User user, string password)
49  {
50  switch (passwordHasher.VerifyHashedPassword(user, user.PasswordHash, password))
51  {
52  case PasswordVerificationResult.Failed:
53  return false;
54  case PasswordVerificationResult.SuccessRehashNeeded:
55  user.PasswordHash = passwordHasher.HashPassword(user, password);
56  //don't update LastPasswordUpdate since it hasn't actually changed
57  break;
58  }
59  return true;
60  }
61 
63  public string GetSecureString() => Convert.ToBase64String(GetSecureBytes(30));
64  }
65 }
string PasswordHash
The hash of the user&#39;s password
Definition: User.cs:13
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Identity;
2 using System;
3 using System.Security.Cryptography;
5 
6 namespace Tgstation.Server.Host.Security
7 {
10  {
15  public static byte[] GetSecureBytes(int amount)
16  {
17  using (var rng = new RNGCryptoServiceProvider())
18  {
19  var byt = new byte[amount];
20  rng.GetBytes(byt);
21  return byt;
22  }
23  }
24 
28  readonly IPasswordHasher<User> passwordHasher;
29 
34  public CryptographySuite(IPasswordHasher<User> passwordHasher) => this.passwordHasher = passwordHasher ?? throw new ArgumentNullException(nameof(passwordHasher));
35 
37  public void SetUserPassword(User user, string newPassword)
38  {
39  if (user == null)
40  throw new ArgumentNullException(nameof(user));
41  if (String.IsNullOrEmpty(newPassword))
42  throw new ArgumentNullException(nameof(newPassword));
43  user.PasswordHash = passwordHasher.HashPassword(user, newPassword);
44  user.LastPasswordUpdate = DateTimeOffset.Now;
45  }
46 
48  public bool CheckUserPassword(User user, string password)
49  {
50  switch (passwordHasher.VerifyHashedPassword(user, user.PasswordHash, password))
51  {
52  case PasswordVerificationResult.Failed:
53  return false;
54  case PasswordVerificationResult.SuccessRehashNeeded:
55  user.PasswordHash = passwordHasher.HashPassword(user, password);
56  //don't update LastPasswordUpdate since it hasn't actually changed
57  break;
58  }
59  return true;
60  }
61 
63  public string GetSecureString() => Convert.ToBase64String(GetSecureBytes(30));
64  }
65 }
string PasswordHash
The hash of the user&#39;s password
Definition: User.cs:13
+
readonly IPasswordHasher< User > passwordHasher
The IPasswordHasher<TUser> for the CryptographySuite
@@ -101,7 +102,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Contains various cryptographic functions
static byte[] GetSecureBytes(int amount)
Generates a secure set of bytes
-
bool CheckUserPassword(User user, string password)
Checks a given password matches a given user &#39;s User.PasswordHash. This may result in User...
diff --git a/_custom_command_8cs_source.html b/_custom_command_8cs_source.html index dff9cac0b6..409fa25217 100644 --- a/_custom_command_8cs_source.html +++ b/_custom_command_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
CustomCommand.cs
-Go to the documentation of this file.
1 using Newtonsoft.Json;
2 using System;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Components.Chat.Commands
7 {
11  public sealed class CustomCommand : ICommand
12  {
14  public string Name { get; set; }
15 
17  public string HelpText { get; set; }
18 
20  [JsonConverter(typeof(BoolConverter))]
21  public bool AdminOnly { get; set; }
22 
27 
32  public void SetHandler(ICustomCommandHandler handler)
33  {
34  if (this.handler != null)
35  throw new InvalidOperationException("SetHandler() already called!");
36  this.handler = handler ?? throw new ArgumentNullException(nameof(handler));
37  }
38 
40  public Task<string> Invoke(string arguments, User user, CancellationToken cancellationToken)
41  {
42  if (handler == null)
43  throw new InvalidOperationException("SetHandler() has not been called!");
44  return handler.HandleChatCommand(Name, arguments, user, cancellationToken);
45  }
46  }
47 }
void SetHandler(ICustomCommandHandler handler)
Set a new handler
+Go to the documentation of this file.
1 using Newtonsoft.Json;
2 using System;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Components.Chat.Commands
7 {
11  public sealed class CustomCommand : ICommand
12  {
14  public string Name { get; set; }
15 
17  public string HelpText { get; set; }
18 
20  [JsonConverter(typeof(BoolConverter))]
21  public bool AdminOnly { get; set; }
22 
27 
32  public void SetHandler(ICustomCommandHandler handler)
33  {
34  if (this.handler != null)
35  throw new InvalidOperationException("SetHandler() already called!");
36  this.handler = handler ?? throw new ArgumentNullException(nameof(handler));
37  }
38 
40  public Task<string> Invoke(string arguments, User user, CancellationToken cancellationToken)
41  {
42  if (handler == null)
43  throw new InvalidOperationException("SetHandler() has not been called!");
44  return handler.HandleChatCommand(Name, arguments, user, cancellationToken);
45  }
46  }
47 }
void SetHandler(ICustomCommandHandler handler)
Set a new handler
+
Task< string > Invoke(string arguments, User user, CancellationToken cancellationToken)
Invoke the ICommand
JsonConverter for decoding bools returned by BYOND
Definition: BoolConverter.cs:9
-
ICustomCommandHandler handler
The ICustomCommandHandler for the CustomCommand
Represents a tgs_chat_user datum
Definition: User.cs:10
diff --git a/_database_context_8cs_source.html b/_database_context_8cs_source.html index 48778c813c..2e72876b4e 100644 --- a/_database_context_8cs_source.html +++ b/_database_context_8cs_source.html @@ -92,9 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DatabaseContext.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using Microsoft.Extensions.Options;
4 using System;
5 using System.Linq;
6 using System.Threading;
7 using System.Threading.Tasks;
9 
10 namespace Tgstation.Server.Host.Models
11 {
13  abstract class DatabaseContext<TParentContext> : DbContext, IDatabaseContext where TParentContext : DbContext
14  {
16  public DbSet<User> Users { get; set; }
17 
19  public DbSet<Instance> Instances { get; set; }
20 
22  public DbSet<CompileJob> CompileJobs { get; set; }
23 
25  public DbSet<RevisionInformation> RevisionInformations { get; set; }
26 
28  public DbSet<DreamMakerSettings> DreamMakerSettings { get; set; }
29 
31  public DbSet<ChatBot> ChatBots { get; set; }
32 
34  public DbSet<DreamDaemonSettings> DreamDaemonSettings { get; set; }
35 
37  public DbSet<RepositorySettings> RepositorySettings { get; set; }
38 
40  public DbSet<InstanceUser> InstanceUsers { get; set; }
41 
43  public DbSet<ChatChannel> ChatChannels { get; set; }
44 
46  public DbSet<Job> Jobs { get; set; }
47 
49  public DbSet<ReattachInformation> ReattachInformations { get; set; }
50 
52  public DbSet<WatchdogReattachInformation> WatchdogReattachInformations { get; set; }
53 
57  public DbSet<TestMerge> TestMerges { get; set; }
58 
62  public DbSet<RevInfoTestMerge> RevInfoTestMerges { get; set; }
63 
67  protected ILogger Logger { get; }
68 
73 
78 
86  public DatabaseContext(DbContextOptions<TParentContext> dbContextOptions, IOptions<DatabaseConfiguration> databaseConfigurationOptions, IDatabaseSeeder databaseSeeder, ILogger logger) : base(dbContextOptions)
87  {
88  DatabaseConfiguration = databaseConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(databaseConfigurationOptions));
89  this.databaseSeeder = databaseSeeder ?? throw new ArgumentNullException(nameof(databaseSeeder));
90  Logger = logger ?? throw new ArgumentNullException(nameof(logger));
91  }
92 
94  protected override void OnModelCreating(ModelBuilder modelBuilder)
95  {
96  Logger.LogTrace("Building entity framework context...");
97  base.OnModelCreating(modelBuilder);
98 
99  var userModel = modelBuilder.Entity<User>();
100  userModel.HasIndex(x => x.CanonicalName).IsUnique();
101  userModel.HasMany(x => x.TestMerges).WithOne(x => x.MergedBy).OnDelete(DeleteBehavior.Restrict);
102 
103  modelBuilder.Entity<InstanceUser>().HasIndex(x => new { x.UserId, x.InstanceId }).IsUnique();
104 
105  modelBuilder.Entity<TestMerge>().HasMany(x => x.RevisonInformations).WithOne(x => x.TestMerge).OnDelete(DeleteBehavior.Cascade);
106 
107  var revInfo = modelBuilder.Entity<RevisionInformation>();
108  revInfo.HasMany(x => x.CompileJobs).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade);
109  revInfo.HasMany(x => x.ActiveTestMerges).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade);
110  revInfo.HasOne(x => x.PrimaryTestMerge).WithOne(x => x.PrimaryRevisionInformation).OnDelete(DeleteBehavior.Restrict);
111  revInfo.HasIndex(x => x.CommitSha).IsUnique();
112 
113  modelBuilder.Entity<CompileJob>().HasIndex(x => x.DirectoryName);
114 
115  modelBuilder.Entity<Job>().HasOne<CompileJob>().WithOne(x => x.Job).OnDelete(DeleteBehavior.Restrict);
116 
117  var chatChannel = modelBuilder.Entity<ChatChannel>();
118  chatChannel.HasIndex(x => new { x.ChatSettingsId, x.IrcChannel }).IsUnique();
119  chatChannel.HasIndex(x => new { x.ChatSettingsId, x.DiscordChannelId }).IsUnique();
120  chatChannel.HasOne(x => x.ChatSettings).WithMany(x => x.Channels).HasForeignKey(x => x.ChatSettingsId).OnDelete(DeleteBehavior.Cascade);
121 
122  modelBuilder.Entity<ChatBot>().HasIndex(x => x.Name).IsUnique();
123 
124  var instanceModel = modelBuilder.Entity<Instance>();
125  instanceModel.HasIndex(x => x.Path).IsUnique();
126  instanceModel.HasMany(x => x.ChatSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
127  instanceModel.HasOne(x => x.DreamDaemonSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
128  instanceModel.HasOne(x => x.DreamMakerSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
129  instanceModel.HasOne(x => x.RepositorySettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
130  instanceModel.HasMany(x => x.RevisionInformations).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
131  instanceModel.HasMany(x => x.InstanceUsers).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
132  instanceModel.HasMany(x => x.Jobs).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
133  instanceModel.HasOne(x => x.WatchdogReattachInformation).WithOne().OnDelete(DeleteBehavior.Cascade);
134  }
135 
137  public async Task Initialize(CancellationToken cancellationToken)
138  {
140  {
141  Logger.LogCritical("DropDatabase configuration option set! Dropping any existing database...");
142  await Database.EnsureDeletedAsync(cancellationToken).ConfigureAwait(false);
143  }
144 
145  var migrations = await Database.GetAppliedMigrationsAsync(cancellationToken).ConfigureAwait(false);
146  var wasEmpty = !migrations.Any();
147 
148  if (wasEmpty || (await Database.GetPendingMigrationsAsync(cancellationToken).ConfigureAwait(false)).Any())
149  {
150  Logger.LogInformation("Migrating database...");
151  await Database.MigrateAsync(cancellationToken).ConfigureAwait(false);
152  }
153  else
154  Logger.LogDebug("No migrations to apply.");
155 
156  wasEmpty |= (await Users.CountAsync(cancellationToken).ConfigureAwait(false)) == 0;
157 
158  if (wasEmpty)
159  {
160  Logger.LogInformation("Seeding database...");
161  await databaseSeeder.SeedDatabase(this, cancellationToken).ConfigureAwait(false);
162  }
164  {
165  Logger.LogWarning("Enabling and resetting admin password due to configuration!");
166  await databaseSeeder.ResetAdminPassword(this, cancellationToken).ConfigureAwait(false);
167  }
168  }
169 
171  public Task Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken);
172  }
173 }
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using Microsoft.Extensions.Options;
4 using System;
5 using System.Linq;
6 using System.Threading;
7 using System.Threading.Tasks;
9 
10 namespace Tgstation.Server.Host.Models
11 {
13  abstract class DatabaseContext<TParentContext> : DbContext, IDatabaseContext where TParentContext : DbContext
14  {
16  public DbSet<User> Users { get; set; }
17 
19  public DbSet<Instance> Instances { get; set; }
20 
22  public DbSet<CompileJob> CompileJobs { get; set; }
23 
25  public DbSet<RevisionInformation> RevisionInformations { get; set; }
26 
28  public DbSet<DreamMakerSettings> DreamMakerSettings { get; set; }
29 
31  public DbSet<ChatBot> ChatBots { get; set; }
32 
34  public DbSet<DreamDaemonSettings> DreamDaemonSettings { get; set; }
35 
37  public DbSet<RepositorySettings> RepositorySettings { get; set; }
38 
40  public DbSet<InstanceUser> InstanceUsers { get; set; }
41 
43  public DbSet<ChatChannel> ChatChannels { get; set; }
44 
46  public DbSet<Job> Jobs { get; set; }
47 
49  public DbSet<ReattachInformation> ReattachInformations { get; set; }
50 
52  public DbSet<WatchdogReattachInformation> WatchdogReattachInformations { get; set; }
53 
57  public DbSet<TestMerge> TestMerges { get; set; }
58 
62  public DbSet<RevInfoTestMerge> RevInfoTestMerges { get; set; }
63 
67  protected ILogger Logger { get; }
68 
73 
78 
86  public DatabaseContext(DbContextOptions<TParentContext> dbContextOptions, IOptions<DatabaseConfiguration> databaseConfigurationOptions, IDatabaseSeeder databaseSeeder, ILogger logger) : base(dbContextOptions)
87  {
88  DatabaseConfiguration = databaseConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(databaseConfigurationOptions));
89  this.databaseSeeder = databaseSeeder ?? throw new ArgumentNullException(nameof(databaseSeeder));
90  Logger = logger ?? throw new ArgumentNullException(nameof(logger));
91  }
92 
94  protected override void OnModelCreating(ModelBuilder modelBuilder)
95  {
96  Logger.LogTrace("Building entity framework context...");
97  base.OnModelCreating(modelBuilder);
98 
99  var userModel = modelBuilder.Entity<User>();
100  userModel.HasIndex(x => x.CanonicalName).IsUnique();
101  userModel.HasMany(x => x.TestMerges).WithOne(x => x.MergedBy).OnDelete(DeleteBehavior.Restrict);
102 
103  modelBuilder.Entity<InstanceUser>().HasIndex(x => new { x.UserId, x.InstanceId }).IsUnique();
104 
105  modelBuilder.Entity<TestMerge>().HasMany(x => x.RevisonInformations).WithOne(x => x.TestMerge).OnDelete(DeleteBehavior.Cascade);
106 
107  var revInfo = modelBuilder.Entity<RevisionInformation>();
108  revInfo.HasMany(x => x.CompileJobs).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade);
109  revInfo.HasMany(x => x.ActiveTestMerges).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade);
110  revInfo.HasOne(x => x.PrimaryTestMerge).WithOne(x => x.PrimaryRevisionInformation).OnDelete(DeleteBehavior.Restrict);
111  revInfo.HasIndex(x => x.CommitSha).IsUnique();
112 
113  modelBuilder.Entity<CompileJob>().HasIndex(x => x.DirectoryName);
114 
115  modelBuilder.Entity<Job>().HasOne<CompileJob>().WithOne(x => x.Job).OnDelete(DeleteBehavior.Restrict);
116 
117  var chatChannel = modelBuilder.Entity<ChatChannel>();
118  chatChannel.HasIndex(x => new { x.ChatSettingsId, x.IrcChannel }).IsUnique();
119  chatChannel.HasIndex(x => new { x.ChatSettingsId, x.DiscordChannelId }).IsUnique();
120  chatChannel.HasOne(x => x.ChatSettings).WithMany(x => x.Channels).HasForeignKey(x => x.ChatSettingsId).OnDelete(DeleteBehavior.Cascade);
121 
122  modelBuilder.Entity<ChatBot>().HasIndex(x => x.Name).IsUnique();
123 
124  var instanceModel = modelBuilder.Entity<Instance>();
125  instanceModel.HasIndex(x => x.Path).IsUnique();
126  instanceModel.HasMany(x => x.ChatSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
127  instanceModel.HasOne(x => x.DreamDaemonSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
128  instanceModel.HasOne(x => x.DreamMakerSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
129  instanceModel.HasOne(x => x.RepositorySettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
130  instanceModel.HasMany(x => x.RevisionInformations).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
131  instanceModel.HasMany(x => x.InstanceUsers).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
132  instanceModel.HasMany(x => x.Jobs).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade);
133  instanceModel.HasOne(x => x.WatchdogReattachInformation).WithOne().OnDelete(DeleteBehavior.Cascade);
134  }
135 
137  public async Task Initialize(CancellationToken cancellationToken)
138  {
140  {
141  Logger.LogCritical("DropDatabase configuration option set! Dropping any existing database...");
142  await Database.EnsureDeletedAsync(cancellationToken).ConfigureAwait(false);
143  }
144 
145  var migrations = await Database.GetAppliedMigrationsAsync(cancellationToken).ConfigureAwait(false);
146  var wasEmpty = !migrations.Any();
147 
148  if (wasEmpty || (await Database.GetPendingMigrationsAsync(cancellationToken).ConfigureAwait(false)).Any())
149  {
150  Logger.LogInformation("Migrating database...");
151  await Database.MigrateAsync(cancellationToken).ConfigureAwait(false);
152  }
153  else
154  Logger.LogDebug("No migrations to apply.");
155 
156  wasEmpty |= (await Users.CountAsync(cancellationToken).ConfigureAwait(false)) == 0;
157 
158  if (wasEmpty)
159  {
160  Logger.LogInformation("Seeding database...");
161  await databaseSeeder.SeedDatabase(this, cancellationToken).ConfigureAwait(false);
162  }
164  {
165  Logger.LogWarning("Enabling and resetting admin password due to configuration!");
166  await databaseSeeder.ResetAdminPassword(this, cancellationToken).ConfigureAwait(false);
167  }
168  }
169 
171  public Task Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken);
172  }
173 }
+ @@ -107,7 +108,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
override void OnModelCreating(ModelBuilder modelBuilder)
Represents an Api.Models.Instance in the database
Definition: Instance.cs:8
-
DatabaseContext(DbContextOptions< TParentContext > dbContextOptions, IOptions< DatabaseConfiguration > databaseConfigurationOptions, IDatabaseSeeder databaseSeeder, ILogger logger)
Construct a DatabaseContext<TParentContext>
diff --git a/_database_context_factory_8cs_source.html b/_database_context_factory_8cs_source.html index cbf5ceaf99..e7ef294103 100644 --- a/_database_context_factory_8cs_source.html +++ b/_database_context_factory_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DatabaseContextFactory.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.DependencyInjection;
2 using System;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Core
7 {
10  {
14  readonly IServiceScopeFactory scopeFactory;
15 
20  public DatabaseContextFactory(IServiceScopeFactory scopeFactory)
21  {
22  this.scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory));
23 
24  using (var scope = scopeFactory.CreateScope())
25  scope.ServiceProvider.GetRequiredService<IDatabaseContext>();
26  }
27 
29  public async Task UseContext(Func<IDatabaseContext, Task> operation)
30  {
31  using (var scope = scopeFactory.CreateScope())
32  await operation(scope.ServiceProvider.GetRequiredService<IDatabaseContext>()).ConfigureAwait(false);
33  }
34  }
35 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.DependencyInjection;
2 using System;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Core
7 {
10  {
14  readonly IServiceScopeFactory scopeFactory;
15 
20  public DatabaseContextFactory(IServiceScopeFactory scopeFactory)
21  {
22  this.scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory));
23 
24  using (var scope = scopeFactory.CreateScope())
25  scope.ServiceProvider.GetRequiredService<IDatabaseContext>();
26  }
27 
29  public async Task UseContext(Func<IDatabaseContext, Task> operation)
30  {
31  using (var scope = scopeFactory.CreateScope())
32  await operation(scope.ServiceProvider.GetRequiredService<IDatabaseContext>()).ConfigureAwait(false);
33  }
34  }
35 }
+
Factory for scoping usage of IDatabaseContexts. Meant for use by Components
DatabaseContextFactory(IServiceScopeFactory scopeFactory)
Construct a DatabaseContextFactory
-
readonly IServiceScopeFactory scopeFactory
The IServiceScopeFactory for the DatabaseContextFactory
async Task UseContext(Func< IDatabaseContext, Task > operation)
Run an operation in the scope of an IDatabaseContext
diff --git a/_database_seeder_8cs_source.html b/_database_seeder_8cs_source.html index e2530a1c4a..1e17fd9ab2 100644 --- a/_database_seeder_8cs_source.html +++ b/_database_seeder_8cs_source.html @@ -92,10 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DatabaseSeeder.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using System;
3 using System.Linq;
4 using System.Threading;
5 using System.Threading.Tasks;
8 
9 namespace Tgstation.Server.Host.Models
10 {
13  {
18 
23  public DatabaseSeeder(ICryptographySuite cryptographySuite) => this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
24 
29  void SeedAdminUser(IDatabaseContext databaseContext)
30  {
31  var admin = new User
32  {
34  CreatedAt = DateTimeOffset.Now,
36  Name = Api.Models.User.AdminName,
37  CanonicalName = Api.Models.User.AdminName.ToUpperInvariant(),
38  Enabled = true,
39  };
40  cryptographySuite.SetUserPassword(admin, Api.Models.User.DefaultAdminPassword);
41  databaseContext.Users.Add(admin);
42  }
43 
45  public async Task SeedDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken)
46  {
47  SeedAdminUser(databaseContext);
48  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
49  }
50 
52  public async Task ResetAdminPassword(IDatabaseContext databaseContext, CancellationToken cancellationToken)
53  {
54  var admin = await databaseContext.Users.Where(x => x.CanonicalName == Api.Models.User.AdminName.ToUpperInvariant()).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
55  if (admin == default)
56  SeedAdminUser(databaseContext);
57  else
58  {
59  admin.Enabled = true;
60  cryptographySuite.SetUserPassword(admin, Api.Models.User.DefaultAdminPassword);
61  }
62 
63  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
64  }
65  }
66 }
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using System;
3 using System.Linq;
4 using System.Threading;
5 using System.Threading.Tasks;
8 
9 namespace Tgstation.Server.Host.Models
10 {
13  {
18 
23  public DatabaseSeeder(ICryptographySuite cryptographySuite) => this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
24 
29  void SeedAdminUser(IDatabaseContext databaseContext)
30  {
31  var admin = new User
32  {
34  CreatedAt = DateTimeOffset.Now,
36  Name = Api.Models.User.AdminName,
37  CanonicalName = Api.Models.User.AdminName.ToUpperInvariant(),
38  Enabled = true,
39  };
40  cryptographySuite.SetUserPassword(admin, Api.Models.User.DefaultAdminPassword);
41  databaseContext.Users.Add(admin);
42  }
43 
45  public async Task SeedDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken)
46  {
47  SeedAdminUser(databaseContext);
48  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
49  }
50 
52  public async Task ResetAdminPassword(IDatabaseContext databaseContext, CancellationToken cancellationToken)
53  {
54  var admin = await databaseContext.Users.Where(x => x.CanonicalName == Api.Models.User.AdminName.ToUpperInvariant()).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
55  if (admin == default)
56  SeedAdminUser(databaseContext);
57  else
58  {
59  admin.Enabled = true;
60  cryptographySuite.SetUserPassword(admin, Api.Models.User.DefaultAdminPassword);
61  }
62 
63  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
64  }
65  }
66 }
async Task ResetAdminPassword(IDatabaseContext databaseContext, CancellationToken cancellationToken)
Changes the admin password in IDatabaseContext back to it&#39;s default and enables the account ...
readonly ICryptographySuite cryptographySuite
The ICryptographySuite for the DatabaseContext<TParentContext>
+
Task Save(CancellationToken cancellationToken)
Saves changes made to the IDatabaseContext
For initially seeding a database
@@ -106,7 +107,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceManagerRights
Rights for managing Models.Instances
void SeedAdminUser(IDatabaseContext databaseContext)
Add a default admin User to a given databaseContext
- diff --git a/_database_type_8cs_source.html b/_database_type_8cs_source.html index 88ab3e76f8..81d9a27a92 100644 --- a/_database_type_8cs_source.html +++ b/_database_type_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DatabaseType.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Configuration
2 {
7  {
11  SqlServer,
15  MySql,
19  MariaDB
20  }
21 }
+Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Configuration
2 {
7  {
11  SqlServer,
15  MySql,
19  MariaDB
20  }
21 }
+ -
DatabaseType
Type of database to user
Definition: DatabaseType.cs:6
diff --git a/_design_time_db_context_factory_helpers_8cs_source.html b/_design_time_db_context_factory_helpers_8cs_source.html index 1335e0a294..50591fa063 100644 --- a/_design_time_db_context_factory_helpers_8cs_source.html +++ b/_design_time_db_context_factory_helpers_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DesignTimeDbContextFactoryHelpers.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Configuration;
2 using Microsoft.Extensions.Options;
3 using System.IO;
4 using System.Reflection;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
13  {
17  const string RootJson = "appsettings.json";
21  const string DevJson = "appsettings.Development.json";
22 
24  public static IOptions<DatabaseConfiguration> GetDbContextOptions()
25  {
26  var builder = new ConfigurationBuilder();
27  builder.SetBasePath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
28  builder.AddJsonFile(RootJson);
29  builder.AddJsonFile(DevJson);
30  var configuration = builder.Build();
31  return Options.Create(configuration.GetSection(DatabaseConfiguration.Section).Get<DatabaseConfiguration>());
32  }
33  }
34 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Configuration;
2 using Microsoft.Extensions.Options;
3 using System.IO;
4 using System.Reflection;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
13  {
17  const string RootJson = "appsettings.json";
21  const string DevJson = "appsettings.Development.json";
22 
24  public static IOptions<DatabaseConfiguration> GetDbContextOptions()
25  {
26  var builder = new ConfigurationBuilder();
27  builder.SetBasePath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
28  builder.AddJsonFile(RootJson);
29  builder.AddJsonFile(DevJson);
30  var configuration = builder.Build();
31  return Options.Create(configuration.GetSection(DatabaseConfiguration.Section).Get<DatabaseConfiguration>());
32  }
33  }
34 }
+
Contains helpers for creating design time Models.DatabaseContext<TParentContext>s ...
const string Section
The key for the Microsoft.Extensions.Configuration.IConfigurationSection the DatabaseConfiguration re...
-
Configuration options for the Models.DatabaseContext<TParentContext>
diff --git a/_discord_connection_string_builder_8cs.html b/_discord_connection_string_builder_8cs.html new file mode 100644 index 0000000000..2a40612ebd --- /dev/null +++ b/_discord_connection_string_builder_8cs.html @@ -0,0 +1,120 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Api/Models/DiscordConnectionStringBuilder.cs File Reference + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+ +
+
DiscordConnectionStringBuilder.cs File Reference
+
+
+ +

Go to the source code of this file.

+ + + + + +

+Classes

class  Tgstation.Server.Api.Models.DiscordConnectionStringBuilder
 ChatConnectionStringBuilder for ChatProvider.Discord More...
 
+ + + +

+Namespaces

namespace  Tgstation.Server.Api.Models
 
+
+ + + + diff --git a/_discord_connection_string_builder_8cs_source.html b/_discord_connection_string_builder_8cs_source.html new file mode 100644 index 0000000000..7816f62480 --- /dev/null +++ b/_discord_connection_string_builder_8cs_source.html @@ -0,0 +1,114 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Api/Models/DiscordConnectionStringBuilder.cs Source File + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+
DiscordConnectionStringBuilder.cs
+
+
+Go to the documentation of this file.
1 using System;
3 
4 namespace Tgstation.Server.Api.Models
5 {
10  {
12  public override bool Valid => !String.IsNullOrEmpty(BotToken);
13 
18  public string BotToken { get; set; }
19 
24 
29  public DiscordConnectionStringBuilder(string connectionString)
30  {
31  BotToken = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
32  }
33 
35  public override string ToString() => BotToken;
36  }
37 }
+ + +
ChatConnectionStringBuilder for ChatProvider.Discord
+ + +
DiscordConnectionStringBuilder(string connectionString)
Construct a DiscordConnectionStringBuilder from a connectionString
+ +
DiscordConnectionStringBuilder()
Construct a DiscordConnectionStringBuilder
+ + +
+ + + + diff --git a/_discord_provider_8cs_source.html b/_discord_provider_8cs_source.html index de43ba0b9c..19f2fcac3f 100644 --- a/_discord_provider_8cs_source.html +++ b/_discord_provider_8cs_source.html @@ -92,10 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DiscordProvider.cs
-Go to the documentation of this file.
1 using Discord;
2 using Discord.WebSocket;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Threading;
8 using System.Threading.Tasks;
10 
11 namespace Tgstation.Server.Host.Components.Chat.Providers
12 {
16  sealed class DiscordProvider : Provider
17  {
19  public override bool Connected => client.ConnectionState == ConnectionState.Connected;
20 
22  public override string BotMention
23  {
24  get
25  {
26  if (!Connected)
27  throw new InvalidOperationException("Provider not connected");
28  return NormalizeMention(client.CurrentUser.Mention);
29  }
30  }
31 
35  readonly ILogger<DiscordProvider> logger;
36 
40  readonly DiscordSocketClient client;
41 
45  readonly string botToken;
46 
50  readonly List<ulong> mappedChannels;
51 
52  static string NormalizeMention(string fromDiscord) => fromDiscord.Replace("!", "", StringComparison.Ordinal);
53 
59  public DiscordProvider(ILogger<DiscordProvider> logger, string botToken)
60  {
61  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
62  this.botToken = botToken ?? throw new ArgumentNullException(nameof(botToken));
63  client = new DiscordSocketClient();
64  client.MessageReceived += Client_MessageReceived;
65  mappedChannels = new List<ulong>();
66  }
67 
69  public override void Dispose() => client.Dispose();
70 
76  Task Client_MessageReceived(SocketMessage e)
77  {
78  if (e.Author.Id == client.CurrentUser.Id)
79  return Task.CompletedTask;
80 
81  var pm = e.Channel is IPrivateChannel;
82 
83  if (!pm && !mappedChannels.Contains(e.Channel.Id))
84  return e.MentionedUsers.Any(x => x.Id == client.CurrentUser.Id) ? SendMessage(e.Channel.Id, "I do not respond to this channel!", default) : Task.CompletedTask;
85 
86  var result = new Message
87  {
88  Content = e.Content,
89  User = new User
90  {
91  RealId = e.Author.Id,
92  Channel = new Channel
93  {
94  RealId = e.Channel.Id,
95  IsPrivate = true,
96  ConnectionName = pm ? e.Author.Username : (e.Channel as ITextChannel)?.Guild.Name ?? "UNKNOWN",
97  FriendlyName = e.Channel.Name
98  //isAdmin and Tag populated by manager
99  },
100  FriendlyName = e.Author.Username,
101  Mention = NormalizeMention(e.Author.Mention)
102  }
103  };
104  EnqueueMessage(result);
105  return Task.CompletedTask;
106  }
107 
109  public override async Task<bool> Connect(CancellationToken cancellationToken)
110  {
111  if (Connected)
112  return true;
113 
114  try
115  {
116  await client.LoginAsync(TokenType.Bot, botToken, true).ConfigureAwait(false);
117 
118  cancellationToken.ThrowIfCancellationRequested();
119 
120  await client.StartAsync().ConfigureAwait(false);
121 
122  var channelsAvailable = new TaskCompletionSource<object>();
123  client.Ready += () =>
124  {
125  channelsAvailable.SetResult(null);
126  return Task.CompletedTask;
127  };
128  using (cancellationToken.Register(() => channelsAvailable.SetCanceled()))
129  await channelsAvailable.Task.ConfigureAwait(false);
130  }
131  catch (Exception e)
132  {
133  logger.LogWarning("Error connecting to Discord: {0}", e);
134  return false;
135  }
136 
137  return true;
138  }
139 
140  public override async Task Disconnect(CancellationToken cancellationToken)
141  {
142  if (!Connected)
143  return;
144 
145  try
146  {
147  await client.StopAsync().ConfigureAwait(false);
148  cancellationToken.ThrowIfCancellationRequested();
149  await client.LogoutAsync().ConfigureAwait(false);
150  }
151  catch (Exception e)
152  {
153  logger.LogWarning("Error disconnecting from discord: {0}", e);
154  }
155  }
156 
158  public override Task<IReadOnlyList<Channel>> MapChannels(IEnumerable<ChatChannel> channels, CancellationToken cancellationToken)
159  {
160  if (channels == null)
161  throw new ArgumentNullException(nameof(channels));
162 
163  if (!Connected)
164  throw new InvalidOperationException("Provider not connected!");
165 
166  Channel GetChannelForChatChannel(ChatChannel channel)
167  {
168  if (!channel.DiscordChannelId.HasValue)
169  throw new InvalidOperationException("ChatChannel missing DiscordChannelId!");
170 
171  if (!(client.GetChannel(channel.DiscordChannelId.Value) is ITextChannel discordChannel))
172  return null;
173 
174  return new Channel
175  {
176  RealId = discordChannel.Id,
177  IsAdmin = channel.IsAdminChannel == true,
178  ConnectionName = discordChannel.Guild.Name,
179  FriendlyName = discordChannel.Name,
180  IsPrivate = false,
181  Tag = channel.Tag
182  };
183  };
184 
185  var enumerator = channels.Select(x => GetChannelForChatChannel(x)).Where(x => x != null);
186 
187  lock (this)
188  {
189  mappedChannels.Clear();
190  mappedChannels.AddRange(enumerator.Select(x => x.RealId));
191  }
192 
193  return Task.FromResult<IReadOnlyList<Channel>>(enumerator.ToList());
194  }
195 
197  public override async Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken)
198  {
199  try
200  {
201  var channel = client.GetChannel(channelId) as IMessageChannel;
202  await (channel?.SendMessageAsync(message, false, null, new RequestOptions
203  {
204  CancelToken = cancellationToken
205  }) ?? Task.CompletedTask).ConfigureAwait(false);
206  }
207  catch (Exception e)
208  {
209  logger.LogWarning("Error sending discord message: {0}", e);
210  }
211  }
212  }
213 }
+Go to the documentation of this file.
1 using Discord;
2 using Discord.WebSocket;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Threading;
8 using System.Threading.Tasks;
10 
11 namespace Tgstation.Server.Host.Components.Chat.Providers
12 {
16  sealed class DiscordProvider : Provider
17  {
19  public override bool Connected => client.ConnectionState == ConnectionState.Connected;
20 
22  public override string BotMention
23  {
24  get
25  {
26  if (!Connected)
27  throw new InvalidOperationException("Provider not connected");
28  return NormalizeMention(client.CurrentUser.Mention);
29  }
30  }
31 
35  readonly ILogger<DiscordProvider> logger;
36 
40  readonly DiscordSocketClient client;
41 
45  readonly string botToken;
46 
50  readonly List<ulong> mappedChannels;
51 
52  static string NormalizeMention(string fromDiscord) => fromDiscord.Replace("!", "", StringComparison.Ordinal);
53 
59  public DiscordProvider(ILogger<DiscordProvider> logger, string botToken)
60  {
61  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
62  this.botToken = botToken ?? throw new ArgumentNullException(nameof(botToken));
63  client = new DiscordSocketClient();
64  client.MessageReceived += Client_MessageReceived;
65  mappedChannels = new List<ulong>();
66  }
67 
69  public override void Dispose() => client.Dispose();
70 
76  Task Client_MessageReceived(SocketMessage e)
77  {
78  if (e.Author.Id == client.CurrentUser.Id)
79  return Task.CompletedTask;
80 
81  var pm = e.Channel is IPrivateChannel;
82 
83  if (!pm && !mappedChannels.Contains(e.Channel.Id))
84  return e.MentionedUsers.Any(x => x.Id == client.CurrentUser.Id) ? SendMessage(e.Channel.Id, "I do not respond to this channel!", default) : Task.CompletedTask;
85 
86  var result = new Message
87  {
88  Content = e.Content,
89  User = new User
90  {
91  RealId = e.Author.Id,
92  Channel = new Channel
93  {
94  RealId = e.Channel.Id,
95  IsPrivate = true,
96  ConnectionName = pm ? e.Author.Username : (e.Channel as ITextChannel)?.Guild.Name ?? "UNKNOWN",
97  FriendlyName = e.Channel.Name
98  //isAdmin and Tag populated by manager
99  },
100  FriendlyName = e.Author.Username,
101  Mention = NormalizeMention(e.Author.Mention)
102  }
103  };
104  EnqueueMessage(result);
105  return Task.CompletedTask;
106  }
107 
109  public override async Task<bool> Connect(CancellationToken cancellationToken)
110  {
111  if (Connected)
112  return true;
113 
114  try
115  {
116  await client.LoginAsync(TokenType.Bot, botToken, true).ConfigureAwait(false);
117 
118  cancellationToken.ThrowIfCancellationRequested();
119 
120  await client.StartAsync().ConfigureAwait(false);
121 
122  var channelsAvailable = new TaskCompletionSource<object>();
123  client.Ready += () =>
124  {
125  channelsAvailable.SetResult(null);
126  return Task.CompletedTask;
127  };
128  using (cancellationToken.Register(() => channelsAvailable.SetCanceled()))
129  await channelsAvailable.Task.ConfigureAwait(false);
130  }
131  catch (Exception e)
132  {
133  logger.LogWarning("Error connecting to Discord: {0}", e);
134  return false;
135  }
136 
137  return true;
138  }
139 
140  public override async Task Disconnect(CancellationToken cancellationToken)
141  {
142  if (!Connected)
143  return;
144 
145  try
146  {
147  await client.StopAsync().ConfigureAwait(false);
148  cancellationToken.ThrowIfCancellationRequested();
149  await client.LogoutAsync().ConfigureAwait(false);
150  }
151  catch (Exception e)
152  {
153  logger.LogWarning("Error disconnecting from discord: {0}", e);
154  }
155  }
156 
158  public override Task<IReadOnlyList<Channel>> MapChannels(IEnumerable<ChatChannel> channels, CancellationToken cancellationToken)
159  {
160  if (channels == null)
161  throw new ArgumentNullException(nameof(channels));
162 
163  if (!Connected)
164  throw new InvalidOperationException("Provider not connected!");
165 
166  Channel GetChannelForChatChannel(ChatChannel channel)
167  {
168  if (!channel.DiscordChannelId.HasValue)
169  throw new InvalidOperationException("ChatChannel missing DiscordChannelId!");
170 
171  if (!(client.GetChannel(channel.DiscordChannelId.Value) is ITextChannel discordChannel))
172  return null;
173 
174  return new Channel
175  {
176  RealId = discordChannel.Id,
177  IsAdmin = channel.IsAdminChannel == true,
178  ConnectionName = discordChannel.Guild.Name,
179  FriendlyName = discordChannel.Name,
180  IsPrivate = false,
181  Tag = channel.Tag
182  };
183  };
184 
185  var enumerator = channels.Select(x => GetChannelForChatChannel(x)).Where(x => x != null);
186 
187  lock (this)
188  {
189  mappedChannels.Clear();
190  mappedChannels.AddRange(enumerator.Select(x => x.RealId));
191  }
192 
193  return Task.FromResult<IReadOnlyList<Channel>>(enumerator.ToList());
194  }
195 
197  public override async Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken)
198  {
199  try
200  {
201  var channel = client.GetChannel(channelId) as IMessageChannel;
202  await (channel?.SendMessageAsync(message, false, null, new RequestOptions
203  {
204  CancelToken = cancellationToken
205  }) ?? Task.CompletedTask).ConfigureAwait(false);
206  }
207  catch (Exception e)
208  {
209  logger.LogWarning("Error sending discord message: {0}", e);
210  }
211  }
212  }
213 }
Indicates a chat channel
Definition: ChatChannel.cs:8
+
override async Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken)
Send a message to the IProvider
@@ -110,7 +111,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly string botToken
The token used for connecting to discord
readonly DiscordSocketClient client
The DiscordSocketClient for the DiscordProvider
string Tag
A custom tag users can define to group channels together
Definition: ChatChannel.cs:41
-
DiscordProvider(ILogger< DiscordProvider > logger, string botToken)
Construct a DiscordProvider
Represents a Providers.IProvider channel
Definition: Channel.cs:10
string Id
Backing field for RealId. Represented as a string to avoid BYOND percision loss
Definition: Channel.cs:15
diff --git a/_dmb_provider_8cs_source.html b/_dmb_provider_8cs_source.html index 5d926328a6..86fdbe1e30 100644 --- a/_dmb_provider_8cs_source.html +++ b/_dmb_provider_8cs_source.html @@ -92,7 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DmbProvider.cs
-Go to the documentation of this file.
1 using System;
2 using Tgstation.Server.Host.IO;
4 
5 namespace Tgstation.Server.Host.Components.Compiler
6 {
8  sealed class DmbProvider : IDmbProvider
9  {
11  public string DmbName => String.Concat(CompileJob.DmeName, DreamMaker.DmbExtension);
12 
14  public string PrimaryDirectory => ioManager.ResolvePath(ioManager.ConcatPath(CompileJob.DirectoryName.ToString(), DreamMaker.ADirectoryName));
15 
17  public string SecondaryDirectory => ioManager.ResolvePath(ioManager.ConcatPath(CompileJob.DirectoryName.ToString(), DreamMaker.BDirectoryName));
18 
21 
25  public CompileJob CompileJob { get; }
26 
34  Action onDispose;
35 
42  public DmbProvider(CompileJob compileJob, IIOManager ioManager, Action onDispose)
43  {
44  CompileJob = compileJob ?? throw new ArgumentNullException(nameof(compileJob));
45  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
46  this.onDispose = onDispose ?? throw new ArgumentNullException(nameof(onDispose));
47  }
48 
50  public void Dispose() => onDispose?.Invoke();
51 
53  public void KeepAlive() => onDispose = null;
54  }
55 }
string DmeName
The .dme file used for compilation
Definition: CompileJob.cs:20
+Go to the documentation of this file.
1 using System;
2 using Tgstation.Server.Host.IO;
4 
5 namespace Tgstation.Server.Host.Components.Compiler
6 {
8  sealed class DmbProvider : IDmbProvider
9  {
11  public string DmbName => String.Concat(CompileJob.DmeName, DreamMaker.DmbExtension);
12 
14  public string PrimaryDirectory => ioManager.ResolvePath(ioManager.ConcatPath(CompileJob.DirectoryName.ToString(), DreamMaker.ADirectoryName));
15 
17  public string SecondaryDirectory => ioManager.ResolvePath(ioManager.ConcatPath(CompileJob.DirectoryName.ToString(), DreamMaker.BDirectoryName));
18 
21 
25  public CompileJob CompileJob { get; }
26 
34  Action onDispose;
35 
42  public DmbProvider(CompileJob compileJob, IIOManager ioManager, Action onDispose)
43  {
44  CompileJob = compileJob ?? throw new ArgumentNullException(nameof(compileJob));
45  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
46  this.onDispose = onDispose ?? throw new ArgumentNullException(nameof(onDispose));
47  }
48 
50  public void Dispose() => onDispose?.Invoke();
51 
53  public void KeepAlive() => onDispose = null;
54  }
55 }
string DmeName
The .dme file used for compilation
Definition: CompileJob.cs:20
+ @@ -103,7 +104,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DmbProvider(CompileJob compileJob, IIOManager ioManager, Action onDispose)
Construct a DmbProvider
Definition: DmbProvider.cs:42
Action onDispose
The Action to run when Dispose is called
Definition: DmbProvider.cs:34
const string DmbExtension
Extension for .dmbs
Definition: DreamMaker.cs:34
-
readonly IIOManager ioManager
The IIOManager for the DmbProvider
Definition: DmbProvider.cs:30
Guid DirectoryName
The Game folder the results were compiled into
Definition: CompileJob.cs:32
diff --git a/_dream_daemon_8cs_source.html b/_dream_daemon_8cs_source.html index 2f0556ba10..166eb7e593 100644 --- a/_dream_daemon_8cs_source.html +++ b/_dream_daemon_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamDaemon.cs
-Go to the documentation of this file.
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public sealed class DreamDaemon : DreamDaemonSettings
9  {
13  public CompileJob ActiveCompileJob { get; set; }
14 
18  public CompileJob StagedCompileJob { get; set; }
19 
23  public bool? Running { get; set; }
24 
28  public DreamDaemonSecurity? CurrentSecurity { get; set; }
29 
33  public ushort? CurrentPort { get; set; }
34 
38  public bool? CurrentAllowWebclient { get; set; }
39  }
40 }
+Go to the documentation of this file.
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public sealed class DreamDaemon : DreamDaemonSettings
9  {
13  public CompileJob ActiveCompileJob { get; set; }
14 
18  public CompileJob StagedCompileJob { get; set; }
19 
23  public bool? Running { get; set; }
24 
28  public DreamDaemonSecurity? CurrentSecurity { get; set; }
29 
33  public ushort? CurrentPort { get; set; }
34 
38  public bool? CurrentAllowWebclient { get; set; }
39  }
40 }
+ - - +
DreamDaemonSecurity
DreamDaemon&#39;s security level
Represents an instance of BYOND&#39;s DreamDaemon game server. Create action starts the server...
Definition: DreamDaemon.cs:8
diff --git a/_dream_daemon_client_8cs_source.html b/_dream_daemon_client_8cs_source.html index e64f31759c..186baeb483 100644 --- a/_dream_daemon_client_8cs_source.html +++ b/_dream_daemon_client_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamDaemonClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using Tgstation.Server.Api;
6 
7 namespace Tgstation.Server.Client.Components
8 {
11  {
19  readonly Instance instance;
20 
26  public DreamDaemonClient(IApiClient apiClient, Instance instance)
27  {
28  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
29  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
30  }
31 
33  public Task Shutdown(CancellationToken cancellationToken) => apiClient.Delete(Routes.DreamDaemon, instance.Id, cancellationToken);
34 
36  public Task<Job> Start(CancellationToken cancellationToken) => apiClient.Create<Job>(Routes.DreamDaemon, instance.Id, cancellationToken);
37 
39  public Task<Job> Restart(CancellationToken cancellationToken) => apiClient.Patch<Job>(Routes.DreamDaemon, instance.Id, cancellationToken);
40 
42  public Task<DreamDaemon> Read(CancellationToken cancellationToken) => apiClient.Read<DreamDaemon>(Routes.DreamDaemon, instance.Id, cancellationToken);
43 
45  public Task<DreamDaemon> Update(DreamDaemon dreamDaemon, CancellationToken cancellationToken) => apiClient.Update<DreamDaemon, DreamDaemon>(Routes.DreamDaemon, dreamDaemon ?? throw new ArgumentNullException(nameof(dreamDaemon)), instance.Id, cancellationToken);
46  }
47 }
DreamDaemonClient(IApiClient apiClient, Instance instance)
Construct a DreamDaemonClient
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using Tgstation.Server.Api;
6 
7 namespace Tgstation.Server.Client.Components
8 {
11  {
19  readonly Instance instance;
20 
26  public DreamDaemonClient(IApiClient apiClient, Instance instance)
27  {
28  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
29  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
30  }
31 
33  public Task Shutdown(CancellationToken cancellationToken) => apiClient.Delete(Routes.DreamDaemon, instance.Id, cancellationToken);
34 
36  public Task<Job> Start(CancellationToken cancellationToken) => apiClient.Create<Job>(Routes.DreamDaemon, instance.Id, cancellationToken);
37 
39  public Task<Job> Restart(CancellationToken cancellationToken) => apiClient.Patch<Job>(Routes.DreamDaemon, instance.Id, cancellationToken);
40 
42  public Task<DreamDaemon> Read(CancellationToken cancellationToken) => apiClient.Read<DreamDaemon>(Routes.DreamDaemon, instance.Id, cancellationToken);
43 
45  public Task<DreamDaemon> Update(DreamDaemon dreamDaemon, CancellationToken cancellationToken) => apiClient.Update<DreamDaemon, DreamDaemon>(Routes.DreamDaemon, dreamDaemon ?? throw new ArgumentNullException(nameof(dreamDaemon)), instance.Id, cancellationToken);
46  }
47 }
DreamDaemonClient(IApiClient apiClient, Instance instance)
Construct a DreamDaemonClient
+
Routes to a server actions
Definition: Routes.cs:9
-
const string DreamDaemon
The Models.DreamDaemon controller
Definition: Routes.cs:44
readonly Instance instance
The Instance for the DreamDaemonClient
diff --git a/_dream_daemon_controller_8cs_source.html b/_dream_daemon_controller_8cs_source.html index 5793a4085f..41242647bd 100644 --- a/_dream_daemon_controller_8cs_source.html +++ b/_dream_daemon_controller_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamDaemonController.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Linq;
6 using System.Linq.Expressions;
7 using System.Net;
8 using System.Reflection;
9 using System.Threading;
10 using System.Threading.Tasks;
11 using Tgstation.Server.Api;
19 
20 namespace Tgstation.Server.Host.Controllers
21 {
25  [Route(Routes.DreamDaemon)]
26  public sealed class DreamDaemonController : ModelController<DreamDaemon>
27  {
36 
45  public DreamDaemonController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, ILogger<DreamDaemonController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
46  {
47  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
48  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
49  }
50 
52  [TgsAuthorize(DreamDaemonRights.Start)]
53  public override async Task<IActionResult> Create([FromBody] DreamDaemon model, CancellationToken cancellationToken)
54  {
55  //alias for launching DD
56  var instance = instanceManager.GetInstance(Instance);
57 
58  if (instance.Watchdog.Running)
59  return StatusCode((int)HttpStatusCode.Gone);
60 
61  var job = new Models.Job
62  {
63  Description = "Launch DreamDaemon",
64  CancelRight = (ulong)DreamDaemonRights.Shutdown,
65  CancelRightsType = RightsType.DreamDaemon,
67  StartedBy = AuthenticationContext.User
68  };
69  await jobManager.RegisterOperation(job,
70  async (paramJob, databaseContext, progressHandler, innerCt) =>
71  {
72  var result = await instance.Watchdog.Launch(innerCt).ConfigureAwait(false);
73  if (result == null)
74  throw new JobException("Watchdog already running!");
75  },
76  cancellationToken).ConfigureAwait(false);
77  return Accepted(job.ToApi());
78  }
79 
81  [TgsAuthorize(DreamDaemonRights.ReadMetadata | DreamDaemonRights.ReadRevision)]
82  public override Task<IActionResult> Read(CancellationToken cancellationToken) => ReadImpl(null, cancellationToken);
83 
90  async Task<IActionResult> ReadImpl(DreamDaemonSettings settings, CancellationToken cancellationToken)
91  {
92  var instance = instanceManager.GetInstance(Instance);
93  var dd = instance.Watchdog;
94 
95  var metadata = (AuthenticationContext.GetRight(RightsType.DreamDaemon) & (ulong)DreamDaemonRights.ReadMetadata) != 0;
96  var revision = (AuthenticationContext.GetRight(RightsType.DreamDaemon) & (ulong)DreamDaemonRights.ReadRevision) != 0;
97 
98  if (settings == null)
99  {
100  settings = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => x.DreamDaemonSettings).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
101  if (settings == default)
102  return StatusCode((int)HttpStatusCode.Gone);
103  }
104 
105  var result = new DreamDaemon();
106  if (metadata)
107  {
108  var alphaActive = dd.AlphaIsActive;
109  var llp = dd.LastLaunchParameters;
110  var rstate = dd.RebootState;
111  result.AutoStart = settings.AutoStart;
112  result.CurrentPort = alphaActive ? llp?.PrimaryPort : llp?.SecondaryPort;
113  result.CurrentSecurity = llp?.SecurityLevel;
114  result.CurrentAllowWebclient = llp?.AllowWebClient;
115  result.PrimaryPort = settings.PrimaryPort;
116  result.AllowWebClient = settings.AllowWebClient;
117  result.Running = dd.Running;
118  result.SecondaryPort = settings.SecondaryPort;
119  result.SecurityLevel = settings.SecurityLevel;
120  result.SoftRestart = rstate == RebootState.Restart;
121  result.SoftShutdown = rstate == RebootState.Shutdown;
122  result.StartupTimeout = settings.StartupTimeout;
123  };
124 
125  if (revision)
126  {
127  var latestCompileJob = instance.LatestCompileJob();
128  result.ActiveCompileJob = (dd.ActiveCompileJob ?? latestCompileJob)?.ToApi();
129  if (latestCompileJob?.Id != result.ActiveCompileJob?.Id)
130  result.StagedCompileJob = latestCompileJob?.ToApi();
131  }
132 
133  return Json(result);
134  }
135 
137  [HttpDelete]
138  [TgsAuthorize(DreamDaemonRights.Shutdown)]
139  public async Task<IActionResult> Delete(CancellationToken cancellationToken)
140  {
141  //alias for stopping DD
142  var instance = instanceManager.GetInstance(Instance);
143  await instance.Watchdog.Terminate(false, cancellationToken).ConfigureAwait(false);
144  return Ok();
145  }
146 
148  [TgsAuthorize(DreamDaemonRights.SetAutoStart | DreamDaemonRights.SetPorts | DreamDaemonRights.SetSecurity | DreamDaemonRights.SetWebClient | DreamDaemonRights.SoftRestart | DreamDaemonRights.SoftShutdown | DreamDaemonRights.Start | DreamDaemonRights.SetStartupTimeout)]
149  public override async Task<IActionResult> Update([FromBody] DreamDaemon model, CancellationToken cancellationToken)
150  {
151  if (model == null)
152  throw new ArgumentNullException(nameof(model));
153 
154  if (model.PrimaryPort == 0)
155  return BadRequest(new ErrorMessage { Message = "Primary port cannot be 0!" });
156 
157  if (model.SecurityLevel == DreamDaemonSecurity.Ultrasafe)
158  return BadRequest(new ErrorMessage { Message = "This version of TGS does not support the ultrasafe DreamDaemon configuration!" });
159 
160  //alias for changing DD settings
161  var current = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => x.DreamDaemonSettings).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
162 
163  if (current == default)
164  return StatusCode((int)HttpStatusCode.Gone);
165 
166  var userRights = (DreamDaemonRights)AuthenticationContext.GetRight(RightsType.DreamDaemon);
167 
168  bool CheckModified<T>(Expression<Func<Api.Models.Internal.DreamDaemonSettings, T>> expression, DreamDaemonRights requiredRight)
169  {
170  var memberSelectorExpression = (MemberExpression)expression.Body;
171  var property = (PropertyInfo)memberSelectorExpression.Member;
172 
173  var newVal = property.GetValue(model);
174  if (newVal == null)
175  return false;
176  if (!userRights.HasFlag(requiredRight) && property.GetValue(current) != newVal)
177  return true;
178 
179  property.SetValue(current, newVal);
180  return false;
181  };
182 
183  var oldSoftRestart = current.SoftRestart;
184  var oldSoftShutdown = current.SoftShutdown;
185 
186  if (CheckModified(x => x.AllowWebClient, DreamDaemonRights.SetWebClient)
187  || CheckModified(x => x.AutoStart, DreamDaemonRights.SetAutoStart)
188  || CheckModified(x => x.PrimaryPort, DreamDaemonRights.SetPorts)
189  || CheckModified(x => x.SecondaryPort, DreamDaemonRights.SetPorts)
190  || CheckModified(x => x.SecurityLevel, DreamDaemonRights.SetSecurity)
191  || CheckModified(x => x.SoftRestart, DreamDaemonRights.SoftRestart)
192  || CheckModified(x => x.SoftShutdown, DreamDaemonRights.SoftShutdown)
193  || CheckModified(x => x.StartupTimeout, DreamDaemonRights.SetStartupTimeout))
194  return Forbid();
195 
196  if (current.PrimaryPort == current.SecondaryPort)
197  return BadRequest(new ErrorMessage { Message = "Primary port and secondary port cannot be the same!" });
198 
199  var wd = instanceManager.GetInstance(Instance).Watchdog;
200 
201  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
202  //run this second because current may be modified by it
203  await wd.ChangeSettings(current, cancellationToken).ConfigureAwait(false);
204 
205  if (!oldSoftRestart.Value && current.SoftRestart.Value)
206  await wd.Restart(true, cancellationToken).ConfigureAwait(false);
207  else if (!oldSoftShutdown.Value && current.SoftShutdown.Value)
208  await wd.Terminate(true, cancellationToken).ConfigureAwait(false);
209  else if ((oldSoftRestart.Value && !current.SoftRestart.Value) || (oldSoftShutdown.Value && !current.SoftShutdown.Value))
210  await wd.ResetRebootState(cancellationToken).ConfigureAwait(false);
211 
212  return await ReadImpl(current, cancellationToken).ConfigureAwait(false);
213  }
214 
220  [HttpPatch]
221  [TgsAuthorize(DreamDaemonRights.Restart)]
222  public async Task<IActionResult> Restart(CancellationToken cancellationToken)
223  {
224  var job = new Models.Job
225  {
226  Instance = Instance,
227  CancelRightsType = RightsType.DreamDaemon,
228  CancelRight = (ulong)DreamDaemonRights.Shutdown,
229  StartedBy = AuthenticationContext.User,
230  Description = "Restart Watchdog"
231  };
232 
233  var watchdog = instanceManager.GetInstance(Instance).Watchdog;
234 
235  await jobManager.RegisterOperation(job, (paramJob, databaseContext, progressReporter, ct) => watchdog.Restart(false, ct), cancellationToken).ConfigureAwait(false);
236  return Accepted(job.ToApi());
237  }
238  }
239 }
async Task< IActionResult > Delete(CancellationToken cancellationToken)
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Linq;
6 using System.Linq.Expressions;
7 using System.Net;
8 using System.Reflection;
9 using System.Threading;
10 using System.Threading.Tasks;
11 using Tgstation.Server.Api;
19 
20 namespace Tgstation.Server.Host.Controllers
21 {
25  [Route(Routes.DreamDaemon)]
26  public sealed class DreamDaemonController : ModelController<DreamDaemon>
27  {
36 
45  public DreamDaemonController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, ILogger<DreamDaemonController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
46  {
47  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
48  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
49  }
50 
52  [TgsAuthorize(DreamDaemonRights.Start)]
53  public override async Task<IActionResult> Create([FromBody] DreamDaemon model, CancellationToken cancellationToken)
54  {
55  //alias for launching DD
56  var instance = instanceManager.GetInstance(Instance);
57 
58  if (instance.Watchdog.Running)
59  return StatusCode((int)HttpStatusCode.Gone);
60 
61  var job = new Models.Job
62  {
63  Description = "Launch DreamDaemon",
64  CancelRight = (ulong)DreamDaemonRights.Shutdown,
65  CancelRightsType = RightsType.DreamDaemon,
67  StartedBy = AuthenticationContext.User
68  };
69  await jobManager.RegisterOperation(job,
70  async (paramJob, databaseContext, progressHandler, innerCt) =>
71  {
72  var result = await instance.Watchdog.Launch(innerCt).ConfigureAwait(false);
73  if (result == null)
74  throw new JobException("Watchdog already running!");
75  },
76  cancellationToken).ConfigureAwait(false);
77  return Accepted(job.ToApi());
78  }
79 
81  [TgsAuthorize(DreamDaemonRights.ReadMetadata | DreamDaemonRights.ReadRevision)]
82  public override Task<IActionResult> Read(CancellationToken cancellationToken) => ReadImpl(null, cancellationToken);
83 
90  async Task<IActionResult> ReadImpl(DreamDaemonSettings settings, CancellationToken cancellationToken)
91  {
92  var instance = instanceManager.GetInstance(Instance);
93  var dd = instance.Watchdog;
94 
95  var metadata = (AuthenticationContext.GetRight(RightsType.DreamDaemon) & (ulong)DreamDaemonRights.ReadMetadata) != 0;
96  var revision = (AuthenticationContext.GetRight(RightsType.DreamDaemon) & (ulong)DreamDaemonRights.ReadRevision) != 0;
97 
98  if (settings == null)
99  {
100  settings = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => x.DreamDaemonSettings).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
101  if (settings == default)
102  return StatusCode((int)HttpStatusCode.Gone);
103  }
104 
105  var result = new DreamDaemon();
106  if (metadata)
107  {
108  var alphaActive = dd.AlphaIsActive;
109  var llp = dd.LastLaunchParameters;
110  var rstate = dd.RebootState;
111  result.AutoStart = settings.AutoStart;
112  result.CurrentPort = alphaActive ? llp?.PrimaryPort : llp?.SecondaryPort;
113  result.CurrentSecurity = llp?.SecurityLevel;
114  result.CurrentAllowWebclient = llp?.AllowWebClient;
115  result.PrimaryPort = settings.PrimaryPort;
116  result.AllowWebClient = settings.AllowWebClient;
117  result.Running = dd.Running;
118  result.SecondaryPort = settings.SecondaryPort;
119  result.SecurityLevel = settings.SecurityLevel;
120  result.SoftRestart = rstate == RebootState.Restart;
121  result.SoftShutdown = rstate == RebootState.Shutdown;
122  result.StartupTimeout = settings.StartupTimeout;
123  };
124 
125  if (revision)
126  {
127  var latestCompileJob = instance.LatestCompileJob();
128  result.ActiveCompileJob = (dd.ActiveCompileJob ?? latestCompileJob)?.ToApi();
129  if (latestCompileJob?.Id != result.ActiveCompileJob?.Id)
130  result.StagedCompileJob = latestCompileJob?.ToApi();
131  }
132 
133  return Json(result);
134  }
135 
137  [HttpDelete]
138  [TgsAuthorize(DreamDaemonRights.Shutdown)]
139  public async Task<IActionResult> Delete(CancellationToken cancellationToken)
140  {
141  //alias for stopping DD
142  var instance = instanceManager.GetInstance(Instance);
143  await instance.Watchdog.Terminate(false, cancellationToken).ConfigureAwait(false);
144  return Ok();
145  }
146 
148  [TgsAuthorize(DreamDaemonRights.SetAutoStart | DreamDaemonRights.SetPorts | DreamDaemonRights.SetSecurity | DreamDaemonRights.SetWebClient | DreamDaemonRights.SoftRestart | DreamDaemonRights.SoftShutdown | DreamDaemonRights.Start | DreamDaemonRights.SetStartupTimeout)]
149  public override async Task<IActionResult> Update([FromBody] DreamDaemon model, CancellationToken cancellationToken)
150  {
151  if (model == null)
152  throw new ArgumentNullException(nameof(model));
153 
154  if (model.PrimaryPort == 0)
155  return BadRequest(new ErrorMessage { Message = "Primary port cannot be 0!" });
156 
157  if (model.SecurityLevel == DreamDaemonSecurity.Ultrasafe)
158  return BadRequest(new ErrorMessage { Message = "This version of TGS does not support the ultrasafe DreamDaemon configuration!" });
159 
160  //alias for changing DD settings
161  var current = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => x.DreamDaemonSettings).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
162 
163  if (current == default)
164  return StatusCode((int)HttpStatusCode.Gone);
165 
166  var userRights = (DreamDaemonRights)AuthenticationContext.GetRight(RightsType.DreamDaemon);
167 
168  bool CheckModified<T>(Expression<Func<Api.Models.Internal.DreamDaemonSettings, T>> expression, DreamDaemonRights requiredRight)
169  {
170  var memberSelectorExpression = (MemberExpression)expression.Body;
171  var property = (PropertyInfo)memberSelectorExpression.Member;
172 
173  var newVal = property.GetValue(model);
174  if (newVal == null)
175  return false;
176  if (!userRights.HasFlag(requiredRight) && property.GetValue(current) != newVal)
177  return true;
178 
179  property.SetValue(current, newVal);
180  return false;
181  };
182 
183  var oldSoftRestart = current.SoftRestart;
184  var oldSoftShutdown = current.SoftShutdown;
185 
186  if (CheckModified(x => x.AllowWebClient, DreamDaemonRights.SetWebClient)
187  || CheckModified(x => x.AutoStart, DreamDaemonRights.SetAutoStart)
188  || CheckModified(x => x.PrimaryPort, DreamDaemonRights.SetPorts)
189  || CheckModified(x => x.SecondaryPort, DreamDaemonRights.SetPorts)
190  || CheckModified(x => x.SecurityLevel, DreamDaemonRights.SetSecurity)
191  || CheckModified(x => x.SoftRestart, DreamDaemonRights.SoftRestart)
192  || CheckModified(x => x.SoftShutdown, DreamDaemonRights.SoftShutdown)
193  || CheckModified(x => x.StartupTimeout, DreamDaemonRights.SetStartupTimeout))
194  return Forbid();
195 
196  if (current.PrimaryPort == current.SecondaryPort)
197  return BadRequest(new ErrorMessage { Message = "Primary port and secondary port cannot be the same!" });
198 
199  var wd = instanceManager.GetInstance(Instance).Watchdog;
200 
201  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
202  //run this second because current may be modified by it
203  await wd.ChangeSettings(current, cancellationToken).ConfigureAwait(false);
204 
205  if (!oldSoftRestart.Value && current.SoftRestart.Value)
206  await wd.Restart(true, cancellationToken).ConfigureAwait(false);
207  else if (!oldSoftShutdown.Value && current.SoftShutdown.Value)
208  await wd.Terminate(true, cancellationToken).ConfigureAwait(false);
209  else if ((oldSoftRestart.Value && !current.SoftRestart.Value) || (oldSoftShutdown.Value && !current.SoftShutdown.Value))
210  await wd.ResetRebootState(cancellationToken).ConfigureAwait(false);
211 
212  return await ReadImpl(current, cancellationToken).ConfigureAwait(false);
213  }
214 
220  [HttpPatch]
221  [TgsAuthorize(DreamDaemonRights.Restart)]
222  public async Task<IActionResult> Restart(CancellationToken cancellationToken)
223  {
224  var job = new Models.Job
225  {
226  Instance = Instance,
227  CancelRightsType = RightsType.DreamDaemon,
228  CancelRight = (ulong)DreamDaemonRights.Shutdown,
229  StartedBy = AuthenticationContext.User,
230  Description = "Restart Watchdog"
231  };
232 
233  var watchdog = instanceManager.GetInstance(Instance).Watchdog;
234 
235  await jobManager.RegisterOperation(job, (paramJob, databaseContext, progressReporter, ct) => watchdog.Restart(false, ct), cancellationToken).ConfigureAwait(false);
236  return Accepted(job.ToApi());
237  }
238  }
239 }
async Task< IActionResult > Delete(CancellationToken cancellationToken)
Manages the runtime of Jobs
Definition: IJobManager.cs:12
@@ -102,6 +102,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RightsType
The type of rights a model uses
Definition: RightsType.cs:6
Operation exceptions thrown from the context of a Models.Job
Definition: JobException.cs:8
+ @@ -117,7 +118,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamDaemonRights
Rights for Models.DreamDaemon
override async Task< IActionResult > Update([FromBody] DreamDaemon model, CancellationToken cancellationToken)
-
DreamDaemonController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, ILogger< DreamDaemonController > logger)
Construct a DreamDaemonController
long Id
The id of the Instance. Not modifiable
Definition: Instance.cs:14
diff --git a/_dream_daemon_launch_parameters_8cs_source.html b/_dream_daemon_launch_parameters_8cs_source.html index 9f2d8a11b5..9b3e641a6d 100644 --- a/_dream_daemon_launch_parameters_8cs_source.html +++ b/_dream_daemon_launch_parameters_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamDaemonLaunchParameters.cs
-Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 
4 namespace Tgstation.Server.Api.Models.Internal
5 {
10  {
14  [Required]
15  public bool? AllowWebClient { get; set; }
16 
20  [Required]
21  public DreamDaemonSecurity? SecurityLevel { get; set; }
22 
26  [Required]
27  public ushort? PrimaryPort { get; set; }
28 
32  [Required]
33  public ushort? SecondaryPort { get; set; }
34 
38  [Required]
39  public uint? StartupTimeout { get; set; }
40 
46  public bool Match(DreamDaemonLaunchParameters otherParameters) =>
47  AllowWebClient == otherParameters.AllowWebClient
48  && SecurityLevel == otherParameters.SecurityLevel
49  && PrimaryPort == otherParameters.PrimaryPort
50  && SecondaryPort == otherParameters.SecondaryPort
51  && StartupTimeout == otherParameters.StartupTimeout;
52  }
53 }
+Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 
4 namespace Tgstation.Server.Api.Models.Internal
5 {
10  {
14  [Required]
15  public bool? AllowWebClient { get; set; }
16 
20  [Required]
21  public DreamDaemonSecurity? SecurityLevel { get; set; }
22 
26  [Required]
27  public ushort? PrimaryPort { get; set; }
28 
32  [Required]
33  public ushort? SecondaryPort { get; set; }
34 
38  [Required]
39  public uint? StartupTimeout { get; set; }
40 
46  public bool Match(DreamDaemonLaunchParameters otherParameters) =>
47  AllowWebClient == otherParameters.AllowWebClient
48  && SecurityLevel == otherParameters.SecurityLevel
49  && PrimaryPort == otherParameters.PrimaryPort
50  && SecondaryPort == otherParameters.SecondaryPort
51  && StartupTimeout == otherParameters.StartupTimeout;
52  }
53 }
+
DreamDaemonSecurity SecurityLevel
The DreamDaemonSecurity level of DreamDaemon
-
DreamDaemonSecurity
DreamDaemon&#39;s security level
ushort PrimaryPort
The first port DreamDaemon uses. This should be the publically advertised port
diff --git a/_dream_daemon_rights_8cs_source.html b/_dream_daemon_rights_8cs_source.html index a20d736f4a..e9c936794d 100644 --- a/_dream_daemon_rights_8cs_source.html +++ b/_dream_daemon_rights_8cs_source.html @@ -92,14 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamDaemonRights.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum DreamDaemonRights : ulong
10  {
14  None = 0,
18  ReadRevision = 1,
22  SetPorts = 2,
26  SetAutoStart = 4,
30  SetSecurity = 8,
34  ReadMetadata = 16,
38  SetWebClient = 32,
42  SoftRestart = 64,
46  SoftShutdown = 128,
50  Restart = 256,
54  Shutdown = 512,
58  Start = 1024,
62  SetStartupTimeout = 2048
63  }
64 }
User can enable Models.Internal.DreamDaemonSettings.SoftRestart
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum DreamDaemonRights : ulong
10  {
14  None = 0,
18  ReadRevision = 1,
22  SetPorts = 2,
26  SetAutoStart = 4,
30  SetSecurity = 8,
34  ReadMetadata = 16,
38  SetWebClient = 32,
42  SoftRestart = 64,
46  SoftShutdown = 128,
50  Restart = 256,
54  Shutdown = 512,
58  Start = 1024,
62  SetStartupTimeout = 2048
63  }
64 }
User can enable Models.Internal.DreamDaemonSettings.SoftRestart
+
User can change Models.Internal.DreamDaemonLaunchParameters.AllowWebClient
User can change both primary and secondary ports
User can change Models.Internal.DreamDaemonLaunchParameters.StartupTimeout
DreamDaemonRights
Rights for Models.DreamDaemon
-
User can read all ports, Models.Internal.DreamDaemonSettings.SoftRestart, Models.Internal.DreamDaemonSettings.SoftShutdown, Models.DreamDaemon.Running, Models.Internal.DreamDaemonLaunchParameters.AllowWebClient, and Models.Internal.DreamDaemonSettings.AutoStart
User can immediately restart Models.DreamDaemon
User can read Models.DreamDaemon.ActiveCompileJob and Models.DreamDaemon.StagedCompileJob ...
diff --git a/_dream_daemon_security_8cs_source.html b/_dream_daemon_security_8cs_source.html index 84d47abdc4..88aed34370 100644 --- a/_dream_daemon_security_8cs_source.html +++ b/_dream_daemon_security_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamDaemonSecurity.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
6  public enum DreamDaemonSecurity
7  {
11  Trusted,
15  Safe,
19  Ultrasafe
20  }
21 }
Server will not be able to run shell commands or access files outside it&#39;s working directory ...
+Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
6  public enum DreamDaemonSecurity
7  {
11  Trusted,
15  Safe,
19  Ultrasafe
20  }
21 }
+
Server will not be able to run shell commands or access files outside it&#39;s working directory ...
Server will not be able to run shell commands or access anything but temporary files. Currently unsupported!
Server is unrestricted in terms of file access and shell commands
-
DreamDaemonSecurity
DreamDaemon&#39;s security level
diff --git a/_dream_daemon_status_8cs_source.html b/_dream_daemon_status_8cs_source.html index 36b9b58cf6..f5d7b8f733 100644 --- a/_dream_daemon_status_8cs_source.html +++ b/_dream_daemon_status_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamDaemonStatus.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
6 #pragma warning disable CA1717 // Only FlagsAttribute enums should have plural names
7  public enum DreamDaemonStatus
8 #pragma warning restore CA1717 // Only FlagsAttribute enums should have plural names
9  {
13  Offline,
21  Online,
22  }
23 }
DreamDaemonStatus
The status of DreamDaemon
+Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
6 #pragma warning disable CA1717 // Only FlagsAttribute enums should have plural names
7  public enum DreamDaemonStatus
8 #pragma warning restore CA1717 // Only FlagsAttribute enums should have plural names
9  {
13  Offline,
21  Online,
22  }
23 }
+
DreamDaemonStatus
The status of DreamDaemon
- diff --git a/_dream_maker_client_8cs_source.html b/_dream_maker_client_8cs_source.html index 9eae27476c..21d886df4f 100644 --- a/_dream_maker_client_8cs_source.html +++ b/_dream_maker_client_8cs_source.html @@ -92,11 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamMakerClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client.Components
9 {
12  {
17 
21  readonly Instance instance;
22 
28  public DreamMakerClient(IApiClient apiClient, Instance instance)
29  {
30  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
31  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
32  }
33 
35  public Task<Job> Compile(CancellationToken cancellationToken) => apiClient.Create<Job>(Routes.DreamMaker, instance.Id, cancellationToken);
36 
38  public Task<CompileJob> GetCompileJob(CompileJob compileJob, CancellationToken cancellationToken) => apiClient.Read<CompileJob>(Routes.SetID(Routes.DreamMaker, compileJob?.Id ?? throw new ArgumentNullException(nameof(compileJob))), instance.Id, cancellationToken);
39 
41  public Task<IReadOnlyList<CompileJob>> GetJobIds(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<CompileJob>>(Routes.List(Routes.DreamMaker), instance.Id, cancellationToken);
42 
44  public Task<DreamMaker> Read(CancellationToken cancellationToken) => apiClient.Read<DreamMaker>(Routes.DreamMaker, instance.Id, cancellationToken);
45 
47  public Task<DreamMaker> Update(DreamMaker dreamMaker, CancellationToken cancellationToken) => apiClient.Update<DreamMaker, DreamMaker>(Routes.DreamMaker, dreamMaker ?? throw new ArgumentNullException(nameof(dreamMaker)), instance.Id, cancellationToken);
48  }
49 }
Represents the state of the DreamMaker compiler. Create action starts a new compile. Delete action cancels the current compile
Definition: DreamMaker.cs:8
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client.Components
9 {
12  {
17 
21  readonly Instance instance;
22 
28  public DreamMakerClient(IApiClient apiClient, Instance instance)
29  {
30  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
31  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
32  }
33 
35  public Task<Job> Compile(CancellationToken cancellationToken) => apiClient.Create<Job>(Routes.DreamMaker, instance.Id, cancellationToken);
36 
38  public Task<CompileJob> GetCompileJob(CompileJob compileJob, CancellationToken cancellationToken) => apiClient.Read<CompileJob>(Routes.SetID(Routes.DreamMaker, compileJob?.Id ?? throw new ArgumentNullException(nameof(compileJob))), instance.Id, cancellationToken);
39 
41  public Task<IReadOnlyList<CompileJob>> GetJobIds(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<CompileJob>>(Routes.List(Routes.DreamMaker), instance.Id, cancellationToken);
42 
44  public Task<DreamMaker> Read(CancellationToken cancellationToken) => apiClient.Read<DreamMaker>(Routes.DreamMaker, instance.Id, cancellationToken);
45 
47  public Task<DreamMaker> Update(DreamMaker dreamMaker, CancellationToken cancellationToken) => apiClient.Update<DreamMaker, DreamMaker>(Routes.DreamMaker, dreamMaker ?? throw new ArgumentNullException(nameof(dreamMaker)), instance.Id, cancellationToken);
48  }
49 }
Represents the state of the DreamMaker compiler. Create action starts a new compile. Delete action cancels the current compile
Definition: DreamMaker.cs:8
+
DreamMakerClient(IApiClient apiClient, Instance instance)
Construct a DreamMakerClient
@@ -104,7 +105,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Routes to a server actions
Definition: Routes.cs:9
readonly Instance instance
The Instance for the DreamMakerClient
const string DreamMaker
The Models.DreamMaker controller
Definition: Routes.cs:64
-
Metadata about a server instance
Definition: Instance.cs:9
static string SetID(string route, long id)
Apply an id postfix to a route
static string List(string route)
Get the /List postfix for a route
diff --git a/_dream_maker_controller_8cs_source.html b/_dream_maker_controller_8cs_source.html index 619f926f59..c06ed6677f 100644 --- a/_dream_maker_controller_8cs_source.html +++ b/_dream_maker_controller_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamMakerController.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Linq;
6 using System.Net;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Tgstation.Server.Api;
16 
17 namespace Tgstation.Server.Host.Controllers
18 {
22  [Route(Routes.DreamMaker)]
23  public sealed class DreamMakerController : ModelController<Api.Models.DreamMaker>
24  {
33 
42  public DreamMakerController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, ILogger<DreamMakerController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
43  {
44  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
45  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
46  }
47 
49  [TgsAuthorize(DreamMakerRights.Read)]
50  public override async Task<IActionResult> Read(CancellationToken cancellationToken)
51  {
52  var instance = instanceManager.GetInstance(Instance);
53  var dreamMakerSettings = await DatabaseContext.DreamMakerSettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
54  return Json(dreamMakerSettings.ToApi());
55  }
56 
58  [TgsAuthorize(DreamMakerRights.CompileJobs)]
59  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
60  {
61  var compileJob = await DatabaseContext.CompileJobs
62  .Where(x => x.Id == id && x.Job.Instance.Id == Instance.Id)
63  .Include(x => x.Job).ThenInclude(x => x.StartedBy)
64  .Include(x => x.RevisionInformation).ThenInclude(x => x.PrimaryTestMerge)
65  .Include(x => x.RevisionInformation).ThenInclude(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge)
66  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
67  if (compileJob == default)
68  return NotFound();
69  return Json(compileJob.ToApi());
70  }
71 
73  [TgsAuthorize(DreamMakerRights.CompileJobs)]
74  public override async Task<IActionResult> List(CancellationToken cancellationToken)
75  {
76  var compileJobs = await DatabaseContext.CompileJobs.Where(x => x.Job.Instance.Id == Instance.Id).OrderByDescending(x => x.Job.StoppedAt).Select(x => new Api.Models.CompileJob
77  {
78  Id = x.Id
79  }).ToListAsync(cancellationToken).ConfigureAwait(false);
80  return Json(compileJobs);
81  }
82 
84  [TgsAuthorize(DreamMakerRights.Compile)]
85  public override async Task<IActionResult> Create([FromBody] DreamMaker model, CancellationToken cancellationToken)
86  {
87  var job = new Models.Job
88  {
89  Description = "Compile active repository code",
90  StartedBy = AuthenticationContext.User,
91  CancelRightsType = RightsType.DreamMaker,
92  CancelRight = (ulong)DreamMakerRights.CancelCompile,
94  };
95  await jobManager.RegisterOperation(job, instanceManager.GetInstance(Instance).CompileProcess, cancellationToken).ConfigureAwait(false);
96  return Accepted(job.ToApi());
97  }
98 
100  [TgsAuthorize(DreamMakerRights.SetDme | DreamMakerRights.SetApiValidationPort | DreamMakerRights.SetApiValidationPort)]
101  public override async Task<IActionResult> Update([FromBody] DreamMaker model, CancellationToken cancellationToken)
102  {
103  if (model.ApiValidationPort == 0)
104  return BadRequest(new ErrorMessage { Message = "API Validation port cannot be 0!" });
105 
106  if (model.ApiValidationSecurityLevel == DreamDaemonSecurity.Ultrasafe)
107  return BadRequest(new ErrorMessage { Message = "This version of TGS does not support the ultrasafe DreamDaemon configuration!" });
108 
109  var hostModel = await DatabaseContext.DreamMakerSettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
110  if (hostModel == null)
111  return StatusCode((int)HttpStatusCode.Gone);
112 
113  if (model.ProjectName != null)
114  {
116  return Forbid();
117  if (model.ProjectName.Length == 0)
118  hostModel.ProjectName = null;
119  else
120  hostModel.ProjectName = model.ProjectName;
121  }
122 
123  if (model.ApiValidationPort.HasValue)
124  {
125  if (!AuthenticationContext.InstanceUser.DreamMakerRights.Value.HasFlag(DreamMakerRights.SetApiValidationPort))
126  return Forbid();
127  hostModel.ApiValidationPort = model.ApiValidationPort;
128  }
129 
130  if (model.ApiValidationSecurityLevel.HasValue)
131  {
132  if (!AuthenticationContext.InstanceUser.DreamMakerRights.Value.HasFlag(DreamMakerRights.SetSecurityLevel))
133  return Forbid();
134  hostModel.ApiValidationSecurityLevel = model.ApiValidationSecurityLevel;
135  }
136 
137  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
138  return await Read(cancellationToken).ConfigureAwait(false);
139  }
140  }
141 }
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Linq;
6 using System.Net;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Tgstation.Server.Api;
16 
17 namespace Tgstation.Server.Host.Controllers
18 {
22  [Route(Routes.DreamMaker)]
23  public sealed class DreamMakerController : ModelController<Api.Models.DreamMaker>
24  {
33 
42  public DreamMakerController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, ILogger<DreamMakerController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
43  {
44  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
45  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
46  }
47 
49  [TgsAuthorize(DreamMakerRights.Read)]
50  public override async Task<IActionResult> Read(CancellationToken cancellationToken)
51  {
52  var instance = instanceManager.GetInstance(Instance);
53  var dreamMakerSettings = await DatabaseContext.DreamMakerSettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
54  return Json(dreamMakerSettings.ToApi());
55  }
56 
58  [TgsAuthorize(DreamMakerRights.CompileJobs)]
59  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
60  {
61  var compileJob = await DatabaseContext.CompileJobs
62  .Where(x => x.Id == id && x.Job.Instance.Id == Instance.Id)
63  .Include(x => x.Job).ThenInclude(x => x.StartedBy)
64  .Include(x => x.RevisionInformation).ThenInclude(x => x.PrimaryTestMerge)
65  .Include(x => x.RevisionInformation).ThenInclude(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge)
66  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
67  if (compileJob == default)
68  return NotFound();
69  return Json(compileJob.ToApi());
70  }
71 
73  [TgsAuthorize(DreamMakerRights.CompileJobs)]
74  public override async Task<IActionResult> List(CancellationToken cancellationToken)
75  {
76  var compileJobs = await DatabaseContext.CompileJobs.Where(x => x.Job.Instance.Id == Instance.Id).OrderByDescending(x => x.Job.StoppedAt).Select(x => new Api.Models.CompileJob
77  {
78  Id = x.Id
79  }).ToListAsync(cancellationToken).ConfigureAwait(false);
80  return Json(compileJobs);
81  }
82 
84  [TgsAuthorize(DreamMakerRights.Compile)]
85  public override async Task<IActionResult> Create([FromBody] DreamMaker model, CancellationToken cancellationToken)
86  {
87  var job = new Models.Job
88  {
89  Description = "Compile active repository code",
90  StartedBy = AuthenticationContext.User,
91  CancelRightsType = RightsType.DreamMaker,
92  CancelRight = (ulong)DreamMakerRights.CancelCompile,
94  };
95  await jobManager.RegisterOperation(job, instanceManager.GetInstance(Instance).CompileProcess, cancellationToken).ConfigureAwait(false);
96  return Accepted(job.ToApi());
97  }
98 
100  [TgsAuthorize(DreamMakerRights.SetDme | DreamMakerRights.SetApiValidationPort | DreamMakerRights.SetApiValidationPort)]
101  public override async Task<IActionResult> Update([FromBody] DreamMaker model, CancellationToken cancellationToken)
102  {
103  if (model.ApiValidationPort == 0)
104  return BadRequest(new ErrorMessage { Message = "API Validation port cannot be 0!" });
105 
106  if (model.ApiValidationSecurityLevel == DreamDaemonSecurity.Ultrasafe)
107  return BadRequest(new ErrorMessage { Message = "This version of TGS does not support the ultrasafe DreamDaemon configuration!" });
108 
109  var hostModel = await DatabaseContext.DreamMakerSettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
110  if (hostModel == null)
111  return StatusCode((int)HttpStatusCode.Gone);
112 
113  if (model.ProjectName != null)
114  {
116  return Forbid();
117  if (model.ProjectName.Length == 0)
118  hostModel.ProjectName = null;
119  else
120  hostModel.ProjectName = model.ProjectName;
121  }
122 
123  if (model.ApiValidationPort.HasValue)
124  {
125  if (!AuthenticationContext.InstanceUser.DreamMakerRights.Value.HasFlag(DreamMakerRights.SetApiValidationPort))
126  return Forbid();
127  hostModel.ApiValidationPort = model.ApiValidationPort;
128  }
129 
130  if (model.ApiValidationSecurityLevel.HasValue)
131  {
132  if (!AuthenticationContext.InstanceUser.DreamMakerRights.Value.HasFlag(DreamMakerRights.SetSecurityLevel))
133  return Forbid();
134  hostModel.ApiValidationSecurityLevel = model.ApiValidationSecurityLevel;
135  }
136 
137  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
138  return await Read(cancellationToken).ConfigureAwait(false);
139  }
140  }
141 }
Manages the runtime of Jobs
Definition: IJobManager.cs:12
readonly IInstanceManager instanceManager
The IInstanceManager for the DreamMakerController
@@ -102,6 +102,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DbSet< DreamMakerSettings > DreamMakerSettings
+
override async Task< IActionResult > GetId(long id, CancellationToken cancellationToken)
Attempt to get a specific a TModel
@@ -113,7 +114,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Routes to a server actions
Definition: Routes.cs:9
const string DreamMaker
The Models.DreamMaker controller
Definition: Routes.cs:64
DreamMakerRights
Rights for Models.DreamMaker
-
override async Task< IActionResult > List(CancellationToken cancellationToken)
Attempt to list entries of the TModel
DreamMakerController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, ILogger< DreamMakerController > logger)
Construct a DreamMakerController
diff --git a/_dream_maker_rights_8cs_source.html b/_dream_maker_rights_8cs_source.html index b06f2f25ca..35dc8767a9 100644 --- a/_dream_maker_rights_8cs_source.html +++ b/_dream_maker_rights_8cs_source.html @@ -92,14 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamMakerRights.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum DreamMakerRights : ulong
10  {
14  None = 0,
18  Read = 1,
22  Compile = 2,
26  CancelCompile = 4,
30  SetDme = 8,
38  CompileJobs = 32,
42  SetSecurityLevel = 64
43  }
44 }
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum DreamMakerRights : ulong
10  {
14  None = 0,
18  Read = 1,
22  Compile = 2,
26  CancelCompile = 4,
30  SetDme = 8,
38  CompileJobs = 32,
42  SetSecurityLevel = 64
43  }
44 }
+
User may modify Models.DreamMaker.ApiValidationPort
User may modify Models.DreamMaker.ApiValidationSecurityLevel
User may list and read all Models.CompileJobs
DreamMakerRights
Rights for Models.DreamMaker
-
User may modify Models.DreamMaker.ProjectName
User can read all chat settings except Models.Internal.ChatBot.ConnectionString
diff --git a/_dream_maker_settings_8cs_source.html b/_dream_maker_settings_8cs_source.html index 8061b22894..16adae2631 100644 --- a/_dream_maker_settings_8cs_source.html +++ b/_dream_maker_settings_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamMakerSettings.cs
-Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Host.Models
4 {
6  public sealed class DreamMakerSettings : Api.Models.DreamMaker
7  {
11  public long Id { get; set; }
12 
16  public long InstanceId { get; set; }
17 
21  [Required]
22  public Instance Instance { get; set; }
23 
28  public Api.Models.DreamMaker ToApi() => new Api.Models.DreamMaker
29  {
30  ProjectName = ProjectName,
31  ApiValidationPort = ApiValidationPort,
32  ApiValidationSecurityLevel = ApiValidationSecurityLevel
33  };
34  }
35 }
+Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Host.Models
4 {
6  public sealed class DreamMakerSettings : Api.Models.DreamMaker
7  {
11  public long Id { get; set; }
12 
16  public long InstanceId { get; set; }
17 
21  [Required]
22  public Instance Instance { get; set; }
23 
28  public Api.Models.DreamMaker ToApi() => new Api.Models.DreamMaker
29  {
30  ProjectName = ProjectName,
31  ApiValidationPort = ApiValidationPort,
32  ApiValidationSecurityLevel = ApiValidationSecurityLevel
33  };
34  }
35 }
+
Represents an Api.Models.Instance in the database
Definition: Instance.cs:8
-
diff --git a/_error_message_8cs_source.html b/_error_message_8cs_source.html index 22dcf5673b..a38cc590bd 100644 --- a/_error_message_8cs_source.html +++ b/_error_message_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ErrorMessage.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public sealed class ErrorMessage
9  {
13  public string Message { get; set; }
14 
18  public Version SeverApiVersion { get; set; } = ApiHeaders.Version;
19  }
20 }
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public sealed class ErrorMessage
9  {
13  public string Message { get; set; }
14 
18  public Version SeverApiVersion { get; set; } = ApiHeaders.Version;
19  }
20 }
+
Represents the header that must be present for every server request
Definition: ApiHeaders.cs:16
-
static Version Version
Get the version of the Api the caller is using
Definition: ApiHeaders.cs:56
Represents an error message returned by the server
Definition: ErrorMessage.cs:8
diff --git a/_event_consumer_8cs_source.html b/_event_consumer_8cs_source.html index 6ce5f89df4..ddaff4f88b 100644 --- a/_event_consumer_8cs_source.html +++ b/_event_consumer_8cs_source.html @@ -92,15 +92,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
EventConsumer.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
7 
8 namespace Tgstation.Server.Host.Components
9 {
12  {
17 
22 
27  public EventConsumer(IConfiguration configuration)
28  {
29  this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
30  }
31 
33  public async Task<bool> HandleEvent(EventType eventType, IEnumerable<string> parameters, CancellationToken cancellationToken)
34  {
35  if (watchdog == null)
36  throw new InvalidOperationException("EventConsumer used without watchdog set!");
37 
38  if (!await configuration.HandleEvent(eventType, parameters, cancellationToken).ConfigureAwait(false))
39  return false;
40  return await watchdog.HandleEvent(eventType, parameters, cancellationToken).ConfigureAwait(false);
41  }
42 
47  public void SetWatchdog(IWatchdog watchdog)
48  {
49  if (watchdog == null)
50  throw new ArgumentNullException(nameof(watchdog));
51  if (this.watchdog != null)
52  throw new InvalidOperationException("watchdog already set!");
53  this.watchdog = watchdog;
54  }
55  }
56 }
async Task< bool > HandleEvent(EventType eventType, IEnumerable< string > parameters, CancellationToken cancellationToken)
Handle a given eventType
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
7 
8 namespace Tgstation.Server.Host.Components
9 {
12  {
17 
22 
27  public EventConsumer(IConfiguration configuration)
28  {
29  this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
30  }
31 
33  public async Task<bool> HandleEvent(EventType eventType, IEnumerable<string> parameters, CancellationToken cancellationToken)
34  {
35  if (watchdog == null)
36  throw new InvalidOperationException("EventConsumer used without watchdog set!");
37 
38  if (!await configuration.HandleEvent(eventType, parameters, cancellationToken).ConfigureAwait(false))
39  return false;
40  return await watchdog.HandleEvent(eventType, parameters, cancellationToken).ConfigureAwait(false);
41  }
42 
47  public void SetWatchdog(IWatchdog watchdog)
48  {
49  if (watchdog == null)
50  throw new ArgumentNullException(nameof(watchdog));
51  if (this.watchdog != null)
52  throw new InvalidOperationException("watchdog already set!");
53  this.watchdog = watchdog;
54  }
55  }
56 }
async Task< bool > HandleEvent(EventType eventType, IEnumerable< string > parameters, CancellationToken cancellationToken)
Handle a given eventType
Consumes EventTypes and takes the appropriate actions
+
IWatchdog watchdog
The IWatchdog for the
readonly IConfiguration configuration
The IConfiguration for the EventConsumer
-
EventConsumer(IConfiguration configuration)
Construct an IEventConsumer
For managing the Configuration directory
diff --git a/_event_notification_8cs_source.html b/_event_notification_8cs_source.html index 9d25feed9e..b56bdcca97 100644 --- a/_event_notification_8cs_source.html +++ b/_event_notification_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
EventNotification.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 
3 namespace Tgstation.Server.Host.Components.Interop
4 {
8  sealed class EventNotification
9  {
13  public EventType Type { get; set; }
14 
18  public IEnumerable<string> Parameters { get; set; }
19  }
20 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 
3 namespace Tgstation.Server.Host.Components.Interop
4 {
8  sealed class EventNotification
9  {
13  public EventType Type { get; set; }
14 
18  public IEnumerable<string> Parameters { get; set; }
19  }
20 }
+ -
diff --git a/_event_type_8cs_source.html b/_event_type_8cs_source.html index a64e9ce763..eabc8482de 100644 --- a/_event_type_8cs_source.html +++ b/_event_type_8cs_source.html @@ -92,7 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
EventType.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Components
2 {
6  public enum EventType
7  {
11  RepoResetOrigin = 0,
15  RepoCheckout = 1,
19  RepoFetch = 2,
28 
32  ByondChangeStart = 5,
36  ByondFail = 6,
44  CompileStart = 8,
48  CompileCancelled = 9,
52  CompileFailure = 10,
56  CompileComplete = 11,
57 
61  DDOtherCrash = 12,
65  DDOtherExit = 13,
66 
71 
75  RepoMergeConflict = 15,
76  }
77 }
+Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Components
2 {
6  public enum EventType
7  {
11  RepoResetOrigin = 0,
15  RepoCheckout = 1,
19  RepoFetch = 2,
28 
32  ByondChangeStart = 5,
36  ByondFail = 6,
44  CompileStart = 8,
48  CompileCancelled = 9,
52  CompileFailure = 10,
56  CompileComplete = 11,
57 
61  DDOtherCrash = 12,
65  DDOtherExit = 13,
66 
71 
75  RepoMergeConflict = 15,
76  }
77 }
+ @@ -102,7 +103,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Parameters: Absolute path to repository root
Parameters: Current version, new version
-
Parameters: Reference name, commit sha
Parameters: Game directory path, origin commit sha
Parameters: Game directory path, "1" if compile succeeded and api validation failed, "0" otherwise
diff --git a/_general_configuration_8cs_source.html b/_general_configuration_8cs_source.html index c66b21ba09..1c5655e639 100644 --- a/_general_configuration_8cs_source.html +++ b/_general_configuration_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
GeneralConfiguration.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Configuration
2 {
6  public sealed class GeneralConfiguration
7  {
11  public const string Section = "General";
12 
16  public string LogFileDirectory { get; set; }
17 
21  public string LogFileLevel { get; set; }
22 
26  public bool DisableFileLogging { get; set; }
27 
31  public uint MinimumPasswordLength { get; set; }
32 
36  public string GitHubAccessToken { get; set; }
37  }
38 }
+Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Configuration
2 {
6  public sealed class GeneralConfiguration
7  {
11  public const string Section = "General";
12 
16  public string LogFileDirectory { get; set; }
17 
21  public string LogFileLevel { get; set; }
22 
26  public bool DisableFileLogging { get; set; }
27 
31  public uint MinimumPasswordLength { get; set; }
32 
36  public string GitHubAccessToken { get; set; }
37  }
38 }
diff --git a/_git_hub_client_factory_8cs_source.html b/_git_hub_client_factory_8cs_source.html index a12789c800..e83a76be4b 100644 --- a/_git_hub_client_factory_8cs_source.html +++ b/_git_hub_client_factory_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
GitHubClientFactory.cs
-Go to the documentation of this file.
1 using System;
2 using Octokit;
3 
4 namespace Tgstation.Server.Host.Core
5 {
8  {
13 
18  public GitHubClientFactory(IApplication application)
19  {
20  this.application = application ?? throw new ArgumentNullException(nameof(application));
21  }
22 
27  GitHubClient CreateBaseClient() => new GitHubClient(new ProductHeaderValue(application.VersionPrefix, application.Version.ToString()));
28 
30  public IGitHubClient CreateClient() => CreateBaseClient();
31 
33  public IGitHubClient CreateClient(string accessToken)
34  {
35  if (accessToken == null)
36  throw new ArgumentNullException(nameof(accessToken));
37  var result = CreateBaseClient();
38  result.Credentials = new Credentials(accessToken);
39  return result;
40  }
41  }
42 }
IGitHubClient CreateClient(string accessToken)
Create a client with authentication using a personal access token
+Go to the documentation of this file.
1 using System;
2 using Octokit;
3 
4 namespace Tgstation.Server.Host.Core
5 {
8  {
13 
18  public GitHubClientFactory(IApplication application)
19  {
20  this.application = application ?? throw new ArgumentNullException(nameof(application));
21  }
22 
27  GitHubClient CreateBaseClient() => new GitHubClient(new ProductHeaderValue(application.VersionPrefix, application.Version.ToString()));
28 
30  public IGitHubClient CreateClient() => CreateBaseClient();
31 
33  public IGitHubClient CreateClient(string accessToken)
34  {
35  if (accessToken == null)
36  throw new ArgumentNullException(nameof(accessToken));
37  var result = CreateBaseClient();
38  result.Credentials = new Credentials(accessToken);
39  return result;
40  }
41  }
42 }
IGitHubClient CreateClient(string accessToken)
Create a client with authentication using a personal access token
Configures the ASP.NET Core web application
Definition: IApplication.cs:8
+
GitHubClientFactory(IApplication application)
Construct a GitHubClientFactory
-
readonly IApplication application
The IApplication for the GitHubClientFactory
diff --git a/_home_controller_8cs_source.html b/_home_controller_8cs_source.html index 7b032846f0..ee4d036bcc 100644 --- a/_home_controller_8cs_source.html +++ b/_home_controller_8cs_source.html @@ -92,11 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
HomeController.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Linq;
6 using System.Net.Http.Headers;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Tgstation.Server.Api;
13 
14 namespace Tgstation.Server.Host.Controllers
15 {
19  [Route(Routes.Root)]
20  public sealed class HomeController : ApiController
21  {
42 
54  public HomeController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ITokenFactory tokenFactory, ISystemIdentityFactory systemIdentityFactory, ICryptographySuite cryptographySuite, IApplication application, IIdentityCache identityCache, ILogger<HomeController> logger) : base(databaseContext, authenticationContextFactory, logger, false)
55  {
56  this.tokenFactory = tokenFactory ?? throw new ArgumentNullException(nameof(tokenFactory));
57  this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory));
58  this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
59  this.application = application ?? throw new ArgumentNullException(nameof(application));
60  this.identityCache = identityCache ?? throw new ArgumentNullException(nameof(identityCache));
61  }
62 
67  [TgsAuthorize]
68  [HttpGet]
69  public JsonResult Home() => Json(new Api.Models.ServerInformation
70  {
71  Version = application.Version,
72  ApiVersion = ApiHeaders.Version
73  });
74 
80  [HttpPost]
81  public async Task<IActionResult> CreateToken(CancellationToken cancellationToken)
82  {
84  return BadRequest(new Api.Models.ErrorMessage { Message = "Cannot create a token using another token!" });
85 
86  ISystemIdentity identity;
87  try
88  {
89  //trust the system over the database because a user's name can change while still having the same SID
90  identity = await systemIdentityFactory.CreateSystemIdentity(ApiHeaders.Username, ApiHeaders.Password, cancellationToken).ConfigureAwait(false);
91  }
92  catch (NotImplementedException)
93  {
94  identity = null;
95  }
96  using (identity)
97  {
98  IQueryable<User> query;
99  if (identity == null)
100  query = DatabaseContext.Users.Where(x => x.CanonicalName == ApiHeaders.Username.ToUpperInvariant());
101  else
102  query = DatabaseContext.Users.Where(x => x.SystemIdentifier == identity.Uid);
103  var user = await query.Select(x => new User
104  {
105  Id = x.Id,
106  PasswordHash = x.PasswordHash,
107  Enabled = x.Enabled,
108  Name = x.Name
109  }).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
110 
111  if (user == null)
112  return Unauthorized();
113 
114  if (identity == null)
115  {
116  var originalHash = user.PasswordHash;
117  if (!cryptographySuite.CheckUserPassword(user, ApiHeaders.Password))
118  return Unauthorized();
119  if (user.PasswordHash != originalHash)
120  {
121  var updatedUser = new User
122  {
123  Id = user.Id
124  };
125  DatabaseContext.Users.Attach(updatedUser);
126  updatedUser.PasswordHash = user.PasswordHash;
127  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
128  }
129  }
130  //check if the name changed and updoot accordingly
131  else if (identity.Username != user.Name)
132  {
133  DatabaseContext.Users.Attach(user);
134  user.Name = identity.Username;
135  user.CanonicalName = user.Name.ToUpperInvariant();
136  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
137  }
138 
139  if (!user.Enabled.Value)
140  return Forbid();
141 
142  var token = tokenFactory.CreateToken(user);
143  if (identity != null)
144  identityCache.CacheSystemIdentity(user, identity, token.ExpiresAt.Value.AddMinutes(1)); //expire the identity slightly after the auth token in case of lag
145 
146  Logger.LogDebug("Successfully logged in user {0}!", user.Id);
147 
148  return Json(token);
149  }
150  }
151  }
152 }
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Linq;
6 using System.Net.Http.Headers;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Tgstation.Server.Api;
13 
14 namespace Tgstation.Server.Host.Controllers
15 {
19  [Route(Routes.Root)]
20  public sealed class HomeController : ApiController
21  {
42 
54  public HomeController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ITokenFactory tokenFactory, ISystemIdentityFactory systemIdentityFactory, ICryptographySuite cryptographySuite, IApplication application, IIdentityCache identityCache, ILogger<HomeController> logger) : base(databaseContext, authenticationContextFactory, logger, false)
55  {
56  this.tokenFactory = tokenFactory ?? throw new ArgumentNullException(nameof(tokenFactory));
57  this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory));
58  this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
59  this.application = application ?? throw new ArgumentNullException(nameof(application));
60  this.identityCache = identityCache ?? throw new ArgumentNullException(nameof(identityCache));
61  }
62 
67  [TgsAuthorize]
68  [HttpGet]
69  public JsonResult Home() => Json(new Api.Models.ServerInformation
70  {
71  Version = application.Version,
72  ApiVersion = ApiHeaders.Version
73  });
74 
80  [HttpPost]
81  public async Task<IActionResult> CreateToken(CancellationToken cancellationToken)
82  {
84  return BadRequest(new Api.Models.ErrorMessage { Message = "Cannot create a token using another token!" });
85 
86  ISystemIdentity identity;
87  try
88  {
89  //trust the system over the database because a user's name can change while still having the same SID
90  identity = await systemIdentityFactory.CreateSystemIdentity(ApiHeaders.Username, ApiHeaders.Password, cancellationToken).ConfigureAwait(false);
91  }
92  catch (NotImplementedException)
93  {
94  identity = null;
95  }
96  using (identity)
97  {
98  IQueryable<User> query;
99  if (identity == null)
100  query = DatabaseContext.Users.Where(x => x.CanonicalName == ApiHeaders.Username.ToUpperInvariant());
101  else
102  query = DatabaseContext.Users.Where(x => x.SystemIdentifier == identity.Uid);
103  var user = await query.Select(x => new User
104  {
105  Id = x.Id,
106  PasswordHash = x.PasswordHash,
107  Enabled = x.Enabled,
108  Name = x.Name
109  }).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
110 
111  if (user == null)
112  return Unauthorized();
113 
114  if (identity == null)
115  {
116  var originalHash = user.PasswordHash;
117  if (!cryptographySuite.CheckUserPassword(user, ApiHeaders.Password))
118  return Unauthorized();
119  if (user.PasswordHash != originalHash)
120  {
121  var updatedUser = new User
122  {
123  Id = user.Id
124  };
125  DatabaseContext.Users.Attach(updatedUser);
126  updatedUser.PasswordHash = user.PasswordHash;
127  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
128  }
129  }
130  //check if the name changed and updoot accordingly
131  else if (identity.Username != user.Name)
132  {
133  DatabaseContext.Users.Attach(user);
134  user.Name = identity.Username;
135  user.CanonicalName = user.Name.ToUpperInvariant();
136  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
137  }
138 
139  if (!user.Enabled.Value)
140  return Forbid();
141 
142  var token = tokenFactory.CreateToken(user);
143  if (identity != null)
144  identityCache.CacheSystemIdentity(user, identity, token.ExpiresAt.Value.AddMinutes(1)); //expire the identity slightly after the auth token in case of lag
145 
146  Logger.LogDebug("Successfully logged in user {0}!", user.Id);
147 
148  return Json(token);
149  }
150  }
151  }
152 }
async Task< IActionResult > CreateToken(CancellationToken cancellationToken)
Attempt to authenticate a User using ApiController.ApiHeaders
Configures the ASP.NET Core web application
Definition: IApplication.cs:8
+
Represents a user on the current System.Runtime.InteropServices.OSPlatform
@@ -116,7 +117,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly IIdentityCache identityCache
The IIdentityCache for the HomeController
bool IsTokenAuthentication
If the header uses password or JWT authentication
Definition: ApiHeaders.cs:91
-
const string Root
The root controller
Definition: Routes.cs:14
diff --git a/_http_client_8cs_source.html b/_http_client_8cs_source.html index 30f5f89d9d..11a6b85194 100644 --- a/_http_client_8cs_source.html +++ b/_http_client_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
HttpClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Net.Http;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client
7 {
9  sealed class HttpClient : IHttpClient
10  {
12  public TimeSpan Timeout
13  {
14  get => httpClient.Timeout;
15  set => httpClient.Timeout = value;
16  }
17 
21  readonly System.Net.Http.HttpClient httpClient;
22 
26  public HttpClient()
27  {
28  httpClient = new System.Net.Http.HttpClient();
29  }
30 
32  public void Dispose() => httpClient.Dispose();
33 
35  public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => httpClient.SendAsync(request, cancellationToken);
36  }
37 }
TimeSpan Timeout
The request timeout
Definition: IHttpClient.cs:16
+Go to the documentation of this file.
1 using System;
2 using System.Net.Http;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client
7 {
9  sealed class HttpClient : IHttpClient
10  {
12  public TimeSpan Timeout
13  {
14  get => httpClient.Timeout;
15  set => httpClient.Timeout = value;
16  }
17 
21  readonly System.Net.Http.HttpClient httpClient;
22 
26  public HttpClient()
27  {
28  httpClient = new System.Net.Http.HttpClient();
29  }
30 
32  public void Dispose() => httpClient.Dispose();
33 
35  public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => httpClient.SendAsync(request, cancellationToken);
36  }
37 }
TimeSpan Timeout
The request timeout
Definition: IHttpClient.cs:16
+
readonly System.Net.Http.HttpClient httpClient
The real System.Net.Http.HttpClient
Definition: HttpClient.cs:21
For sending HTTP requests
Definition: IHttpClient.cs:11
-
HttpClient()
Construct an HttpClient
Definition: HttpClient.cs:26
diff --git a/_i_administration_client_8cs_source.html b/_i_administration_client_8cs_source.html index 1142f863eb..11371786a6 100644 --- a/_i_administration_client_8cs_source.html +++ b/_i_administration_client_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IAdministrationClient.cs
-Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Client
6 {
10  public interface IAdministrationClient
11  {
17  Task<Administration> Read(CancellationToken cancellationToken);
18 
25  Task Update(Administration administration, CancellationToken cancellationToken);
26 
32  Task Restart(CancellationToken cancellationToken);
33  }
34 }
+Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Client
6 {
10  public interface IAdministrationClient
11  {
17  Task<Administration> Read(CancellationToken cancellationToken);
18 
25  Task Update(Administration administration, CancellationToken cancellationToken);
26 
32  Task Restart(CancellationToken cancellationToken);
33  }
34 }
+ -
diff --git a/_i_api_client_8cs_source.html b/_i_api_client_8cs_source.html index 58d8294208..b8e3564f45 100644 --- a/_i_api_client_8cs_source.html +++ b/_i_api_client_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IApiClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using Tgstation.Server.Api;
5 
6 namespace Tgstation.Server.Client
7 {
11  interface IApiClient : IDisposable
12  {
13  ApiHeaders Headers { get; }
14 
15  Uri Url { get; }
16 
17  TimeSpan Timeout { get; set; }
18 
23  void AddRequestLogger(IRequestLogger requestLogger);
24 
25  Task<TResult> Create<TBody, TResult>(string route, TBody body, CancellationToken cancellationToken);
26  Task<TResult> Create<TResult>(string route, CancellationToken cancellationToken);
27  Task<TResult> Read<TResult>(string route, CancellationToken cancellationToken);
28  Task<TResult> Update<TBody, TResult>(string route, TBody body, CancellationToken cancellationToken);
29  Task<TResult> Update<TResult>(string route, CancellationToken cancellationToken);
30  Task Update<TBody>(string route, TBody body, CancellationToken cancellationToken);
31  Task Delete(string route, CancellationToken cancellationToken);
32 
33  Task<TResult> Create<TBody, TResult>(string route, TBody body, long instanceId, CancellationToken cancellationToken);
34  Task<TResult> Create<TResult>(string route, long instanceId, CancellationToken cancellationToken);
35  Task<TResult> Patch<TResult>(string route, long instanceId, CancellationToken cancellationToken);
36  Task<TResult> Read<TResult>(string route, long instanceId, CancellationToken cancellationToken);
37  Task<TResult> Update<TBody, TResult>(string route, TBody body, long instanceId, CancellationToken cancellationToken);
38  Task Delete(string route, long instanceId, CancellationToken cancellationToken);
39  Task Delete<TBody>(string route, TBody body, long instanceId, CancellationToken cancellationToken);
40  Task<TResult> Delete<TResult>(string route, long instanceId, CancellationToken cancellationToken);
41  }
42 }
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using Tgstation.Server.Api;
5 
6 namespace Tgstation.Server.Client
7 {
11  interface IApiClient : IDisposable
12  {
13  ApiHeaders Headers { get; }
14 
15  Uri Url { get; }
16 
17  TimeSpan Timeout { get; set; }
18 
23  void AddRequestLogger(IRequestLogger requestLogger);
24 
25  Task<TResult> Create<TBody, TResult>(string route, TBody body, CancellationToken cancellationToken);
26  Task<TResult> Create<TResult>(string route, CancellationToken cancellationToken);
27  Task<TResult> Read<TResult>(string route, CancellationToken cancellationToken);
28  Task<TResult> Update<TBody, TResult>(string route, TBody body, CancellationToken cancellationToken);
29  Task<TResult> Update<TResult>(string route, CancellationToken cancellationToken);
30  Task Update<TBody>(string route, TBody body, CancellationToken cancellationToken);
31  Task Delete(string route, CancellationToken cancellationToken);
32 
33  Task<TResult> Create<TBody, TResult>(string route, TBody body, long instanceId, CancellationToken cancellationToken);
34  Task<TResult> Create<TResult>(string route, long instanceId, CancellationToken cancellationToken);
35  Task<TResult> Patch<TResult>(string route, long instanceId, CancellationToken cancellationToken);
36  Task<TResult> Read<TResult>(string route, long instanceId, CancellationToken cancellationToken);
37  Task<TResult> Update<TBody, TResult>(string route, TBody body, long instanceId, CancellationToken cancellationToken);
38  Task Delete(string route, long instanceId, CancellationToken cancellationToken);
39  Task Delete<TBody>(string route, TBody body, long instanceId, CancellationToken cancellationToken);
40  Task<TResult> Delete<TResult>(string route, long instanceId, CancellationToken cancellationToken);
41  }
42 }
+
Represents the header that must be present for every server request
Definition: ApiHeaders.cs:16
-
For logging HTTP requests and responses
diff --git a/_i_api_client_factory_8cs_source.html b/_i_api_client_factory_8cs_source.html index 695b9dc0dd..6316ae978e 100644 --- a/_i_api_client_factory_8cs_source.html +++ b/_i_api_client_factory_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IApiClientFactory.cs
-Go to the documentation of this file.
1 using System;
2 using Tgstation.Server.Api;
3 
4 namespace Tgstation.Server.Client
5 {
9  interface IApiClientFactory
10  {
17  IApiClient CreateApiClient(Uri url, ApiHeaders apiHeaders);
18  }
19 }
+Go to the documentation of this file.
1 using System;
2 using Tgstation.Server.Api;
3 
4 namespace Tgstation.Server.Client
5 {
9  interface IApiClientFactory
10  {
17  IApiClient CreateApiClient(Uri url, ApiHeaders apiHeaders);
18  }
19 }
+
Represents the header that must be present for every server request
Definition: ApiHeaders.cs:16
-
Web interface for the API
Definition: IApiClient.cs:11
diff --git a/_i_application_8cs_source.html b/_i_application_8cs_source.html index 841e23ee50..d37ba421e2 100644 --- a/_i_application_8cs_source.html +++ b/_i_application_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IApplication.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Host.Core
4 {
8  public interface IApplication
9  {
13  string VersionPrefix { get; }
14 
18  string VersionString { get; }
19 
23  Version Version { get; }
24 
29  void Ready(Exception initializationError);
30  }
31 }
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Host.Core
4 {
8  public interface IApplication
9  {
13  string VersionPrefix { get; }
14 
18  string VersionString { get; }
19 
23  Version Version { get; }
24 
29  void Ready(Exception initializationError);
30  }
31 }
Configures the ASP.NET Core web application
Definition: IApplication.cs:8
+ -
diff --git a/_i_authentication_context_8cs_source.html b/_i_authentication_context_8cs_source.html index 8fd0725d44..f7ce8336fd 100644 --- a/_i_authentication_context_8cs_source.html +++ b/_i_authentication_context_8cs_source.html @@ -92,15 +92,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IAuthenticationContext.cs
-Go to the documentation of this file.
1 using System;
4 
5 namespace Tgstation.Server.Host.Security
6 {
11  {
15  User User { get; }
16 
21 
27  ulong GetRight(RightsType rightsType);
28 
32  ISystemIdentity SystemIdentity { get; }
33 
38  IAuthenticationContext Clone();
39  }
40 }
RightsType
The type of rights a model uses
Definition: RightsType.cs:6
+Go to the documentation of this file.
1 using System;
4 
5 namespace Tgstation.Server.Host.Security
6 {
11  {
15  User User { get; }
16 
21 
27  ulong GetRight(RightsType rightsType);
28 
32  ISystemIdentity SystemIdentity { get; }
33 
38  IAuthenticationContext Clone();
39  }
40 }
RightsType
The type of rights a model uses
Definition: RightsType.cs:6
+
Represents a user on the current System.Runtime.InteropServices.OSPlatform
Represents the currently authenticated Api.Models.User
- diff --git a/_i_authentication_context_factory_8cs_source.html b/_i_authentication_context_factory_8cs_source.html index 1ca2372309..3d25384619 100644 --- a/_i_authentication_context_factory_8cs_source.html +++ b/_i_authentication_context_factory_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IAuthenticationContextFactory.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Security
6 {
11  {
15  IAuthenticationContext CurrentAuthenticationContext { get; }
16 
25  Task CreateAuthenticationContext(long userId, long? instanceId, DateTimeOffset validBefore, CancellationToken cancellationToken);
26  }
27 }
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Security
6 {
11  {
15  IAuthenticationContext CurrentAuthenticationContext { get; }
16 
25  Task CreateAuthenticationContext(long userId, long? instanceId, DateTimeOffset validBefore, CancellationToken cancellationToken);
26  }
27 }
+
Represents the currently authenticated Api.Models.User
-
diff --git a/_i_byond_client_8cs_source.html b/_i_byond_client_8cs_source.html index 41c9a3ce09..60b69d9fe3 100644 --- a/_i_byond_client_8cs_source.html +++ b/_i_byond_client_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IByondClient.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client.Components
7 {
11  public interface IByondClient
12  {
18  Task<Byond> ActiveVersion(CancellationToken cancellationToken);
19 
25  Task<IReadOnlyList<Byond>> InstalledVersions(CancellationToken cancellationToken);
26 
33  Task<Byond> SetActiveVersion(Byond byond, CancellationToken cancellationToken);
34  }
35 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client.Components
7 {
11  public interface IByondClient
12  {
18  Task<Byond> ActiveVersion(CancellationToken cancellationToken);
19 
25  Task<IReadOnlyList<Byond>> InstalledVersions(CancellationToken cancellationToken);
26 
33  Task<Byond> SetActiveVersion(Byond byond, CancellationToken cancellationToken);
34  }
35 }
+
For managing the Byond installation
Definition: IByondClient.cs:11
-
Represents a BYOND installation
Definition: Byond.cs:8
diff --git a/_i_byond_executable_lock_8cs_source.html b/_i_byond_executable_lock_8cs_source.html index c7237c2719..1fdb8f08f5 100644 --- a/_i_byond_executable_lock_8cs_source.html +++ b/_i_byond_executable_lock_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IByondExecutableLock.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Host.Components.Byond
4 {
8  public interface IByondExecutableLock : IDisposable
9  {
13  Version Version { get; }
14 
18  string DreamDaemonPath { get; }
19 
23  string DreamMakerPath { get; }
24 
28  void DoNotDeleteThisSession();
29  }
30 }
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Host.Components.Byond
4 {
8  public interface IByondExecutableLock : IDisposable
9  {
13  Version Version { get; }
14 
18  string DreamDaemonPath { get; }
19 
23  string DreamMakerPath { get; }
24 
28  void DoNotDeleteThisSession();
29  }
30 }
+ -
Represents usage of the two primary BYOND server executables
diff --git a/_i_byond_installer_8cs_source.html b/_i_byond_installer_8cs_source.html index 9537d08f86..a3be4730c0 100644 --- a/_i_byond_installer_8cs_source.html +++ b/_i_byond_installer_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IByondInstaller.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Components.Byond
6 {
10  interface IByondInstaller
11  {
15  string DreamDaemonName { get; }
16 
20  string DreamMakerName { get; }
21 
28  Task<byte[]> DownloadVersion(Version version, CancellationToken cancellationToken);
29 
37  Task InstallByond(string path, Version version, CancellationToken cancellationToken);
38 
44  Task CleanCache(CancellationToken cancellationToken);
45  }
46 }
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Components.Byond
6 {
10  interface IByondInstaller
11  {
15  string DreamDaemonName { get; }
16 
20  string DreamMakerName { get; }
21 
28  Task<byte[]> DownloadVersion(Version version, CancellationToken cancellationToken);
29 
37  Task InstallByond(string path, Version version, CancellationToken cancellationToken);
38 
44  Task CleanCache(CancellationToken cancellationToken);
45  }
46 }
+
For downloading and installing BYOND extractions for a given system
-
diff --git a/_i_byond_manager_8cs_source.html b/_i_byond_manager_8cs_source.html index f1df8b5f10..e800377ebb 100644 --- a/_i_byond_manager_8cs_source.html +++ b/_i_byond_manager_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IByondManager.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Collections.Generic;
4 using System.Threading;
5 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Components.Byond
8 {
13  {
17  Version ActiveVersion { get; }
18 
22  IReadOnlyList<Version> InstalledVersions { get; }
23 
29  Task ChangeVersion(Version version, CancellationToken cancellationToken);
30 
37  Task<IByondExecutableLock> UseExecutables(Version requiredVersion, CancellationToken cancellationToken);
38  }
39 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Collections.Generic;
4 using System.Threading;
5 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Components.Byond
8 {
13  {
17  Version ActiveVersion { get; }
18 
22  IReadOnlyList<Version> InstalledVersions { get; }
23 
29  Task ChangeVersion(Version version, CancellationToken cancellationToken);
30 
37  Task<IByondExecutableLock> UseExecutables(Version requiredVersion, CancellationToken cancellationToken);
38  }
39 }
+ -
For managing the BYOND installation
diff --git a/_i_chat_8cs_source.html b/_i_chat_8cs_source.html index dfa2264dab..f23dda6d79 100644 --- a/_i_chat_8cs_source.html +++ b/_i_chat_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IChat.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Collections.Generic;
4 using System.Threading;
5 using System.Threading.Tasks;
7 
8 namespace Tgstation.Server.Host.Components.Chat
9 {
13  public interface IChat : IHostedService, IDisposable
14  {
20  bool Connected(long connectionId);
21 
26  void RegisterCommandHandler(ICustomCommandHandler customCommandHandler);
27 
34  Task ChangeSettings(ChatBot newSettings, CancellationToken cancellationToken);
35 
42  Task DeleteConnection(long connectionId, CancellationToken cancellationToken);
43 
51  Task ChangeChannels(long connectionId, IEnumerable<Api.Models.ChatChannel> newChannels, CancellationToken cancellationToken);
52 
60  Task SendMessage(string message, IEnumerable<ulong> channelIds, CancellationToken cancellationToken);
61 
68  Task SendWatchdogMessage(string message, CancellationToken cancellationToken);
69 
76  Task SendUpdateMessage(string message, CancellationToken cancellationToken);
77 
84  Task SendBroadcast(string message, CancellationToken cancellationToken);
85 
94  Task<IJsonTrackingContext> TrackJsons(string basePath, string channelsJsonName, string commandsJsonName, CancellationToken cancellationToken);
95  }
96 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Collections.Generic;
4 using System.Threading;
5 using System.Threading.Tasks;
7 
8 namespace Tgstation.Server.Host.Components.Chat
9 {
13  public interface IChat : IHostedService, IDisposable
14  {
20  bool Connected(long connectionId);
21 
26  void RegisterCommandHandler(ICustomCommandHandler customCommandHandler);
27 
34  Task ChangeSettings(ChatBot newSettings, CancellationToken cancellationToken);
35 
42  Task DeleteConnection(long connectionId, CancellationToken cancellationToken);
43 
51  Task ChangeChannels(long connectionId, IEnumerable<Api.Models.ChatChannel> newChannels, CancellationToken cancellationToken);
52 
60  Task SendMessage(string message, IEnumerable<ulong> channelIds, CancellationToken cancellationToken);
61 
68  Task SendWatchdogMessage(string message, CancellationToken cancellationToken);
69 
76  Task SendUpdateMessage(string message, CancellationToken cancellationToken);
77 
84  Task SendBroadcast(string message, CancellationToken cancellationToken);
85 
94  Task<IJsonTrackingContext> TrackJsons(string basePath, string channelsJsonName, string commandsJsonName, CancellationToken cancellationToken);
95  }
96 }
+ -
Manage the server chat bots
Definition: ChatBot.cs:8
+
Manage the server chat bots
Definition: ChatBot.cs:10
For managing connected chat services
Definition: IChat.cs:13
- - +
Handles Commands.ICommands that map to those defined in a IJsonTrackingContext
diff --git a/_i_chat_bots_client_8cs_source.html b/_i_chat_bots_client_8cs_source.html index 9c63ec3451..8e25f71807 100644 --- a/_i_chat_bots_client_8cs_source.html +++ b/_i_chat_bots_client_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IChatBotsClient.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client.Components
7 {
11  public interface IChatBotsClient
12  {
18  Task<IReadOnlyList<ChatBot>> List(CancellationToken cancellationToken);
19 
26  Task<ChatBot> Create(ChatBot settings, CancellationToken cancellationToken);
27 
34  Task<ChatBot> Update(ChatBot settings, CancellationToken cancellationToken);
35 
42  Task<ChatBot> GetId(ChatBot settings, CancellationToken cancellationToken);
43 
50  Task Delete(ChatBot settings, CancellationToken cancellationToken);
51  }
52 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client.Components
7 {
11  public interface IChatBotsClient
12  {
18  Task<IReadOnlyList<ChatBot>> List(CancellationToken cancellationToken);
19 
26  Task<ChatBot> Create(ChatBot settings, CancellationToken cancellationToken);
27 
34  Task<ChatBot> Update(ChatBot settings, CancellationToken cancellationToken);
35 
42  Task<ChatBot> GetId(ChatBot settings, CancellationToken cancellationToken);
43 
50  Task Delete(ChatBot settings, CancellationToken cancellationToken);
51  }
52 }
+ -
diff --git a/_i_chat_factory_8cs_source.html b/_i_chat_factory_8cs_source.html index 4b1832c278..e3de265866 100644 --- a/_i_chat_factory_8cs_source.html +++ b/_i_chat_factory_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IChatFactory.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 
3 namespace Tgstation.Server.Host.Components.Chat
4 {
8  interface IChatFactory
9  {
15  IChat CreateChat(IEnumerable<Models.ChatBot> initialChatBots);
16  }
17 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 
3 namespace Tgstation.Server.Host.Components.Chat
4 {
8  interface IChatFactory
9  {
15  IChat CreateChat(IEnumerable<Models.ChatBot> initialChatBots);
16  }
17 }
+
For managing connected chat services
Definition: IChat.cs:13
-
diff --git a/_i_claims_injector_8cs_source.html b/_i_claims_injector_8cs_source.html index fcfd671182..141dacf5c3 100644 --- a/_i_claims_injector_8cs_source.html +++ b/_i_claims_injector_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IClaimsInjector.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Authentication.JwtBearer;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Security
6 {
10  interface IClaimsInjector
11  {
18  Task InjectClaimsIntoContext(TokenValidatedContext tokenValidatedContext, CancellationToken cancellationToken);
19  }
20 }
- +Go to the documentation of this file.
1 using Microsoft.AspNetCore.Authentication.JwtBearer;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Security
6 {
10  interface IClaimsInjector
11  {
18  Task InjectClaimsIntoContext(TokenValidatedContext tokenValidatedContext, CancellationToken cancellationToken);
19  }
20 }
+
For injecting System.Security.Claims.Claims that Controllers.TgsAuthorizeAttribute can look for ...
diff --git a/_i_comm_context_8cs_source.html b/_i_comm_context_8cs_source.html index 5a37836846..c0d0e2fb82 100644 --- a/_i_comm_context_8cs_source.html +++ b/_i_comm_context_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ICommContext.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Host.Components.Interop
4 {
9  {
14  void RegisterHandler(ICommHandler handler);
15  }
16 }
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Host.Components.Interop
4 {
9  {
14  void RegisterHandler(ICommHandler handler);
15  }
16 }
+
Represents a registration of an interop session
Definition: ICommContext.cs:8
- diff --git a/_i_comm_handler_8cs_source.html b/_i_comm_handler_8cs_source.html index bb076892fd..179d2f9998 100644 --- a/_i_comm_handler_8cs_source.html +++ b/_i_comm_handler_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ICommHandler.cs
-Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.Components.Interop
5 {
9  interface ICommHandler
10  {
17  Task HandleInterop(CommCommand command, CancellationToken cancellationToken);
18  }
19 }
+Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.Components.Interop
5 {
9  interface ICommHandler
10  {
17  Task HandleInterop(CommCommand command, CancellationToken cancellationToken);
18  }
19 }
+ -
diff --git a/_i_command_8cs_source.html b/_i_command_8cs_source.html index 4a73a353a8..63701aef1b 100644 --- a/_i_command_8cs_source.html +++ b/_i_command_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ICommand.cs
-Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.Components.Chat.Commands
5 {
9  public interface ICommand
10  {
14  string Name { get; }
15 
19  string HelpText { get; }
20 
24  bool AdminOnly { get; }
25 
33  Task<string> Invoke(string arguments, User user, CancellationToken cancellationToken);
34  }
35 }
- +Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.Components.Chat.Commands
5 {
9  public interface ICommand
10  {
14  string Name { get; }
15 
19  string HelpText { get; }
20 
24  bool AdminOnly { get; }
25 
33  Task<string> Invoke(string arguments, User user, CancellationToken cancellationToken);
34  }
35 }
+
Represents a tgs_chat_user datum
Definition: User.cs:10
Represents a command that can be invoked by talking to chat bots
Definition: ICommand.cs:9
diff --git a/_i_command_factory_8cs_source.html b/_i_command_factory_8cs_source.html index c287b31aee..fe7d4fd8a0 100644 --- a/_i_command_factory_8cs_source.html +++ b/_i_command_factory_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ICommandFactory.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 
3 namespace Tgstation.Server.Host.Components.Chat.Commands
4 {
8  interface ICommandFactory
9  {
14  IReadOnlyList<ICommand> GenerateCommands();
15  }
16 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 
3 namespace Tgstation.Server.Host.Components.Chat.Commands
4 {
8  interface ICommandFactory
9  {
14  IReadOnlyList<ICommand> GenerateCommands();
15  }
16 }
+ -
diff --git a/_i_compile_job_consumer_8cs_source.html b/_i_compile_job_consumer_8cs_source.html index cf4f08dc36..231008f8ed 100644 --- a/_i_compile_job_consumer_8cs_source.html +++ b/_i_compile_job_consumer_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ICompileJobConsumer.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Threading;
4 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Components.Compiler
8 {
13  {
20  Task LoadCompileJob(CompileJob job, CancellationToken cancellationToken);
21  }
22 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Threading;
4 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Components.Compiler
8 {
13  {
20  Task LoadCompileJob(CompileJob job, CancellationToken cancellationToken);
21  }
22 }
+ - diff --git a/_i_configuration_8cs_source.html b/_i_configuration_8cs_source.html index d669cf9ddf..7d478de963 100644 --- a/_i_configuration_8cs_source.html +++ b/_i_configuration_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IConfiguration.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Collections.Generic;
4 using System.Threading;
5 using System.Threading.Tasks;
8 
9 namespace Tgstation.Server.Host.Components.StaticFiles
10 {
15  {
23  Task<ServerSideModifications> CopyDMFilesTo(string dmeFile, string destination, CancellationToken cancellationToken);
24 
31  Task SymlinkStaticFilesTo(string destination, CancellationToken cancellationToken);
32 
40  Task<IReadOnlyList<ConfigurationFile>> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken);
41 
49  Task<ConfigurationFile> Read(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken);
50 
58  Task<bool> CreateDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken);
59 
67  Task<bool> DeleteDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken);
68 
78  Task<ConfigurationFile> Write(string configurationRelativePath, ISystemIdentity systemIdentity, byte[] data, string previousHash, CancellationToken cancellationToken);
79  }
80 }
Consumes EventTypes and takes the appropriate actions
+Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Collections.Generic;
4 using System.Threading;
5 using System.Threading.Tasks;
8 
9 namespace Tgstation.Server.Host.Components.StaticFiles
10 {
15  {
23  Task<ServerSideModifications> CopyDMFilesTo(string dmeFile, string destination, CancellationToken cancellationToken);
24 
31  Task SymlinkStaticFilesTo(string destination, CancellationToken cancellationToken);
32 
40  Task<IReadOnlyList<ConfigurationFile>> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken);
41 
49  Task<ConfigurationFile> Read(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken);
50 
58  Task<bool> CreateDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken);
59 
67  Task<bool> DeleteDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken);
68 
78  Task<ConfigurationFile> Write(string configurationRelativePath, ISystemIdentity systemIdentity, byte[] data, string previousHash, CancellationToken cancellationToken);
79  }
80 }
Consumes EventTypes and takes the appropriate actions
+
Represents a user on the current System.Runtime.InteropServices.OSPlatform
-
For managing the Configuration directory
diff --git a/_i_configuration_client_8cs_source.html b/_i_configuration_client_8cs_source.html index 0ba7834bd4..acdb0fea89 100644 --- a/_i_configuration_client_8cs_source.html +++ b/_i_configuration_client_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IConfigurationClient.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client.Components
7 {
11  public interface IConfigurationClient
12  {
19  Task<IReadOnlyList<ConfigurationFile>> List(string directory, CancellationToken cancellationToken);
20 
27  Task<ConfigurationFile> Read(ConfigurationFile file, CancellationToken cancellationToken);
28 
35  Task<ConfigurationFile> Write(ConfigurationFile file, CancellationToken cancellationToken);
36 
43  Task DeleteEmptyDirectory(ConfigurationFile directory, CancellationToken cancellationToken);
44  }
45 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client.Components
7 {
11  public interface IConfigurationClient
12  {
19  Task<IReadOnlyList<ConfigurationFile>> List(string directory, CancellationToken cancellationToken);
20 
27  Task<ConfigurationFile> Read(ConfigurationFile file, CancellationToken cancellationToken);
28 
35  Task<ConfigurationFile> Write(ConfigurationFile file, CancellationToken cancellationToken);
36 
43  Task DeleteEmptyDirectory(ConfigurationFile directory, CancellationToken cancellationToken);
44  }
45 }
+ -
Represents a game configuration file. Create and delete actions uncerimonuously overwrite/delete file...
diff --git a/_i_credentials_provider_8cs_source.html b/_i_credentials_provider_8cs_source.html index 138e12c1a7..03737fd776 100644 --- a/_i_credentials_provider_8cs_source.html +++ b/_i_credentials_provider_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ICredentialsProvider.cs
-Go to the documentation of this file.
1 using LibGit2Sharp.Handlers;
2 
3 namespace Tgstation.Server.Host.Components.Repository
4 {
9  {
16  CredentialsHandler GenerateHandler(string username, string password);
17  }
18 }
+Go to the documentation of this file.
1 using LibGit2Sharp.Handlers;
2 
3 namespace Tgstation.Server.Host.Components.Repository
4 {
9  {
16  CredentialsHandler GenerateHandler(string username, string password);
17  }
18 }
+ -
diff --git a/_i_cryptography_suite_8cs_source.html b/_i_cryptography_suite_8cs_source.html index f9f4f06ef8..d04fce60b8 100644 --- a/_i_cryptography_suite_8cs_source.html +++ b/_i_cryptography_suite_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ICryptographySuite.cs
-Go to the documentation of this file.
2 
3 namespace Tgstation.Server.Host.Security
4 {
8  public interface ICryptographySuite
9  {
15  void SetUserPassword(User user, string newPassword);
16 
23  bool CheckUserPassword(User user, string password);
24 
29  string GetSecureString();
30  }
31 }
+Go to the documentation of this file.
2 
3 namespace Tgstation.Server.Host.Security
4 {
8  public interface ICryptographySuite
9  {
15  void SetUserPassword(User user, string newPassword);
16 
23  bool CheckUserPassword(User user, string password);
24 
29  string GetSecureString();
30  }
31 }
+
Contains various cryptographic functions
- diff --git a/_i_custom_command_handler_8cs_source.html b/_i_custom_command_handler_8cs_source.html index 70ac243ec2..d6575bf2c6 100644 --- a/_i_custom_command_handler_8cs_source.html +++ b/_i_custom_command_handler_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ICustomCommandHandler.cs
-Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.Components.Chat
5 {
9  public interface ICustomCommandHandler
10  {
19  Task<string> HandleChatCommand(string commandName, string arguments, User sender, CancellationToken cancellationToken);
20  }
21 }
- +Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.Components.Chat
5 {
9  public interface ICustomCommandHandler
10  {
19  Task<string> HandleChatCommand(string commandName, string arguments, User sender, CancellationToken cancellationToken);
20  }
21 }
+
Represents a tgs_chat_user datum
Definition: User.cs:10
Handles Commands.ICommands that map to those defined in a IJsonTrackingContext
diff --git a/_i_database_context_8cs_source.html b/_i_database_context_8cs_source.html index 2d1b7f7103..4a5326b336 100644 --- a/_i_database_context_8cs_source.html +++ b/_i_database_context_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IDatabaseContext.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Models
6 {
10  public interface IDatabaseContext
11  {
15  DbSet<User> Users { get; }
16 
20  DbSet<Instance> Instances { get; }
21 
25  DbSet<InstanceUser> InstanceUsers { get; }
26 
30  DbSet<Job> Jobs { get; }
31 
35  DbSet<CompileJob> CompileJobs { get; }
36 
40  DbSet<RevisionInformation> RevisionInformations { get; }
41 
45  DbSet<DreamMakerSettings> DreamMakerSettings { get; set; }
46 
50  DbSet<DreamDaemonSettings> DreamDaemonSettings { get; set; }
51 
55  DbSet<ChatBot> ChatBots { get; set; }
56 
60  DbSet<ChatChannel> ChatChannels { get; set; }
61 
65  DbSet<RepositorySettings> RepositorySettings { get; set; }
66 
70  DbSet<ReattachInformation> ReattachInformations { get; set; }
71 
75  DbSet<WatchdogReattachInformation> WatchdogReattachInformations { get; set; }
76 
82  Task Save(CancellationToken cancellationToken);
83 
89  Task Initialize(CancellationToken cancellationToken);
90  }
91 }
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Models
6 {
10  public interface IDatabaseContext
11  {
15  DbSet<User> Users { get; }
16 
20  DbSet<Instance> Instances { get; }
21 
25  DbSet<InstanceUser> InstanceUsers { get; }
26 
30  DbSet<Job> Jobs { get; }
31 
35  DbSet<CompileJob> CompileJobs { get; }
36 
40  DbSet<RevisionInformation> RevisionInformations { get; }
41 
45  DbSet<DreamMakerSettings> DreamMakerSettings { get; set; }
46 
50  DbSet<DreamDaemonSettings> DreamDaemonSettings { get; set; }
51 
55  DbSet<ChatBot> ChatBots { get; set; }
56 
60  DbSet<ChatChannel> ChatChannels { get; set; }
61 
65  DbSet<RepositorySettings> RepositorySettings { get; set; }
66 
70  DbSet<ReattachInformation> ReattachInformations { get; set; }
71 
75  DbSet<WatchdogReattachInformation> WatchdogReattachInformations { get; set; }
76 
82  Task Save(CancellationToken cancellationToken);
83 
89  Task Initialize(CancellationToken cancellationToken);
90  }
91 }
+ -
diff --git a/_i_database_context_factory_8cs_source.html b/_i_database_context_factory_8cs_source.html index 0a8588fc36..b8cb1c51dc 100644 --- a/_i_database_context_factory_8cs_source.html +++ b/_i_database_context_factory_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IDatabaseContextFactory.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Core
6 {
11  {
17  Task UseContext(Func<IDatabaseContext, Task> operation);
18  }
19 }
+Go to the documentation of this file.
1 using System;
2 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Core
6 {
11  {
17  Task UseContext(Func<IDatabaseContext, Task> operation);
18  }
19 }
+
Factory for scoping usage of IDatabaseContexts. Meant for use by Components
- diff --git a/_i_database_seeder_8cs_source.html b/_i_database_seeder_8cs_source.html index c65457d0c1..47dc63553b 100644 --- a/_i_database_seeder_8cs_source.html +++ b/_i_database_seeder_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IDatabaseSeeder.cs
-Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.Models
5 {
9  interface IDatabaseSeeder
10  {
17  Task SeedDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken);
18 
25  Task ResetAdminPassword(IDatabaseContext databaseContext, CancellationToken cancellationToken);
26  }
27 }
+Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.Models
5 {
9  interface IDatabaseSeeder
10  {
17  Task SeedDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken);
18 
25  Task ResetAdminPassword(IDatabaseContext databaseContext, CancellationToken cancellationToken);
26  }
27 }
+
For initially seeding a database
-
diff --git a/_i_dmb_factory_8cs_source.html b/_i_dmb_factory_8cs_source.html index 06d7105ae0..3d2062aca2 100644 --- a/_i_dmb_factory_8cs_source.html +++ b/_i_dmb_factory_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IDmbFactory.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Components.Compiler
7 {
11  public interface IDmbFactory
12  {
17  Task OnNewerDmb { get; }
18 
22  bool DmbAvailable { get; }
23 
29  IDmbProvider LockNextDmb(int lockCount);
30 
37  Task<IDmbProvider> FromCompileJob(CompileJob compileJob, CancellationToken cancellationToken);
38 
45  Task CleanUnusedCompileJobs(CompileJob exceptThisOne, CancellationToken cancellationToken);
46 
47  }
48 }
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Components.Compiler
7 {
11  public interface IDmbFactory
12  {
17  Task OnNewerDmb { get; }
18 
22  bool DmbAvailable { get; }
23 
29  IDmbProvider LockNextDmb(int lockCount);
30 
37  Task<IDmbProvider> FromCompileJob(CompileJob compileJob, CancellationToken cancellationToken);
38 
45  Task CleanUnusedCompileJobs(CompileJob exceptThisOne, CancellationToken cancellationToken);
46 
47  }
48 }
+ -
Provides absolute paths to the latest compiled .dmbs
Definition: IDmbProvider.cs:9
diff --git a/_i_dmb_provider_8cs_source.html b/_i_dmb_provider_8cs_source.html index 354b395433..78d35e4389 100644 --- a/_i_dmb_provider_8cs_source.html +++ b/_i_dmb_provider_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IDmbProvider.cs
-Go to the documentation of this file.
1 using System;
3 
4 namespace Tgstation.Server.Host.Components
5 {
9  public interface IDmbProvider : IDisposable
10  {
14  string DmbName { get; }
15 
19  string PrimaryDirectory { get; }
20 
24  string SecondaryDirectory { get; }
25 
30 
34  void KeepAlive();
35  }
36 }
+Go to the documentation of this file.
1 using System;
3 
4 namespace Tgstation.Server.Host.Components
5 {
9  public interface IDmbProvider : IDisposable
10  {
14  string DmbName { get; }
15 
19  string PrimaryDirectory { get; }
20 
24  string SecondaryDirectory { get; }
25 
30 
34  void KeepAlive();
35  }
36 }
+ -
Provides absolute paths to the latest compiled .dmbs
Definition: IDmbProvider.cs:9
diff --git a/_i_dream_daemon_client_8cs_source.html b/_i_dream_daemon_client_8cs_source.html index 64ab9c796c..08d10f3007 100644 --- a/_i_dream_daemon_client_8cs_source.html +++ b/_i_dream_daemon_client_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IDreamDaemonClient.cs
-Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Client.Components
6 {
10  public interface IDreamDaemonClient
11  {
17  Task<DreamDaemon> Read(CancellationToken cancellationToken);
18 
24  Task<Job> Start(CancellationToken cancellationToken);
25 
31  Task<Job> Restart(CancellationToken cancellationToken);
32 
38  Task Shutdown(CancellationToken cancellationToken);
39 
46  Task<DreamDaemon> Update(DreamDaemon dreamDaemon, CancellationToken cancellationToken);
47  }
48 }
+Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Client.Components
6 {
10  public interface IDreamDaemonClient
11  {
17  Task<DreamDaemon> Read(CancellationToken cancellationToken);
18 
24  Task<Job> Start(CancellationToken cancellationToken);
25 
31  Task<Job> Restart(CancellationToken cancellationToken);
32 
38  Task Shutdown(CancellationToken cancellationToken);
39 
46  Task<DreamDaemon> Update(DreamDaemon dreamDaemon, CancellationToken cancellationToken);
47  }
48 }
+ -
Represents an instance of BYOND&#39;s DreamDaemon game server. Create action starts the server...
Definition: DreamDaemon.cs:8
diff --git a/_i_dream_maker_8cs_source.html b/_i_dream_maker_8cs_source.html index a8dad70d68..ddfa02cbdf 100644 --- a/_i_dream_maker_8cs_source.html +++ b/_i_dream_maker_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IDreamMaker.cs
-Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Components.Compiler
7 {
11  public interface IDreamMaker
12  {
22  Task<Models.CompileJob> Compile(Models.RevisionInformation revisionInformation, Api.Models.DreamMaker dreamMakerSettings, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken);
23  }
24 }
+Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Components.Compiler
7 {
11  public interface IDreamMaker
12  {
22  Task<Models.CompileJob> Compile(Models.RevisionInformation revisionInformation, Api.Models.DreamMaker dreamMakerSettings, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken);
23  }
24 }
+
Represents an on-disk git repository
Definition: IRepository.cs:11
- diff --git a/_i_dream_maker_client_8cs_source.html b/_i_dream_maker_client_8cs_source.html index 71cf73b764..e8ef97ea53 100644 --- a/_i_dream_maker_client_8cs_source.html +++ b/_i_dream_maker_client_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IDreamMakerClient.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client.Components
7 {
11  public interface IDreamMakerClient
12  {
18  Task<DreamMaker> Read(CancellationToken cancellationToken);
19 
26  Task<DreamMaker> Update(DreamMaker dreamMaker, CancellationToken cancellationToken);
27 
33  Task<Job> Compile(CancellationToken cancellationToken);
34 
40  Task<IReadOnlyList<CompileJob>> GetJobIds(CancellationToken cancellationToken);
41 
48  Task<CompileJob> GetCompileJob(CompileJob compileJob, CancellationToken cancellationToken);
49  }
50 }
Represents the state of the DreamMaker compiler. Create action starts a new compile. Delete action cancels the current compile
Definition: DreamMaker.cs:8
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client.Components
7 {
11  public interface IDreamMakerClient
12  {
18  Task<DreamMaker> Read(CancellationToken cancellationToken);
19 
26  Task<DreamMaker> Update(DreamMaker dreamMaker, CancellationToken cancellationToken);
27 
33  Task<Job> Compile(CancellationToken cancellationToken);
34 
40  Task<IReadOnlyList<CompileJob>> GetJobIds(CancellationToken cancellationToken);
41 
48  Task<CompileJob> GetCompileJob(CompileJob compileJob, CancellationToken cancellationToken);
49  }
50 }
Represents the state of the DreamMaker compiler. Create action starts a new compile. Delete action cancels the current compile
Definition: DreamMaker.cs:8
+ -
diff --git a/_i_event_consumer_8cs_source.html b/_i_event_consumer_8cs_source.html index c2fb535a75..78fc80e0a7 100644 --- a/_i_event_consumer_8cs_source.html +++ b/_i_event_consumer_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IEventConsumer.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Components
6 {
10  public interface IEventConsumer
11  {
19  Task<bool> HandleEvent(EventType eventType, IEnumerable<string> parameters, CancellationToken cancellationToken);
20  }
21 }
Consumes EventTypes and takes the appropriate actions
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Components
6 {
10  public interface IEventConsumer
11  {
19  Task<bool> HandleEvent(EventType eventType, IEnumerable<string> parameters, CancellationToken cancellationToken);
20  }
21 }
Consumes EventTypes and takes the appropriate actions
+ -
diff --git a/_i_git_hub_client_factory_8cs_source.html b/_i_git_hub_client_factory_8cs_source.html index 2aea618f96..24feb5b019 100644 --- a/_i_git_hub_client_factory_8cs_source.html +++ b/_i_git_hub_client_factory_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IGitHubClientFactory.cs
-Go to the documentation of this file.
1 using Octokit;
2 
3 namespace Tgstation.Server.Host.Core
4 {
8  public interface IGitHubClientFactory
9  {
14  IGitHubClient CreateClient();
15 
21  IGitHubClient CreateClient(string accessToken);
22  }
23 }
+Go to the documentation of this file.
1 using Octokit;
2 
3 namespace Tgstation.Server.Host.Core
4 {
8  public interface IGitHubClientFactory
9  {
14  IGitHubClient CreateClient();
15 
21  IGitHubClient CreateClient(string accessToken);
22  }
23 }
+ -
diff --git a/_i_http_client_8cs_source.html b/_i_http_client_8cs_source.html index 8374c7c4e9..136e0e8760 100644 --- a/_i_http_client_8cs_source.html +++ b/_i_http_client_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IHttpClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Net.Http;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client
7 {
12  {
16  TimeSpan Timeout { get; set; }
17 
24  Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
25  }
26 }
+Go to the documentation of this file.
1 using System;
2 using System.Net.Http;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client
7 {
12  {
16  TimeSpan Timeout { get; set; }
17 
24  Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
25  }
26 }
+
For sending HTTP requests
Definition: IHttpClient.cs:11
-
diff --git a/_i_i_o_manager_8cs_source.html b/_i_i_o_manager_8cs_source.html index ea5c7dd7f4..f02915c9a9 100644 --- a/_i_i_o_manager_8cs_source.html +++ b/_i_i_o_manager_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IIOManager.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.IO
7 {
11  public interface IIOManager
12  {
18  string ResolvePath(string path);
19 
25  string GetFileName(string path);
26 
32  string GetFileNameWithoutExtension(string path);
33 
39  bool PathContainsParentAccess(string path);
40 
49  Task CopyDirectory(string src, string dest, IEnumerable<string> ignore, CancellationToken cancellationToken);
50 
57  Task<bool> FileExists(string path, CancellationToken cancellationToken);
58 
65  Task<bool> DirectoryExists(string path, CancellationToken cancellationToken);
66 
73  Task<byte[]> ReadAllBytes(string path, CancellationToken cancellationToken);
74 
81  Task<IReadOnlyList<string>> GetDirectories(string path, CancellationToken cancellationToken);
82 
89  Task<IReadOnlyList<string>> GetFiles(string path, CancellationToken cancellationToken);
90 
98  Task WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken);
99 
107  Task CopyFile(string src, string dest, CancellationToken cancellationToken);
108 
114  string GetDirectoryName(string path);
115 
123  Task<List<string>> GetFilesWithExtension(string path, string extension, CancellationToken cancellationToken);
124 
131  Task DeleteFile(string path, CancellationToken cancellationToken);
132 
139  Task CreateDirectory(string path, CancellationToken cancellationToken);
140 
147  Task DeleteDirectory(string path, CancellationToken cancellationToken);
148 
154  string ConcatPath(params string[] paths);
155 
163  Task MoveFile(string source, string destination, CancellationToken cancellationToken);
164 
172  Task MoveDirectory(string source, string destination, CancellationToken cancellationToken);
173 
180  Task<byte[]> DownloadFile(Uri url, CancellationToken cancellationToken);
181 
189  Task ZipToDirectory(string path, byte[] zipFileBytes, CancellationToken cancellationToken);
190  }
191 }
- +Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.IO
7 {
11  public interface IIOManager
12  {
18  string ResolvePath(string path);
19 
25  string GetFileName(string path);
26 
32  string GetFileNameWithoutExtension(string path);
33 
39  bool PathContainsParentAccess(string path);
40 
49  Task CopyDirectory(string src, string dest, IEnumerable<string> ignore, CancellationToken cancellationToken);
50 
57  Task<bool> FileExists(string path, CancellationToken cancellationToken);
58 
65  Task<bool> DirectoryExists(string path, CancellationToken cancellationToken);
66 
73  Task<byte[]> ReadAllBytes(string path, CancellationToken cancellationToken);
74 
81  Task<IReadOnlyList<string>> GetDirectories(string path, CancellationToken cancellationToken);
82 
89  Task<IReadOnlyList<string>> GetFiles(string path, CancellationToken cancellationToken);
90 
98  Task WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken);
99 
107  Task CopyFile(string src, string dest, CancellationToken cancellationToken);
108 
114  string GetDirectoryName(string path);
115 
123  Task<List<string>> GetFilesWithExtension(string path, string extension, CancellationToken cancellationToken);
124 
131  Task DeleteFile(string path, CancellationToken cancellationToken);
132 
139  Task CreateDirectory(string path, CancellationToken cancellationToken);
140 
147  Task DeleteDirectory(string path, CancellationToken cancellationToken);
148 
154  string ConcatPath(params string[] paths);
155 
163  Task MoveFile(string source, string destination, CancellationToken cancellationToken);
164 
172  Task MoveDirectory(string source, string destination, CancellationToken cancellationToken);
173 
180  Task<byte[]> DownloadFile(Uri url, CancellationToken cancellationToken);
181 
189  Task ZipToDirectory(string path, byte[] zipFileBytes, CancellationToken cancellationToken);
190  }
191 }
+
Interface for using filesystems
Definition: IIOManager.cs:11
diff --git a/_i_identity_cache_8cs_source.html b/_i_identity_cache_8cs_source.html index a67bab44fa..67fb29ed3b 100644 --- a/_i_identity_cache_8cs_source.html +++ b/_i_identity_cache_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IIdentityCache.cs
-Go to the documentation of this file.
1 using System;
3 
4 namespace Tgstation.Server.Host.Security
5 {
9  public interface IIdentityCache
10  {
17  void CacheSystemIdentity(User user, ISystemIdentity systemIdentity, DateTimeOffset expiry);
18 
24  ISystemIdentity LoadCachedIdentity(User user);
25  }
26 }
Represents a user on the current System.Runtime.InteropServices.OSPlatform
+Go to the documentation of this file.
1 using System;
3 
4 namespace Tgstation.Server.Host.Security
5 {
9  public interface IIdentityCache
10  {
17  void CacheSystemIdentity(User user, ISystemIdentity systemIdentity, DateTimeOffset expiry);
18 
24  ISystemIdentity LoadCachedIdentity(User user);
25  }
26 }
+
Represents a user on the current System.Runtime.InteropServices.OSPlatform
- diff --git a/_i_instance_8cs_source.html b/_i_instance_8cs_source.html index 0a2c38b5af..fbb2260147 100644 --- a/_i_instance_8cs_source.html +++ b/_i_instance_8cs_source.html @@ -92,18 +92,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IInstance.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Threading;
4 using System.Threading.Tasks;
11 
12 namespace Tgstation.Server.Host.Components
13 {
17  public interface IInstance : IHostedService, IDisposable
18  {
23 
28 
33 
37  IChat Chat { get; }
38 
43 
48  CompileJob LatestCompileJob();
49 
54  void Rename(string newName);
55 
61  Task SetAutoUpdateInterval(uint newInterval);
62 
71  Task CompileProcess(Job job, IDatabaseContext databaseContext, Action<int> progressReporter, CancellationToken cancellationToken);
72  }
73 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Threading;
4 using System.Threading.Tasks;
11 
12 namespace Tgstation.Server.Host.Components
13 {
17  public interface IInstance : IHostedService, IDisposable
18  {
23 
28 
33 
37  IChat Chat { get; }
38 
43 
48  CompileJob LatestCompileJob();
49 
54  void Rename(string newName);
55 
61  Task SetAutoUpdateInterval(uint newInterval);
62 
71  Task CompileProcess(Job job, IDatabaseContext databaseContext, Action<int> progressReporter, CancellationToken cancellationToken);
72  }
73 }
+
For managing connected chat services
Definition: IChat.cs:13
For interacting with the instance services
Definition: IInstance.cs:17
-
For managing the BYOND installation
For managing the Configuration directory
diff --git a/_i_instance_client_8cs_source.html b/_i_instance_client_8cs_source.html index 9a8671fc04..5db5121f14 100644 --- a/_i_instance_client_8cs_source.html +++ b/_i_instance_client_8cs_source.html @@ -92,10 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IInstanceClient.cs
-Go to the documentation of this file.
1 using System;
3 
4 namespace Tgstation.Server.Client.Components
5 {
9  public interface IInstanceClient
10  {
14  Instance Metadata { get; }
15 
20 
25 
30 
34  IConfigurationClient Configuration { get; }
35 
39  IInstanceUserClient Users { get; }
40 
44  IChatBotsClient ChatBots { get; }
45 
50 
54  IJobsClient Jobs { get; }
55  }
56 }
+Go to the documentation of this file.
1 using System;
3 
4 namespace Tgstation.Server.Client.Components
5 {
9  public interface IInstanceClient
10  {
14  Instance Metadata { get; }
15 
20 
25 
30 
34  IConfigurationClient Configuration { get; }
35 
39  IInstanceUserClient Users { get; }
40 
44  IChatBotsClient ChatBots { get; }
45 
50 
54  IJobsClient Jobs { get; }
55  }
56 }
Represents the state of the DreamMaker compiler. Create action starts a new compile. Delete action cancels the current compile
Definition: DreamMaker.cs:8
+
For managing the Byond installation
Definition: IByondClient.cs:11
@@ -104,7 +105,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Represents a git repository
Definition: Repository.cs:8
-
Metadata about a server instance
Definition: Instance.cs:9
diff --git a/_i_instance_factory_8cs_source.html b/_i_instance_factory_8cs_source.html index 36248bf2be..0deef421df 100644 --- a/_i_instance_factory_8cs_source.html +++ b/_i_instance_factory_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IInstanceFactory.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 
3 namespace Tgstation.Server.Host.Components
4 {
9  {
15  IInstance CreateInstance(Models.Instance metadata);
16  }
17 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 
3 namespace Tgstation.Server.Host.Components
4 {
9  {
15  IInstance CreateInstance(Models.Instance metadata);
16  }
17 }
+
For interacting with the instance services
Definition: IInstance.cs:17
-
diff --git a/_i_instance_manager_8cs_source.html b/_i_instance_manager_8cs_source.html index e140268855..0731e9df1f 100644 --- a/_i_instance_manager_8cs_source.html +++ b/_i_instance_manager_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IInstanceManager.cs
-Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Components
6 {
10  public interface IInstanceManager
11  {
17  IInstance GetInstance(Models.Instance metadata);
18 
25  Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken);
26 
34  Task OfflineInstance(Models.Instance metadata, User user, CancellationToken cancellationToken);
35 
43  Task MoveInstance(Models.Instance metadata, string newPath, CancellationToken cancellationToken);
44  }
45 }
+Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Components
6 {
10  public interface IInstanceManager
11  {
17  IInstance GetInstance(Models.Instance metadata);
18 
25  Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken);
26 
34  Task OfflineInstance(Models.Instance metadata, User user, CancellationToken cancellationToken);
35 
43  Task MoveInstance(Models.Instance metadata, string newPath, CancellationToken cancellationToken);
44  }
45 }
+
For interacting with the instance services
Definition: IInstance.cs:17
- diff --git a/_i_instance_manager_client_8cs_source.html b/_i_instance_manager_client_8cs_source.html index a7613e74fe..ba64cb9509 100644 --- a/_i_instance_manager_client_8cs_source.html +++ b/_i_instance_manager_client_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IInstanceManagerClient.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Client
8 {
12  public interface IInstanceManagerClient
13  {
19  Task<IReadOnlyList<Instance>> List(CancellationToken cancellationToken);
20 
27  Task<Instance> CreateOrAttach(Instance instance, CancellationToken cancellationToken);
28 
35  Task<Instance> Update(Instance instance, CancellationToken cancellationToken);
36 
43  Task<Instance> GetId(Instance instance, CancellationToken cancellationToken);
44 
51  Task Detach(Instance instance, CancellationToken cancellationToken);
52 
58  IInstanceClient CreateClient(Instance instance);
59  }
60 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Client
8 {
12  public interface IInstanceManagerClient
13  {
19  Task<IReadOnlyList<Instance>> List(CancellationToken cancellationToken);
20 
27  Task<Instance> CreateOrAttach(Instance instance, CancellationToken cancellationToken);
28 
35  Task<Instance> Update(Instance instance, CancellationToken cancellationToken);
36 
43  Task<Instance> GetId(Instance instance, CancellationToken cancellationToken);
44 
51  Task Detach(Instance instance, CancellationToken cancellationToken);
52 
58  IInstanceClient CreateClient(Instance instance);
59  }
60 }
+ -
Metadata about a server instance
Definition: Instance.cs:9
diff --git a/_i_instance_user_client_8cs_source.html b/_i_instance_user_client_8cs_source.html index 1655097b5e..e491ee3871 100644 --- a/_i_instance_user_client_8cs_source.html +++ b/_i_instance_user_client_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IInstanceUserClient.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client.Components
7 {
11  public interface IInstanceUserClient
12  {
18  Task<InstanceUser> Read(CancellationToken cancellationToken);
19 
26  Task<InstanceUser> GetId(InstanceUser instanceUser, CancellationToken cancellationToken);
27 
33  Task<IReadOnlyList<InstanceUser>> List(CancellationToken cancellationToken);
34 
41  Task<InstanceUser> Update(InstanceUser instanceUser, CancellationToken cancellationToken);
42 
49  Task<InstanceUser> Create(InstanceUser instanceUser, CancellationToken cancellationToken);
50 
57  Task Delete(InstanceUser instanceUser, CancellationToken cancellationToken);
58  }
59 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client.Components
7 {
11  public interface IInstanceUserClient
12  {
18  Task<InstanceUser> Read(CancellationToken cancellationToken);
19 
26  Task<InstanceUser> GetId(InstanceUser instanceUser, CancellationToken cancellationToken);
27 
33  Task<IReadOnlyList<InstanceUser>> List(CancellationToken cancellationToken);
34 
41  Task<InstanceUser> Update(InstanceUser instanceUser, CancellationToken cancellationToken);
42 
49  Task<InstanceUser> Create(InstanceUser instanceUser, CancellationToken cancellationToken);
50 
57  Task Delete(InstanceUser instanceUser, CancellationToken cancellationToken);
58  }
59 }
+
Represents a Users permissions in an Instance
Definition: InstanceUser.cs:9
-
diff --git a/_i_job_manager_8cs_source.html b/_i_job_manager_8cs_source.html index 7588da1d89..b975a50d06 100644 --- a/_i_job_manager_8cs_source.html +++ b/_i_job_manager_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IJobManager.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Threading;
4 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Core
8 {
12  public interface IJobManager : IHostedService
13  {
17  int? JobProgress(Job job);
18 
26  Task RegisterOperation(Job job, Func<Job, IDatabaseContext, Action<int>, CancellationToken, Task> operation, CancellationToken cancellationToken);
27 
36 #pragma warning disable CA1068 // CancellationToken parameters must come last https://github.com/dotnet/roslyn-analyzers/issues/1816
37  Task WaitForJobCompletion(Job job, User canceller, CancellationToken jobCancellationToken, CancellationToken cancellationToken);
38 #pragma warning restore CA1068 // CancellationToken parameters must come last
39 
48  Task<bool> CancelJob(Job job, User user, bool blocking, CancellationToken cancellationToken);
49  }
50 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Hosting;
2 using System;
3 using System.Threading;
4 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Core
8 {
12  public interface IJobManager : IHostedService
13  {
17  int? JobProgress(Job job);
18 
26  Task RegisterOperation(Job job, Func<Job, IDatabaseContext, Action<int>, CancellationToken, Task> operation, CancellationToken cancellationToken);
27 
36 #pragma warning disable CA1068 // CancellationToken parameters must come last https://github.com/dotnet/roslyn-analyzers/issues/1816
37  Task WaitForJobCompletion(Job job, User canceller, CancellationToken jobCancellationToken, CancellationToken cancellationToken);
38 #pragma warning restore CA1068 // CancellationToken parameters must come last
39 
48  Task<bool> CancelJob(Job job, User user, bool blocking, CancellationToken cancellationToken);
49  }
50 }
Manages the runtime of Jobs
Definition: IJobManager.cs:12
+ - diff --git a/_i_jobs_client_8cs_source.html b/_i_jobs_client_8cs_source.html index 5e92dd6a47..b49a0288d7 100644 --- a/_i_jobs_client_8cs_source.html +++ b/_i_jobs_client_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IJobsClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Client.Components
8 {
12  public interface IJobsClient
13  {
19  Task<IReadOnlyList<Job>> List(CancellationToken cancellationToken);
20 
26  Task<IReadOnlyList<Job>> ListActive(CancellationToken cancellationToken);
27 
34  Task<Job> GetId(Job job, CancellationToken cancellationToken);
35 
42  Task Cancel(Job job, CancellationToken cancellationToken);
43  }
44 }
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Client.Components
8 {
12  public interface IJobsClient
13  {
19  Task<IReadOnlyList<Job>> List(CancellationToken cancellationToken);
20 
26  Task<IReadOnlyList<Job>> ListActive(CancellationToken cancellationToken);
27 
34  Task<Job> GetId(Job job, CancellationToken cancellationToken);
35 
42  Task Cancel(Job job, CancellationToken cancellationToken);
43  }
44 }
+ -
Represents a long running job on the server. Model is read-only, updates attempt to cancel the job ...
Definition: Job.cs:6
diff --git a/_i_json_tracking_context_8cs_source.html b/_i_json_tracking_context_8cs_source.html index 040d4412ab..f8157eea31 100644 --- a/_i_json_tracking_context_8cs_source.html +++ b/_i_json_tracking_context_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IJsonTrackingContext.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Components.Chat
8 {
12  public interface IJsonTrackingContext : IDisposable
13  {
19  Task<IReadOnlyList<CustomCommand>> GetCustomCommands(CancellationToken cancellationToken);
20 
27  Task SetChannels(IEnumerable<Channel> channels, CancellationToken cancellationToken);
28  }
29 }
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Components.Chat
8 {
12  public interface IJsonTrackingContext : IDisposable
13  {
19  Task<IReadOnlyList<CustomCommand>> GetCustomCommands(CancellationToken cancellationToken);
20 
27  Task SetChannels(IEnumerable<Channel> channels, CancellationToken cancellationToken);
28  }
29 }
+ -
Represents a tracking of dynamic chat json files
diff --git a/_i_post_write_handler_8cs_source.html b/_i_post_write_handler_8cs_source.html index edbb8a274a..c841a4ff66 100644 --- a/_i_post_write_handler_8cs_source.html +++ b/_i_post_write_handler_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IPostWriteHandler.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Core
2 {
3  interface IPostWriteHandler
4  {
9  void HandleWrite(string filePath);
10  }
11 }
- +Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Core
2 {
3  interface IPostWriteHandler
4  {
9  void HandleWrite(string filePath);
10  }
11 }
+
diff --git a/_i_process_8cs_source.html b/_i_process_8cs_source.html index 6b88bf5dcf..98fba6f682 100644 --- a/_i_process_8cs_source.html +++ b/_i_process_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IProcess.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.Core
5 {
9  interface IProcess : IProcessBase
10  {
14  int Id { get; }
15 
19  Task Startup { get; }
20 
25  string GetErrorOutput();
26 
31  string GetStandardOutput();
32 
37  string GetCombinedOutput();
38 
42  void Terminate();
43  }
44 }
- +Go to the documentation of this file.
1 using System;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.Core
5 {
9  interface IProcess : IProcessBase
10  {
14  int Id { get; }
15 
19  Task Startup { get; }
20 
25  string GetErrorOutput();
26 
31  string GetStandardOutput();
32 
37  string GetCombinedOutput();
38 
42  void Terminate();
43  }
44 }
+
Abstraction over a System.Diagnostics.Process
Definition: IProcess.cs:9
Represents process lifetime
Definition: IProcessBase.cs:9
diff --git a/_i_process_base_8cs_source.html b/_i_process_base_8cs_source.html index 2ebae95a48..d0b2c7d0f3 100644 --- a/_i_process_base_8cs_source.html +++ b/_i_process_base_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IProcessBase.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.Core
5 {
10  {
14  Task<int> Lifetime { get; }
15 
19  void SetHighPriority();
20  }
21 }
- +Go to the documentation of this file.
1 using System;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.Core
5 {
10  {
14  Task<int> Lifetime { get; }
15 
19  void SetHighPriority();
20  }
21 }
+
Represents process lifetime
Definition: IProcessBase.cs:9
diff --git a/_i_process_executor_8cs_source.html b/_i_process_executor_8cs_source.html index b6129bc1ff..8996988855 100644 --- a/_i_process_executor_8cs_source.html +++ b/_i_process_executor_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IProcessExecutor.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Core
2 {
6  interface IProcessExecutor
7  {
18  IProcess LaunchProcess(string fileName, string workingDirectory, string arguments = null, bool readOutput = false, bool readError = false, bool noShellExecute = false);
19 
25  IProcess GetProcess(int id);
26  }
27 }
- +Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Core
2 {
6  interface IProcessExecutor
7  {
18  IProcess LaunchProcess(string fileName, string workingDirectory, string arguments = null, bool readOutput = false, bool readError = false, bool noShellExecute = false);
19 
25  IProcess GetProcess(int id);
26  }
27 }
+
Abstraction over a System.Diagnostics.Process
Definition: IProcess.cs:9
diff --git a/_i_provider_8cs_source.html b/_i_provider_8cs_source.html index 5f4c547aed..c7f4f8fafb 100644 --- a/_i_provider_8cs_source.html +++ b/_i_provider_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IProvider.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Components.Chat.Providers
7 {
11  interface IProvider : IDisposable
12  {
16  bool Connected { get; }
17 
21  string BotMention { get; }
22 
29  Task<Message> NextMessage(CancellationToken cancellationToken);
30 
36  Task<bool> Connect(CancellationToken cancellationToken);
37 
43  Task Disconnect(CancellationToken cancellationToken);
44 
51  Task<IReadOnlyList<Channel>> MapChannels(IEnumerable<Api.Models.ChatChannel> channels, CancellationToken cancellationToken);
52 
60  Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken);
61  }
62 }
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Components.Chat.Providers
7 {
11  interface IProvider : IDisposable
12  {
16  bool Connected { get; }
17 
21  string BotMention { get; }
22 
29  Task<Message> NextMessage(CancellationToken cancellationToken);
30 
36  Task<bool> Connect(CancellationToken cancellationToken);
37 
43  Task Disconnect(CancellationToken cancellationToken);
44 
51  Task<IReadOnlyList<Channel>> MapChannels(IEnumerable<Api.Models.ChatChannel> channels, CancellationToken cancellationToken);
52 
60  Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken);
61  }
62 }
+
For interacting with a chat service
Definition: IProvider.cs:11
-
diff --git a/_i_provider_factory_8cs_source.html b/_i_provider_factory_8cs_source.html index 41fd2a8cee..f927d1e591 100644 --- a/_i_provider_factory_8cs_source.html +++ b/_i_provider_factory_8cs_source.html @@ -92,14 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IProviderFactory.cs
-Go to the documentation of this file.
3 
4 namespace Tgstation.Server.Host.Components.Chat
5 {
9  interface IProviderFactory
10  {
16  IProvider CreateProvider(ChatBot settings);
17  }
18 }
+Go to the documentation of this file.
3 
4 namespace Tgstation.Server.Host.Components.Chat
5 {
9  interface IProviderFactory
10  {
16  IProvider CreateProvider(ChatBot settings);
17  }
18 }
+
For interacting with a chat service
Definition: IProvider.cs:11
-
Manage the server chat bots
Definition: ChatBot.cs:8
+
Manage the server chat bots
Definition: ChatBot.cs:10
- - + diff --git a/_i_reattach_info_handler_8cs_source.html b/_i_reattach_info_handler_8cs_source.html index 126c36b12f..3b447efd2f 100644 --- a/_i_reattach_info_handler_8cs_source.html +++ b/_i_reattach_info_handler_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IReattachInfoHandler.cs
-Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.Components.Watchdog
5 {
9  public interface IReattachInfoHandler
10  {
17  Task Save(WatchdogReattachInformation reattachInformation, CancellationToken cancellationToken);
18 
24  Task<WatchdogReattachInformation> Load(CancellationToken cancellationToken);
25  }
26 }
- +Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.Components.Watchdog
5 {
9  public interface IReattachInfoHandler
10  {
17  Task Save(WatchdogReattachInformation reattachInformation, CancellationToken cancellationToken);
18 
24  Task<WatchdogReattachInformation> Load(CancellationToken cancellationToken);
25  }
26 }
+
Handles saving and loading WatchdogReattachInformation
diff --git a/_i_repository_8cs_source.html b/_i_repository_8cs_source.html index 85d632bb88..62f535feb5 100644 --- a/_i_repository_8cs_source.html +++ b/_i_repository_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IRepository.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Components.Repository
7 {
11  public interface IRepository : IDisposable
12  {
16  bool IsGitHubRepository { get; }
17 
21  string GitHubOwner { get; }
22 
26  string GitHubRepoName { get; }
27 
31  bool Tracking { get; }
32 
36  string Head { get; }
37 
41  string Reference { get; }
42 
46  string Origin { get; }
47 
54  Task<bool> IsSha(string committish, CancellationToken cancellationToken);
55 
63  Task CheckoutObject(string committish, Action<int> progressReporter, CancellationToken cancellationToken);
64 
76  Task<bool?> AddTestMerge(TestMergeParameters testMergeParameters, string committerName, string committerEmail, string username, string password, Action<int> progressReporter, CancellationToken cancellationToken);
77 
86  Task FetchOrigin(string username, string password, Action<int> progressReporter, CancellationToken cancellationToken);
87 
94  Task ResetToOrigin(Action<int> progressReporter, CancellationToken cancellationToken);
95 
103  Task ResetToSha(string sha, Action<int> progressReporter, CancellationToken cancellationToken);
104 
113  Task<bool?> MergeOrigin(string committerName, string committerEmail, Action<int> progressReporter, CancellationToken cancellationToken);
114 
126  Task Sychronize(string username, string password, string committerName, string committerEmail, Action<int> progressReporter, bool synchronizeTrackedBranch, CancellationToken cancellationToken);
127 
134  Task CopyTo(string path, CancellationToken cancellationToken);
135  }
136 }
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Components.Repository
7 {
11  public interface IRepository : IDisposable
12  {
16  bool IsGitHubRepository { get; }
17 
21  string GitHubOwner { get; }
22 
26  string GitHubRepoName { get; }
27 
31  bool Tracking { get; }
32 
36  string Head { get; }
37 
41  string Reference { get; }
42 
46  string Origin { get; }
47 
54  Task<bool> IsSha(string committish, CancellationToken cancellationToken);
55 
63  Task CheckoutObject(string committish, Action<int> progressReporter, CancellationToken cancellationToken);
64 
76  Task<bool?> AddTestMerge(TestMergeParameters testMergeParameters, string committerName, string committerEmail, string username, string password, Action<int> progressReporter, CancellationToken cancellationToken);
77 
86  Task FetchOrigin(string username, string password, Action<int> progressReporter, CancellationToken cancellationToken);
87 
94  Task ResetToOrigin(Action<int> progressReporter, CancellationToken cancellationToken);
95 
103  Task ResetToSha(string sha, Action<int> progressReporter, CancellationToken cancellationToken);
104 
113  Task<bool?> MergeOrigin(string committerName, string committerEmail, Action<int> progressReporter, CancellationToken cancellationToken);
114 
126  Task Sychronize(string username, string password, string committerName, string committerEmail, Action<int> progressReporter, bool synchronizeTrackedBranch, CancellationToken cancellationToken);
127 
134  Task CopyTo(string path, CancellationToken cancellationToken);
135  }
136 }
+
Represents an on-disk git repository
Definition: IRepository.cs:11
Represents a git repository
Definition: Repository.cs:8
-
Parameters for creating a TestMerge
diff --git a/_i_repository_client_8cs_source.html b/_i_repository_client_8cs_source.html index 74d07e1b3f..2cb81ac431 100644 --- a/_i_repository_client_8cs_source.html +++ b/_i_repository_client_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IRepositoryClient.cs
-Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Client.Components
6 {
10  public interface IRepositoryClient
11  {
17  Task<Repository> Read(CancellationToken cancellationToken);
18 
25  Task<Repository> Update(Repository repository, CancellationToken cancellationToken);
26 
33  Task<Repository> Clone(Repository repository, CancellationToken cancellationToken);
34 
40  Task<Repository> Delete(CancellationToken cancellationToken);
41  }
42 }
+Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Client.Components
6 {
10  public interface IRepositoryClient
11  {
17  Task<Repository> Read(CancellationToken cancellationToken);
18 
25  Task<Repository> Update(Repository repository, CancellationToken cancellationToken);
26 
33  Task<Repository> Clone(Repository repository, CancellationToken cancellationToken);
34 
40  Task<Repository> Delete(CancellationToken cancellationToken);
41  }
42 }
+
Represents a git repository
Definition: Repository.cs:8
-
diff --git a/_i_repository_manager_8cs_source.html b/_i_repository_manager_8cs_source.html index cd9d82bec4..64f1e752a1 100644 --- a/_i_repository_manager_8cs_source.html +++ b/_i_repository_manager_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IRepositoryManager.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Components.Repository
6 {
10  public interface IRepositoryManager : IDisposable
11  {
15  bool InUse { get; }
16 
20  bool CloneInProgress { get; }
21 
27  Task<IRepository> LoadRepository(CancellationToken cancellationToken);
28 
39  Task<IRepository> CloneRepository(Uri url, string initialBranch, string username, string password, Action<int> progressReporter, CancellationToken cancellationToken);
40 
46  Task DeleteRepository(CancellationToken cancellationToken);
47  }
48 }
- +Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Components.Repository
6 {
10  public interface IRepositoryManager : IDisposable
11  {
15  bool InUse { get; }
16 
20  bool CloneInProgress { get; }
21 
27  Task<IRepository> LoadRepository(CancellationToken cancellationToken);
28 
39  Task<IRepository> CloneRepository(Uri url, string initialBranch, string username, string password, Action<int> progressReporter, CancellationToken cancellationToken);
40 
46  Task DeleteRepository(CancellationToken cancellationToken);
47  }
48 }
+
Factory for creating and loading IRepositorys
diff --git a/_i_request_logger_8cs_source.html b/_i_request_logger_8cs_source.html index bac77c1dd1..6e4c1e8292 100644 --- a/_i_request_logger_8cs_source.html +++ b/_i_request_logger_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IRequestLogger.cs
-Go to the documentation of this file.
1 using System;
2 using System.Net.Http;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client
7 {
11  public interface IRequestLogger
12  {
19  Task LogRequest(HttpRequestMessage requestMessage, CancellationToken cancellationToken);
20 
27  Task LogResponse(HttpResponseMessage responseMessage, CancellationToken cancellationToken);
28  }
29 }
- +Go to the documentation of this file.
1 using System;
2 using System.Net.Http;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client
7 {
11  public interface IRequestLogger
12  {
19  Task LogRequest(HttpRequestMessage requestMessage, CancellationToken cancellationToken);
20 
27  Task LogResponse(HttpResponseMessage responseMessage, CancellationToken cancellationToken);
28  }
29 }
+
For logging HTTP requests and responses
diff --git a/_i_server_8cs_source.html b/_i_server_8cs_source.html index 9ae494748b..0b4141d080 100644 --- a/_i_server_8cs_source.html +++ b/_i_server_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IServer.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host
6 {
10  public interface IServer : IDisposable
11  {
15  bool RestartRequested { get; }
16 
22  Task RunAsync(CancellationToken cancellationToken);
23  }
24 }
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host
6 {
10  public interface IServer : IDisposable
11  {
15  bool RestartRequested { get; }
16 
22  Task RunAsync(CancellationToken cancellationToken);
23  }
24 }
+
Represents the host
Definition: IServer.cs:10
-
diff --git a/_i_server_client_8cs_source.html b/_i_server_client_8cs_source.html index 528b4f9c87..9032fa5fc7 100644 --- a/_i_server_client_8cs_source.html +++ b/_i_server_client_8cs_source.html @@ -92,14 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IServerClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client
7 {
11  public interface IServerClient : IDisposable
12  {
16  Uri Url { get; }
17 
21  Token Token { get; }
22 
26  TimeSpan Timeout { get; set; }
27 
31  IInstanceManagerClient Instances { get; }
32 
37 
41  IUsersClient Users { get; }
42 
46  Task<ServerInformation> Version(CancellationToken cancellationToken);
47 
52  void AddRequestLogger(IRequestLogger requestLogger);
53  }
54 }
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client
7 {
11  public interface IServerClient : IDisposable
12  {
16  Uri Url { get; }
17 
21  Token Token { get; }
22 
26  TimeSpan Timeout { get; set; }
27 
31  IInstanceManagerClient Instances { get; }
32 
37 
41  IUsersClient Users { get; }
42 
46  Task<ServerInformation> Version(CancellationToken cancellationToken);
47 
52  void AddRequestLogger(IRequestLogger requestLogger);
53  }
54 }
+
Main client for communicating with a server
-
Represents a JWT returned by the API
Definition: Token.cs:8
For logging HTTP requests and responses
diff --git a/_i_server_client_factory_8cs_source.html b/_i_server_client_factory_8cs_source.html index 07e79565d9..42c2bcc418 100644 --- a/_i_server_client_factory_8cs_source.html +++ b/_i_server_client_factory_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IServerClientFactory.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client
7 {
11  public interface IServerClientFactory
12  {
22  Task<IServerClient> CreateServerClient(Uri host, string username, string password, TimeSpan timeout = default, CancellationToken cancellationToken = default);
23 
31  IServerClient CreateServerClient(Uri host, Token token, TimeSpan timeout = default);
32  }
33 }
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client
7 {
11  public interface IServerClientFactory
12  {
22  Task<IServerClient> CreateServerClient(Uri host, string username, string password, TimeSpan timeout = default, CancellationToken cancellationToken = default);
23 
31  IServerClient CreateServerClient(Uri host, Token token, TimeSpan timeout = default);
32  }
33 }
+
Main client for communicating with a server
-
Represents a JWT returned by the API
Definition: Token.cs:8
Factory for creating IServerClients
diff --git a/_i_server_control_8cs_source.html b/_i_server_control_8cs_source.html index be4e7bea2f..c8c4df4053 100644 --- a/_i_server_control_8cs_source.html +++ b/_i_server_control_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IServerControl.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using Tgstation.Server.Host.IO;
5 
6 namespace Tgstation.Server.Host.Core
7 {
11  public interface IServerControl
12  {
20  Task<bool> ApplyUpdate(byte[] updateZipData, IIOManager ioManager, CancellationToken cancellationToken);
21 
26  void RegisterForRestart(Action action);
27 
32  bool Restart();
33  }
34 }
- +Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using Tgstation.Server.Host.IO;
5 
6 namespace Tgstation.Server.Host.Core
7 {
11  public interface IServerControl
12  {
20  Task<bool> ApplyUpdate(byte[] updateZipData, IIOManager ioManager, CancellationToken cancellationToken);
21 
26  void RegisterForRestart(Action action);
27 
32  bool Restart();
33  }
34 }
+
Interface for using filesystems
Definition: IIOManager.cs:11
diff --git a/_i_server_factory_8cs_source.html b/_i_server_factory_8cs_source.html index 4dbe706403..f8feb3929c 100644 --- a/_i_server_factory_8cs_source.html +++ b/_i_server_factory_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IServerFactory.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host
2 {
6  public interface IServerFactory
7  {
14  IServer CreateServer(string[] args, string updatePath);
15  }
16 }
+Go to the documentation of this file.
1 namespace Tgstation.Server.Host
2 {
6  public interface IServerFactory
7  {
14  IServer CreateServer(string[] args, string updatePath);
15  }
16 }
+
Represents the host
Definition: IServer.cs:10
-
diff --git a/_i_session_controller_8cs_source.html b/_i_session_controller_8cs_source.html index 9deec72a2a..a84b46b386 100644 --- a/_i_session_controller_8cs_source.html +++ b/_i_session_controller_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ISessionController.cs
-Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Components.Watchdog
6 {
11  {
15  Task<LaunchResult> LaunchResult { get; }
16 
20  bool IsPrimary { get; }
21 
25  bool TerminationWasRequested { get; }
26 
31 
35  IDmbProvider Dmb { get; }
36 
40  ushort? Port { get; }
41 
45  bool ClosePortOnReboot { get; set; }
46 
51 
55  Task OnReboot { get; }
56 
61  ReattachInformation Release();
62 
69  Task<string> SendCommand(string command, CancellationToken cancellationToken);
70 
77  Task<bool> SetPort(ushort newPort, CancellationToken cancellatonToken);
78 
85  Task<bool> SetRebootState(RebootState newRebootState, CancellationToken cancellationToken);
86 
90  void ResetRebootState();
91  }
92 }
Represents the result of trying to start a DD process
Definition: LaunchResult.cs:9
+Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Components.Watchdog
6 {
11  {
15  Task<LaunchResult> LaunchResult { get; }
16 
20  bool IsPrimary { get; }
21 
25  bool TerminationWasRequested { get; }
26 
31 
35  IDmbProvider Dmb { get; }
36 
40  ushort? Port { get; }
41 
45  bool ClosePortOnReboot { get; set; }
46 
51 
55  Task OnReboot { get; }
56 
61  ReattachInformation Release();
62 
69  Task<string> SendCommand(string command, CancellationToken cancellationToken);
70 
77  Task<bool> SetPort(ushort newPort, CancellationToken cancellatonToken);
78 
85  Task<bool> SetRebootState(RebootState newRebootState, CancellationToken cancellationToken);
86 
90  void ResetRebootState();
91  }
92 }
Represents the result of trying to start a DD process
Definition: LaunchResult.cs:9
+
Handles communication with a DreamDaemon IProcess
-
Represents process lifetime
Definition: IProcessBase.cs:9
Provides absolute paths to the latest compiled .dmbs
Definition: IDmbProvider.cs:9
diff --git a/_i_session_controller_factory_8cs_source.html b/_i_session_controller_factory_8cs_source.html index bd77232fb3..13499d0114 100644 --- a/_i_session_controller_factory_8cs_source.html +++ b/_i_session_controller_factory_8cs_source.html @@ -92,14 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ISessionControllerFactory.cs
-Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Components.Watchdog
7 {
12  {
24  Task<ISessionController> LaunchNew(DreamDaemonLaunchParameters launchParameters, IDmbProvider dmbProvider, IByondExecutableLock currentByondLock, bool primaryPort, bool primaryDirectory, bool apiValidate, CancellationToken cancellationToken);
25 
32  Task<ISessionController> Reattach(ReattachInformation reattachInformation, CancellationToken cancellationToken);
33  }
34 }
+Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Components.Watchdog
7 {
12  {
24  Task<ISessionController> LaunchNew(DreamDaemonLaunchParameters launchParameters, IDmbProvider dmbProvider, IByondExecutableLock currentByondLock, bool primaryPort, bool primaryDirectory, bool apiValidate, CancellationToken cancellationToken);
25 
32  Task<ISessionController> Reattach(ReattachInformation reattachInformation, CancellationToken cancellationToken);
33  }
34 }
+ - - +
Represents usage of the two primary BYOND server executables
diff --git a/_i_symlink_factory_8cs_source.html b/_i_symlink_factory_8cs_source.html index 5ecf1cc102..3b9d145be2 100644 --- a/_i_symlink_factory_8cs_source.html +++ b/_i_symlink_factory_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ISymlinkFactory.cs
-Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.IO
5 {
9  interface ISymlinkFactory
10  {
18  Task CreateSymbolicLink(string targetPath, string linkPath, CancellationToken cancellationToken);
19  }
20 }
+Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.IO
5 {
9  interface ISymlinkFactory
10  {
18  Task CreateSymbolicLink(string targetPath, string linkPath, CancellationToken cancellationToken);
19  }
20 }
+ -
diff --git a/_i_synchronous_i_o_manager_8cs_source.html b/_i_synchronous_i_o_manager_8cs_source.html index a66447f5fd..ee28e40d63 100644 --- a/_i_synchronous_i_o_manager_8cs_source.html +++ b/_i_synchronous_i_o_manager_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ISynchronousIOManager.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 
4 namespace Tgstation.Server.Host.IO
5 {
10  {
17  IEnumerable<string> GetFiles(string path, CancellationToken cancellationToken);
18 
25  IEnumerable<string> GetDirectories(string path, CancellationToken cancellationToken);
26 
33  bool CreateDirectory(string path, CancellationToken cancellationToken);
34 
40  byte[] ReadFile(string path);
41 
47  bool DeleteDirectory(string path);
48 
57  bool WriteFileChecked(string path, byte[] data, ref string sha1InOut, CancellationToken cancellationToken);
58 
64  bool IsDirectory(string path);
65  }
66 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 
4 namespace Tgstation.Server.Host.IO
5 {
10  {
17  IEnumerable<string> GetFiles(string path, CancellationToken cancellationToken);
18 
25  IEnumerable<string> GetDirectories(string path, CancellationToken cancellationToken);
26 
33  bool CreateDirectory(string path, CancellationToken cancellationToken);
34 
40  byte[] ReadFile(string path);
41 
47  bool DeleteDirectory(string path);
48 
57  bool WriteFileChecked(string path, byte[] data, ref string sha1InOut, CancellationToken cancellationToken);
58 
64  bool IsDirectory(string path);
65  }
66 }
+
For accessing the disk in a synchronous manner
-
diff --git a/_i_system_identity_8cs_source.html b/_i_system_identity_8cs_source.html index b203fc0125..481577582d 100644 --- a/_i_system_identity_8cs_source.html +++ b/_i_system_identity_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ISystemIdentity.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Security
7 {
11  public interface ISystemIdentity : IDisposable
12  {
16  string Uid { get; }
17 
21  string Username { get; }
22 
27  ISystemIdentity Clone();
28 
35  Task RunImpersonated(Action action, CancellationToken cancellationToken);
36  }
37 }
Represents a user on the current System.Runtime.InteropServices.OSPlatform
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Security
7 {
11  public interface ISystemIdentity : IDisposable
12  {
16  string Uid { get; }
17 
21  string Username { get; }
22 
27  ISystemIdentity Clone();
28 
35  Task RunImpersonated(Action action, CancellationToken cancellationToken);
36  }
37 }
+
Represents a user on the current System.Runtime.InteropServices.OSPlatform
-
diff --git a/_i_system_identity_factory_8cs_source.html b/_i_system_identity_factory_8cs_source.html index 5203a565d8..9cc96cff77 100644 --- a/_i_system_identity_factory_8cs_source.html +++ b/_i_system_identity_factory_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ISystemIdentityFactory.cs
-Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Security
6 {
10  public interface ISystemIdentityFactory
11  {
18  Task<ISystemIdentity> CreateSystemIdentity(User user, CancellationToken cancellationToken);
19 
27  Task<ISystemIdentity> CreateSystemIdentity(string username, string password, CancellationToken cancellationToken);
28  }
29 }
+Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Security
6 {
10  public interface ISystemIdentityFactory
11  {
18  Task<ISystemIdentity> CreateSystemIdentity(User user, CancellationToken cancellationToken);
19 
27  Task<ISystemIdentity> CreateSystemIdentity(string username, string password, CancellationToken cancellationToken);
28  }
29 }
+ - diff --git a/_i_token_factory_8cs_source.html b/_i_token_factory_8cs_source.html index fc948c868d..5ec20026cd 100644 --- a/_i_token_factory_8cs_source.html +++ b/_i_token_factory_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ITokenFactory.cs
-Go to the documentation of this file.
1 using System;
3 
4 namespace Tgstation.Server.Host.Security
5 {
9  public interface ITokenFactory
10  {
16  Token CreateToken(Models.User user);
17  }
18 }
+Go to the documentation of this file.
1 using System;
3 
4 namespace Tgstation.Server.Host.Security
5 {
9  public interface ITokenFactory
10  {
16  Token CreateToken(Models.User user);
17  }
18 }
+ -
Represents a JWT returned by the API
Definition: Token.cs:8
diff --git a/_i_users_client_8cs_source.html b/_i_users_client_8cs_source.html index 6466627bf9..2888a6efee 100644 --- a/_i_users_client_8cs_source.html +++ b/_i_users_client_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IUsersClient.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client
7 {
11  public interface IUsersClient
12  {
18  Task<User> Read(CancellationToken cancellationToken);
19 
26  Task<User> GetId(User user, CancellationToken cancellationToken);
27 
33  Task<IReadOnlyList<User>> List(CancellationToken cancellationToken);
34 
41  Task<User> Create(UserUpdate user, CancellationToken cancellationToken);
42 
49  Task<User> Update(UserUpdate user, CancellationToken cancellationToken);
50  }
51 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Client
7 {
11  public interface IUsersClient
12  {
18  Task<User> Read(CancellationToken cancellationToken);
19 
26  Task<User> GetId(User user, CancellationToken cancellationToken);
27 
33  Task<IReadOnlyList<User>> List(CancellationToken cancellationToken);
34 
41  Task<User> Create(UserUpdate user, CancellationToken cancellationToken);
42 
49  Task<User> Update(UserUpdate user, CancellationToken cancellationToken);
50  }
51 }
+ -
For editing a given User. Will never be returned by the API
Definition: UserUpdate.cs:6
diff --git a/_identity_cache_8cs_source.html b/_identity_cache_8cs_source.html index 3691697590..a1e909e150 100644 --- a/_identity_cache_8cs_source.html +++ b/_identity_cache_8cs_source.html @@ -92,14 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IdentityCache.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
4 
5 namespace Tgstation.Server.Host.Security
6 {
9  {
10  readonly Dictionary<long, IdentityCacheObject> cachedIdentities;
11 
12  public IdentityCache()
13  {
14  cachedIdentities = new Dictionary<long, IdentityCacheObject>();
15  }
16 
18  public void Dispose()
19  {
20  foreach (var I in cachedIdentities)
21  I.Value.Dispose();
22  }
23 
25  public void CacheSystemIdentity(User user, ISystemIdentity systemIdentity, DateTimeOffset expiry)
26  {
27  if (user == null)
28  throw new ArgumentNullException(nameof(user));
29  if (systemIdentity == null)
30  throw new ArgumentNullException(nameof(systemIdentity));
31  lock (cachedIdentities)
32  {
33  if (cachedIdentities.TryGetValue(user.Id, out var identCache))
34  identCache.Dispose(); //also clears it out
35  identCache = new IdentityCacheObject(systemIdentity.Clone(), () =>
36  {
37  lock (cachedIdentities)
38  cachedIdentities.Remove(user.Id);
39  }, expiry);
40  cachedIdentities.Add(user.Id, identCache);
41  }
42  }
43 
46  {
47  if (user == null)
48  throw new ArgumentNullException(nameof(user));
49  lock (cachedIdentities)
50  {
51  if (cachedIdentities.TryGetValue(user.Id, out var identity))
52  return identity.SystemIdentity;
53  return null;
54  }
55  }
56  }
57 }
Represents a user on the current System.Runtime.InteropServices.OSPlatform
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
4 
5 namespace Tgstation.Server.Host.Security
6 {
9  {
10  readonly Dictionary<long, IdentityCacheObject> cachedIdentities;
11 
12  public IdentityCache()
13  {
14  cachedIdentities = new Dictionary<long, IdentityCacheObject>();
15  }
16 
18  public void Dispose()
19  {
20  foreach (var I in cachedIdentities)
21  I.Value.Dispose();
22  }
23 
25  public void CacheSystemIdentity(User user, ISystemIdentity systemIdentity, DateTimeOffset expiry)
26  {
27  if (user == null)
28  throw new ArgumentNullException(nameof(user));
29  if (systemIdentity == null)
30  throw new ArgumentNullException(nameof(systemIdentity));
31  lock (cachedIdentities)
32  {
33  if (cachedIdentities.TryGetValue(user.Id, out var identCache))
34  identCache.Dispose(); //also clears it out
35  identCache = new IdentityCacheObject(systemIdentity.Clone(), () =>
36  {
37  lock (cachedIdentities)
38  cachedIdentities.Remove(user.Id);
39  }, expiry);
40  cachedIdentities.Add(user.Id, identCache);
41  }
42  }
43 
46  {
47  if (user == null)
48  throw new ArgumentNullException(nameof(user));
49  lock (cachedIdentities)
50  {
51  if (cachedIdentities.TryGetValue(user.Id, out var identity))
52  return identity.SystemIdentity;
53  return null;
54  }
55  }
56  }
57 }
+
Represents a user on the current System.Runtime.InteropServices.OSPlatform
readonly Dictionary< long, IdentityCacheObject > cachedIdentities
-
void CacheSystemIdentity(User user, ISystemIdentity systemIdentity, DateTimeOffset expiry)
Keep a user &#39;s systemIdentity alive until an expiry time
long Id
The ID of the User
Definition: User.cs:15
ISystemIdentity LoadCachedIdentity(User user)
Attempt to load a cached ISystemIdentity
diff --git a/_identity_cache_object_8cs_source.html b/_identity_cache_object_8cs_source.html index 918fb7b7f4..cc82dc5d9a 100644 --- a/_identity_cache_object_8cs_source.html +++ b/_identity_cache_object_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IdentityCacheObject.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Security
6 {
11  {
15  public ISystemIdentity SystemIdentity { get; }
16 
20  readonly CancellationTokenSource cancellationTokenSource;
21 
25  readonly Task task;
26 
33  public IdentityCacheObject(ISystemIdentity systemIdentity, Action onExpiry, DateTimeOffset expiry)
34  {
35  SystemIdentity = systemIdentity ?? throw new ArgumentNullException(nameof(systemIdentity));
36  if (onExpiry == null)
37  throw new ArgumentNullException(nameof(onExpiry));
38  var now = DateTimeOffset.Now;
39  if (expiry < now)
40  throw new ArgumentOutOfRangeException(nameof(expiry), expiry, "expiry must be greater than DateTimeOffset.Now!");
41 
42  cancellationTokenSource = new CancellationTokenSource();
43 
44  async Task DisposeOnExipiry(CancellationToken cancellationToken)
45  {
46  using (SystemIdentity)
47  try
48  {
49  await Task.Delay(expiry - now, cancellationToken).ConfigureAwait(false);
50  }
51  finally
52  {
53  onExpiry();
54  }
55  }
56 
57  task = DisposeOnExipiry(cancellationTokenSource.Token);
58  }
59 
61  public void Dispose()
62  {
63  cancellationTokenSource.Cancel();
64  try
65  {
66  task.GetAwaiter().GetResult();
67  }
68  catch (OperationCanceledException) { }
69  cancellationTokenSource.Dispose();
70  }
71  }
72 }
readonly Task task
The Task to clean up SystemIdentity
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Security
6 {
11  {
15  public ISystemIdentity SystemIdentity { get; }
16 
20  readonly CancellationTokenSource cancellationTokenSource;
21 
25  readonly Task task;
26 
33  public IdentityCacheObject(ISystemIdentity systemIdentity, Action onExpiry, DateTimeOffset expiry)
34  {
35  SystemIdentity = systemIdentity ?? throw new ArgumentNullException(nameof(systemIdentity));
36  if (onExpiry == null)
37  throw new ArgumentNullException(nameof(onExpiry));
38  var now = DateTimeOffset.Now;
39  if (expiry < now)
40  throw new ArgumentOutOfRangeException(nameof(expiry), expiry, "expiry must be greater than DateTimeOffset.Now!");
41 
42  cancellationTokenSource = new CancellationTokenSource();
43 
44  async Task DisposeOnExipiry(CancellationToken cancellationToken)
45  {
46  using (SystemIdentity)
47  try
48  {
49  await Task.Delay(expiry - now, cancellationToken).ConfigureAwait(false);
50  }
51  finally
52  {
53  onExpiry();
54  }
55  }
56 
57  task = DisposeOnExipiry(cancellationTokenSource.Token);
58  }
59 
61  public void Dispose()
62  {
63  cancellationTokenSource.Cancel();
64  try
65  {
66  task.GetAwaiter().GetResult();
67  }
68  catch (OperationCanceledException) { }
69  cancellationTokenSource.Dispose();
70  }
71  }
72 }
readonly Task task
The Task to clean up SystemIdentity
+
Represents a user on the current System.Runtime.InteropServices.OSPlatform
-
readonly CancellationTokenSource cancellationTokenSource
The cancellationTokenSource for the IdentityCache
For keeping a specific ISystemIdentity alive for a period of time
diff --git a/_instance_client_8cs_source.html b/_instance_client_8cs_source.html index 79aca4ffc2..e55f55d420 100644 --- a/_instance_client_8cs_source.html +++ b/_instance_client_8cs_source.html @@ -92,11 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceClient.cs
-Go to the documentation of this file.
1 using System;
3 
4 namespace Tgstation.Server.Client.Components
5 {
8  {
10  public Instance Metadata { get; }
11 
13  public IByondClient Byond { get; }
14 
16  public IRepositoryClient Repository { get; }
17 
20 
22  public IConfigurationClient Configuration { get; }
23 
25  public IInstanceUserClient Users { get; }
26 
28  public IChatBotsClient ChatBots { get; }
29 
31  public IDreamMakerClient DreamMaker { get; }
32 
34  public IJobsClient Jobs { get; }
35 
40 
46  public InstanceClient(IApiClient apiClient, Instance instance)
47  {
48  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
49  Metadata = instance ?? throw new ArgumentNullException(nameof(instance));
50 
51  Byond = new ByondClient(apiClient, instance);
52  Repository = new RepositoryClient(apiClient, instance);
53  DreamDaemon = new DreamDaemonClient(apiClient, instance);
54  Configuration = new ConfigurationClient(apiClient, instance);
55  Users = new InstanceUserClient(apiClient, instance);
56  ChatBots = new ChatBotsClient(apiClient, instance);
57  DreamMaker = new DreamMakerClient(apiClient, instance);
58  Jobs = new JobsClient(apiClient, instance);
59  }
60  }
61 }
+Go to the documentation of this file.
1 using System;
3 
4 namespace Tgstation.Server.Client.Components
5 {
8  {
10  public Instance Metadata { get; }
11 
13  public IByondClient Byond { get; }
14 
16  public IRepositoryClient Repository { get; }
17 
20 
22  public IConfigurationClient Configuration { get; }
23 
25  public IInstanceUserClient Users { get; }
26 
28  public IChatBotsClient ChatBots { get; }
29 
31  public IDreamMakerClient DreamMaker { get; }
32 
34  public IJobsClient Jobs { get; }
35 
40 
46  public InstanceClient(IApiClient apiClient, Instance instance)
47  {
48  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
49  Metadata = instance ?? throw new ArgumentNullException(nameof(instance));
50 
51  Byond = new ByondClient(apiClient, instance);
52  Repository = new RepositoryClient(apiClient, instance);
53  DreamDaemon = new DreamDaemonClient(apiClient, instance);
54  Configuration = new ConfigurationClient(apiClient, instance);
55  Users = new InstanceUserClient(apiClient, instance);
56  ChatBots = new ChatBotsClient(apiClient, instance);
57  DreamMaker = new DreamMakerClient(apiClient, instance);
58  Jobs = new JobsClient(apiClient, instance);
59  }
60  }
61 }
Represents the state of the DreamMaker compiler. Create action starts a new compile. Delete action cancels the current compile
Definition: DreamMaker.cs:8
+
For managing the Byond installation
Definition: IByondClient.cs:11
readonly IApiClient apiClient
The IApiClient for the InstanceClient
@@ -108,7 +109,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Represents a git repository
Definition: Repository.cs:8
- diff --git a/_instance_controller_8cs_source.html b/_instance_controller_8cs_source.html index ac37a8861e..a0d203992d 100644 --- a/_instance_controller_8cs_source.html +++ b/_instance_controller_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceController.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Collections.Generic;
6 using System.Globalization;
7 using System.IO;
8 using System.Linq;
9 using System.Linq.Expressions;
10 using System.Net;
11 using System.Reflection;
12 using System.Runtime.InteropServices;
13 using System.Threading;
14 using System.Threading.Tasks;
15 using Tgstation.Server.Api;
20 using Tgstation.Server.Host.IO;
23 
24 namespace Tgstation.Server.Host.Controllers
25 {
29  [Route(Routes.InstanceManager)]
30  public sealed class InstanceController : ModelController<Api.Models.Instance>
31  {
35  const string InstanceAttachFileName = "TGS4_ALLOW_INSTANCE_ATTACH";
36 
37  const string MoveInstanceJobPrefix = "Move instance ID ";
38 
51 
56 
67  public InstanceController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, IIOManager ioManager, IApplication application, ILogger<InstanceController> logger) : base(databaseContext, authenticationContextFactory, logger, false)
68  {
69  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
70  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
71  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
72  this.application = application ?? throw new ArgumentNullException(nameof(application));
73  }
74 
75  void NormalizeModelPath(Api.Models.Instance model, out string absolutePath)
76  {
77  if (model.Path == null)
78  {
79  absolutePath = null;
80  return;
81  }
82  absolutePath = ioManager.ResolvePath(model.Path);
83  if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
84  model.Path = absolutePath.ToUpperInvariant();
85  else
86  model.Path = absolutePath;
87  }
88 
89  Models.InstanceUser InstanceAdminUser() => new Models.InstanceUser
90  {
91  ByondRights = (ByondRights)~0U,
99  };
100 
102  [TgsAuthorize(InstanceManagerRights.Create)]
103  public override async Task<IActionResult> Create([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
104  {
105  if (model == null)
106  throw new ArgumentNullException(nameof(model));
107 
108  if (String.IsNullOrWhiteSpace(model.Name))
109  return BadRequest(new ErrorMessage { Message = "name must not be empty!" });
110 
111  if (model.Path == null)
112  return BadRequest(new ErrorMessage { Message = "path must not be empty!" });
113 
114  NormalizeModelPath(model, out var rawPath);
115  var dirExistsTask = ioManager.DirectoryExists(model.Path, cancellationToken);
116  bool attached = false;
117  if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false))
118  if (!await ioManager.FileExists(ioManager.ConcatPath(model.Path, InstanceAttachFileName), cancellationToken).ConfigureAwait(false))
119  return Conflict(new ErrorMessage { Message = "Path not empty!" });
120  else
121  attached = true;
122 
123  var newInstance = new Models.Instance
124  {
125  ConfigurationType = model.ConfigurationType ?? ConfigurationType.Disallowed,
127  {
128  AllowWebClient = false,
129  AutoStart = false,
130  PrimaryPort = 1337,
131  SecondaryPort = 1338,
132  SecurityLevel = DreamDaemonSecurity.Safe,
133  SoftRestart = false,
134  SoftShutdown = false,
135  StartupTimeout = 20
136  },
138  {
139  ApiValidationPort = 1339,
140  ApiValidationSecurityLevel = DreamDaemonSecurity.Safe
141  },
142  Name = model.Name,
143  Online = false,
144  Path = model.Path,
145  AutoUpdateInterval = model.AutoUpdateInterval ?? 0,
147  {
148  CommitterEmail = "tgstation-server@users.noreply.github.com",
149  CommitterName = application.VersionPrefix,
150  PushTestMergeCommits = false,
151  ShowTestMergeCommitters = false,
152  AutoUpdatesKeepTestMerges = false,
153  AutoUpdatesSynchronize = false
154  },
155  //give this user full privileges on the instance
156  InstanceUsers = new List<Models.InstanceUser>
157  {
158  InstanceAdminUser()
159  }
160  };
161 
162  DatabaseContext.Instances.Add(newInstance);
163  try
164  {
165  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
166 
167  try
168  {
169  //actually reserve it now
170  await ioManager.CreateDirectory(rawPath, cancellationToken).ConfigureAwait(false);
171  await ioManager.DeleteFile(ioManager.ConcatPath(rawPath, InstanceAttachFileName), cancellationToken).ConfigureAwait(false);
172  }
173  catch
174  {
175  //oh shit delete the model
176  DatabaseContext.Instances.Remove(newInstance);
177 
178  await DatabaseContext.Save(default).ConfigureAwait(false);
179  throw;
180  }
181  }
182  catch (IOException e)
183  {
184  return Conflict(new ErrorMessage { Message = e.Message });
185  }
186  catch (DbUpdateException e)
187  {
188  return Conflict(new ErrorMessage { Message = e.Message });
189  }
190 
191  Logger.LogInformation("{0} {1} instance {2}: {3} ({4})", AuthenticationContext.User.Name, attached ? "attached" : "created", newInstance.Name, newInstance.Id, newInstance.Path);
192 
193  var api = newInstance.ToApi();
194  return attached ? (IActionResult)Json(api) : StatusCode((int)HttpStatusCode.Created, api);
195  }
196 
198  [TgsAuthorize(InstanceManagerRights.Delete)]
199  public override async Task<IActionResult> Delete(long id, CancellationToken cancellationToken)
200  {
201  var originalModel = await DatabaseContext.Instances.Where(x => x.Id == id)
202  .Include(x => x.WatchdogReattachInformation)
203  .Include(x => x.WatchdogReattachInformation.Alpha)
204  .Include(x => x.WatchdogReattachInformation.Bravo)
205  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
206  if (originalModel == default)
207  return StatusCode((int)HttpStatusCode.Gone);
208  if (originalModel.Online.Value)
209  return Conflict(new ErrorMessage
210  {
211  Message = "Cannot detach an online instance!"
212  });
213 
214  if (originalModel.WatchdogReattachInformation != null)
215  {
216  DatabaseContext.WatchdogReattachInformations.Remove(originalModel.WatchdogReattachInformation);
217  if (originalModel.WatchdogReattachInformation.Alpha != null)
218  DatabaseContext.ReattachInformations.Remove(originalModel.WatchdogReattachInformation.Alpha);
219  if (originalModel.WatchdogReattachInformation.Bravo != null)
220  DatabaseContext.ReattachInformations.Remove(originalModel.WatchdogReattachInformation.Bravo);
221  }
222 
223  DatabaseContext.Instances.Remove(originalModel);
224 
225  var attachFileName = ioManager.ConcatPath(originalModel.Path, InstanceAttachFileName);
226  await ioManager.WriteAllBytes(attachFileName, Array.Empty<byte>(), default).ConfigureAwait(false);
227  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); //cascades everything
228  return Ok();
229  }
230 
232  [TgsAuthorize(InstanceManagerRights.Relocate | InstanceManagerRights.Rename | InstanceManagerRights.SetAutoUpdate | InstanceManagerRights.SetConfiguration | InstanceManagerRights.SetOnline)]
233  public override async Task<IActionResult> Update([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
234  {
235  var instanceQuery = DatabaseContext.Instances.Where(x => x.Id == model.Id);
236 
237  var moveJob = await instanceQuery
238  .SelectMany(x => x.Jobs).
239  Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix, StringComparison.Ordinal))
240  .Select(x => new Models.Job
241  {
242  Id = x.Id
243  }).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
244 
245  if (moveJob != default)
246  //cancel it now
247  await jobManager.CancelJob(moveJob, AuthenticationContext.User, true, cancellationToken).ConfigureAwait(false);
248 
249  var usersInstanceUserTask = instanceQuery.SelectMany(x => x.InstanceUsers).Where(x => x.UserId == AuthenticationContext.User.Id).FirstOrDefaultAsync(cancellationToken);
250 
251  var originalModel = await instanceQuery
252  .Include(x => x.RepositorySettings)
253  .Include(x => x.ChatSettings)
254  .ThenInclude(x => x.Channels)
255  .Include(x => x.DreamDaemonSettings) //need these for onlining
256  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
257  if (originalModel == default(Models.Instance))
258  return StatusCode((int)HttpStatusCode.Gone);
259 
260  var userRights = (InstanceManagerRights)AuthenticationContext.GetRight(RightsType.InstanceManager);
261  bool CheckModified<T>(Expression<Func<Api.Models.Instance, T>> expression, InstanceManagerRights requiredRight)
262  {
263  var memberSelectorExpression = (MemberExpression)expression.Body;
264  var property = (PropertyInfo)memberSelectorExpression.Member;
265 
266  var newVal = property.GetValue(model);
267  if (newVal == null)
268  return false;
269  if (!userRights.HasFlag(requiredRight) && property.GetValue(originalModel) != newVal)
270  return true;
271 
272  property.SetValue(originalModel, newVal);
273  return false;
274  };
275 
276  string originalModelPath = null;
277  string rawPath = null;
278  if (model.Path != null)
279  {
280  NormalizeModelPath(model, out rawPath);
281 
282  if (model.Path != originalModel.Path)
283  {
284  if (!userRights.HasFlag(InstanceManagerRights.Relocate))
285  return Forbid();
286  if (originalModel.Online.Value && model.Online != true)
287  return Conflict(new ErrorMessage { Message = "Cannot relocate an online instance!" });
288 
289  var dirExistsTask = ioManager.DirectoryExists(model.Path, cancellationToken);
290  if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false))
291  return Conflict(new ErrorMessage { Message = "Path not empty!" });
292 
293  originalModelPath = originalModel.Path;
294  originalModel.Path = model.Path;
295  }
296  }
297 
298  var originalOnline = originalModel.Online.Value;
299  var renamed = model.Name != null && originalModel.Name != model.Name;
300 
301  if (CheckModified(x => x.AutoUpdateInterval, InstanceManagerRights.SetAutoUpdate)
302  || CheckModified(x => x.ConfigurationType, InstanceManagerRights.SetConfiguration)
303  || CheckModified(x => x.Name, InstanceManagerRights.Rename)
304  || CheckModified(x => x.Online, InstanceManagerRights.SetOnline))
305  return Forbid();
306 
307  //ensure the current user has write privilege on the instance
308  var usersInstanceUser = await usersInstanceUserTask.ConfigureAwait(false);
309  if (usersInstanceUser == default)
310  originalModel.InstanceUsers.Add(InstanceAdminUser());
311  else
312  usersInstanceUser.InstanceUserRights |= InstanceUserRights.WriteUsers;
313 
314  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
315 
316  if (renamed)
317  instanceManager.GetInstance(originalModel).Rename(originalModel.Name);
318 
319  var oldAutoStart = originalModel.DreamDaemonSettings.AutoStart;
320  try
321  {
322  if (originalOnline && model.Online == false)
323  await instanceManager.OfflineInstance(originalModel, AuthenticationContext.User, cancellationToken).ConfigureAwait(false);
324  else if (!originalOnline && model.Online == true)
325  {
326  //force autostart false here because we don't want any long running jobs right now
327  //remember to document this
328  originalModel.DreamDaemonSettings.AutoStart = false;
329  await instanceManager.OnlineInstance(originalModel, cancellationToken).ConfigureAwait(false);
330  }
331  }
332  catch (Exception e)
333  {
334  Logger.LogError("Error changing instance online state! Exception: {0}", e);
335  originalModel.Online = originalOnline;
336  originalModel.DreamDaemonSettings.AutoStart = oldAutoStart;
337  if (originalModelPath != null)
338  originalModel.Path = originalModelPath;
339  await DatabaseContext.Save(default).ConfigureAwait(false);
340  throw;
341  }
342 
343  var api = originalModel.ToApi();
344  if (originalModelPath != null)
345  {
346  var job = new Models.Job
347  {
348  Description = String.Format(CultureInfo.InvariantCulture, MoveInstanceJobPrefix + "{0} from {1} to {2}", originalModel.Id, originalModel.Path, rawPath),
349  Instance = originalModel,
350  CancelRightsType = RightsType.InstanceManager,
351  CancelRight = (ulong)InstanceManagerRights.Relocate,
352  StartedBy = AuthenticationContext.User
353  };
354 
355  await jobManager.RegisterOperation(job, (paramJob, databaseContext, progressHandler, ct) => instanceManager.MoveInstance(originalModel, rawPath, ct), cancellationToken).ConfigureAwait(false);
356  api.MoveJob = job.ToApi();
357  }
358 
359  return Json(api);
360  }
361 
363  [TgsAuthorize(InstanceManagerRights.List | InstanceManagerRights.Read)]
364  public override async Task<IActionResult> List(CancellationToken cancellationToken)
365  {
366  IQueryable<Models.Instance> query = DatabaseContext.Instances;
368  query = query.Where(x => x.InstanceUsers.Any(y => y.UserId == AuthenticationContext.User.Id)).Where(x => x.InstanceUsers.Any(y => y.AnyRights));
369 
370  var moveJobTasks = query
371  .SelectMany(x => x.Jobs)
372  .Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix, StringComparison.Ordinal))
373  .Include(x => x.StartedBy).ThenInclude(x => x.CreatedBy)
374  .Include(x => x.Instance)
375  .ToListAsync(cancellationToken);
376  var instances = await query.ToListAsync(cancellationToken).ConfigureAwait(false);
377  var apis = instances.Select(x => x.ToApi());
378  var moveJobs = await moveJobTasks.ConfigureAwait(false);
379  foreach(var I in moveJobs)
380  apis.Where(x => x.Id == I.Instance.Id).First().MoveJob = I.ToApi(); //if this .First() fails i will personally murder kevinz000 because I just know he is somehow responsible
381  return Json(apis);
382  }
383 
385  [TgsAuthorize(InstanceManagerRights.List | InstanceManagerRights.Read)]
386  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
387  {
388  var query = DatabaseContext.Instances.Where(x => x.Id == id);
389  var cantList = !AuthenticationContext.User.InstanceManagerRights.Value.HasFlag(InstanceManagerRights.List);
390 
391  if (cantList)
392  query = query.Include(x => x.InstanceUsers);
393 
394  var moveJobTask = query
395  .SelectMany(x => x.Jobs)
396  .Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix, StringComparison.Ordinal))
397  .Include(x => x.StartedBy).ThenInclude(x => x.CreatedBy)
398  .FirstOrDefaultAsync(cancellationToken);
399  var instance = await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
400 
401  if (instance == null)
402  return StatusCode((int)HttpStatusCode.Gone);
403 
404  if (cantList && !instance.InstanceUsers.Any(x => x.UserId == AuthenticationContext.User.Id && x.AnyRights))
405  return Forbid();
406 
407  var api = instance.ToApi();
408  api.MoveJob = (await moveJobTask.ConfigureAwait(false))?.ToApi();
409  return Json(api);
410  }
411  }
412 }
string Name
The name of the User
Definition: User.cs:38
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Collections.Generic;
6 using System.Globalization;
7 using System.IO;
8 using System.Linq;
9 using System.Linq.Expressions;
10 using System.Net;
11 using System.Reflection;
12 using System.Runtime.InteropServices;
13 using System.Threading;
14 using System.Threading.Tasks;
15 using Tgstation.Server.Api;
20 using Tgstation.Server.Host.IO;
23 
24 namespace Tgstation.Server.Host.Controllers
25 {
29  [Route(Routes.InstanceManager)]
30  public sealed class InstanceController : ModelController<Api.Models.Instance>
31  {
35  const string InstanceAttachFileName = "TGS4_ALLOW_INSTANCE_ATTACH";
36 
37  const string MoveInstanceJobPrefix = "Move instance ID ";
38 
51 
56 
67  public InstanceController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, IIOManager ioManager, IApplication application, ILogger<InstanceController> logger) : base(databaseContext, authenticationContextFactory, logger, false)
68  {
69  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
70  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
71  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
72  this.application = application ?? throw new ArgumentNullException(nameof(application));
73  }
74 
75  void NormalizeModelPath(Api.Models.Instance model, out string absolutePath)
76  {
77  if (model.Path == null)
78  {
79  absolutePath = null;
80  return;
81  }
82  absolutePath = ioManager.ResolvePath(model.Path);
83  if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
84  model.Path = absolutePath.ToUpperInvariant();
85  else
86  model.Path = absolutePath;
87  }
88 
89  Models.InstanceUser InstanceAdminUser() => new Models.InstanceUser
90  {
91  ByondRights = (ByondRights)~0U,
99  };
100 
102  [TgsAuthorize(InstanceManagerRights.Create)]
103  public override async Task<IActionResult> Create([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
104  {
105  if (model == null)
106  throw new ArgumentNullException(nameof(model));
107 
108  if (String.IsNullOrWhiteSpace(model.Name))
109  return BadRequest(new ErrorMessage { Message = "name must not be empty!" });
110 
111  if (model.Path == null)
112  return BadRequest(new ErrorMessage { Message = "path must not be empty!" });
113 
114  NormalizeModelPath(model, out var rawPath);
115  var dirExistsTask = ioManager.DirectoryExists(model.Path, cancellationToken);
116  bool attached = false;
117  if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false))
118  if (!await ioManager.FileExists(ioManager.ConcatPath(model.Path, InstanceAttachFileName), cancellationToken).ConfigureAwait(false))
119  return Conflict(new ErrorMessage { Message = "Path not empty!" });
120  else
121  attached = true;
122 
123  var newInstance = new Models.Instance
124  {
125  ConfigurationType = model.ConfigurationType ?? ConfigurationType.Disallowed,
127  {
128  AllowWebClient = false,
129  AutoStart = false,
130  PrimaryPort = 1337,
131  SecondaryPort = 1338,
132  SecurityLevel = DreamDaemonSecurity.Safe,
133  SoftRestart = false,
134  SoftShutdown = false,
135  StartupTimeout = 20
136  },
138  {
139  ApiValidationPort = 1339,
140  ApiValidationSecurityLevel = DreamDaemonSecurity.Safe
141  },
142  Name = model.Name,
143  Online = false,
144  Path = model.Path,
145  AutoUpdateInterval = model.AutoUpdateInterval ?? 0,
147  {
148  CommitterEmail = "tgstation-server@users.noreply.github.com",
149  CommitterName = application.VersionPrefix,
150  PushTestMergeCommits = false,
151  ShowTestMergeCommitters = false,
152  AutoUpdatesKeepTestMerges = false,
153  AutoUpdatesSynchronize = false
154  },
155  //give this user full privileges on the instance
156  InstanceUsers = new List<Models.InstanceUser>
157  {
158  InstanceAdminUser()
159  }
160  };
161 
162  DatabaseContext.Instances.Add(newInstance);
163  try
164  {
165  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
166 
167  try
168  {
169  //actually reserve it now
170  await ioManager.CreateDirectory(rawPath, cancellationToken).ConfigureAwait(false);
171  await ioManager.DeleteFile(ioManager.ConcatPath(rawPath, InstanceAttachFileName), cancellationToken).ConfigureAwait(false);
172  }
173  catch
174  {
175  //oh shit delete the model
176  DatabaseContext.Instances.Remove(newInstance);
177 
178  await DatabaseContext.Save(default).ConfigureAwait(false);
179  throw;
180  }
181  }
182  catch (IOException e)
183  {
184  return Conflict(new ErrorMessage { Message = e.Message });
185  }
186  catch (DbUpdateException e)
187  {
188  return Conflict(new ErrorMessage { Message = e.Message });
189  }
190 
191  Logger.LogInformation("{0} {1} instance {2}: {3} ({4})", AuthenticationContext.User.Name, attached ? "attached" : "created", newInstance.Name, newInstance.Id, newInstance.Path);
192 
193  var api = newInstance.ToApi();
194  return attached ? (IActionResult)Json(api) : StatusCode((int)HttpStatusCode.Created, api);
195  }
196 
198  [TgsAuthorize(InstanceManagerRights.Delete)]
199  public override async Task<IActionResult> Delete(long id, CancellationToken cancellationToken)
200  {
201  var originalModel = await DatabaseContext.Instances.Where(x => x.Id == id)
202  .Include(x => x.WatchdogReattachInformation)
203  .Include(x => x.WatchdogReattachInformation.Alpha)
204  .Include(x => x.WatchdogReattachInformation.Bravo)
205  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
206  if (originalModel == default)
207  return StatusCode((int)HttpStatusCode.Gone);
208  if (originalModel.Online.Value)
209  return Conflict(new ErrorMessage
210  {
211  Message = "Cannot detach an online instance!"
212  });
213 
214  if (originalModel.WatchdogReattachInformation != null)
215  {
216  DatabaseContext.WatchdogReattachInformations.Remove(originalModel.WatchdogReattachInformation);
217  if (originalModel.WatchdogReattachInformation.Alpha != null)
218  DatabaseContext.ReattachInformations.Remove(originalModel.WatchdogReattachInformation.Alpha);
219  if (originalModel.WatchdogReattachInformation.Bravo != null)
220  DatabaseContext.ReattachInformations.Remove(originalModel.WatchdogReattachInformation.Bravo);
221  }
222 
223  DatabaseContext.Instances.Remove(originalModel);
224 
225  var attachFileName = ioManager.ConcatPath(originalModel.Path, InstanceAttachFileName);
226  await ioManager.WriteAllBytes(attachFileName, Array.Empty<byte>(), default).ConfigureAwait(false);
227  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); //cascades everything
228  return Ok();
229  }
230 
232  [TgsAuthorize(InstanceManagerRights.Relocate | InstanceManagerRights.Rename | InstanceManagerRights.SetAutoUpdate | InstanceManagerRights.SetConfiguration | InstanceManagerRights.SetOnline)]
233  public override async Task<IActionResult> Update([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
234  {
235  var instanceQuery = DatabaseContext.Instances.Where(x => x.Id == model.Id);
236 
237  var moveJob = await instanceQuery
238  .SelectMany(x => x.Jobs).
239  Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix, StringComparison.Ordinal))
240  .Select(x => new Models.Job
241  {
242  Id = x.Id
243  }).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
244 
245  if (moveJob != default)
246  //cancel it now
247  await jobManager.CancelJob(moveJob, AuthenticationContext.User, true, cancellationToken).ConfigureAwait(false);
248 
249  var usersInstanceUserTask = instanceQuery.SelectMany(x => x.InstanceUsers).Where(x => x.UserId == AuthenticationContext.User.Id).FirstOrDefaultAsync(cancellationToken);
250 
251  var originalModel = await instanceQuery
252  .Include(x => x.RepositorySettings)
253  .Include(x => x.ChatSettings)
254  .ThenInclude(x => x.Channels)
255  .Include(x => x.DreamDaemonSettings) //need these for onlining
256  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
257  if (originalModel == default(Models.Instance))
258  return StatusCode((int)HttpStatusCode.Gone);
259 
260  var userRights = (InstanceManagerRights)AuthenticationContext.GetRight(RightsType.InstanceManager);
261  bool CheckModified<T>(Expression<Func<Api.Models.Instance, T>> expression, InstanceManagerRights requiredRight)
262  {
263  var memberSelectorExpression = (MemberExpression)expression.Body;
264  var property = (PropertyInfo)memberSelectorExpression.Member;
265 
266  var newVal = property.GetValue(model);
267  if (newVal == null)
268  return false;
269  if (!userRights.HasFlag(requiredRight) && property.GetValue(originalModel) != newVal)
270  return true;
271 
272  property.SetValue(originalModel, newVal);
273  return false;
274  };
275 
276  string originalModelPath = null;
277  string rawPath = null;
278  if (model.Path != null)
279  {
280  NormalizeModelPath(model, out rawPath);
281 
282  if (model.Path != originalModel.Path)
283  {
284  if (!userRights.HasFlag(InstanceManagerRights.Relocate))
285  return Forbid();
286  if (originalModel.Online.Value && model.Online != true)
287  return Conflict(new ErrorMessage { Message = "Cannot relocate an online instance!" });
288 
289  var dirExistsTask = ioManager.DirectoryExists(model.Path, cancellationToken);
290  if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false))
291  return Conflict(new ErrorMessage { Message = "Path not empty!" });
292 
293  originalModelPath = originalModel.Path;
294  originalModel.Path = model.Path;
295  }
296  }
297 
298  var originalOnline = originalModel.Online.Value;
299  var renamed = model.Name != null && originalModel.Name != model.Name;
300 
301  if (CheckModified(x => x.AutoUpdateInterval, InstanceManagerRights.SetAutoUpdate)
302  || CheckModified(x => x.ConfigurationType, InstanceManagerRights.SetConfiguration)
303  || CheckModified(x => x.Name, InstanceManagerRights.Rename)
304  || CheckModified(x => x.Online, InstanceManagerRights.SetOnline))
305  return Forbid();
306 
307  //ensure the current user has write privilege on the instance
308  var usersInstanceUser = await usersInstanceUserTask.ConfigureAwait(false);
309  if (usersInstanceUser == default)
310  originalModel.InstanceUsers.Add(InstanceAdminUser());
311  else
312  usersInstanceUser.InstanceUserRights |= InstanceUserRights.WriteUsers;
313 
314  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
315 
316  if (renamed)
317  instanceManager.GetInstance(originalModel).Rename(originalModel.Name);
318 
319  var oldAutoStart = originalModel.DreamDaemonSettings.AutoStart;
320  try
321  {
322  if (originalOnline && model.Online == false)
323  await instanceManager.OfflineInstance(originalModel, AuthenticationContext.User, cancellationToken).ConfigureAwait(false);
324  else if (!originalOnline && model.Online == true)
325  {
326  //force autostart false here because we don't want any long running jobs right now
327  //remember to document this
328  originalModel.DreamDaemonSettings.AutoStart = false;
329  await instanceManager.OnlineInstance(originalModel, cancellationToken).ConfigureAwait(false);
330  }
331  }
332  catch (Exception e)
333  {
334  Logger.LogError("Error changing instance online state! Exception: {0}", e);
335  originalModel.Online = originalOnline;
336  originalModel.DreamDaemonSettings.AutoStart = oldAutoStart;
337  if (originalModelPath != null)
338  originalModel.Path = originalModelPath;
339  await DatabaseContext.Save(default).ConfigureAwait(false);
340  throw;
341  }
342 
343  var api = originalModel.ToApi();
344  if (originalModelPath != null)
345  {
346  var job = new Models.Job
347  {
348  Description = String.Format(CultureInfo.InvariantCulture, MoveInstanceJobPrefix + "{0} from {1} to {2}", originalModel.Id, originalModel.Path, rawPath),
349  Instance = originalModel,
350  CancelRightsType = RightsType.InstanceManager,
351  CancelRight = (ulong)InstanceManagerRights.Relocate,
352  StartedBy = AuthenticationContext.User
353  };
354 
355  await jobManager.RegisterOperation(job, (paramJob, databaseContext, progressHandler, ct) => instanceManager.MoveInstance(originalModel, rawPath, ct), cancellationToken).ConfigureAwait(false);
356  api.MoveJob = job.ToApi();
357  }
358 
359  return Json(api);
360  }
361 
363  [TgsAuthorize(InstanceManagerRights.List | InstanceManagerRights.Read)]
364  public override async Task<IActionResult> List(CancellationToken cancellationToken)
365  {
366  IQueryable<Models.Instance> query = DatabaseContext.Instances;
368  query = query.Where(x => x.InstanceUsers.Any(y => y.UserId == AuthenticationContext.User.Id)).Where(x => x.InstanceUsers.Any(y => y.AnyRights));
369 
370  var moveJobTasks = query
371  .SelectMany(x => x.Jobs)
372  .Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix, StringComparison.Ordinal))
373  .Include(x => x.StartedBy).ThenInclude(x => x.CreatedBy)
374  .Include(x => x.Instance)
375  .ToListAsync(cancellationToken);
376  var instances = await query.ToListAsync(cancellationToken).ConfigureAwait(false);
377  var apis = instances.Select(x => x.ToApi());
378  var moveJobs = await moveJobTasks.ConfigureAwait(false);
379  foreach(var I in moveJobs)
380  apis.Where(x => x.Id == I.Instance.Id).First().MoveJob = I.ToApi(); //if this .First() fails i will personally murder kevinz000 because I just know he is somehow responsible
381  return Json(apis);
382  }
383 
385  [TgsAuthorize(InstanceManagerRights.List | InstanceManagerRights.Read)]
386  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
387  {
388  var query = DatabaseContext.Instances.Where(x => x.Id == id);
389  var cantList = !AuthenticationContext.User.InstanceManagerRights.Value.HasFlag(InstanceManagerRights.List);
390 
391  if (cantList)
392  query = query.Include(x => x.InstanceUsers);
393 
394  var moveJobTask = query
395  .SelectMany(x => x.Jobs)
396  .Where(x => !x.StoppedAt.HasValue && x.Description.StartsWith(MoveInstanceJobPrefix, StringComparison.Ordinal))
397  .Include(x => x.StartedBy).ThenInclude(x => x.CreatedBy)
398  .FirstOrDefaultAsync(cancellationToken);
399  var instance = await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
400 
401  if (instance == null)
402  return StatusCode((int)HttpStatusCode.Gone);
403 
404  if (cantList && !instance.InstanceUsers.Any(x => x.UserId == AuthenticationContext.User.Id && x.AnyRights))
405  return Forbid();
406 
407  var api = instance.ToApi();
408  api.MoveJob = (await moveJobTask.ConfigureAwait(false))?.ToApi();
409  return Json(api);
410  }
411  }
412 }
string Name
The name of the User
Definition: User.cs:38
Manages the runtime of Jobs
Definition: IJobManager.cs:12
@@ -108,6 +108,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceUserRights
Rights for an Models.Instance
Configures the ASP.NET Core web application
Definition: IApplication.cs:8
+
InstanceManagerRights InstanceManagerRights
The Rights.InstanceManagerRights for the User
Definition: User.cs:50
readonly IInstanceManager instanceManager
The IInstanceManager for the InstanceController
readonly IApplication application
The IApplication for the InstanceController
@@ -129,7 +130,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceManagerRights
Rights for managing Models.Instances
string Message
A human readable description of the error
Definition: ErrorMessage.cs:13
DreamMakerRights
Rights for Models.DreamMaker
-
override async Task< IActionResult > Create([FromBody] Api.Models.Instance model, CancellationToken cancellationToken)
RepositoryRights
Rights for a Models.Repository
diff --git a/_instance_factory_8cs_source.html b/_instance_factory_8cs_source.html index 60b6694bcb..2a3b1f9231 100644 --- a/_instance_factory_8cs_source.html +++ b/_instance_factory_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceFactory.cs
-Go to the documentation of this file.
1 using Byond.TopicSender;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Threading;
5 using System.Threading.Tasks;
13 using Tgstation.Server.Host.IO;
15 
16 namespace Tgstation.Server.Host.Components
17 {
20  {
25 
30 
35 
39  readonly ILoggerFactory loggerFactory;
40 
44  readonly IByondTopicSender byondTopicSender;
45 
50 
55 
60 
65 
70 
75 
80 
85 
90 
95 
100 
120  public InstanceFactory(IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, ILoggerFactory loggerFactory, IByondTopicSender byondTopicSender, IServerControl serverUpdater, ICryptographySuite cryptographySuite, ISynchronousIOManager synchronousIOManager, ISymlinkFactory symlinkFactory, IByondInstaller byondInstaller, IProviderFactory providerFactory, IProcessExecutor processExecutor, IPostWriteHandler postWriteHandler, IWatchdogFactory watchdogFactory, IJobManager jobManager, ICredentialsProvider credentialsProvider)
121  {
122  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
123  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
124  this.application = application ?? throw new ArgumentNullException(nameof(application));
125  this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
126  this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender));
127  this.serverUpdater = serverUpdater ?? throw new ArgumentNullException(nameof(serverUpdater));
128  this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
129  this.synchronousIOManager = synchronousIOManager ?? throw new ArgumentNullException(nameof(synchronousIOManager));
130  this.symlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory));
131  this.byondInstaller = byondInstaller ?? throw new ArgumentNullException(nameof(byondInstaller));
132  this.providerFactory = providerFactory ?? throw new ArgumentNullException(nameof(providerFactory));
133  this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor));
134  this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler));
135  this.watchdogFactory = watchdogFactory ?? throw new ArgumentNullException(nameof(watchdogFactory));
136  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
137  this.credentialsProvider = credentialsProvider ?? throw new ArgumentNullException(nameof(credentialsProvider));
138  }
139 
141  public IInstance CreateInstance(Models.Instance metadata)
142  {
143  //Create the ioManager for the instance
144  var instanceIoManager = new ResolvingIOManager(ioManager, metadata.Path);
145 
146  //various other ioManagers
147  var repoIoManager = new ResolvingIOManager(instanceIoManager, "Repository");
148  var byondIOManager = new ResolvingIOManager(instanceIoManager, "Byond");
149  var gameIoManager = new ResolvingIOManager(instanceIoManager, "Game");
150  var configurationIoManager = new ResolvingIOManager(instanceIoManager, "Configuration");
151 
152  var configuration = new StaticFiles.Configuration(configurationIoManager, synchronousIOManager, symlinkFactory, processExecutor, postWriteHandler, loggerFactory.CreateLogger<StaticFiles.Configuration>());
153  var eventConsumer = new EventConsumer(configuration);
154 
155  var dmbFactory = new DmbFactory(databaseContextFactory, gameIoManager, loggerFactory.CreateLogger<DmbFactory>(), metadata.CloneMetadata());
156  try
157  {
158  var repoManager = new RepositoryManager(metadata.RepositorySettings, repoIoManager, eventConsumer, credentialsProvider, loggerFactory.CreateLogger<Repository.Repository>(), loggerFactory.CreateLogger<RepositoryManager>());
159  try
160  {
161  var byond = new ByondManager(byondIOManager, byondInstaller, loggerFactory.CreateLogger<ByondManager>());
162 
163  var commandFactory = new CommandFactory(application, byond, repoManager, databaseContextFactory, metadata);
164  var chatFactory = new ChatFactory(instanceIoManager, loggerFactory, commandFactory, providerFactory);
165 
166  var chat = chatFactory.CreateChat(metadata.ChatSettings);
167  try
168  {
169  var sessionControllerFactory = new SessionControllerFactory(processExecutor, byond, byondTopicSender, cryptographySuite, application, gameIoManager, chat, loggerFactory, metadata.CloneMetadata());
170  var reattachInfoHandler = new ReattachInfoHandler(databaseContextFactory, dmbFactory, loggerFactory.CreateLogger<ReattachInfoHandler>(), metadata.CloneMetadata());
171  var watchdog = watchdogFactory.CreateWatchdog(chat, dmbFactory, reattachInfoHandler, configuration, sessionControllerFactory, metadata.CloneMetadata(), metadata.DreamDaemonSettings);
172  eventConsumer.SetWatchdog(watchdog);
173  commandFactory.SetWatchdog(watchdog);
174  try
175  {
176  var dreamMaker = new DreamMaker(byond, gameIoManager, configuration, sessionControllerFactory, dmbFactory, application, eventConsumer, chat, processExecutor, watchdog, loggerFactory.CreateLogger<DreamMaker>());
177 
178  return new Instance(metadata.CloneMetadata(), repoManager, byond, dreamMaker, watchdog, chat, configuration, dmbFactory, databaseContextFactory, dmbFactory, jobManager, loggerFactory.CreateLogger<Instance>());
179  }
180  catch
181  {
182  watchdog.Dispose();
183  throw;
184  }
185  }
186  catch
187  {
188  chat.Dispose();
189  throw;
190  }
191  }
192  catch
193  {
194  repoManager.Dispose();
195  throw;
196  }
197  }
198  catch
199  {
200  dmbFactory.Dispose();
201  throw;
202  }
203  }
204 
206  public Task StartAsync(CancellationToken cancellationToken) => byondInstaller.CleanCache(cancellationToken);
207 
209  public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
210  }
211 }
+Go to the documentation of this file.
1 using Byond.TopicSender;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Threading;
5 using System.Threading.Tasks;
13 using Tgstation.Server.Host.IO;
15 
16 namespace Tgstation.Server.Host.Components
17 {
20  {
25 
30 
35 
39  readonly ILoggerFactory loggerFactory;
40 
44  readonly IByondTopicSender byondTopicSender;
45 
50 
55 
60 
65 
70 
75 
80 
85 
90 
95 
100 
120  public InstanceFactory(IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, ILoggerFactory loggerFactory, IByondTopicSender byondTopicSender, IServerControl serverUpdater, ICryptographySuite cryptographySuite, ISynchronousIOManager synchronousIOManager, ISymlinkFactory symlinkFactory, IByondInstaller byondInstaller, IProviderFactory providerFactory, IProcessExecutor processExecutor, IPostWriteHandler postWriteHandler, IWatchdogFactory watchdogFactory, IJobManager jobManager, ICredentialsProvider credentialsProvider)
121  {
122  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
123  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
124  this.application = application ?? throw new ArgumentNullException(nameof(application));
125  this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
126  this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender));
127  this.serverUpdater = serverUpdater ?? throw new ArgumentNullException(nameof(serverUpdater));
128  this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
129  this.synchronousIOManager = synchronousIOManager ?? throw new ArgumentNullException(nameof(synchronousIOManager));
130  this.symlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory));
131  this.byondInstaller = byondInstaller ?? throw new ArgumentNullException(nameof(byondInstaller));
132  this.providerFactory = providerFactory ?? throw new ArgumentNullException(nameof(providerFactory));
133  this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor));
134  this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler));
135  this.watchdogFactory = watchdogFactory ?? throw new ArgumentNullException(nameof(watchdogFactory));
136  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
137  this.credentialsProvider = credentialsProvider ?? throw new ArgumentNullException(nameof(credentialsProvider));
138  }
139 
141  public IInstance CreateInstance(Models.Instance metadata)
142  {
143  //Create the ioManager for the instance
144  var instanceIoManager = new ResolvingIOManager(ioManager, metadata.Path);
145 
146  //various other ioManagers
147  var repoIoManager = new ResolvingIOManager(instanceIoManager, "Repository");
148  var byondIOManager = new ResolvingIOManager(instanceIoManager, "Byond");
149  var gameIoManager = new ResolvingIOManager(instanceIoManager, "Game");
150  var configurationIoManager = new ResolvingIOManager(instanceIoManager, "Configuration");
151 
152  var configuration = new StaticFiles.Configuration(configurationIoManager, synchronousIOManager, symlinkFactory, processExecutor, postWriteHandler, loggerFactory.CreateLogger<StaticFiles.Configuration>());
153  var eventConsumer = new EventConsumer(configuration);
154 
155  var dmbFactory = new DmbFactory(databaseContextFactory, gameIoManager, loggerFactory.CreateLogger<DmbFactory>(), metadata.CloneMetadata());
156  try
157  {
158  var repoManager = new RepositoryManager(metadata.RepositorySettings, repoIoManager, eventConsumer, credentialsProvider, loggerFactory.CreateLogger<Repository.Repository>(), loggerFactory.CreateLogger<RepositoryManager>());
159  try
160  {
161  var byond = new ByondManager(byondIOManager, byondInstaller, loggerFactory.CreateLogger<ByondManager>());
162 
163  var commandFactory = new CommandFactory(application, byond, repoManager, databaseContextFactory, metadata);
164  var chatFactory = new ChatFactory(instanceIoManager, loggerFactory, commandFactory, providerFactory);
165 
166  var chat = chatFactory.CreateChat(metadata.ChatSettings);
167  try
168  {
169  var sessionControllerFactory = new SessionControllerFactory(processExecutor, byond, byondTopicSender, cryptographySuite, application, gameIoManager, chat, loggerFactory, metadata.CloneMetadata());
170  var reattachInfoHandler = new ReattachInfoHandler(databaseContextFactory, dmbFactory, loggerFactory.CreateLogger<ReattachInfoHandler>(), metadata.CloneMetadata());
171  var watchdog = watchdogFactory.CreateWatchdog(chat, dmbFactory, reattachInfoHandler, configuration, sessionControllerFactory, metadata.CloneMetadata(), metadata.DreamDaemonSettings);
172  eventConsumer.SetWatchdog(watchdog);
173  commandFactory.SetWatchdog(watchdog);
174  try
175  {
176  var dreamMaker = new DreamMaker(byond, gameIoManager, configuration, sessionControllerFactory, dmbFactory, application, eventConsumer, chat, processExecutor, watchdog, loggerFactory.CreateLogger<DreamMaker>());
177 
178  return new Instance(metadata.CloneMetadata(), repoManager, byond, dreamMaker, watchdog, chat, configuration, dmbFactory, databaseContextFactory, dmbFactory, jobManager, loggerFactory.CreateLogger<Instance>());
179  }
180  catch
181  {
182  watchdog.Dispose();
183  throw;
184  }
185  }
186  catch
187  {
188  chat.Dispose();
189  throw;
190  }
191  }
192  catch
193  {
194  repoManager.Dispose();
195  throw;
196  }
197  }
198  catch
199  {
200  dmbFactory.Dispose();
201  throw;
202  }
203  }
204 
206  public Task StartAsync(CancellationToken cancellationToken) => byondInstaller.CleanCache(cancellationToken);
207 
209  public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
210  }
211 }
readonly ISynchronousIOManager synchronousIOManager
The ISynchronousIOManager for the InstanceFactory
Manages the runtime of Jobs
Definition: IJobManager.cs:12
readonly IByondInstaller byondInstaller
The IByondInstaller for the InstanceFactory
@@ -101,6 +101,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
An IIOManager that resolve relative paths from another IIOManager to a subdirectory of that ...
Configures the ASP.NET Core web application
Definition: IApplication.cs:8
+
readonly ILoggerFactory loggerFactory
The ILoggerFactory for the InstanceFactory
@@ -132,7 +133,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
For interacting with the instance services
Definition: IInstance.cs:17
-
readonly ICredentialsProvider credentialsProvider
The ICredentialsProvider for the InstanceFactory
readonly IProviderFactory providerFactory
The IProviderFactory for the InstanceFactory
readonly IIOManager ioManager
The IIOManager for the InstanceFactory
diff --git a/_instance_manager_8cs_source.html b/_instance_manager_8cs_source.html index b302efabc4..88fa26b1aa 100644 --- a/_instance_manager_8cs_source.html +++ b/_instance_manager_8cs_source.html @@ -92,10 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceManager.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Hosting;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Threading;
8 using System.Threading.Tasks;
10 using Tgstation.Server.Host.IO;
11 
12 namespace Tgstation.Server.Host.Components
13 {
16  {
21 
26 
31 
36 
41 
45  readonly ILogger<InstanceManager> logger;
46 
50  readonly Dictionary<long, IInstance> instances;
51 
55  readonly List<Task> shutdownTasks;
56 
60  readonly CancellationTokenSource shutdownCancellationTokenSource;
61 
72  public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, IJobManager jobManager, IServerControl serverControl, ILogger<InstanceManager> logger)
73  {
74  this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory));
75  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
76  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
77  this.application = application ?? throw new ArgumentNullException(nameof(application));
78  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
79 
80  if (serverControl == null)
81  throw new ArgumentNullException(nameof(serverControl));
82 
83  shutdownCancellationTokenSource = new CancellationTokenSource();
84  var cancellationToken = shutdownCancellationTokenSource.Token;
85  serverControl.RegisterForRestart(() =>
86  {
87  lock (this)
88  shutdownTasks.AddRange(instances.Select(x => x.Value.Chat.SendBroadcast("TGS: Restart requested...", cancellationToken)));
89  });
90 
91  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
92 
93  instances = new Dictionary<long, IInstance>();
94  shutdownTasks = new List<Task>();
95  }
96 
98  public void Dispose()
99  {
100  foreach (var I in instances)
101  I.Value.Dispose();
102  shutdownCancellationTokenSource.Dispose();
103  }
104 
106  public IInstance GetInstance(Models.Instance metadata)
107  {
108  if (metadata == null)
109  throw new ArgumentNullException(nameof(metadata));
110  lock (this)
111  {
112  if (!instances.TryGetValue(metadata.Id, out IInstance instance))
113  throw new InvalidOperationException("Instance not online!");
114  return instance;
115  }
116  }
117 
119  public async Task MoveInstance(Models.Instance instance, string newPath, CancellationToken cancellationToken)
120  {
121  if (newPath == null)
122  throw new ArgumentNullException(nameof(newPath));
123  if (instance.Online.Value)
124  throw new InvalidOperationException("Cannot move an online instance!");
125  var oldPath = instance.Path;
126  await ioManager.CopyDirectory(oldPath, newPath, null, cancellationToken).ConfigureAwait(false);
127  await databaseContextFactory.UseContext(db =>
128  {
129  var targetInstance = new Models.Instance
130  {
131  Id = instance.Id
132  };
133  db.Instances.Attach(targetInstance);
134  targetInstance.Path = newPath;
135  return db.Save(cancellationToken);
136  }).ConfigureAwait(false);
137  await ioManager.DeleteDirectory(oldPath, cancellationToken).ConfigureAwait(false);
138  }
139 
141  public async Task OfflineInstance(Models.Instance metadata, Models.User user, CancellationToken cancellationToken)
142  {
143  if (metadata == null)
144  throw new ArgumentNullException(nameof(metadata));
145  logger.LogInformation("Offlining instance ID {0}", metadata.Id);
146  IInstance instance;
147  lock (this)
148  {
149  if (!instances.TryGetValue(metadata.Id, out instance))
150  throw new InvalidOperationException("Instance not online!");
151  instances.Remove(metadata.Id);
152  }
153  try
154  {
155  //we are the one responsible for cancelling his jobs
156  var tasks = new List<Task>();
157  await databaseContextFactory.UseContext(async db =>
158  {
159  var jobs = db.Jobs.Where(x => x.Instance.Id == metadata.Id).Select(x => new Models.Job
160  {
161  Id = x.Id
162  }).ToAsyncEnumerable();
163  await jobs.ForEachAsync(job =>
164  {
165  lock (tasks)
166  tasks.Add(jobManager.CancelJob(job, user, true, cancellationToken));
167  }, cancellationToken).ConfigureAwait(false);
168  }).ConfigureAwait(false);
169 
170  await Task.WhenAll(tasks).ConfigureAwait(false);
171 
172  await instance.StopAsync(cancellationToken).ConfigureAwait(false);
173  }
174  finally
175  {
176  instance.Dispose();
177  }
178  }
179 
181  public async Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken)
182  {
183  if (metadata == null)
184  throw new ArgumentNullException(nameof(metadata));
185  logger.LogInformation("Onlining instance ID {0} ({1}) at {2}", metadata.Id, metadata.Name, metadata.Path);
186  var instance = instanceFactory.CreateInstance(metadata);
187  try
188  {
189  lock (this)
190  {
191  if (instances.ContainsKey(metadata.Id))
192  throw new InvalidOperationException("Instance already online!");
193  instances.Add(metadata.Id, instance);
194  }
195  }
196  catch
197  {
198  instance.Dispose();
199  throw;
200  }
201  await instance.StartAsync(cancellationToken).ConfigureAwait(false);
202  }
203 
205  public Task StartAsync(CancellationToken cancellationToken) => databaseContextFactory.UseContext(async databaseContext =>
206  {
207  try
208  {
209  var factoryStartup = instanceFactory.StartAsync(cancellationToken);
210  await databaseContext.Initialize(cancellationToken).ConfigureAwait(false);
211  await jobManager.StartAsync(cancellationToken).ConfigureAwait(false);
212  var dbInstances = databaseContext.Instances.Where(x => x.Online.Value)
213  .Include(x => x.RepositorySettings)
214  .Include(x => x.ChatSettings)
215  .ThenInclude(x => x.Channels)
216  .Include(x => x.DreamDaemonSettings)
217  .ToAsyncEnumerable();
218  var tasks = new List<Task>();
219  await factoryStartup.ConfigureAwait(false);
220  await dbInstances.ForEachAsync(metadata => tasks.Add(metadata.Online.Value ? OnlineInstance(metadata, cancellationToken) : Task.CompletedTask), cancellationToken).ConfigureAwait(false);
221  await Task.WhenAll(tasks).ConfigureAwait(false);
222  logger.LogInformation("Instance manager ready!");
223  application.Ready(null);
224  }
225  catch (OperationCanceledException)
226  {
227  logger.LogInformation("Cancelled instance manager initialization!");
228  }
229  catch (Exception e)
230  {
231  logger.LogCritical("Instance manager startup error! Exception: {0}", e);
232  application.Ready(e);
233  }
234  });
235 
237  public async Task StopAsync(CancellationToken cancellationToken)
238  {
239  await jobManager.StopAsync(cancellationToken).ConfigureAwait(false);
240 
241  using (cancellationToken.Register(() => shutdownCancellationTokenSource.Cancel()))
242  await Task.WhenAll(shutdownTasks).ConfigureAwait(false);
243  await Task.WhenAll(instances.Select(x => x.Value.StopAsync(cancellationToken))).ConfigureAwait(false);
244 
245  await instanceFactory.StopAsync(cancellationToken).ConfigureAwait(false);
246  }
247  }
248 }
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Hosting;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Threading;
8 using System.Threading.Tasks;
10 using Tgstation.Server.Host.IO;
11 
12 namespace Tgstation.Server.Host.Components
13 {
16  {
21 
26 
31 
36 
41 
45  readonly ILogger<InstanceManager> logger;
46 
50  readonly Dictionary<long, IInstance> instances;
51 
55  readonly List<Task> shutdownTasks;
56 
60  readonly CancellationTokenSource shutdownCancellationTokenSource;
61 
72  public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, IJobManager jobManager, IServerControl serverControl, ILogger<InstanceManager> logger)
73  {
74  this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory));
75  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
76  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
77  this.application = application ?? throw new ArgumentNullException(nameof(application));
78  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
79 
80  if (serverControl == null)
81  throw new ArgumentNullException(nameof(serverControl));
82 
83  shutdownCancellationTokenSource = new CancellationTokenSource();
84  var cancellationToken = shutdownCancellationTokenSource.Token;
85  serverControl.RegisterForRestart(() =>
86  {
87  lock (this)
88  shutdownTasks.AddRange(instances.Select(x => x.Value.Chat.SendBroadcast("TGS: Restart requested...", cancellationToken)));
89  });
90 
91  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
92 
93  instances = new Dictionary<long, IInstance>();
94  shutdownTasks = new List<Task>();
95  }
96 
98  public void Dispose()
99  {
100  foreach (var I in instances)
101  I.Value.Dispose();
102  shutdownCancellationTokenSource.Dispose();
103  }
104 
106  public IInstance GetInstance(Models.Instance metadata)
107  {
108  if (metadata == null)
109  throw new ArgumentNullException(nameof(metadata));
110  lock (this)
111  {
112  if (!instances.TryGetValue(metadata.Id, out IInstance instance))
113  throw new InvalidOperationException("Instance not online!");
114  return instance;
115  }
116  }
117 
119  public async Task MoveInstance(Models.Instance instance, string newPath, CancellationToken cancellationToken)
120  {
121  if (newPath == null)
122  throw new ArgumentNullException(nameof(newPath));
123  if (instance.Online.Value)
124  throw new InvalidOperationException("Cannot move an online instance!");
125  var oldPath = instance.Path;
126  await ioManager.CopyDirectory(oldPath, newPath, null, cancellationToken).ConfigureAwait(false);
127  await databaseContextFactory.UseContext(db =>
128  {
129  var targetInstance = new Models.Instance
130  {
131  Id = instance.Id
132  };
133  db.Instances.Attach(targetInstance);
134  targetInstance.Path = newPath;
135  return db.Save(cancellationToken);
136  }).ConfigureAwait(false);
137  await ioManager.DeleteDirectory(oldPath, cancellationToken).ConfigureAwait(false);
138  }
139 
141  public async Task OfflineInstance(Models.Instance metadata, Models.User user, CancellationToken cancellationToken)
142  {
143  if (metadata == null)
144  throw new ArgumentNullException(nameof(metadata));
145  logger.LogInformation("Offlining instance ID {0}", metadata.Id);
146  IInstance instance;
147  lock (this)
148  {
149  if (!instances.TryGetValue(metadata.Id, out instance))
150  throw new InvalidOperationException("Instance not online!");
151  instances.Remove(metadata.Id);
152  }
153  try
154  {
155  //we are the one responsible for cancelling his jobs
156  var tasks = new List<Task>();
157  await databaseContextFactory.UseContext(async db =>
158  {
159  var jobs = db.Jobs.Where(x => x.Instance.Id == metadata.Id).Select(x => new Models.Job
160  {
161  Id = x.Id
162  }).ToAsyncEnumerable();
163  await jobs.ForEachAsync(job =>
164  {
165  lock (tasks)
166  tasks.Add(jobManager.CancelJob(job, user, true, cancellationToken));
167  }, cancellationToken).ConfigureAwait(false);
168  }).ConfigureAwait(false);
169 
170  await Task.WhenAll(tasks).ConfigureAwait(false);
171 
172  await instance.StopAsync(cancellationToken).ConfigureAwait(false);
173  }
174  finally
175  {
176  instance.Dispose();
177  }
178  }
179 
181  public async Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken)
182  {
183  if (metadata == null)
184  throw new ArgumentNullException(nameof(metadata));
185  logger.LogInformation("Onlining instance ID {0} ({1}) at {2}", metadata.Id, metadata.Name, metadata.Path);
186  var instance = instanceFactory.CreateInstance(metadata);
187  try
188  {
189  lock (this)
190  {
191  if (instances.ContainsKey(metadata.Id))
192  throw new InvalidOperationException("Instance already online!");
193  instances.Add(metadata.Id, instance);
194  }
195  }
196  catch
197  {
198  instance.Dispose();
199  throw;
200  }
201  await instance.StartAsync(cancellationToken).ConfigureAwait(false);
202  }
203 
205  public Task StartAsync(CancellationToken cancellationToken) => databaseContextFactory.UseContext(async databaseContext =>
206  {
207  try
208  {
209  var factoryStartup = instanceFactory.StartAsync(cancellationToken);
210  await databaseContext.Initialize(cancellationToken).ConfigureAwait(false);
211  await jobManager.StartAsync(cancellationToken).ConfigureAwait(false);
212  var dbInstances = databaseContext.Instances.Where(x => x.Online.Value)
213  .Include(x => x.RepositorySettings)
214  .Include(x => x.ChatSettings)
215  .ThenInclude(x => x.Channels)
216  .Include(x => x.DreamDaemonSettings)
217  .ToAsyncEnumerable();
218  var tasks = new List<Task>();
219  await factoryStartup.ConfigureAwait(false);
220  await dbInstances.ForEachAsync(metadata => tasks.Add(metadata.Online.Value ? OnlineInstance(metadata, cancellationToken) : Task.CompletedTask), cancellationToken).ConfigureAwait(false);
221  await Task.WhenAll(tasks).ConfigureAwait(false);
222  logger.LogInformation("Instance manager ready!");
223  application.Ready(null);
224  }
225  catch (OperationCanceledException)
226  {
227  logger.LogInformation("Cancelled instance manager initialization!");
228  }
229  catch (Exception e)
230  {
231  logger.LogCritical("Instance manager startup error! Exception: {0}", e);
232  application.Ready(e);
233  }
234  });
235 
237  public async Task StopAsync(CancellationToken cancellationToken)
238  {
239  await jobManager.StopAsync(cancellationToken).ConfigureAwait(false);
240 
241  using (cancellationToken.Register(() => shutdownCancellationTokenSource.Cancel()))
242  await Task.WhenAll(shutdownTasks).ConfigureAwait(false);
243  await Task.WhenAll(instances.Select(x => x.Value.StopAsync(cancellationToken))).ConfigureAwait(false);
244 
245  await instanceFactory.StopAsync(cancellationToken).ConfigureAwait(false);
246  }
247  }
248 }
Manages the runtime of Jobs
Definition: IJobManager.cs:12
IInstance GetInstance(Models.Instance metadata)
Get the IInstance associated with given metadata
Configures the ASP.NET Core web application
Definition: IApplication.cs:8
+
async Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken)
Online an IInstance
Factory for scoping usage of IDatabaseContexts. Meant for use by Components
@@ -106,7 +107,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly IJobManager jobManager
The IJobManager for the InstanceManager
For interacting with the instance services
Definition: IInstance.cs:17
readonly List< Task > shutdownTasks
List<T> of Tasks to finish in StopAsync(CancellationToken)
-
async Task StopAsync(CancellationToken cancellationToken)
diff --git a/_instance_manager_client_8cs_source.html b/_instance_manager_client_8cs_source.html index 80ef91a7da..db05b4e684 100644 --- a/_instance_manager_client_8cs_source.html +++ b/_instance_manager_client_8cs_source.html @@ -92,9 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceManagerClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
8 
9 namespace Tgstation.Server.Client
10 {
13  {
18 
22  readonly Dictionary<long, IInstanceClient> cachedClients;
23 
29  {
30  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
31 
32  cachedClients = new Dictionary<long, IInstanceClient>();
33  }
34 
36  public Task<Instance> CreateOrAttach(Instance instance, CancellationToken cancellationToken) => apiClient.Create<Instance, Instance>(Routes.InstanceManager, instance ?? throw new ArgumentNullException(nameof(instance)), cancellationToken);
37 
39  public Task Detach(Instance instance, CancellationToken cancellationToken) => apiClient.Delete(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken);
40 
42  public Task<IReadOnlyList<Instance>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<Instance>>(Routes.List(Routes.InstanceManager), cancellationToken);
43 
45  public Task<Instance> Update(Instance instance, CancellationToken cancellationToken) => apiClient.Update<Instance, Instance>(Routes.InstanceManager, instance ?? throw new ArgumentNullException(nameof(instance)), cancellationToken);
46 
48  public Task<Instance> GetId(Instance instance, CancellationToken cancellationToken) => apiClient.Read<Instance>(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken);
49 
52  {
53  if (!cachedClients.TryGetValue(instance?.Id ?? throw new ArgumentNullException(nameof(instance)), out var client))
54  {
55  client = new InstanceClient(apiClient, instance);
56  cachedClients.Add(instance.Id, client);
57  }
58  return client;
59  }
60  }
61 }
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
8 
9 namespace Tgstation.Server.Client
10 {
13  {
18 
22  readonly Dictionary<long, IInstanceClient> cachedClients;
23 
29  {
30  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
31 
32  cachedClients = new Dictionary<long, IInstanceClient>();
33  }
34 
36  public Task<Instance> CreateOrAttach(Instance instance, CancellationToken cancellationToken) => apiClient.Create<Instance, Instance>(Routes.InstanceManager, instance ?? throw new ArgumentNullException(nameof(instance)), cancellationToken);
37 
39  public Task Detach(Instance instance, CancellationToken cancellationToken) => apiClient.Delete(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken);
40 
42  public Task<IReadOnlyList<Instance>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<Instance>>(Routes.List(Routes.InstanceManager), cancellationToken);
43 
45  public Task<Instance> Update(Instance instance, CancellationToken cancellationToken) => apiClient.Update<Instance, Instance>(Routes.InstanceManager, instance ?? throw new ArgumentNullException(nameof(instance)), cancellationToken);
46 
48  public Task<Instance> GetId(Instance instance, CancellationToken cancellationToken) => apiClient.Read<Instance>(Routes.SetID(Routes.InstanceManager, instance?.Id ?? throw new ArgumentNullException(nameof(instance))), cancellationToken);
49 
52  {
53  if (!cachedClients.TryGetValue(instance?.Id ?? throw new ArgumentNullException(nameof(instance)), out var client))
54  {
55  client = new InstanceClient(apiClient, instance);
56  cachedClients.Add(instance.Id, client);
57  }
58  return client;
59  }
60  }
61 }
+
IInstanceClient CreateClient(Instance instance)
Create an IInstanceClient for a given Instance
@@ -104,7 +105,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly IApiClient apiClient
The IApiClient for the InstanceManagerClient
InstanceManagerClient(IApiClient apiClient)
Construct an InstanceManagerClient
-
readonly Dictionary< long, IInstanceClient > cachedClients
Map of already created IInstanceClients
long Id
The id of the Instance. Not modifiable
Definition: Instance.cs:14
diff --git a/_instance_manager_rights_8cs_source.html b/_instance_manager_rights_8cs_source.html index f2a60378e7..2aa9dd2497 100644 --- a/_instance_manager_rights_8cs_source.html +++ b/_instance_manager_rights_8cs_source.html @@ -92,14 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceManagerRights.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum InstanceManagerRights : ulong
10  {
14  None = 0,
18  Read = 1,
22  Create = 2,
26  Rename = 4,
30  Relocate = 8,
34  SetOnline = 16,
38  Delete = 32,
42  List = 64,
46  SetConfiguration = 128,
50  SetAutoUpdate = 256
51  }
52 }
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum InstanceManagerRights : ulong
10  {
14  None = 0,
18  Read = 1,
22  Create = 2,
26  Rename = 4,
30  Relocate = 8,
34  SetOnline = 16,
38  Delete = 32,
42  List = 64,
46  SetConfiguration = 128,
50  SetAutoUpdate = 256
51  }
52 }
+
User can change Models.Instance.ConfigurationType
InstanceManagerRights
Rights for managing Models.Instances
User can create new Models.ChatBot
-
User can change Models.Instance.AutoUpdateInterval
User can online Models.Instances they can view
User can relocate Models.Instances they can view
diff --git a/_instance_manager_test_8cs_source.html b/_instance_manager_test_8cs_source.html index debf3a64a0..437535d849 100644 --- a/_instance_manager_test_8cs_source.html +++ b/_instance_manager_test_8cs_source.html @@ -92,11 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceManagerTest.cs
-Go to the documentation of this file.
1 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using System;
3 using System.IO;
4 using System.Reflection;
5 using System.Threading;
6 using System.Threading.Tasks;
11 
12 namespace Tgstation.Server.Tests
13 {
14  sealed class InstanceManagerTest
15  {
17  readonly string testRootPath;
18 
19  long counter;
20 
21  public InstanceManagerTest(IInstanceManagerClient instanceManagerClient, string testRootPath)
22  {
23  this.instanceManagerClient = instanceManagerClient ?? throw new ArgumentNullException(nameof(instanceManagerClient));
24  this.testRootPath = testRootPath ?? throw new ArgumentNullException(nameof(testRootPath));
25 
26  counter = 0;
27  }
28 
29  Task<Api.Models.Instance> CreateTestInstance(CancellationToken cancellationToken) => instanceManagerClient.CreateOrAttach(new Api.Models.Instance
30  {
31  Name = "TestInstance-" + ++counter,
32  Path = Path.Combine(testRootPath, Guid.NewGuid().ToString()),
33  Online = true
34  }, cancellationToken);
35 
36  public async Task Run(CancellationToken cancellationToken)
37  {
38  var firstTest = await CreateTestInstance(cancellationToken).ConfigureAwait(false);
39  //instances always start offline
40  Assert.AreEqual(false, firstTest.Online);
41  //check it exists
42  Assert.IsTrue(Directory.Exists(firstTest.Path));
43 
44  //cant create instances in existent directories
45  var testNonEmpty = Path.Combine(testRootPath, Guid.NewGuid().ToString());
46  Directory.CreateDirectory(testNonEmpty);
47  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.CreateOrAttach(new Api.Models.Instance
48  {
49  Path = testNonEmpty,
50  Name = "NonEmptyTest"
51  }, cancellationToken)).ConfigureAwait(false);
52  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.CreateOrAttach(firstTest, cancellationToken)).ConfigureAwait(false);
53 
54  //can't move to existent directories
55  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.Update(new Api.Models.Instance
56  {
57  Id = firstTest.Id,
58  Path = testNonEmpty
59  }, cancellationToken)).ConfigureAwait(false);
60 
61  //test basic move
62  Directory.Delete(testNonEmpty);
63  var initialPath = firstTest.Path;
64  firstTest = await instanceManagerClient.Update(new Api.Models.Instance
65  {
66  Id = firstTest.Id,
67  Path = testNonEmpty
68  }, cancellationToken).ConfigureAwait(false);
69 
70  Assert.IsNotNull(firstTest.MoveJob);
71 
72  do
73  {
74  firstTest = await instanceManagerClient.GetId(firstTest, cancellationToken).ConfigureAwait(false);
75  await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
76  } while (firstTest.MoveJob != null);
77 
78 
79  //online it for real for component tests
80  firstTest.Online = true;
81  firstTest.ConfigurationType = ConfigurationType.HostWrite;
82  firstTest = await instanceManagerClient.Update(firstTest, cancellationToken).ConfigureAwait(false);
83  Assert.AreEqual(true, firstTest.Online);
84  Assert.AreEqual(ConfigurationType.HostWrite, firstTest.ConfigurationType);
85 
86  //can't move online instance
87  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.Update(new Api.Models.Instance
88  {
89  Id = firstTest.Id,
90  Path = initialPath
91  }, cancellationToken)).ConfigureAwait(false);
92 
93  var testSuite1 = new InstanceTest(instanceManagerClient.CreateClient(firstTest));
94  await testSuite1.RunTests(cancellationToken).ConfigureAwait(false);
95 
96  //can't detach online instance
97  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.Detach(firstTest, cancellationToken)).ConfigureAwait(false);
98 
99  firstTest.Online = false;
100  firstTest = await instanceManagerClient.Update(firstTest, cancellationToken).ConfigureAwait(false);
101  await instanceManagerClient.Detach(firstTest, cancellationToken).ConfigureAwait(false);
102 
103  var instanceAttachFileName = (string)typeof(InstanceController).GetField("InstanceAttachFileName", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
104  var attachPath = Path.Combine(firstTest.Path, instanceAttachFileName);
105  Assert.IsTrue(File.Exists(attachPath));
106 
107  //can recreate detached instance
108  firstTest = await instanceManagerClient.CreateOrAttach(firstTest, cancellationToken).ConfigureAwait(false);
109 
110  //but only if the attach file exists
111  await instanceManagerClient.Detach(firstTest, cancellationToken).ConfigureAwait(false);
112  File.Delete(attachPath);
113  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.CreateOrAttach(firstTest, cancellationToken)).ConfigureAwait(false);
114  }
115  }
116 }
+Go to the documentation of this file.
1 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using System;
3 using System.IO;
4 using System.Reflection;
5 using System.Threading;
6 using System.Threading.Tasks;
11 
12 namespace Tgstation.Server.Tests
13 {
14  sealed class InstanceManagerTest
15  {
17  readonly string testRootPath;
18 
19  long counter;
20 
21  public InstanceManagerTest(IInstanceManagerClient instanceManagerClient, string testRootPath)
22  {
23  this.instanceManagerClient = instanceManagerClient ?? throw new ArgumentNullException(nameof(instanceManagerClient));
24  this.testRootPath = testRootPath ?? throw new ArgumentNullException(nameof(testRootPath));
25 
26  counter = 0;
27  }
28 
29  Task<Api.Models.Instance> CreateTestInstance(CancellationToken cancellationToken) => instanceManagerClient.CreateOrAttach(new Api.Models.Instance
30  {
31  Name = "TestInstance-" + ++counter,
32  Path = Path.Combine(testRootPath, Guid.NewGuid().ToString()),
33  Online = true
34  }, cancellationToken);
35 
36  public async Task Run(CancellationToken cancellationToken)
37  {
38  var firstTest = await CreateTestInstance(cancellationToken).ConfigureAwait(false);
39  //instances always start offline
40  Assert.AreEqual(false, firstTest.Online);
41  //check it exists
42  Assert.IsTrue(Directory.Exists(firstTest.Path));
43 
44  //cant create instances in existent directories
45  var testNonEmpty = Path.Combine(testRootPath, Guid.NewGuid().ToString());
46  Directory.CreateDirectory(testNonEmpty);
47  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.CreateOrAttach(new Api.Models.Instance
48  {
49  Path = testNonEmpty,
50  Name = "NonEmptyTest"
51  }, cancellationToken)).ConfigureAwait(false);
52  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.CreateOrAttach(firstTest, cancellationToken)).ConfigureAwait(false);
53 
54  //can't move to existent directories
55  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.Update(new Api.Models.Instance
56  {
57  Id = firstTest.Id,
58  Path = testNonEmpty
59  }, cancellationToken)).ConfigureAwait(false);
60 
61  //test basic move
62  Directory.Delete(testNonEmpty);
63  var initialPath = firstTest.Path;
64  firstTest = await instanceManagerClient.Update(new Api.Models.Instance
65  {
66  Id = firstTest.Id,
67  Path = testNonEmpty
68  }, cancellationToken).ConfigureAwait(false);
69 
70  Assert.IsNotNull(firstTest.MoveJob);
71 
72  do
73  {
74  firstTest = await instanceManagerClient.GetId(firstTest, cancellationToken).ConfigureAwait(false);
75  await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
76  } while (firstTest.MoveJob != null);
77 
78 
79  //online it for real for component tests
80  firstTest.Online = true;
81  firstTest.ConfigurationType = ConfigurationType.HostWrite;
82  firstTest = await instanceManagerClient.Update(firstTest, cancellationToken).ConfigureAwait(false);
83  Assert.AreEqual(true, firstTest.Online);
84  Assert.AreEqual(ConfigurationType.HostWrite, firstTest.ConfigurationType);
85 
86  //can't move online instance
87  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.Update(new Api.Models.Instance
88  {
89  Id = firstTest.Id,
90  Path = initialPath
91  }, cancellationToken)).ConfigureAwait(false);
92 
93  var testSuite1 = new InstanceTest(instanceManagerClient.CreateClient(firstTest));
94  await testSuite1.RunTests(cancellationToken).ConfigureAwait(false);
95 
96  //can't detach online instance
97  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.Detach(firstTest, cancellationToken)).ConfigureAwait(false);
98 
99  firstTest.Online = false;
100  firstTest = await instanceManagerClient.Update(firstTest, cancellationToken).ConfigureAwait(false);
101  await instanceManagerClient.Detach(firstTest, cancellationToken).ConfigureAwait(false);
102 
103  var instanceAttachFileName = (string)typeof(InstanceController).GetField("InstanceAttachFileName", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
104  var attachPath = Path.Combine(firstTest.Path, instanceAttachFileName);
105  Assert.IsTrue(File.Exists(attachPath));
106 
107  //can recreate detached instance
108  firstTest = await instanceManagerClient.CreateOrAttach(firstTest, cancellationToken).ConfigureAwait(false);
109 
110  //but only if the attach file exists
111  await instanceManagerClient.Detach(firstTest, cancellationToken).ConfigureAwait(false);
112  File.Delete(attachPath);
113  await Assert.ThrowsExceptionAsync<ConflictException>(() => instanceManagerClient.CreateOrAttach(firstTest, cancellationToken)).ConfigureAwait(false);
114  }
115  }
116 }
async Task RunTests(CancellationToken cancellationToken)
Definition: InstanceTest.cs:17
readonly IInstanceManagerClient instanceManagerClient
+ @@ -106,7 +107,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
async Task Run(CancellationToken cancellationToken)
-
Occurs when the client performs an action that would result in data conflict
diff --git a/_instance_test_8cs_source.html b/_instance_test_8cs_source.html index 1a2d936cd3..968d009666 100644 --- a/_instance_test_8cs_source.html +++ b/_instance_test_8cs_source.html @@ -92,15 +92,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceTest.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Tests.Instance
7 {
8  sealed class InstanceTest
9  {
11 
12  public InstanceTest(IInstanceClient instanceClient)
13  {
14  this.instanceClient = instanceClient ?? throw new ArgumentNullException(nameof(instanceClient));
15  }
16 
17  public async Task RunTests(CancellationToken cancellationToken)
18  {
19  var byondTests = new ByondTest(instanceClient.Byond, instanceClient.Jobs);
20  var configTests = new ConfigurationTest(instanceClient.Configuration, instanceClient.Metadata);
21 
22  var byondTest = byondTests.Run(cancellationToken);
23  await configTests.Run(cancellationToken).ConfigureAwait(false);
24  await byondTest.ConfigureAwait(false);
25  }
26  }
27 }
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Tests.Instance
7 {
8  sealed class InstanceTest
9  {
11 
12  public InstanceTest(IInstanceClient instanceClient)
13  {
14  this.instanceClient = instanceClient ?? throw new ArgumentNullException(nameof(instanceClient));
15  }
16 
17  public async Task RunTests(CancellationToken cancellationToken)
18  {
19  var byondTests = new ByondTest(instanceClient.Byond, instanceClient.Jobs);
20  var configTests = new ConfigurationTest(instanceClient.Configuration, instanceClient.Metadata);
21 
22  var byondTest = byondTests.Run(cancellationToken);
23  await configTests.Run(cancellationToken).ConfigureAwait(false);
24  await byondTest.ConfigureAwait(false);
25  }
26  }
27 }
async Task RunTests(CancellationToken cancellationToken)
Definition: InstanceTest.cs:17
+ -
InstanceTest(IInstanceClient instanceClient)
Definition: InstanceTest.cs:12
diff --git a/_instance_user_client_8cs_source.html b/_instance_user_client_8cs_source.html index 3628b79f00..dbade72530 100644 --- a/_instance_user_client_8cs_source.html +++ b/_instance_user_client_8cs_source.html @@ -92,8 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceUserClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client.Components
9 {
12  {
20  readonly Instance instance;
21 
27  public InstanceUserClient(IApiClient apiClient, Instance instance)
28  {
29  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
30  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
31  }
32 
34  public Task<InstanceUser> Create(InstanceUser instanceUser, CancellationToken cancellationToken) => apiClient.Create<InstanceUser, InstanceUser>(Routes.InstanceUser, instanceUser ?? throw new ArgumentNullException(nameof(instanceUser)), instance.Id, cancellationToken);
35 
36  public Task Delete(InstanceUser instanceUser, CancellationToken cancellationToken) => apiClient.Delete(Routes.SetID(Routes.InstanceUser, instanceUser.UserId.Value), instance.Id, cancellationToken);
37 
39  public Task<InstanceUser> Read(CancellationToken cancellationToken) => apiClient.Read<InstanceUser>(Routes.InstanceUser, instance.Id, cancellationToken);
40 
42  public Task<InstanceUser> Update(InstanceUser instanceUser, CancellationToken cancellationToken) => apiClient.Update<InstanceUser, InstanceUser>(Routes.InstanceUser, instanceUser ?? throw new ArgumentNullException(nameof(instanceUser)), instance.Id, cancellationToken);
43 
45  public Task<IReadOnlyList<InstanceUser>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<InstanceUser>>(Routes.List(Routes.InstanceUser), instance.Id, cancellationToken);
46 
48  public Task<InstanceUser> GetId(InstanceUser instanceUser, CancellationToken cancellationToken) => apiClient.Read<InstanceUser>(Routes.SetID(Routes.InstanceUser, instanceUser?.UserId ?? throw new ArgumentNullException(nameof(instanceUser))), instance.Id, cancellationToken);
49  }
50 }
readonly IApiClient apiClient
The IApiClient for the InstanceUserClient
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client.Components
9 {
12  {
20  readonly Instance instance;
21 
27  public InstanceUserClient(IApiClient apiClient, Instance instance)
28  {
29  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
30  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
31  }
32 
34  public Task<InstanceUser> Create(InstanceUser instanceUser, CancellationToken cancellationToken) => apiClient.Create<InstanceUser, InstanceUser>(Routes.InstanceUser, instanceUser ?? throw new ArgumentNullException(nameof(instanceUser)), instance.Id, cancellationToken);
35 
36  public Task Delete(InstanceUser instanceUser, CancellationToken cancellationToken) => apiClient.Delete(Routes.SetID(Routes.InstanceUser, instanceUser.UserId.Value), instance.Id, cancellationToken);
37 
39  public Task<InstanceUser> Read(CancellationToken cancellationToken) => apiClient.Read<InstanceUser>(Routes.InstanceUser, instance.Id, cancellationToken);
40 
42  public Task<InstanceUser> Update(InstanceUser instanceUser, CancellationToken cancellationToken) => apiClient.Update<InstanceUser, InstanceUser>(Routes.InstanceUser, instanceUser ?? throw new ArgumentNullException(nameof(instanceUser)), instance.Id, cancellationToken);
43 
45  public Task<IReadOnlyList<InstanceUser>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<InstanceUser>>(Routes.List(Routes.InstanceUser), instance.Id, cancellationToken);
46 
48  public Task<InstanceUser> GetId(InstanceUser instanceUser, CancellationToken cancellationToken) => apiClient.Read<InstanceUser>(Routes.SetID(Routes.InstanceUser, instanceUser?.UserId ?? throw new ArgumentNullException(nameof(instanceUser))), instance.Id, cancellationToken);
49  }
50 }
readonly IApiClient apiClient
The IApiClient for the InstanceUserClient
+
readonly Instance instance
The Instance for the InstanceUserClient
const string InstanceUser
The Models.InstanceUser controller
Definition: Routes.cs:54
@@ -102,7 +103,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Routes to a server actions
Definition: Routes.cs:9
Represents a Users permissions in an Instance
Definition: InstanceUser.cs:9
-
Metadata about a server instance
Definition: Instance.cs:9
static string SetID(string route, long id)
Apply an id postfix to a route
diff --git a/_instance_user_controller_8cs_source.html b/_instance_user_controller_8cs_source.html index d30270b827..b2c94238eb 100644 --- a/_instance_user_controller_8cs_source.html +++ b/_instance_user_controller_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceUserController.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Linq;
6 using System.Net;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Tgstation.Server.Api;
14 using Z.EntityFramework.Plus;
15 
16 namespace Tgstation.Server.Host.Controllers
17 {
21  [Route(Routes.InstanceUser)]
22  public sealed class InstanceUserController : ModelController<Api.Models.InstanceUser>
23  {
30  public InstanceUserController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger<InstanceUserController> logger) : base(databaseContext, authenticationContextFactory, logger, true) //false instance requirement, we handle this ourself
31  { }
32 
38  BadRequestObjectResult StandardModelChecks(Api.Models.InstanceUser model)
39  {
40  if (model == null)
41  throw new ArgumentNullException(nameof(model));
42 
43  if (!model.UserId.HasValue)
44  return BadRequest(new ErrorMessage { Message = "Missing UserId!" });
45 
46  return null;
47  }
48 
50  [TgsAuthorize(InstanceUserRights.CreateUsers)]
51  public override async Task<IActionResult> Create([FromBody] Api.Models.InstanceUser model, CancellationToken cancellationToken)
52  {
53  var test = StandardModelChecks(model);
54  if (test != null)
55  return test;
56 
57  var dbUser = new Models.InstanceUser
58  {
59  ByondRights = model.ByondRights ?? ByondRights.None,
60  ChatBotRights = model.ChatBotRights ?? ChatBotRights.None,
61  ConfigurationRights = model.ConfigurationRights ?? ConfigurationRights.None,
62  DreamDaemonRights = model.DreamDaemonRights ?? DreamDaemonRights.None,
63  DreamMakerRights = model.DreamMakerRights ?? DreamMakerRights.None,
64  RepositoryRights = model.RepositoryRights ?? RepositoryRights.None,
65  InstanceUserRights = model.InstanceUserRights ?? InstanceUserRights.None,
66  UserId = model.UserId,
67  InstanceId = Instance.Id
68  };
69 
70  DatabaseContext.InstanceUsers.Add(dbUser);
71 
72  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
73  return StatusCode((int)HttpStatusCode.Created, dbUser.ToApi());
74  }
75 
77  [TgsAuthorize(InstanceUserRights.WriteUsers)]
78  public override async Task<IActionResult> Update([FromBody] Api.Models.InstanceUser model, CancellationToken cancellationToken)
79  {
80  var test = StandardModelChecks(model);
81  if (test != null)
82  return test;
83 
84  var originalUser = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).SelectMany(x => x.InstanceUsers).Where(x => x.UserId == model.UserId).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
85  if (originalUser == null)
86  return StatusCode((int)HttpStatusCode.Gone);
87 
88  originalUser.ByondRights = model.ByondRights ?? originalUser.ByondRights;
89  originalUser.ChatBotRights = model.ChatBotRights ?? originalUser.ChatBotRights;
90  originalUser.ConfigurationRights = model.ConfigurationRights ?? originalUser.ConfigurationRights;
91  originalUser.DreamDaemonRights = model.DreamDaemonRights ?? originalUser.DreamDaemonRights;
92  originalUser.DreamMakerRights = model.DreamMakerRights ?? originalUser.DreamMakerRights;
93 
94  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
95  return Json(originalUser.ToApi());
96  }
97 
99  [TgsAuthorize]
100  public override Task<IActionResult> Read(CancellationToken cancellationToken) => Task.FromResult(AuthenticationContext.InstanceUser != null ? (IActionResult)Json(AuthenticationContext.InstanceUser.ToApi()) : NotFound());
101 
103  [TgsAuthorize(InstanceUserRights.ReadUsers)]
104  public override async Task<IActionResult> List(CancellationToken cancellationToken)
105  {
106  var users = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).SelectMany(x => x.InstanceUsers).ToListAsync(cancellationToken).ConfigureAwait(false);
107  return Json(users.Select(x => x.ToApi()));
108  }
109 
111  [TgsAuthorize(InstanceUserRights.ReadUsers)]
112  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
113  {
114  //this functions as userId
115  var user = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).SelectMany(x => x.InstanceUsers).Where(x => x.UserId == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
116  if (user == default)
117  return StatusCode((int)HttpStatusCode.Gone);
118  return Json(user.ToApi());
119  }
120 
122  [TgsAuthorize(InstanceUserRights.WriteUsers)]
123  public override async Task<IActionResult> Delete(long id, CancellationToken cancellationToken)
124  {
125  await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).SelectMany(x => x.InstanceUsers).Where(x => x.UserId == id).DeleteAsync(cancellationToken).ConfigureAwait(false);
126  return Ok();
127  }
128  }
129 }
InstanceUserController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger< InstanceUserController > logger)
Construct a UserController
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Linq;
6 using System.Net;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Tgstation.Server.Api;
14 using Z.EntityFramework.Plus;
15 
16 namespace Tgstation.Server.Host.Controllers
17 {
21  [Route(Routes.InstanceUser)]
22  public sealed class InstanceUserController : ModelController<Api.Models.InstanceUser>
23  {
30  public InstanceUserController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger<InstanceUserController> logger) : base(databaseContext, authenticationContextFactory, logger, true) //false instance requirement, we handle this ourself
31  { }
32 
38  BadRequestObjectResult StandardModelChecks(Api.Models.InstanceUser model)
39  {
40  if (model == null)
41  throw new ArgumentNullException(nameof(model));
42 
43  if (!model.UserId.HasValue)
44  return BadRequest(new ErrorMessage { Message = "Missing UserId!" });
45 
46  return null;
47  }
48 
50  [TgsAuthorize(InstanceUserRights.CreateUsers)]
51  public override async Task<IActionResult> Create([FromBody] Api.Models.InstanceUser model, CancellationToken cancellationToken)
52  {
53  var test = StandardModelChecks(model);
54  if (test != null)
55  return test;
56 
57  var dbUser = new Models.InstanceUser
58  {
59  ByondRights = model.ByondRights ?? ByondRights.None,
60  ChatBotRights = model.ChatBotRights ?? ChatBotRights.None,
61  ConfigurationRights = model.ConfigurationRights ?? ConfigurationRights.None,
62  DreamDaemonRights = model.DreamDaemonRights ?? DreamDaemonRights.None,
63  DreamMakerRights = model.DreamMakerRights ?? DreamMakerRights.None,
64  RepositoryRights = model.RepositoryRights ?? RepositoryRights.None,
65  InstanceUserRights = model.InstanceUserRights ?? InstanceUserRights.None,
66  UserId = model.UserId,
67  InstanceId = Instance.Id
68  };
69 
70  DatabaseContext.InstanceUsers.Add(dbUser);
71 
72  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
73  return StatusCode((int)HttpStatusCode.Created, dbUser.ToApi());
74  }
75 
77  [TgsAuthorize(InstanceUserRights.WriteUsers)]
78  public override async Task<IActionResult> Update([FromBody] Api.Models.InstanceUser model, CancellationToken cancellationToken)
79  {
80  var test = StandardModelChecks(model);
81  if (test != null)
82  return test;
83 
84  var originalUser = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).SelectMany(x => x.InstanceUsers).Where(x => x.UserId == model.UserId).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
85  if (originalUser == null)
86  return StatusCode((int)HttpStatusCode.Gone);
87 
88  originalUser.ByondRights = model.ByondRights ?? originalUser.ByondRights;
89  originalUser.ChatBotRights = model.ChatBotRights ?? originalUser.ChatBotRights;
90  originalUser.ConfigurationRights = model.ConfigurationRights ?? originalUser.ConfigurationRights;
91  originalUser.DreamDaemonRights = model.DreamDaemonRights ?? originalUser.DreamDaemonRights;
92  originalUser.DreamMakerRights = model.DreamMakerRights ?? originalUser.DreamMakerRights;
93 
94  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
95  return Json(originalUser.ToApi());
96  }
97 
99  [TgsAuthorize]
100  public override Task<IActionResult> Read(CancellationToken cancellationToken) => Task.FromResult(AuthenticationContext.InstanceUser != null ? (IActionResult)Json(AuthenticationContext.InstanceUser.ToApi()) : NotFound());
101 
103  [TgsAuthorize(InstanceUserRights.ReadUsers)]
104  public override async Task<IActionResult> List(CancellationToken cancellationToken)
105  {
106  var users = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).SelectMany(x => x.InstanceUsers).ToListAsync(cancellationToken).ConfigureAwait(false);
107  return Json(users.Select(x => x.ToApi()));
108  }
109 
111  [TgsAuthorize(InstanceUserRights.ReadUsers)]
112  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
113  {
114  //this functions as userId
115  var user = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).SelectMany(x => x.InstanceUsers).Where(x => x.UserId == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
116  if (user == default)
117  return StatusCode((int)HttpStatusCode.Gone);
118  return Json(user.ToApi());
119  }
120 
122  [TgsAuthorize(InstanceUserRights.WriteUsers)]
123  public override async Task<IActionResult> Delete(long id, CancellationToken cancellationToken)
124  {
125  await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).SelectMany(x => x.InstanceUsers).Where(x => x.UserId == id).DeleteAsync(cancellationToken).ConfigureAwait(false);
126  return Ok();
127  }
128  }
129 }
InstanceUserController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger< InstanceUserController > logger)
Construct a UserController
ConfigurationRights
Rights for Models.ConfigurationFile
@@ -100,6 +100,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ByondRights
Rights for Models.Byond
Definition: ByondRights.cs:9
InstanceUserRights
Rights for an Models.Instance
+
const string InstanceUser
The Models.InstanceUser controller
Definition: Routes.cs:54
@@ -115,7 +116,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
BadRequestObjectResult StandardModelChecks(Api.Models.InstanceUser model)
Checks a model for errors
DreamMakerRights
Rights for Models.DreamMaker
-
RepositoryRights
Rights for a Models.Repository
diff --git a/_instance_user_rights_8cs_source.html b/_instance_user_rights_8cs_source.html index 949e41ebc6..1e820a43f9 100644 --- a/_instance_user_rights_8cs_source.html +++ b/_instance_user_rights_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceUserRights.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum InstanceUserRights : ulong
10  {
14  None = 0,
18  ReadUsers = 1,
22  WriteUsers = 2,
26  CreateUsers = 4
27  }
28 }
User can edit themself and other Models.Users and also create others
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum InstanceUserRights : ulong
10  {
14  None = 0,
18  ReadUsers = 1,
22  WriteUsers = 2,
26  CreateUsers = 4
27  }
28 }
User can edit themself and other Models.Users and also create others
InstanceUserRights
Rights for an Models.Instance
+
User can read info and rights of other users
-
Allow adding additional Models.InstanceUser to the Models.Instance
diff --git a/_insufficient_permissions_exception_8cs_source.html b/_insufficient_permissions_exception_8cs_source.html index ccb54a5a59..4876ca8371 100644 --- a/_insufficient_permissions_exception_8cs_source.html +++ b/_insufficient_permissions_exception_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InsufficientPermissionsException.cs
-Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
11  {
16  {
17  Message = "The credentials provided do not have sufficient rights to make this request!",
18  SeverApiVersion = null
19  }, HttpStatusCode.Forbidden)
20  { }
21 
26  public InsufficientPermissionsException(string message) : base(message) { }
27 
33  public InsufficientPermissionsException(string message, Exception innerException) : base(message, innerException) { }
34  }
35 }
+Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
11  {
16  {
17  Message = "The credentials provided do not have sufficient rights to make this request!",
18  SeverApiVersion = null
19  }, HttpStatusCode.Forbidden)
20  { }
21 
26  public InsufficientPermissionsException(string message) : base(message) { }
27 
33  public InsufficientPermissionsException(string message, Exception innerException) : base(message, innerException) { }
34  }
35 }
+
InsufficientPermissionsException(string message, Exception innerException)
Construct an InsufficientPermissionsException with a message and innerException ...
InsufficientPermissionsException(string message)
Construct an InsufficientPermissionsException with a message
-
Occurs when the client attempts to perform an action they do not have the rights for ...
Represents an error message returned by the server
Definition: ErrorMessage.cs:8
diff --git a/_irc_connection_string_builder_8cs.html b/_irc_connection_string_builder_8cs.html new file mode 100644 index 0000000000..19fb952824 --- /dev/null +++ b/_irc_connection_string_builder_8cs.html @@ -0,0 +1,120 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Api/Models/IrcConnectionStringBuilder.cs File Reference + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+ +
+
IrcConnectionStringBuilder.cs File Reference
+
+
+ +

Go to the source code of this file.

+ + + + + +

+Classes

class  Tgstation.Server.Api.Models.IrcConnectionStringBuilder
 ChatConnectionStringBuilder for ChatProvider.Irc More...
 
+ + + +

+Namespaces

namespace  Tgstation.Server.Api.Models
 
+
+ + + + diff --git a/_irc_connection_string_builder_8cs_source.html b/_irc_connection_string_builder_8cs_source.html new file mode 100644 index 0000000000..a091f53e23 --- /dev/null +++ b/_irc_connection_string_builder_8cs_source.html @@ -0,0 +1,116 @@ + + + + + + +tgstation-server: src/Tgstation.Server.Api/Models/IrcConnectionStringBuilder.cs Source File + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+
IrcConnectionStringBuilder.cs
+
+
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
5 
6 namespace Tgstation.Server.Api.Models
7 {
12  {
14  public override bool Valid => Address != null && Port.HasValue && Port != 0 && UseSsl.HasValue && (PasswordType.HasValue ^ Password == null);
15 
19  public string Address { get; set; }
20 
24  public ushort? Port { get; set; }
25 
29  public string Nickname { get; set; }
30 
34  public bool? UseSsl { get; set; }
35 
39  public IrcPasswordType? PasswordType { get; set; }
40 
44  public string Password { get; set; }
45 
50 
55  public IrcConnectionStringBuilder(string connectionString)
56  {
57  if (connectionString == null)
58  throw new ArgumentNullException(nameof(connectionString));
59  var splits = connectionString.Split(';');
60 
61  Address = splits[0];
62 
63  if (splits.Length < 2)
64  return;
65 
66  if (UInt16.TryParse(splits[1], out var port))
67  Port = port;
68 
69  if (splits.Length < 3)
70  return;
71 
72  Nickname = splits[2];
73 
74  if (splits.Length < 4)
75  return;
76 
77  if (Boolean.TryParse(splits[3], out var useSsl))
78  UseSsl = useSsl;
79 
80  if (splits.Length < 5)
81  return;
82  if (Enum.TryParse<IrcPasswordType>(splits[4], out var passwordType))
83  switch (passwordType)
84  {
85  case IrcPasswordType.NickServ:
86  case IrcPasswordType.Sasl:
87  case IrcPasswordType.Server:
88  PasswordType = passwordType;
89  break;
90  }
91 
92  if (splits.Length < 6)
93  return;
94 
95  var rest = new List<string>(splits);
96  rest.RemoveRange(0, 5);
97  Password = String.Join(";", rest);
98  }
99 
101  public override string ToString()
102  {
103  var sb = new StringBuilder();
104  sb.Append(Address);
105  sb.Append(';');
106  sb.Append(Port);
107  sb.Append(';');
108  sb.Append(Nickname);
109  sb.Append(';');
110  if(UseSsl.HasValue)
111  sb.Append(Convert.ToInt32(UseSsl.Value));
112  if (PasswordType.HasValue)
113  {
114  sb.Append(';');
115  sb.Append((int)PasswordType);
116  sb.Append(';');
117  sb.Append(Password);
118  }
119  return sb.ToString();
120  }
121  }
122 }
+ + + +
IrcConnectionStringBuilder(string connectionString)
Construct a DiscordConnectionStringBuilder from a connectionString
+ + +
IrcConnectionStringBuilder()
Construct an IrcConnectionStringBuilder
+
IrcPasswordType
Represents the type of a password for a ChatProvider.Irc
+ +
ChatConnectionStringBuilder for ChatProvider.Irc
+
override string ToString()
Gets the ChatBot.ConnectionString associated with the ChatConnectionStringBuilder ...
+ +
+ + + + diff --git a/_irc_password_type_8cs.html b/_irc_password_type_8cs.html index 74309c437e..8ec1116c31 100644 --- a/_irc_password_type_8cs.html +++ b/_irc_password_type_8cs.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host/Components/Chat/Providers/IrcPasswordType.cs File Reference +tgstation-server: src/Tgstation.Server.Api/Models/IrcPasswordType.cs File Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/_irc_password_type_8cs_source.html b/_irc_password_type_8cs_source.html index 749ada5b0e..39a7b51a98 100644 --- a/_irc_password_type_8cs_source.html +++ b/_irc_password_type_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host/Components/Chat/Providers/IrcPasswordType.cs Source File +tgstation-server: src/Tgstation.Server.Api/Models/IrcPasswordType.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
@@ -92,11 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IrcPasswordType.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Components.Chat.Providers
2 {
7  {
11  Server,
15  Sasl,
19  NickServ
20  }
21 }
-
IrcPasswordType
Represents the type of a password passed to the constructor of IrcProvider
- - - +Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
6  public enum IrcPasswordType
7  {
11  Server,
15  Sasl,
19  NickServ
20  }
21 }
+ + +
IrcPasswordType
Represents the type of a password for a ChatProvider.Irc
diff --git a/_irc_provider_8cs_source.html b/_irc_provider_8cs_source.html index e1ae4f1811..c2023fd720 100644 --- a/_irc_provider_8cs_source.html +++ b/_irc_provider_8cs_source.html @@ -92,13 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IrcProvider.cs
-Go to the documentation of this file.
1 using Meebey.SmartIrc4net;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Collections.Generic;
5 using System.Globalization;
6 using System.Linq;
7 using System.Text;
8 using System.Threading;
9 using System.Threading.Tasks;
12 
13 namespace Tgstation.Server.Host.Components.Chat.Providers
14 {
18  sealed class IrcProvider : Provider
19  {
20  const int TimeoutSeconds = 5;
21 
23  public override bool Connected => client.IsConnected;
24 
26  public override string BotMention => client.Nickname;
27 
31  readonly ILogger<IrcProvider> logger;
32 
36  readonly IrcFeatures client;
37 
41  readonly string address;
45  readonly ushort port;
49  readonly string nickname;
53  readonly string password;
58 
62  readonly Dictionary<ulong, string> channelIdMap;
63 
67  readonly Dictionary<ulong, string> queryChannelIdMap;
68 
73 
77  Task listenTask;
78 
83 
95  public IrcProvider(ILogger<IrcProvider> logger, IApplication application, string address, ushort port, string nickname, string password, IrcPasswordType? passwordType, bool useSsl)
96  {
97  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
98  if (application == null)
99  throw new ArgumentNullException(nameof(application));
100 
101  this.address = address ?? throw new ArgumentNullException(nameof(address));
102  this.port = port;
103  this.nickname = nickname ?? throw new ArgumentNullException(nameof(nickname));
104 
105  if (passwordType.HasValue && password == null)
106  throw new ArgumentNullException(nameof(password));
107 
108  if (password != null && !passwordType.HasValue)
109  throw new ArgumentNullException(nameof(passwordType));
110 
111  this.password = password;
112  this.passwordType = passwordType;
113 
114  client = new IrcFeatures
115  {
116  SupportNonRfc = true,
117  CtcpUserInfo = "You are going to play. And I am going to watch. And everything will be just fine...",
118  AutoRejoin = true,
119  AutoRejoinOnKick = true,
120  AutoRelogin = true,
121  AutoRetry = true,
122  AutoRetryLimit = TimeoutSeconds,
123  AutoRetryDelay = TimeoutSeconds,
124  ActiveChannelSyncing = true,
125  AutoNickHandling = true,
126  CtcpVersion = application.VersionString,
127  UseSsl = useSsl
128  };
129  if (useSsl)
130  client.ValidateServerCertificate = true; //dunno if it defaults to that or what
131 
132  client.OnChannelMessage += Client_OnChannelMessage;
133  client.OnQueryMessage += Client_OnQueryMessage;
134 
135  channelIdMap = new Dictionary<ulong, string>();
136  queryChannelIdMap = new Dictionary<ulong, string>();
137  channelIdCounter = 1;
138  disconnecting = false;
139  }
140 
142  public override void Dispose()
143  {
144  if (Connected)
145  {
146  disconnecting = true;
147  client.Disconnect(); //just closes the socket
148  }
149  }
150 
156  void HandleMessage(IrcEventArgs e, bool isPrivate)
157  {
158  if (e.Data.Nick.ToUpperInvariant() == client.Nickname.ToUpperInvariant())
159  return;
160 
161  var username = e.Data.Nick;
162  var channelName = isPrivate ? username : e.Data.Channel;
163 
164  ulong MapAndGetChannelId(Dictionary<ulong, string> dicToCheck)
165  {
166  ulong? resultId = null;
167  if (!dicToCheck.Any(x =>
168  {
169  if (x.Value != channelName)
170  return false;
171  resultId = x.Key;
172  return true;
173  }))
174  {
175  resultId = channelIdCounter++;
176  dicToCheck.Add(resultId.Value, channelName);
177  if (dicToCheck == queryChannelIdMap)
178  channelIdMap.Add(resultId.Value, null);
179  }
180  return resultId.Value;
181  };
182 
183  ulong userId, channelId;
184  lock (this)
185  {
186  userId = MapAndGetChannelId(queryChannelIdMap);
187  channelId = isPrivate ? userId : MapAndGetChannelId(channelIdMap);
188  }
189 
190  var message = new Message
191  {
192  Content = e.Data.Message,
193  User = new User
194  {
195  Channel = new Channel
196  {
197  ConnectionName = address,
198  FriendlyName = isPrivate ? String.Format(CultureInfo.InvariantCulture, "PM: {0}", channelName) : channelName,
199  RealId = channelId,
200  IsPrivate = isPrivate
201  //isAdmin and Tag populated by manager
202  },
203  FriendlyName = username,
204  RealId = userId,
205  Mention = username
206  }
207  };
208 
209  EnqueueMessage(message);
210  }
211 
217  void Client_OnQueryMessage(object sender, IrcEventArgs e) => HandleMessage(e, true);
218 
224  void Client_OnChannelMessage(object sender, IrcEventArgs e) => HandleMessage(e, false);
225 
227  public override Task<bool> Connect(CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
228  {
229  disconnecting = false;
230  lock (this)
231  try
232  {
233  client.Connect(address, port);
234 
235  cancellationToken.ThrowIfCancellationRequested();
236 
237  if (passwordType == IrcPasswordType.Server)
238  client.Login(nickname, nickname, 0, nickname, password);
239  else
240  {
241  if (passwordType == IrcPasswordType.Sasl)
242  {
243  client.WriteLine("CAP REQ :sasl", Priority.Critical); //needs to be put in the buffer before anything else
244  cancellationToken.ThrowIfCancellationRequested();
245  }
246  client.Login(nickname, nickname, 0, nickname);
247  }
248 
249  if (passwordType == IrcPasswordType.NickServ)
250  {
251  cancellationToken.ThrowIfCancellationRequested();
252  client.SendMessage(SendType.Message, "NickServ", String.Format(CultureInfo.InvariantCulture, "IDENTIFY {0}", password));
253  }
254  else if (passwordType == IrcPasswordType.Sasl)
255  {
256  //wait for the sasl ack or timeout
257  var recievedAck = false;
258  var recievedPlus = false;
259  client.OnReadLine += (sender, e) =>
260  {
261  if (e.Line.Contains("ACK :sasl", StringComparison.Ordinal))
262  recievedAck = true;
263  else if (e.Line.Contains("AUTHENTICATE +", StringComparison.Ordinal))
264  recievedPlus = true;
265  };
266 
267  var startTime = DateTimeOffset.Now;
268  var endTime = DateTimeOffset.Now.AddSeconds(TimeoutSeconds);
269  cancellationToken.ThrowIfCancellationRequested();
270  for (; !recievedAck && DateTimeOffset.Now <= endTime; Task.Delay(10, cancellationToken).GetAwaiter().GetResult())
271  client.Listen(false);
272 
273  client.WriteLine("AUTHENTICATE PLAIN", Priority.Critical);
274  cancellationToken.ThrowIfCancellationRequested();
275 
276  for (; !recievedPlus && DateTimeOffset.Now <= endTime; Task.Delay(10, cancellationToken).GetAwaiter().GetResult())
277  client.Listen(false);
278 
279  //Stolen! https://github.com/znc/znc/blob/1e697580155d5a38f8b5a377f3b1d94aaa979539/modules/sasl.cpp#L196
280  var authString = String.Format(CultureInfo.InvariantCulture, "{0}{1}{0}{1}{2}", nickname, '\0', password);
281  var b64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(authString));
282  var authLine = String.Format(CultureInfo.InvariantCulture, "AUTHENTICATE {0}", b64);
283  var chars = authLine.ToCharArray();
284  client.WriteLine(authLine, Priority.Critical);
285 
286  cancellationToken.ThrowIfCancellationRequested();
287  client.WriteLine("CAP END", Priority.Critical);
288  }
289 
290  client.Listen(false);
291 
292  listenTask = Task.Factory.StartNew(() =>
293  {
294  while (!disconnecting && client.IsConnected)
295  {
296  client.ListenOnce(true);
297  if (disconnecting || !client.IsConnected)
298  break;
299  client.Listen(false);
300  //ensure we have the correct nick
301  if (client.Nickname != nickname && client.GetIrcUser(nickname) == null)
302  client.RfcNick(nickname);
303  }
304  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
305  }
306  catch (Exception e)
307  {
308  logger.LogWarning("Unable to connect to IRC: {0}", e);
309  }
310  return true;
311  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
312 
314  public override async Task Disconnect(CancellationToken cancellationToken)
315  {
316  if (!Connected)
317  return;
318  try
319  {
320  await Task.Factory.StartNew(() =>
321  {
322  try
323  {
324  client.RfcQuit("Mr. Stark, I don't feel so good...", Priority.Critical); //priocritical otherwise it wont go through
325  }
326  catch (Exception e)
327  {
328  logger.LogWarning("Error quitting IRC: {0}", e);
329  }
330  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
331  Dispose();
332  await listenTask.ConfigureAwait(false);
333  }
334  catch (Exception e)
335  {
336  logger.LogWarning("Error disconnecting from IRC! Exception: {0}", e);
337  }
338  }
339 
341  public override Task<IReadOnlyList<Channel>> MapChannels(IEnumerable<ChatChannel> channels, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
342  {
343  if (channels.Any(x => x.IrcChannel == null))
344  throw new InvalidOperationException("ChatChannel missing IrcChannel!");
345  lock (this)
346  {
347  var hs = new HashSet<string>(); //for unique inserts
348  foreach (var I in channels)
349  hs.Add(I.IrcChannel);
350  var toPart = new List<string>();
351  foreach (var I in client.JoinedChannels)
352  if (!hs.Remove(I))
353  toPart.Add(I);
354 
355  foreach (var I in toPart)
356  client.RfcPart(I, "Pretty nice abscond!");
357  foreach (var I in hs)
358  client.RfcJoin(I);
359 
360  return (IReadOnlyList<Channel>)channels.Select(x =>
361  {
362  ulong? id = null;
363  if (!channelIdMap.Any(y =>
364  {
365  if (y.Value != x.IrcChannel)
366  return false;
367  id = y.Key;
368  return true;
369  }))
370  {
371  id = channelIdCounter++;
372  channelIdMap.Add(id.Value, x.IrcChannel);
373  }
374  return new Channel
375  {
376  RealId = id.Value,
377  IsAdmin = x.IsAdminChannel == true,
378  ConnectionName = address,
379  FriendlyName = channelIdMap[id.Value],
380  IsPrivate = false,
381  Tag = x.Tag
382  };
383  }).ToList();
384  }
385  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
386 
388  public override Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
389  {
390  var channelName = channelIdMap[channelId];
391  SendType sendType;
392  if (channelName == null)
393  {
394  channelName = queryChannelIdMap[channelId];
395  sendType = SendType.Notice;
396  }
397  else
398  sendType = SendType.Message;
399  try
400  {
401  client.SendMessage(sendType, channelName, message);
402  }
403  catch (Exception e)
404  {
405  logger.LogWarning("Unable to send to channel: {0}", e);
406  }
407  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
408  }
409 }
+Go to the documentation of this file.
1 using Meebey.SmartIrc4net;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Collections.Generic;
5 using System.Globalization;
6 using System.Linq;
7 using System.Text;
8 using System.Threading;
9 using System.Threading.Tasks;
12 
13 namespace Tgstation.Server.Host.Components.Chat.Providers
14 {
18  sealed class IrcProvider : Provider
19  {
20  const int TimeoutSeconds = 5;
21 
23  public override bool Connected => client.IsConnected;
24 
26  public override string BotMention => client.Nickname;
27 
31  readonly ILogger<IrcProvider> logger;
32 
36  readonly IrcFeatures client;
37 
41  readonly string address;
45  readonly ushort port;
49  readonly string nickname;
53  readonly string password;
58 
62  readonly Dictionary<ulong, string> channelIdMap;
63 
67  readonly Dictionary<ulong, string> queryChannelIdMap;
68 
73 
77  Task listenTask;
78 
83 
95  public IrcProvider(ILogger<IrcProvider> logger, IApplication application, string address, ushort port, string nickname, string password, IrcPasswordType? passwordType, bool useSsl)
96  {
97  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
98  if (application == null)
99  throw new ArgumentNullException(nameof(application));
100 
101  this.address = address ?? throw new ArgumentNullException(nameof(address));
102  this.port = port;
103  this.nickname = nickname ?? throw new ArgumentNullException(nameof(nickname));
104 
105  if (passwordType.HasValue && password == null)
106  throw new ArgumentNullException(nameof(password));
107 
108  if (password != null && !passwordType.HasValue)
109  throw new ArgumentNullException(nameof(passwordType));
110 
111  this.password = password;
112  this.passwordType = passwordType;
113 
114  client = new IrcFeatures
115  {
116  SupportNonRfc = true,
117  CtcpUserInfo = "You are going to play. And I am going to watch. And everything will be just fine...",
118  AutoRejoin = true,
119  AutoRejoinOnKick = true,
120  AutoRelogin = true,
121  AutoRetry = true,
122  AutoRetryLimit = TimeoutSeconds,
123  AutoRetryDelay = TimeoutSeconds,
124  ActiveChannelSyncing = true,
125  AutoNickHandling = true,
126  CtcpVersion = application.VersionString,
127  UseSsl = useSsl
128  };
129  if (useSsl)
130  client.ValidateServerCertificate = true; //dunno if it defaults to that or what
131 
132  client.OnChannelMessage += Client_OnChannelMessage;
133  client.OnQueryMessage += Client_OnQueryMessage;
134 
135  channelIdMap = new Dictionary<ulong, string>();
136  queryChannelIdMap = new Dictionary<ulong, string>();
137  channelIdCounter = 1;
138  disconnecting = false;
139  }
140 
142  public override void Dispose()
143  {
144  if (Connected)
145  {
146  disconnecting = true;
147  client.Disconnect(); //just closes the socket
148  }
149  }
150 
156  void HandleMessage(IrcEventArgs e, bool isPrivate)
157  {
158  if (e.Data.Nick.ToUpperInvariant() == client.Nickname.ToUpperInvariant())
159  return;
160 
161  var username = e.Data.Nick;
162  var channelName = isPrivate ? username : e.Data.Channel;
163 
164  ulong MapAndGetChannelId(Dictionary<ulong, string> dicToCheck)
165  {
166  ulong? resultId = null;
167  if (!dicToCheck.Any(x =>
168  {
169  if (x.Value != channelName)
170  return false;
171  resultId = x.Key;
172  return true;
173  }))
174  {
175  resultId = channelIdCounter++;
176  dicToCheck.Add(resultId.Value, channelName);
177  if (dicToCheck == queryChannelIdMap)
178  channelIdMap.Add(resultId.Value, null);
179  }
180  return resultId.Value;
181  };
182 
183  ulong userId, channelId;
184  lock (this)
185  {
186  userId = MapAndGetChannelId(queryChannelIdMap);
187  channelId = isPrivate ? userId : MapAndGetChannelId(channelIdMap);
188  }
189 
190  var message = new Message
191  {
192  Content = e.Data.Message,
193  User = new User
194  {
195  Channel = new Channel
196  {
197  ConnectionName = address,
198  FriendlyName = isPrivate ? String.Format(CultureInfo.InvariantCulture, "PM: {0}", channelName) : channelName,
199  RealId = channelId,
200  IsPrivate = isPrivate
201  //isAdmin and Tag populated by manager
202  },
203  FriendlyName = username,
204  RealId = userId,
205  Mention = username
206  }
207  };
208 
209  EnqueueMessage(message);
210  }
211 
217  void Client_OnQueryMessage(object sender, IrcEventArgs e) => HandleMessage(e, true);
218 
224  void Client_OnChannelMessage(object sender, IrcEventArgs e) => HandleMessage(e, false);
225 
227  public override Task<bool> Connect(CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
228  {
229  disconnecting = false;
230  lock (this)
231  try
232  {
233  client.Connect(address, port);
234 
235  cancellationToken.ThrowIfCancellationRequested();
236 
237  if (passwordType == IrcPasswordType.Server)
238  client.Login(nickname, nickname, 0, nickname, password);
239  else
240  {
241  if (passwordType == IrcPasswordType.Sasl)
242  {
243  client.WriteLine("CAP REQ :sasl", Priority.Critical); //needs to be put in the buffer before anything else
244  cancellationToken.ThrowIfCancellationRequested();
245  }
246  client.Login(nickname, nickname, 0, nickname);
247  }
248 
249  if (passwordType == IrcPasswordType.NickServ)
250  {
251  cancellationToken.ThrowIfCancellationRequested();
252  client.SendMessage(SendType.Message, "NickServ", String.Format(CultureInfo.InvariantCulture, "IDENTIFY {0}", password));
253  }
254  else if (passwordType == IrcPasswordType.Sasl)
255  {
256  //wait for the sasl ack or timeout
257  var recievedAck = false;
258  var recievedPlus = false;
259  client.OnReadLine += (sender, e) =>
260  {
261  if (e.Line.Contains("ACK :sasl", StringComparison.Ordinal))
262  recievedAck = true;
263  else if (e.Line.Contains("AUTHENTICATE +", StringComparison.Ordinal))
264  recievedPlus = true;
265  };
266 
267  var startTime = DateTimeOffset.Now;
268  var endTime = DateTimeOffset.Now.AddSeconds(TimeoutSeconds);
269  cancellationToken.ThrowIfCancellationRequested();
270  for (; !recievedAck && DateTimeOffset.Now <= endTime; Task.Delay(10, cancellationToken).GetAwaiter().GetResult())
271  client.Listen(false);
272 
273  client.WriteLine("AUTHENTICATE PLAIN", Priority.Critical);
274  cancellationToken.ThrowIfCancellationRequested();
275 
276  for (; !recievedPlus && DateTimeOffset.Now <= endTime; Task.Delay(10, cancellationToken).GetAwaiter().GetResult())
277  client.Listen(false);
278 
279  //Stolen! https://github.com/znc/znc/blob/1e697580155d5a38f8b5a377f3b1d94aaa979539/modules/sasl.cpp#L196
280  var authString = String.Format(CultureInfo.InvariantCulture, "{0}{1}{0}{1}{2}", nickname, '\0', password);
281  var b64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(authString));
282  var authLine = String.Format(CultureInfo.InvariantCulture, "AUTHENTICATE {0}", b64);
283  var chars = authLine.ToCharArray();
284  client.WriteLine(authLine, Priority.Critical);
285 
286  cancellationToken.ThrowIfCancellationRequested();
287  client.WriteLine("CAP END", Priority.Critical);
288  }
289 
290  client.Listen(false);
291 
292  listenTask = Task.Factory.StartNew(() =>
293  {
294  while (!disconnecting && client.IsConnected)
295  {
296  client.ListenOnce(true);
297  if (disconnecting || !client.IsConnected)
298  break;
299  client.Listen(false);
300  //ensure we have the correct nick
301  if (client.Nickname != nickname && client.GetIrcUser(nickname) == null)
302  client.RfcNick(nickname);
303  }
304  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
305  }
306  catch (Exception e)
307  {
308  logger.LogWarning("Unable to connect to IRC: {0}", e);
309  }
310  return true;
311  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
312 
314  public override async Task Disconnect(CancellationToken cancellationToken)
315  {
316  if (!Connected)
317  return;
318  try
319  {
320  await Task.Factory.StartNew(() =>
321  {
322  try
323  {
324  client.RfcQuit("Mr. Stark, I don't feel so good...", Priority.Critical); //priocritical otherwise it wont go through
325  }
326  catch (Exception e)
327  {
328  logger.LogWarning("Error quitting IRC: {0}", e);
329  }
330  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
331  Dispose();
332  await listenTask.ConfigureAwait(false);
333  }
334  catch (Exception e)
335  {
336  logger.LogWarning("Error disconnecting from IRC! Exception: {0}", e);
337  }
338  }
339 
341  public override Task<IReadOnlyList<Channel>> MapChannels(IEnumerable<ChatChannel> channels, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
342  {
343  if (channels.Any(x => x.IrcChannel == null))
344  throw new InvalidOperationException("ChatChannel missing IrcChannel!");
345  lock (this)
346  {
347  var hs = new HashSet<string>(); //for unique inserts
348  foreach (var I in channels)
349  hs.Add(I.IrcChannel);
350  var toPart = new List<string>();
351  foreach (var I in client.JoinedChannels)
352  if (!hs.Remove(I))
353  toPart.Add(I);
354 
355  foreach (var I in toPart)
356  client.RfcPart(I, "Pretty nice abscond!");
357  foreach (var I in hs)
358  client.RfcJoin(I);
359 
360  return (IReadOnlyList<Channel>)channels.Select(x =>
361  {
362  ulong? id = null;
363  if (!channelIdMap.Any(y =>
364  {
365  if (y.Value != x.IrcChannel)
366  return false;
367  id = y.Key;
368  return true;
369  }))
370  {
371  id = channelIdCounter++;
372  channelIdMap.Add(id.Value, x.IrcChannel);
373  }
374  return new Channel
375  {
376  RealId = id.Value,
377  IsAdmin = x.IsAdminChannel == true,
378  ConnectionName = address,
379  FriendlyName = channelIdMap[id.Value],
380  IsPrivate = false,
381  Tag = x.Tag
382  };
383  }).ToList();
384  }
385  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
386 
388  public override Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
389  {
390  var channelName = channelIdMap[channelId];
391  SendType sendType;
392  if (channelName == null)
393  {
394  channelName = queryChannelIdMap[channelId];
395  sendType = SendType.Notice;
396  }
397  else
398  sendType = SendType.Message;
399  try
400  {
401  client.SendMessage(sendType, channelName, message);
402  }
403  catch (Exception e)
404  {
405  logger.LogWarning("Unable to send to channel: {0}", e);
406  }
407  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
408  }
409 }
ulong channelIdCounter
Id counter for channelIdMap
Definition: IrcProvider.cs:72
readonly Dictionary< ulong, string > queryChannelIdMap
Map of Channel.RealIds to query users
Definition: IrcProvider.cs:67
readonly IrcFeatures client
The IrcFeatures client
Definition: IrcProvider.cs:36
readonly ushort port
Port of the server to connect to
Definition: IrcProvider.cs:45
Configures the ASP.NET Core web application
Definition: IApplication.cs:8
+
readonly ILogger< IrcProvider > logger
The ILogger for the IrcProvider
Definition: IrcProvider.cs:31
@@ -112,14 +113,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
void HandleMessage(IrcEventArgs e, bool isPrivate)
Handle an IRC message
Definition: IrcProvider.cs:156
-
IrcPasswordType
Represents the type of a password passed to the constructor of IrcProvider
IrcProvider(ILogger< IrcProvider > logger, IApplication application, string address, ushort port, string nickname, string password, IrcPasswordType?passwordType, bool useSsl)
Construct an IrcProvider
Definition: IrcProvider.cs:95
-
Task listenTask
The Task used for IrcConnection.Listen(bool)
Definition: IrcProvider.cs:77
Represents a Providers.IProvider channel
Definition: Channel.cs:10
readonly IrcPasswordType passwordType
The IrcPasswordType of password
Definition: IrcProvider.cs:57
Represents a tgs_chat_user datum
Definition: User.cs:10
+
IrcPasswordType
Represents the type of a password for a ChatProvider.Irc
string VersionString
A more verbose version of Version
Definition: IApplication.cs:18
diff --git a/_job_controller_8cs_source.html b/_job_controller_8cs_source.html index 85452080c3..a213f947d9 100644 --- a/_job_controller_8cs_source.html +++ b/_job_controller_8cs_source.html @@ -92,13 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
JobController.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Linq;
6 using System.Net;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Tgstation.Server.Api;
13 
14 namespace Tgstation.Server.Host.Controllers
15 {
19  [Route(Routes.Jobs)]
20  public sealed class JobController : ModelController<Api.Models.Job>
21  {
26 
34  public JobController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, ILogger<JobController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
35  {
36  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
37  }
38 
40  [TgsAuthorize]
41  public override async Task<IActionResult> Read(CancellationToken cancellationToken)
42  {
43  var result = await DatabaseContext.Jobs.Where(x => x.Instance.Id == Instance.Id && !x.StoppedAt.HasValue).OrderByDescending(x => x.StartedAt).ToListAsync(cancellationToken).ConfigureAwait(false);
44  return Json(result.Select(x => x.ToApi()));
45  }
46 
48  [TgsAuthorize]
49  public override async Task<IActionResult> List(CancellationToken cancellationToken)
50  {
51  //you KNOW this will need pagination eventually right?
52  var jobs = await DatabaseContext.Jobs.Where(x => x.Instance.Id == Instance.Id).OrderByDescending(x => x.StartedAt).Select(x => new Api.Models.Job
53  {
54  Id = x.Id
55  }).ToListAsync(cancellationToken).ConfigureAwait(false);
56  return Json(jobs);
57  }
58 
60  [TgsAuthorize]
61  public override async Task<IActionResult> Delete(long id, CancellationToken cancellationToken)
62  {
63  //don't care if an instance post or not at this point
64  var job = await DatabaseContext.Jobs.Where(x => x.Id == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
65  if (job == default(Job))
66  return NotFound();
67 
68  if (job.StoppedAt != null)
69  return StatusCode((int)HttpStatusCode.Gone);
70 
71  if (job.CancelRight.HasValue && job.CancelRightsType.HasValue && (AuthenticationContext.GetRight(job.CancelRightsType.Value) & job.CancelRight.Value) == 0)
72  return Forbid();
73 
74  var cancelled = await jobManager.CancelJob(job, AuthenticationContext.User, false, cancellationToken).ConfigureAwait(false);
75  return cancelled ? (IActionResult)Accepted() : StatusCode((int)HttpStatusCode.Gone);
76  }
77 
79  [TgsAuthorize]
80  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
81  {
82  var job = await DatabaseContext.Jobs.Where(x => x.Id == id).Include(x => x.StartedBy).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
83  if (job == default(Job))
84  return NotFound();
85  var api = job.ToApi();
86  api.Progress = jobManager.JobProgress(job);
87  return Json(api);
88  }
89  }
90 }
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Linq;
6 using System.Net;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Tgstation.Server.Api;
13 
14 namespace Tgstation.Server.Host.Controllers
15 {
19  [Route(Routes.Jobs)]
20  public sealed class JobController : ModelController<Api.Models.Job>
21  {
26 
34  public JobController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, ILogger<JobController> logger) : base(databaseContext, authenticationContextFactory, logger, true)
35  {
36  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
37  }
38 
40  [TgsAuthorize]
41  public override async Task<IActionResult> Read(CancellationToken cancellationToken)
42  {
43  var result = await DatabaseContext.Jobs.Where(x => x.Instance.Id == Instance.Id && !x.StoppedAt.HasValue).OrderByDescending(x => x.StartedAt).ToListAsync(cancellationToken).ConfigureAwait(false);
44  return Json(result.Select(x => x.ToApi()));
45  }
46 
48  [TgsAuthorize]
49  public override async Task<IActionResult> List(CancellationToken cancellationToken)
50  {
51  //you KNOW this will need pagination eventually right?
52  var jobs = await DatabaseContext.Jobs.Where(x => x.Instance.Id == Instance.Id).OrderByDescending(x => x.StartedAt).Select(x => new Api.Models.Job
53  {
54  Id = x.Id
55  }).ToListAsync(cancellationToken).ConfigureAwait(false);
56  return Json(jobs);
57  }
58 
60  [TgsAuthorize]
61  public override async Task<IActionResult> Delete(long id, CancellationToken cancellationToken)
62  {
63  //don't care if an instance post or not at this point
64  var job = await DatabaseContext.Jobs.Where(x => x.Id == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
65  if (job == default(Job))
66  return NotFound();
67 
68  if (job.StoppedAt != null)
69  return StatusCode((int)HttpStatusCode.Gone);
70 
71  if (job.CancelRight.HasValue && job.CancelRightsType.HasValue && (AuthenticationContext.GetRight(job.CancelRightsType.Value) & job.CancelRight.Value) == 0)
72  return Forbid();
73 
74  var cancelled = await jobManager.CancelJob(job, AuthenticationContext.User, false, cancellationToken).ConfigureAwait(false);
75  return cancelled ? (IActionResult)Accepted() : StatusCode((int)HttpStatusCode.Gone);
76  }
77 
79  [TgsAuthorize]
80  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
81  {
82  var job = await DatabaseContext.Jobs.Where(x => x.Id == id).Include(x => x.StartedBy).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
83  if (job == default(Job))
84  return NotFound();
85  var api = job.ToApi();
86  api.Progress = jobManager.JobProgress(job);
87  return Json(api);
88  }
89  }
90 }
Manages the runtime of Jobs
Definition: IJobManager.cs:12
override async Task< IActionResult > List(CancellationToken cancellationToken)
Attempt to list entries of the TModel
override async Task< IActionResult > Read(CancellationToken cancellationToken)
Attempt to read a TModel
+
JobController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, ILogger< JobController > logger)
Construct a HomeController
@@ -109,7 +110,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Routes to a server actions
Definition: Routes.cs:9
Represents an Api.Models.Instance in the database
Definition: Instance.cs:8
-
const string Jobs
The Models.Job controller
Definition: Routes.cs:69
long Id
The id of the Instance. Not modifiable
Definition: Instance.cs:14
diff --git a/_job_exception_8cs_source.html b/_job_exception_8cs_source.html index 8e314de9a8..7eb194ad8b 100644 --- a/_job_exception_8cs_source.html +++ b/_job_exception_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
JobException.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Host
4 {
8  public sealed class JobException : Exception
9  {
13  public JobException()
14  {
15  }
16 
21  public JobException(string message) : base(message)
22  {
23  }
24 
30  public JobException(string message, Exception innerException) : base(message, innerException)
31  {
32  }
33  }
34 }
JobException()
Construct a JobException
Definition: JobException.cs:13
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Host
4 {
8  public sealed class JobException : Exception
9  {
13  public JobException()
14  {
15  }
16 
21  public JobException(string message) : base(message)
22  {
23  }
24 
30  public JobException(string message, Exception innerException) : base(message, innerException)
31  {
32  }
33  }
34 }
JobException()
Construct a JobException
Definition: JobException.cs:13
Operation exceptions thrown from the context of a Models.Job
Definition: JobException.cs:8
+ -
JobException(string message)
Construct a JobException with a message
Definition: JobException.cs:21
JobException(string message, Exception innerException)
Construct a JobException with a message and innerException
Definition: JobException.cs:30
diff --git a/_job_handler_8cs_source.html b/_job_handler_8cs_source.html index 75051d8c3d..9e633d8d5f 100644 --- a/_job_handler_8cs_source.html +++ b/_job_handler_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
JobHandler.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Core
6 {
10  sealed class JobHandler : IDisposable
11  {
15  readonly Task task;
19  readonly CancellationTokenSource cancellationTokenSource;
20 
26  JobHandler(Task task, CancellationTokenSource cancellationTokenSource)
27  {
28  this.task = task;
29  this.cancellationTokenSource = cancellationTokenSource;
30  }
31 
33  public void Dispose() => cancellationTokenSource.Dispose();
34 
38  public int? Progress { get; set; }
39 
45  public async Task Wait(CancellationToken cancellationToken)
46  {
47  TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
48  using (cancellationToken.Register(() => tcs.SetCanceled()))
49  await Task.WhenAny(tcs.Task, task).ConfigureAwait(false);
50  cancellationToken.ThrowIfCancellationRequested();
51  }
52 
56  public void Cancel() => cancellationTokenSource.Cancel();
57 
63  public static JobHandler Create(Func<CancellationToken, Task> job)
64  {
65  var cts = new CancellationTokenSource();
66  return new JobHandler(job(cts.Token), cts);
67  }
68  }
69 }
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Core
6 {
10  sealed class JobHandler : IDisposable
11  {
15  readonly Task task;
19  readonly CancellationTokenSource cancellationTokenSource;
20 
26  JobHandler(Task task, CancellationTokenSource cancellationTokenSource)
27  {
28  this.task = task;
29  this.cancellationTokenSource = cancellationTokenSource;
30  }
31 
33  public void Dispose() => cancellationTokenSource.Dispose();
34 
38  public int? Progress { get; set; }
39 
45  public async Task Wait(CancellationToken cancellationToken)
46  {
47  TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
48  using (cancellationToken.Register(() => tcs.SetCanceled()))
49  await Task.WhenAny(tcs.Task, task).ConfigureAwait(false);
50  cancellationToken.ThrowIfCancellationRequested();
51  }
52 
56  public void Cancel() => cancellationTokenSource.Cancel();
57 
63  public static JobHandler Create(Func<CancellationToken, Task> job)
64  {
65  var cts = new CancellationTokenSource();
66  return new JobHandler(job(cts.Token), cts);
67  }
68  }
69 }
+
async Task Wait(CancellationToken cancellationToken)
Wait for task to complete
Definition: JobHandler.cs:45
static JobHandler Create(Func< CancellationToken, Task > job)
Create a JobHandler
Definition: JobHandler.cs:63
-
readonly CancellationTokenSource cancellationTokenSource
The CancellationTokenSource for task
Definition: JobHandler.cs:19
readonly Task task
The Task being run
Definition: JobHandler.cs:15
diff --git a/_job_manager_8cs_source.html b/_job_manager_8cs_source.html index 9685247ab9..37925d86f5 100644 --- a/_job_manager_8cs_source.html +++ b/_job_manager_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
JobManager.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Threading;
7 using System.Threading.Tasks;
9 
10 namespace Tgstation.Server.Host.Core
11 {
14  {
19 
23  readonly ILogger<JobManager> logger;
24 
28  readonly Dictionary<long, JobHandler> jobs;
29 
35  public JobManager(IDatabaseContextFactory databaseContextFactory, ILogger<JobManager> logger)
36  {
37  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
38  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
39  jobs = new Dictionary<long, JobHandler>();
40  }
41 
43  public void Dispose()
44  {
45  foreach (var job in jobs)
46  job.Value.Dispose();
47  }
48 
55  {
56  lock (this)
57  {
58  if (!jobs.TryGetValue(job.Id, out JobHandler jobHandler))
59  throw new InvalidOperationException("Job not running!");
60  return jobHandler;
61  }
62  }
63 
71  async Task RunJob(Job job, Func<Job, IDatabaseContext, CancellationToken, Task> operation, CancellationToken cancellationToken)
72  {
73  try
74  {
75  await databaseContextFactory.UseContext(async databaseContext =>
76  {
77  async Task HandleExceptions(Task task)
78  {
79  try
80  {
81  await task.ConfigureAwait(false);
82  }
83  catch (OperationCanceledException)
84  {
85  logger.LogDebug("Job {0} cancelled!", job.Id);
86  job.Cancelled = true;
87  }
88  catch (Exception e)
89  {
90  job.ExceptionDetails = e is JobException ? e.Message : e.ToString();
91  logger.LogDebug("Job {0} exited with error! Exception: {1}", job.Id, job.ExceptionDetails);
92  }
93  finally
94  {
95  job.StoppedAt = DateTimeOffset.Now;
96  }
97  }
98 
99  async Task RunJobInternal()
100  {
101  var oldJob = job;
102  job = new Job { Id = oldJob.Id };
103  databaseContext.Jobs.Attach(job);
104 
105  await operation(job, databaseContext, cancellationToken).ConfigureAwait(false);
106 
107  logger.LogDebug("Job {0} completed!", job.Id);
108  };
109 
110  await HandleExceptions(RunJobInternal()).ConfigureAwait(false);
111 
112  await databaseContext.Save(default).ConfigureAwait(false);
113 
114  bool JobErroredOrCancelled() => job.ExceptionDetails != null || job.Cancelled == true;
115 
116  //ok so, now it's time for the post commit step if it exists
117  if (!JobErroredOrCancelled() && job.PostComplete != null)
118  {
119  await HandleExceptions(job.PostComplete(cancellationToken)).ConfigureAwait(false);
120  if (JobErroredOrCancelled())
121  await databaseContext.Save(default).ConfigureAwait(false);
122  }
123  }).ConfigureAwait(false);
124  }
125  finally
126  {
127  lock (this)
128  {
129  var handler = jobs[job.Id];
130  jobs.Remove(job.Id);
131  handler.Dispose();
132  }
133  }
134  }
135 
137  public Task RegisterOperation(Job job, Func<Job, IDatabaseContext, Action<int>, CancellationToken, Task> operation, CancellationToken cancellationToken) => databaseContextFactory.UseContext(async databaseContext =>
138  {
139  job.StartedAt = DateTimeOffset.Now;
140  job.Cancelled = false;
141  job.Instance = new Instance
142  {
143  Id = job.Instance.Id
144  };
145  databaseContext.Instances.Attach(job.Instance);
146  if (job.StartedBy != null)
147  {
148  job.StartedBy = new User
149  {
150  Id = job.StartedBy.Id
151  };
152  databaseContext.Users.Attach(job.StartedBy);
153  }
154  databaseContext.Jobs.Add(job);
155  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
156  logger.LogDebug("Starting job {0}: {1}...", job.Id, job.Description);
157  var jobHandler = JobHandler.Create(x => RunJob(job, (jobParam, serviceProvider, ct) =>
158  operation(jobParam, serviceProvider, y =>
159  {
160  lock (this)
161  if (jobs.TryGetValue(job.Id, out var handler))
162  handler.Progress = y;
163  }, ct),
164  x));
165  lock (this)
166  jobs.Add(job.Id, jobHandler);
167  });
168 
170  public async Task StartAsync(CancellationToken cancellationToken)
171  {
172  logger.LogTrace("Starting job manager...");
173  await databaseContextFactory.UseContext(async databaseContext =>
174  {
175  //mark all jobs as cancelled
176  var badJobs = await databaseContext.Jobs.Where(y => !y.StoppedAt.HasValue).Select(y => y.Id).ToListAsync(cancellationToken).ConfigureAwait(false);
177  if (badJobs.Count > 0)
178  {
179  logger.LogTrace("Cleaning {0} unfinished jobs...", badJobs.Count);
180  foreach (var I in badJobs)
181  {
182  var job = new Job { Id = I };
183  databaseContext.Jobs.Attach(job);
184  job.Cancelled = true;
185  job.StoppedAt = DateTimeOffset.Now;
186  }
187  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
188  }
189  }).ConfigureAwait(false);
190  logger.LogDebug("Job manager started!");
191  }
192 
194  public async Task StopAsync(CancellationToken cancellationToken)
195  {
196  var joinTasks = jobs.Select(x =>
197  {
198  x.Value.Cancel();
199  return x.Value.Wait(cancellationToken);
200  });
201  await Task.WhenAll(joinTasks).ConfigureAwait(false);
202  }
203 
205  public async Task<bool> CancelJob(Job job, User user, bool blocking, CancellationToken cancellationToken)
206  {
207  if (job == null)
208  throw new ArgumentNullException(nameof(job));
209  if (user == null)
210  throw new ArgumentNullException(nameof(user));
211  JobHandler handler;
212  try
213  {
214  handler = CheckGetJob(job);
215  }
216  catch (InvalidOperationException)
217  {
218  //this is fine
219  return false;
220  }
221  handler.Cancel(); //this will ensure the db update is only done once
222  await databaseContextFactory.UseContext(async databaseContext =>
223  {
224  job = new Job { Id = job.Id };
225  databaseContext.Jobs.Attach(job);
226  user = new User { Id = user.Id };
227  databaseContext.Users.Attach(user);
228  job.CancelledBy = user;
229  //let either startup or cancellation set job.cancelled
230  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
231  }).ConfigureAwait(false);
232  if (blocking)
233  await handler.Wait(cancellationToken).ConfigureAwait(false);
234  return true;
235  }
236 
238  public int? JobProgress(Job job)
239  {
240  if (job == null)
241  throw new ArgumentNullException(nameof(job));
242  lock (this)
243  {
244  if (!jobs.TryGetValue(job.Id, out var handler))
245  return null;
246  return handler.Progress;
247  }
248  }
249 
251  public async Task WaitForJobCompletion(Job job, User canceller, CancellationToken jobCancellationToken, CancellationToken cancellationToken)
252  {
253  if (job == null)
254  throw new ArgumentNullException(nameof(job));
255  if (canceller == null)
256  throw new ArgumentNullException(nameof(canceller));
257  JobHandler handler;
258  lock (this)
259  {
260  if (!jobs.TryGetValue(job.Id, out handler))
261  return;
262  }
263  Task cancelTask = null;
264  using (jobCancellationToken.Register(() => cancelTask = CancelJob(job, canceller, true, cancellationToken)))
265  await handler.Wait(cancellationToken).ConfigureAwait(false);
266 
267  if (cancelTask != null)
268  await cancelTask.ConfigureAwait(false);
269  }
270  }
271 }
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Threading;
7 using System.Threading.Tasks;
9 
10 namespace Tgstation.Server.Host.Core
11 {
14  {
19 
23  readonly ILogger<JobManager> logger;
24 
28  readonly Dictionary<long, JobHandler> jobs;
29 
35  public JobManager(IDatabaseContextFactory databaseContextFactory, ILogger<JobManager> logger)
36  {
37  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
38  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
39  jobs = new Dictionary<long, JobHandler>();
40  }
41 
43  public void Dispose()
44  {
45  foreach (var job in jobs)
46  job.Value.Dispose();
47  }
48 
55  {
56  lock (this)
57  {
58  if (!jobs.TryGetValue(job.Id, out JobHandler jobHandler))
59  throw new InvalidOperationException("Job not running!");
60  return jobHandler;
61  }
62  }
63 
71  async Task RunJob(Job job, Func<Job, IDatabaseContext, CancellationToken, Task> operation, CancellationToken cancellationToken)
72  {
73  try
74  {
75  await databaseContextFactory.UseContext(async databaseContext =>
76  {
77  async Task HandleExceptions(Task task)
78  {
79  try
80  {
81  await task.ConfigureAwait(false);
82  }
83  catch (OperationCanceledException)
84  {
85  logger.LogDebug("Job {0} cancelled!", job.Id);
86  job.Cancelled = true;
87  }
88  catch (Exception e)
89  {
90  job.ExceptionDetails = e is JobException ? e.Message : e.ToString();
91  logger.LogDebug("Job {0} exited with error! Exception: {1}", job.Id, job.ExceptionDetails);
92  }
93  finally
94  {
95  job.StoppedAt = DateTimeOffset.Now;
96  }
97  }
98 
99  async Task RunJobInternal()
100  {
101  var oldJob = job;
102  job = new Job { Id = oldJob.Id };
103  databaseContext.Jobs.Attach(job);
104 
105  await operation(job, databaseContext, cancellationToken).ConfigureAwait(false);
106 
107  logger.LogDebug("Job {0} completed!", job.Id);
108  };
109 
110  await HandleExceptions(RunJobInternal()).ConfigureAwait(false);
111 
112  await databaseContext.Save(default).ConfigureAwait(false);
113 
114  bool JobErroredOrCancelled() => job.ExceptionDetails != null || job.Cancelled == true;
115 
116  //ok so, now it's time for the post commit step if it exists
117  if (!JobErroredOrCancelled() && job.PostComplete != null)
118  {
119  await HandleExceptions(job.PostComplete(cancellationToken)).ConfigureAwait(false);
120  if (JobErroredOrCancelled())
121  await databaseContext.Save(default).ConfigureAwait(false);
122  }
123  }).ConfigureAwait(false);
124  }
125  finally
126  {
127  lock (this)
128  {
129  var handler = jobs[job.Id];
130  jobs.Remove(job.Id);
131  handler.Dispose();
132  }
133  }
134  }
135 
137  public Task RegisterOperation(Job job, Func<Job, IDatabaseContext, Action<int>, CancellationToken, Task> operation, CancellationToken cancellationToken) => databaseContextFactory.UseContext(async databaseContext =>
138  {
139  job.StartedAt = DateTimeOffset.Now;
140  job.Cancelled = false;
141  job.Instance = new Instance
142  {
143  Id = job.Instance.Id
144  };
145  databaseContext.Instances.Attach(job.Instance);
146  if (job.StartedBy != null)
147  {
148  job.StartedBy = new User
149  {
150  Id = job.StartedBy.Id
151  };
152  databaseContext.Users.Attach(job.StartedBy);
153  }
154  databaseContext.Jobs.Add(job);
155  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
156  logger.LogDebug("Starting job {0}: {1}...", job.Id, job.Description);
157  var jobHandler = JobHandler.Create(x => RunJob(job, (jobParam, serviceProvider, ct) =>
158  operation(jobParam, serviceProvider, y =>
159  {
160  lock (this)
161  if (jobs.TryGetValue(job.Id, out var handler))
162  handler.Progress = y;
163  }, ct),
164  x));
165  lock (this)
166  jobs.Add(job.Id, jobHandler);
167  });
168 
170  public async Task StartAsync(CancellationToken cancellationToken)
171  {
172  logger.LogTrace("Starting job manager...");
173  await databaseContextFactory.UseContext(async databaseContext =>
174  {
175  //mark all jobs as cancelled
176  var badJobs = await databaseContext.Jobs.Where(y => !y.StoppedAt.HasValue).Select(y => y.Id).ToListAsync(cancellationToken).ConfigureAwait(false);
177  if (badJobs.Count > 0)
178  {
179  logger.LogTrace("Cleaning {0} unfinished jobs...", badJobs.Count);
180  foreach (var I in badJobs)
181  {
182  var job = new Job { Id = I };
183  databaseContext.Jobs.Attach(job);
184  job.Cancelled = true;
185  job.StoppedAt = DateTimeOffset.Now;
186  }
187  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
188  }
189  }).ConfigureAwait(false);
190  logger.LogDebug("Job manager started!");
191  }
192 
194  public async Task StopAsync(CancellationToken cancellationToken)
195  {
196  var joinTasks = jobs.Select(x =>
197  {
198  x.Value.Cancel();
199  return x.Value.Wait(cancellationToken);
200  });
201  await Task.WhenAll(joinTasks).ConfigureAwait(false);
202  }
203 
205  public async Task<bool> CancelJob(Job job, User user, bool blocking, CancellationToken cancellationToken)
206  {
207  if (job == null)
208  throw new ArgumentNullException(nameof(job));
209  if (user == null)
210  throw new ArgumentNullException(nameof(user));
211  JobHandler handler;
212  try
213  {
214  handler = CheckGetJob(job);
215  }
216  catch (InvalidOperationException)
217  {
218  //this is fine
219  return false;
220  }
221  handler.Cancel(); //this will ensure the db update is only done once
222  await databaseContextFactory.UseContext(async databaseContext =>
223  {
224  job = new Job { Id = job.Id };
225  databaseContext.Jobs.Attach(job);
226  user = new User { Id = user.Id };
227  databaseContext.Users.Attach(user);
228  job.CancelledBy = user;
229  //let either startup or cancellation set job.cancelled
230  await databaseContext.Save(cancellationToken).ConfigureAwait(false);
231  }).ConfigureAwait(false);
232  if (blocking)
233  await handler.Wait(cancellationToken).ConfigureAwait(false);
234  return true;
235  }
236 
238  public int? JobProgress(Job job)
239  {
240  if (job == null)
241  throw new ArgumentNullException(nameof(job));
242  lock (this)
243  {
244  if (!jobs.TryGetValue(job.Id, out var handler))
245  return null;
246  return handler.Progress;
247  }
248  }
249 
251  public async Task WaitForJobCompletion(Job job, User canceller, CancellationToken jobCancellationToken, CancellationToken cancellationToken)
252  {
253  if (job == null)
254  throw new ArgumentNullException(nameof(job));
255  if (canceller == null)
256  throw new ArgumentNullException(nameof(canceller));
257  JobHandler handler;
258  lock (this)
259  {
260  if (!jobs.TryGetValue(job.Id, out handler))
261  return;
262  }
263  Task cancelTask = null;
264  using (jobCancellationToken.Register(() => cancelTask = CancelJob(job, canceller, true, cancellationToken)))
265  await handler.Wait(cancellationToken).ConfigureAwait(false);
266 
267  if (cancelTask != null)
268  await cancelTask.ConfigureAwait(false);
269  }
270  }
271 }
Manages the runtime of Jobs
Definition: IJobManager.cs:12
@@ -100,6 +100,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
async Task StartAsync(CancellationToken cancellationToken)
Definition: JobManager.cs:170
Operation exceptions thrown from the context of a Models.Job
Definition: JobException.cs:8
User StartedBy
See Api.Models.Job.StartedBy
Definition: Job.cs:16
+
readonly Dictionary< long, JobHandler > jobs
Dictionary<TKey, TValue> of Api.Models.Internal.Job.Id to running JobHandlers
Definition: JobManager.cs:28
@@ -122,7 +123,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly ILogger< JobManager > logger
The ILogger for the JobManager
Definition: JobManager.cs:23
Represents an Api.Models.Instance in the database
Definition: Instance.cs:8
-
long Id
The id of the Instance. Not modifiable
Definition: Instance.cs:14
int JobProgress(Job job)
Get the Api.Models.Job.Progress for a job
Definition: JobManager.cs:238
long Id
The ID of the User
Definition: User.cs:15
diff --git a/_jobs_client_8cs_source.html b/_jobs_client_8cs_source.html index dcf379e13b..7ddc9a2c14 100644 --- a/_jobs_client_8cs_source.html +++ b/_jobs_client_8cs_source.html @@ -92,14 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
JobsClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client.Components
9 {
11  sealed class JobsClient : IJobsClient
12  {
20  readonly Instance instance;
21 
27  public JobsClient(IApiClient apiClient, Instance instance)
28  {
29  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
30  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
31  }
32 
34  public Task Cancel(Job job, CancellationToken cancellationToken) => apiClient.Delete(Routes.SetID(Routes.Jobs, job?.Id ?? throw new ArgumentNullException(nameof(job))), instance.Id, cancellationToken);
35 
37  public Task<IReadOnlyList<Job>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<Job>>(Routes.List(Routes.Jobs), instance.Id, cancellationToken);
38 
40  public Task<IReadOnlyList<Job>> ListActive(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<Job>>(Routes.Jobs, instance.Id, cancellationToken);
41 
43  public Task<Job> GetId(Job job, CancellationToken cancellationToken) => apiClient.Read<Job>(Routes.SetID(Routes.Jobs, job?.Id ?? throw new ArgumentNullException(nameof(job))), instance.Id, cancellationToken);
44  }
45 }
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client.Components
9 {
11  sealed class JobsClient : IJobsClient
12  {
20  readonly Instance instance;
21 
27  public JobsClient(IApiClient apiClient, Instance instance)
28  {
29  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
30  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
31  }
32 
34  public Task Cancel(Job job, CancellationToken cancellationToken) => apiClient.Delete(Routes.SetID(Routes.Jobs, job?.Id ?? throw new ArgumentNullException(nameof(job))), instance.Id, cancellationToken);
35 
37  public Task<IReadOnlyList<Job>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<Job>>(Routes.List(Routes.Jobs), instance.Id, cancellationToken);
38 
40  public Task<IReadOnlyList<Job>> ListActive(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<Job>>(Routes.Jobs, instance.Id, cancellationToken);
41 
43  public Task<Job> GetId(Job job, CancellationToken cancellationToken) => apiClient.Read<Job>(Routes.SetID(Routes.Jobs, job?.Id ?? throw new ArgumentNullException(nameof(job))), instance.Id, cancellationToken);
44  }
45 }
+
JobsClient(IApiClient apiClient, Instance instance)
Construct a JobsClient
Definition: JobsClient.cs:27
Routes to a server actions
Definition: Routes.cs:9
-
const string Jobs
The Models.Job controller
Definition: Routes.cs:69
readonly IApiClient apiClient
The IApiClient for the JobsClient
Definition: JobsClient.cs:16
diff --git a/_json_file_8cs_source.html b/_json_file_8cs_source.html index 5cbba69558..8bc0d189ca 100644 --- a/_json_file_8cs_source.html +++ b/_json_file_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
JsonFile.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
4 
5 namespace Tgstation.Server.Host.Components.Interop
6 {
10  sealed class JsonFile
11  {
15  public string AccessIdentifier { get; set; }
16 
20  public bool ApiValidateOnly { get; set; }
21 
25  public string InstanceName { get; set; }
26 
30  public string ChatChannelsJson { get; set; }
31 
35  public string ChatCommandsJson { get; set; }
36 
40  public string ServerCommandsJson { get; set; }
41 
45  public Api.Models.Internal.RevisionInformation Revision { get; set; }
46 
50  public DreamDaemonSecurity SecurityLevel { get; set; }
51 
55  public List<TestMerge> TestMerges { get; } = new List<TestMerge>();
56  }
57 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
4 
5 namespace Tgstation.Server.Host.Components.Interop
6 {
10  sealed class JsonFile
11  {
15  public string AccessIdentifier { get; set; }
16 
20  public bool ApiValidateOnly { get; set; }
21 
25  public string InstanceName { get; set; }
26 
30  public string ChatChannelsJson { get; set; }
31 
35  public string ChatCommandsJson { get; set; }
36 
40  public string ServerCommandsJson { get; set; }
41 
45  public Api.Models.Internal.RevisionInformation Revision { get; set; }
46 
50  public DreamDaemonSecurity SecurityLevel { get; set; }
51 
55  public List<TestMerge> TestMerges { get; } = new List<TestMerge>();
56  }
57 }
+
Representation of the initial json passed to DreamDaemon
Definition: JsonFile.cs:10
- - +
DreamDaemonSecurity
DreamDaemon&#39;s security level
diff --git a/_json_sub_file_list_8cs_source.html b/_json_sub_file_list_8cs_source.html index 1c04f49be3..a9de74148a 100644 --- a/_json_sub_file_list_8cs_source.html +++ b/_json_sub_file_list_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
JsonSubFileList.cs
-Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 
4 namespace Tgstation.Server.Host.Components.Interop
5 {
9  public class JsonSubFileList
10  {
14  [Required]
15  public string ChatCommandsJson { get; set; }
16 
20  [Required]
21  public string ChatChannelsJson { get; set; }
22 
26  [Required]
27  public string ServerCommandsJson { get; set; }
28 
32  protected JsonSubFileList() { }
33 
39  {
40  if (copy == null)
41  throw new ArgumentNullException(nameof(copy));
42  ChatChannelsJson = copy.ChatChannelsJson;
43  ChatCommandsJson = copy.ChatCommandsJson;
44  ServerCommandsJson = copy.ServerCommandsJson;
45  }
46  }
47 }
string ServerCommandsJson
Path to the server commands json file
+Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 
4 namespace Tgstation.Server.Host.Components.Interop
5 {
9  public class JsonSubFileList
10  {
14  [Required]
15  public string ChatCommandsJson { get; set; }
16 
20  [Required]
21  public string ChatChannelsJson { get; set; }
22 
26  [Required]
27  public string ServerCommandsJson { get; set; }
28 
32  protected JsonSubFileList() { }
33 
39  {
40  if (copy == null)
41  throw new ArgumentNullException(nameof(copy));
42  ChatChannelsJson = copy.ChatChannelsJson;
43  ChatCommandsJson = copy.ChatCommandsJson;
44  ServerCommandsJson = copy.ServerCommandsJson;
45  }
46  }
47 }
string ServerCommandsJson
Path to the server commands json file
+
string ChatCommandsJson
Path to the chat commands json file
JsonSubFileList(JsonSubFileList copy)
Construct an JsonSubFileList from a copy
Information used in for reattaching and interop
-
string ChatChannelsJson
Path to the chat channels json file
diff --git a/_json_tracking_context_8cs_source.html b/_json_tracking_context_8cs_source.html index 0fbb75efe1..b4d831bbaa 100644 --- a/_json_tracking_context_8cs_source.html +++ b/_json_tracking_context_8cs_source.html @@ -92,17 +92,17 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
JsonTrackingContext.cs
-Go to the documentation of this file.
1 using Newtonsoft.Json;
2 using Newtonsoft.Json.Serialization;
3 using System;
4 using System.Collections.Generic;
5 using System.Text;
6 using System.Threading;
7 using System.Threading.Tasks;
10 using Tgstation.Server.Host.IO;
11 
12 namespace Tgstation.Server.Host.Components.Chat
13 {
16  {
19  readonly Action onDispose;
20 
21  readonly string commandsPath;
22  readonly string channelsPath;
23 
24  readonly SemaphoreSlim channelsSemaphore;
25 
26  public JsonTrackingContext(IIOManager ioManager, ICustomCommandHandler customCommandHandler, Action onDispose, string commandsPath, string channelsPath)
27  {
28  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
29  this.customCommandHandler = customCommandHandler ?? throw new ArgumentNullException(nameof(customCommandHandler));
30  this.onDispose = onDispose ?? throw new ArgumentNullException(nameof(onDispose));
31  this.commandsPath = commandsPath ?? throw new ArgumentNullException(nameof(commandsPath));
32  this.channelsPath = channelsPath ?? throw new ArgumentNullException(nameof(channelsPath));
33 
34  channelsSemaphore = new SemaphoreSlim(1);
35  }
36 
38  public void Dispose() => onDispose();
39 
41  public async Task<IReadOnlyList<CustomCommand>> GetCustomCommands(CancellationToken cancellationToken)
42  {
43  try
44  {
45  var resultBytes = await ioManager.ReadAllBytes(commandsPath, cancellationToken).ConfigureAwait(false);
46  var resultJson = Encoding.UTF8.GetString(resultBytes);
47  var result = JsonConvert.DeserializeObject<List<CustomCommand>>(resultJson, new JsonSerializerSettings
48  {
49  ContractResolver = new DefaultContractResolver
50  {
51  NamingStrategy = new SnakeCaseNamingStrategy()
52  }
53  });
54  foreach (var I in result)
55  I.SetHandler(customCommandHandler);
56  return result;
57  }
58  catch
59  {
60  return new List<CustomCommand>();
61  }
62  }
63 
65  public async Task SetChannels(IEnumerable<Channel> channels, CancellationToken cancellationToken)
66  {
67  using (await SemaphoreSlimContext.Lock(channelsSemaphore, cancellationToken).ConfigureAwait(false))
68  await ioManager.WriteAllBytes(channelsPath, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(channels, Formatting.Indented, new JsonSerializerSettings
69  {
70  ContractResolver = new CamelCasePropertyNamesContractResolver()
71  })), cancellationToken).ConfigureAwait(false);
72  }
73  }
74 }
+Go to the documentation of this file.
1 using Newtonsoft.Json;
2 using Newtonsoft.Json.Serialization;
3 using System;
4 using System.Collections.Generic;
5 using System.Text;
6 using System.Threading;
7 using System.Threading.Tasks;
10 using Tgstation.Server.Host.IO;
11 
12 namespace Tgstation.Server.Host.Components.Chat
13 {
16  {
19  readonly Action onDispose;
20 
21  readonly string commandsPath;
22  readonly string channelsPath;
23 
24  readonly SemaphoreSlim channelsSemaphore;
25 
26  public JsonTrackingContext(IIOManager ioManager, ICustomCommandHandler customCommandHandler, Action onDispose, string commandsPath, string channelsPath)
27  {
28  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
29  this.customCommandHandler = customCommandHandler ?? throw new ArgumentNullException(nameof(customCommandHandler));
30  this.onDispose = onDispose ?? throw new ArgumentNullException(nameof(onDispose));
31  this.commandsPath = commandsPath ?? throw new ArgumentNullException(nameof(commandsPath));
32  this.channelsPath = channelsPath ?? throw new ArgumentNullException(nameof(channelsPath));
33 
34  channelsSemaphore = new SemaphoreSlim(1);
35  }
36 
38  public void Dispose() => onDispose();
39 
41  public async Task<IReadOnlyList<CustomCommand>> GetCustomCommands(CancellationToken cancellationToken)
42  {
43  try
44  {
45  var resultBytes = await ioManager.ReadAllBytes(commandsPath, cancellationToken).ConfigureAwait(false);
46  var resultJson = Encoding.UTF8.GetString(resultBytes);
47  var result = JsonConvert.DeserializeObject<List<CustomCommand>>(resultJson, new JsonSerializerSettings
48  {
49  ContractResolver = new DefaultContractResolver
50  {
51  NamingStrategy = new SnakeCaseNamingStrategy()
52  }
53  });
54  foreach (var I in result)
55  I.SetHandler(customCommandHandler);
56  return result;
57  }
58  catch
59  {
60  return new List<CustomCommand>();
61  }
62  }
63 
65  public async Task SetChannels(IEnumerable<Channel> channels, CancellationToken cancellationToken)
66  {
67  using (await SemaphoreSlimContext.Lock(channelsSemaphore, cancellationToken).ConfigureAwait(false))
68  await ioManager.WriteAllBytes(channelsPath, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(channels, Formatting.Indented, new JsonSerializerSettings
69  {
70  ContractResolver = new CamelCasePropertyNamesContractResolver()
71  })), cancellationToken).ConfigureAwait(false);
72  }
73  }
74 }
async Task SetChannels(IEnumerable< Channel > channels, CancellationToken cancellationToken)
Writes information about connected channels to the IJsonTrackingContext
JsonTrackingContext(IIOManager ioManager, ICustomCommandHandler customCommandHandler, Action onDispose, string commandsPath, string channelsPath)
+ -
Represents a tracking of dynamic chat json files
diff --git a/_kek_command_8cs_source.html b/_kek_command_8cs_source.html index d97a97408b..a32397dafd 100644 --- a/_kek_command_8cs_source.html +++ b/_kek_command_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
KekCommand.cs
-Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.Components.Chat.Commands
5 {
9  sealed class KekCommand : ICommand
10  {
14  const string Kek = "kek";
15 
17  public string Name => Kek;
18 
20  public string HelpText => Kek;
21 
23  public bool AdminOnly => false;
24 
26  public Task<string> Invoke(string arguments, User user, CancellationToken cancellationToken) => Task.FromResult(Kek);
27  }
28 }
+Go to the documentation of this file.
1 using System.Threading;
2 using System.Threading.Tasks;
3 
4 namespace Tgstation.Server.Host.Components.Chat.Commands
5 {
9  sealed class KekCommand : ICommand
10  {
14  const string Kek = "kek";
15 
17  public string Name => Kek;
18 
20  public string HelpText => Kek;
21 
23  public bool AdminOnly => false;
24 
26  public Task<string> Invoke(string arguments, User user, CancellationToken cancellationToken) => Task.FromResult(Kek);
27  }
28 }
+ -
Represents a tgs_chat_user datum
Definition: User.cs:10
Represents a command that can be invoked by talking to chat bots
Definition: ICommand.cs:9
diff --git a/_launch_result_8cs_source.html b/_launch_result_8cs_source.html index 38c0d2a702..29e6166b8a 100644 --- a/_launch_result_8cs_source.html +++ b/_launch_result_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
LaunchResult.cs
-Go to the documentation of this file.
1 using System;
2 using System.Globalization;
3 
4 namespace Tgstation.Server.Host.Components.Watchdog
5 {
9  public sealed class LaunchResult
10  {
14  public TimeSpan? StartupTime { get; set; }
15 
19  public int? ExitCode { get; set; }
20 
22  public override string ToString() => String.Format(CultureInfo.InvariantCulture, "Exit Code: {0}, Time {1}ms", ExitCode, StartupTime?.TotalMilliseconds);
23  }
24 }
Represents the result of trying to start a DD process
Definition: LaunchResult.cs:9
+Go to the documentation of this file.
1 using System;
2 using System.Globalization;
3 
4 namespace Tgstation.Server.Host.Components.Watchdog
5 {
9  public sealed class LaunchResult
10  {
14  public TimeSpan? StartupTime { get; set; }
15 
19  public int? ExitCode { get; set; }
20 
22  public override string ToString() => String.Format(CultureInfo.InvariantCulture, "Exit Code: {0}, Time {1}ms", ExitCode, StartupTime?.TotalMilliseconds);
23  }
24 }
Represents the result of trying to start a DD process
Definition: LaunchResult.cs:9
+ -
diff --git a/_method_not_supported_exception_8cs_source.html b/_method_not_supported_exception_8cs_source.html index 419be8df92..47e5e5d7b8 100644 --- a/_method_not_supported_exception_8cs_source.html +++ b/_method_not_supported_exception_8cs_source.html @@ -92,14 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
MethodNotSupportedException.cs
-Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
11  {
16  {
17  Message = "This method is not currently supported!",
18  SeverApiVersion = null
19  }, HttpStatusCode.NotImplemented)
20  { }
21 
26  public MethodNotSupportedException(string message) : base(message) { }
27 
33  public MethodNotSupportedException(string message, Exception innerException) : base(message, innerException) { }
34  }
35 }
+Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
11  {
16  {
17  Message = "This method is not currently supported!",
18  SeverApiVersion = null
19  }, HttpStatusCode.NotImplemented)
20  { }
21 
26  public MethodNotSupportedException(string message) : base(message) { }
27 
33  public MethodNotSupportedException(string message, Exception innerException) : base(message, innerException) { }
34  }
35 }
MethodNotSupportedException()
Construct an MethodNotSupportedException
+
MethodNotSupportedException(string message, Exception innerException)
Construct an MethodNotSupportedException with a message and innerException
MethodNotSupportedException(string message)
Construct an MethodNotSupportedException with a message
-
Occurs when the client tries to use a currently unsupported API
Represents an error message returned by the server
Definition: ErrorMessage.cs:8
diff --git a/_model_controller_8cs_source.html b/_model_controller_8cs_source.html index 83ccad67d6..f9871201be 100644 --- a/_model_controller_8cs_source.html +++ b/_model_controller_8cs_source.html @@ -92,14 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ModelController.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.Extensions.Logging;
3 using System.Linq;
4 using System.Threading;
5 using System.Threading.Tasks;
6 using Tgstation.Server.Api;
9 
10 namespace Tgstation.Server.Host.Controllers
11 {
16  public abstract class ModelController<TModel> : ApiController where TModel : class
17  {
25  public ModelController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger logger, bool requireInstance) : base(databaseContext, authenticationContextFactory, logger, requireInstance) { }
26 
33  [HttpPut]
34  public virtual Task<IActionResult> Create([FromBody]TModel model, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
35 
41  [HttpGet]
42  public virtual Task<IActionResult> Read(CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
43 
50  [HttpGet("{id}")]
51  public virtual Task<IActionResult> GetId(long id, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
52 
59  [HttpPost]
60  public virtual Task<IActionResult> Update([FromBody]TModel model, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
61 
68  [HttpDelete("{id}")]
69  public virtual Task<IActionResult> Delete(long id, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
70 
76  [HttpGet("List")]
77  public virtual Task<IActionResult> List(CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
78  }
79 }
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.Extensions.Logging;
3 using System.Linq;
4 using System.Threading;
5 using System.Threading.Tasks;
6 using Tgstation.Server.Api;
9 
10 namespace Tgstation.Server.Host.Controllers
11 {
16  public abstract class ModelController<TModel> : ApiController where TModel : class
17  {
25  public ModelController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger logger, bool requireInstance) : base(databaseContext, authenticationContextFactory, logger, requireInstance) { }
26 
33  [HttpPut]
34  public virtual Task<IActionResult> Create([FromBody]TModel model, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
35 
41  [HttpGet]
42  public virtual Task<IActionResult> Read(CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
43 
50  [HttpGet("{id}")]
51  public virtual Task<IActionResult> GetId(long id, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
52 
59  [HttpPost]
60  public virtual Task<IActionResult> Update([FromBody]TModel model, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
61 
68  [HttpDelete("{id}")]
69  public virtual Task<IActionResult> Delete(long id, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
70 
76  [HttpGet("List")]
77  public virtual Task<IActionResult> List(CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound());
78  }
79 }
+
ModelController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger logger, bool requireInstance)
Construct a ModelController<TModel>
-
An ApiController representing a TModel
diff --git a/_models_2_reattach_information_8cs_source.html b/_models_2_reattach_information_8cs_source.html index 6ccc2ed714..6467dcdc4a 100644 --- a/_models_2_reattach_information_8cs_source.html +++ b/_models_2_reattach_information_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ReattachInformation.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Models
2 {
7  {
11  public long Id { get; set; }
12 
16  public CompileJob CompileJob { get; set; }
17  }
18 }
Database representation of Components.Watchdog.ReattachInformation
+Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Models
2 {
7  {
11  public long Id { get; set; }
12 
16  public CompileJob CompileJob { get; set; }
17  }
18 }
+
Database representation of Components.Watchdog.ReattachInformation
-
diff --git a/_models_2_watchdog_reattach_information_8cs_source.html b/_models_2_watchdog_reattach_information_8cs_source.html index c8dd431165..66996b8fa4 100644 --- a/_models_2_watchdog_reattach_information_8cs_source.html +++ b/_models_2_watchdog_reattach_information_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
WatchdogReattachInformation.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Models
2 {
7  {
11  public long Id { get; set; }
12 
16  public long InstanceId { get; set; }
17 
21  public ReattachInformation Alpha { get; set; }
22 
26  public ReattachInformation Bravo { get; set; }
27  }
28 }
Database representation of Components.Watchdog.WatchdogReattachInformation
+Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Models
2 {
7  {
11  public long Id { get; set; }
12 
16  public long InstanceId { get; set; }
17 
21  public ReattachInformation Alpha { get; set; }
22 
26  public ReattachInformation Bravo { get; set; }
27  }
28 }
Database representation of Components.Watchdog.WatchdogReattachInformation
+
Database representation of Components.Watchdog.ReattachInformation
-
diff --git a/_monitor_action_8cs_source.html b/_monitor_action_8cs_source.html index 437fe285d7..6fcfed903a 100644 --- a/_monitor_action_8cs_source.html +++ b/_monitor_action_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
MonitorAction.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Components.Watchdog
2 {
7  {
8  Continue,
9  Restart,
10  Break,
11  Exit
12  }
13 }
+Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Components.Watchdog
2 {
7  {
8  Continue,
9  Restart,
10  Break,
11  Exit
12  }
13 }
+ -
MonitorAction
The action for the monitor loop to take when control is returned to it
Definition: MonitorAction.cs:6
diff --git a/_monitor_activation_reason_8cs_source.html b/_monitor_activation_reason_8cs_source.html index 2f0a56c0eb..98a1651bfb 100644 --- a/_monitor_activation_reason_8cs_source.html +++ b/_monitor_activation_reason_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
MonitorActivationReason.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Components.Watchdog
2 {
4  {
12  }
13 }
+Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Components.Watchdog
2 {
4  {
12  }
13 }
+ - diff --git a/_monitor_state_8cs_source.html b/_monitor_state_8cs_source.html index ead3d56019..8d00fcf4d0 100644 --- a/_monitor_state_8cs_source.html +++ b/_monitor_state_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
MonitorState.cs
-Go to the documentation of this file.
1 using Newtonsoft.Json;
2 
3 namespace Tgstation.Server.Host.Components.Watchdog
4 {
8  sealed class MonitorState
9  {
13  public bool RebootingInactiveServer { get; set; }
14 
18  public bool InactiveServerHasStagedDmb { get; set; }
19 
23  public bool InactiveServerCritFail { get; set; }
24 
28  public MonitorAction NextAction { get; set; }
29 
33  [JsonIgnore]
34  public ISessionController ActiveServer { get; set; }
35 
39  [JsonIgnore]
40  public ISessionController InactiveServer { get; set; }
41  }
42 }
Handles communication with a DreamDaemon IProcess
- +Go to the documentation of this file.
1 using Newtonsoft.Json;
2 
3 namespace Tgstation.Server.Host.Components.Watchdog
4 {
8  sealed class MonitorState
9  {
13  public bool RebootingInactiveServer { get; set; }
14 
18  public bool InactiveServerHasStagedDmb { get; set; }
19 
23  public bool InactiveServerCritFail { get; set; }
24 
28  public MonitorAction NextAction { get; set; }
29 
33  [JsonIgnore]
34  public ISessionController ActiveServer { get; set; }
35 
39  [JsonIgnore]
40  public ISessionController InactiveServer { get; set; }
41  }
42 }
+
Handles communication with a DreamDaemon IProcess
MonitorAction
The action for the monitor loop to take when control is returned to it
Definition: MonitorAction.cs:6
The (absolute) state of the Watchdog
Definition: MonitorState.cs:8
diff --git a/_my_sql_database_context_8cs_source.html b/_my_sql_database_context_8cs_source.html index 1112df32e6..d294fd9a78 100644 --- a/_my_sql_database_context_8cs_source.html +++ b/_my_sql_database_context_8cs_source.html @@ -92,14 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
MySqlDatabaseContext.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using Microsoft.Extensions.Options;
4 using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
5 using System;
7 
8 namespace Tgstation.Server.Host.Models
9 {
13  sealed class MySqlDatabaseContext : DatabaseContext<MySqlDatabaseContext>
14  {
22  public MySqlDatabaseContext(DbContextOptions<MySqlDatabaseContext> dbContextOptions, IOptions<DatabaseConfiguration> databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger<MySqlDatabaseContext> logger) : base(dbContextOptions, databaseConfiguration, databaseSeeder, logger)
23  { }
24 
26  protected override void OnConfiguring(DbContextOptionsBuilder options)
27  {
28  base.OnConfiguring(options);
29  if (!String.IsNullOrEmpty(DatabaseConfiguration.MySqlServerVersion))
30  options.UseMySql(DatabaseConfiguration.ConnectionString, mySqlOptions => mySqlOptions.ServerVersion(Version.Parse(DatabaseConfiguration.MySqlServerVersion), DatabaseConfiguration.DatabaseType == DatabaseType.MariaDB ? ServerType.MariaDb : ServerType.MySql));
31  else
32  options.UseMySql(DatabaseConfiguration.ConnectionString);
33  }
34  }
35 }
string ConnectionString
The connection string for the database
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using Microsoft.Extensions.Options;
4 using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
5 using System;
7 
8 namespace Tgstation.Server.Host.Models
9 {
13  sealed class MySqlDatabaseContext : DatabaseContext<MySqlDatabaseContext>
14  {
22  public MySqlDatabaseContext(DbContextOptions<MySqlDatabaseContext> dbContextOptions, IOptions<DatabaseConfiguration> databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger<MySqlDatabaseContext> logger) : base(dbContextOptions, databaseConfiguration, databaseSeeder, logger)
23  { }
24 
26  protected override void OnConfiguring(DbContextOptionsBuilder options)
27  {
28  base.OnConfiguring(options);
29  if (!String.IsNullOrEmpty(DatabaseConfiguration.MySqlServerVersion))
30  options.UseMySql(DatabaseConfiguration.ConnectionString, mySqlOptions => mySqlOptions.ServerVersion(Version.Parse(DatabaseConfiguration.MySqlServerVersion), DatabaseConfiguration.DatabaseType == DatabaseType.MariaDB ? ServerType.MariaDb : ServerType.MySql));
31  else
32  options.UseMySql(DatabaseConfiguration.ConnectionString);
33  }
34  }
35 }
string ConnectionString
The connection string for the database
MySqlDatabaseContext(DbContextOptions< MySqlDatabaseContext > dbContextOptions, IOptions< DatabaseConfiguration > databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger< MySqlDatabaseContext > logger)
Construct a MySqlDatabaseContext
+
override void OnConfiguring(DbContextOptionsBuilder options)
For initially seeding a database
DatabaseType DatabaseType
The Configuration.DatabaseType to create
-
string MySqlServerVersion
The string form of the System.Version of a target MySQL/MariaDB server
Configuration options for the Models.DatabaseContext<TParentContext>
diff --git a/_my_sql_database_context_model_snapshot_8cs_source.html b/_my_sql_database_context_model_snapshot_8cs_source.html index b7ca435f07..2281794644 100644 --- a/_my_sql_database_context_model_snapshot_8cs_source.html +++ b/_my_sql_database_context_model_snapshot_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
MySqlDatabaseContextModelSnapshot.cs
-Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
7 
8 namespace Tgstation.Server.Host.Models.Migrations
9 {
12  {
13  protected override void BuildModel(ModelBuilder modelBuilder)
14  {
15 #pragma warning disable 612, 618
16  modelBuilder
17  .HasAnnotation("ProductVersion", "2.1.3-rtm-32065")
18  .HasAnnotation("Relational:MaxIdentifierLength", 64);
19 
20  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
21  {
22  b.Property<long>("Id")
23  .ValueGeneratedOnAdd();
24 
25  b.Property<string>("ConnectionString")
26  .IsRequired();
27 
28  b.Property<bool?>("Enabled");
29 
30  b.Property<long>("InstanceId");
31 
32  b.Property<string>("Name")
33  .IsRequired();
34 
35  b.Property<int?>("Provider");
36 
37  b.HasKey("Id");
38 
39  b.HasIndex("InstanceId");
40 
41  b.HasIndex("Name")
42  .IsUnique();
43 
44  b.ToTable("ChatBots");
45  });
46 
47  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
48  {
49  b.Property<long>("Id")
50  .ValueGeneratedOnAdd();
51 
52  b.Property<long>("ChatSettingsId");
53 
54  b.Property<ulong?>("DiscordChannelId");
55 
56  b.Property<string>("IrcChannel");
57 
58  b.Property<bool?>("IsAdminChannel")
59  .IsRequired();
60 
61  b.Property<bool?>("IsUpdatesChannel")
62  .IsRequired();
63 
64  b.Property<bool?>("IsWatchdogChannel")
65  .IsRequired();
66 
67  b.Property<string>("Tag");
68 
69  b.HasKey("Id");
70 
71  b.HasIndex("ChatSettingsId", "DiscordChannelId")
72  .IsUnique();
73 
74  b.HasIndex("ChatSettingsId", "IrcChannel")
75  .IsUnique();
76 
77  b.ToTable("ChatChannels");
78  });
79 
80  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
81  {
82  b.Property<long>("Id")
83  .ValueGeneratedOnAdd();
84 
85  b.Property<string>("ByondVersion")
86  .IsRequired();
87 
88  b.Property<Guid?>("DirectoryName")
89  .IsRequired();
90 
91  b.Property<string>("DmeName")
92  .IsRequired();
93 
94  b.Property<long>("JobId");
95 
96  b.Property<int>("MinimumSecurityLevel");
97 
98  b.Property<string>("Output")
99  .IsRequired();
100 
101  b.Property<long>("RevisionInformationId");
102 
103  b.HasKey("Id");
104 
105  b.HasIndex("DirectoryName");
106 
107  b.HasIndex("JobId")
108  .IsUnique();
109 
110  b.HasIndex("RevisionInformationId");
111 
112  b.ToTable("CompileJobs");
113  });
114 
115  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
116  {
117  b.Property<long>("Id")
118  .ValueGeneratedOnAdd();
119 
120  b.Property<string>("AccessToken");
121 
122  b.Property<bool?>("AllowWebClient")
123  .IsRequired();
124 
125  b.Property<bool?>("AutoStart")
126  .IsRequired();
127 
128  b.Property<long>("InstanceId");
129 
130  b.Property<ushort?>("PrimaryPort")
131  .IsRequired();
132 
133  b.Property<int?>("ProcessId");
134 
135  b.Property<ushort?>("SecondaryPort")
136  .IsRequired();
137 
138  b.Property<int>("SecurityLevel");
139 
140  b.Property<bool?>("SoftRestart")
141  .IsRequired();
142 
143  b.Property<bool?>("SoftShutdown")
144  .IsRequired();
145 
146  b.Property<uint?>("StartupTimeout")
147  .IsRequired();
148 
149  b.HasKey("Id");
150 
151  b.HasIndex("InstanceId")
152  .IsUnique();
153 
154  b.ToTable("DreamDaemonSettings");
155  });
156 
157  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
158  {
159  b.Property<long>("Id")
160  .ValueGeneratedOnAdd();
161 
162  b.Property<ushort?>("ApiValidationPort")
163  .IsRequired();
164 
165  b.Property<int>("ApiValidationSecurityLevel");
166 
167  b.Property<long>("InstanceId");
168 
169  b.Property<string>("ProjectName");
170 
171  b.HasKey("Id");
172 
173  b.HasIndex("InstanceId")
174  .IsUnique();
175 
176  b.ToTable("DreamMakerSettings");
177  });
178 
179  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
180  {
181  b.Property<long>("Id")
182  .ValueGeneratedOnAdd();
183 
184  b.Property<uint?>("AutoUpdateInterval")
185  .IsRequired();
186 
187  b.Property<int>("ConfigurationType");
188 
189  b.Property<string>("Name")
190  .IsRequired();
191 
192  b.Property<bool?>("Online")
193  .IsRequired();
194 
195  b.Property<string>("Path")
196  .IsRequired();
197 
198  b.HasKey("Id");
199 
200  b.HasIndex("Path")
201  .IsUnique();
202 
203  b.ToTable("Instances");
204  });
205 
206  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
207  {
208  b.Property<long>("Id")
209  .ValueGeneratedOnAdd();
210 
211  b.Property<ulong>("ByondRights");
212 
213  b.Property<ulong>("ChatBotRights");
214 
215  b.Property<ulong>("ConfigurationRights");
216 
217  b.Property<ulong>("DreamDaemonRights");
218 
219  b.Property<ulong>("DreamMakerRights");
220 
221  b.Property<long>("InstanceId");
222 
223  b.Property<ulong>("InstanceUserRights");
224 
225  b.Property<ulong>("RepositoryRights");
226 
227  b.Property<long?>("UserId")
228  .IsRequired();
229 
230  b.HasKey("Id");
231 
232  b.HasIndex("InstanceId");
233 
234  b.HasIndex("UserId", "InstanceId")
235  .IsUnique();
236 
237  b.ToTable("InstanceUsers");
238  });
239 
240  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
241  {
242  b.Property<long>("Id")
243  .ValueGeneratedOnAdd();
244 
245  b.Property<ulong?>("CancelRight");
246 
247  b.Property<ulong?>("CancelRightsType");
248 
249  b.Property<bool?>("Cancelled")
250  .IsRequired();
251 
252  b.Property<long?>("CancelledById");
253 
254  b.Property<string>("Description")
255  .IsRequired();
256 
257  b.Property<string>("ExceptionDetails");
258 
259  b.Property<long>("InstanceId");
260 
261  b.Property<DateTimeOffset?>("StartedAt")
262  .IsRequired();
263 
264  b.Property<long>("StartedById");
265 
266  b.Property<DateTimeOffset?>("StoppedAt");
267 
268  b.HasKey("Id");
269 
270  b.HasIndex("CancelledById");
271 
272  b.HasIndex("InstanceId");
273 
274  b.HasIndex("StartedById");
275 
276  b.ToTable("Jobs");
277  });
278 
279  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
280  {
281  b.Property<long>("Id")
282  .ValueGeneratedOnAdd();
283 
284  b.Property<string>("AccessIdentifier")
285  .IsRequired();
286 
287  b.Property<string>("ChatChannelsJson")
288  .IsRequired();
289 
290  b.Property<string>("ChatCommandsJson")
291  .IsRequired();
292 
293  b.Property<long?>("CompileJobId");
294 
295  b.Property<bool>("IsPrimary");
296 
297  b.Property<ushort>("Port");
298 
299  b.Property<int>("ProcessId");
300 
301  b.Property<int>("RebootState");
302 
303  b.Property<string>("ServerCommandsJson")
304  .IsRequired();
305 
306  b.HasKey("Id");
307 
308  b.HasIndex("CompileJobId");
309 
310  b.ToTable("ReattachInformations");
311  });
312 
313  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
314  {
315  b.Property<long>("Id")
316  .ValueGeneratedOnAdd();
317 
318  b.Property<string>("AccessToken");
319 
320  b.Property<string>("AccessUser");
321 
322  b.Property<bool?>("AutoUpdatesKeepTestMerges")
323  .IsRequired();
324 
325  b.Property<bool?>("AutoUpdatesSynchronize")
326  .IsRequired();
327 
328  b.Property<string>("CommitterEmail")
329  .IsRequired();
330 
331  b.Property<string>("CommitterName")
332  .IsRequired();
333 
334  b.Property<long>("InstanceId");
335 
336  b.Property<bool?>("PushTestMergeCommits")
337  .IsRequired();
338 
339  b.Property<bool?>("ShowTestMergeCommitters")
340  .IsRequired();
341 
342  b.HasKey("Id");
343 
344  b.HasIndex("InstanceId")
345  .IsUnique();
346 
347  b.ToTable("RepositorySettings");
348  });
349 
350  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
351  {
352  b.Property<long>("Id")
353  .ValueGeneratedOnAdd();
354 
355  b.Property<long>("RevisionInformationId");
356 
357  b.Property<long>("TestMergeId");
358 
359  b.HasKey("Id");
360 
361  b.HasIndex("RevisionInformationId");
362 
363  b.HasIndex("TestMergeId");
364 
365  b.ToTable("RevInfoTestMerges");
366  });
367 
368  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
369  {
370  b.Property<long>("Id")
371  .ValueGeneratedOnAdd();
372 
373  b.Property<string>("CommitSha")
374  .IsRequired()
375  .HasMaxLength(40);
376 
377  b.Property<long>("InstanceId");
378 
379  b.Property<string>("OriginCommitSha")
380  .IsRequired()
381  .HasMaxLength(40);
382 
383  b.HasKey("Id");
384 
385  b.HasIndex("CommitSha")
386  .IsUnique();
387 
388  b.HasIndex("InstanceId");
389 
390  b.ToTable("RevisionInformations");
391  });
392 
393  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
394  {
395  b.Property<long>("Id")
396  .ValueGeneratedOnAdd();
397 
398  b.Property<string>("Author")
399  .IsRequired();
400 
401  b.Property<string>("BodyAtMerge")
402  .IsRequired();
403 
404  b.Property<string>("Comment");
405 
406  b.Property<DateTimeOffset>("MergedAt");
407 
408  b.Property<long>("MergedById");
409 
410  b.Property<int?>("Number")
411  .IsRequired();
412 
413  b.Property<long?>("PrimaryRevisionInformationId")
414  .IsRequired();
415 
416  b.Property<string>("PullRequestRevision")
417  .IsRequired();
418 
419  b.Property<string>("TitleAtMerge")
420  .IsRequired();
421 
422  b.Property<string>("Url")
423  .IsRequired();
424 
425  b.HasKey("Id");
426 
427  b.HasIndex("MergedById");
428 
429  b.HasIndex("PrimaryRevisionInformationId")
430  .IsUnique();
431 
432  b.ToTable("TestMerges");
433  });
434 
435  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
436  {
437  b.Property<long>("Id")
438  .ValueGeneratedOnAdd();
439 
440  b.Property<ulong>("AdministrationRights");
441 
442  b.Property<string>("CanonicalName")
443  .IsRequired();
444 
445  b.Property<DateTimeOffset?>("CreatedAt")
446  .IsRequired();
447 
448  b.Property<long?>("CreatedById");
449 
450  b.Property<bool?>("Enabled")
451  .IsRequired();
452 
453  b.Property<ulong>("InstanceManagerRights");
454 
455  b.Property<DateTimeOffset?>("LastPasswordUpdate");
456 
457  b.Property<string>("Name")
458  .IsRequired();
459 
460  b.Property<string>("PasswordHash");
461 
462  b.Property<string>("SystemIdentifier");
463 
464  b.HasKey("Id");
465 
466  b.HasIndex("CanonicalName")
467  .IsUnique();
468 
469  b.HasIndex("CreatedById");
470 
471  b.ToTable("Users");
472  });
473 
474  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
475  {
476  b.Property<long>("Id")
477  .ValueGeneratedOnAdd();
478 
479  b.Property<long?>("AlphaId");
480 
481  b.Property<bool>("AlphaIsActive");
482 
483  b.Property<long?>("BravoId");
484 
485  b.Property<long>("InstanceId");
486 
487  b.HasKey("Id");
488 
489  b.HasIndex("AlphaId");
490 
491  b.HasIndex("BravoId");
492 
493  b.HasIndex("InstanceId")
494  .IsUnique();
495 
496  b.ToTable("WatchdogReattachInformations");
497  });
498 
499  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
500  {
501  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
502  .WithMany("ChatSettings")
503  .HasForeignKey("InstanceId")
504  .OnDelete(DeleteBehavior.Cascade);
505  });
506 
507  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
508  {
509  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
510  .WithMany("Channels")
511  .HasForeignKey("ChatSettingsId")
512  .OnDelete(DeleteBehavior.Cascade);
513  });
514 
515  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
516  {
517  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
518  .WithOne()
519  .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
520  .OnDelete(DeleteBehavior.Restrict);
521 
522  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
523  .WithMany("CompileJobs")
524  .HasForeignKey("RevisionInformationId")
525  .OnDelete(DeleteBehavior.Cascade);
526  });
527 
528  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
529  {
530  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
531  .WithOne("DreamDaemonSettings")
532  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
533  .OnDelete(DeleteBehavior.Cascade);
534  });
535 
536  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
537  {
538  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
539  .WithOne("DreamMakerSettings")
540  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
541  .OnDelete(DeleteBehavior.Cascade);
542  });
543 
544  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
545  {
546  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
547  .WithMany("InstanceUsers")
548  .HasForeignKey("InstanceId")
549  .OnDelete(DeleteBehavior.Cascade);
550 
551  b.HasOne("Tgstation.Server.Host.Models.User")
552  .WithMany("InstanceUsers")
553  .HasForeignKey("UserId")
554  .OnDelete(DeleteBehavior.Cascade);
555  });
556 
557  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
558  {
559  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
560  .WithMany()
561  .HasForeignKey("CancelledById");
562 
563  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
564  .WithMany("Jobs")
565  .HasForeignKey("InstanceId")
566  .OnDelete(DeleteBehavior.Cascade);
567 
568  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
569  .WithMany()
570  .HasForeignKey("StartedById")
571  .OnDelete(DeleteBehavior.Cascade);
572  });
573 
574  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
575  {
576  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
577  .WithMany()
578  .HasForeignKey("CompileJobId");
579  });
580 
581  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
582  {
583  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
584  .WithOne("RepositorySettings")
585  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
586  .OnDelete(DeleteBehavior.Cascade);
587  });
588 
589  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
590  {
591  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
592  .WithMany("ActiveTestMerges")
593  .HasForeignKey("RevisionInformationId")
594  .OnDelete(DeleteBehavior.Cascade);
595 
596  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
597  .WithMany("RevisonInformations")
598  .HasForeignKey("TestMergeId")
599  .OnDelete(DeleteBehavior.Cascade);
600  });
601 
602  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
603  {
604  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
605  .WithMany("RevisionInformations")
606  .HasForeignKey("InstanceId")
607  .OnDelete(DeleteBehavior.Cascade);
608  });
609 
610  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
611  {
612  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
613  .WithMany("TestMerges")
614  .HasForeignKey("MergedById")
615  .OnDelete(DeleteBehavior.Restrict);
616 
617  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
618  .WithOne("PrimaryTestMerge")
619  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
620  .OnDelete(DeleteBehavior.Restrict);
621  });
622 
623  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
624  {
625  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
626  .WithMany("CreatedUsers")
627  .HasForeignKey("CreatedById");
628  });
629 
630  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
631  {
632  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
633  .WithMany()
634  .HasForeignKey("AlphaId");
635 
636  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
637  .WithMany()
638  .HasForeignKey("BravoId");
639 
640  b.HasOne("Tgstation.Server.Host.Models.Instance")
641  .WithOne("WatchdogReattachInformation")
642  .HasForeignKey("Tgstation.Server.Host.Models.WatchdogReattachInformation", "InstanceId")
643  .OnDelete(DeleteBehavior.Cascade);
644  });
645 #pragma warning restore 612, 618
646  }
647  }
648 }
+Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
7 
8 namespace Tgstation.Server.Host.Models.Migrations
9 {
12  {
13  protected override void BuildModel(ModelBuilder modelBuilder)
14  {
15 #pragma warning disable 612, 618
16  modelBuilder
17  .HasAnnotation("ProductVersion", "2.1.3-rtm-32065")
18  .HasAnnotation("Relational:MaxIdentifierLength", 64);
19 
20  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
21  {
22  b.Property<long>("Id")
23  .ValueGeneratedOnAdd();
24 
25  b.Property<string>("ConnectionString")
26  .IsRequired();
27 
28  b.Property<bool?>("Enabled");
29 
30  b.Property<long>("InstanceId");
31 
32  b.Property<string>("Name")
33  .IsRequired();
34 
35  b.Property<int?>("Provider");
36 
37  b.HasKey("Id");
38 
39  b.HasIndex("InstanceId");
40 
41  b.HasIndex("Name")
42  .IsUnique();
43 
44  b.ToTable("ChatBots");
45  });
46 
47  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
48  {
49  b.Property<long>("Id")
50  .ValueGeneratedOnAdd();
51 
52  b.Property<long>("ChatSettingsId");
53 
54  b.Property<ulong?>("DiscordChannelId");
55 
56  b.Property<string>("IrcChannel");
57 
58  b.Property<bool?>("IsAdminChannel")
59  .IsRequired();
60 
61  b.Property<bool?>("IsUpdatesChannel")
62  .IsRequired();
63 
64  b.Property<bool?>("IsWatchdogChannel")
65  .IsRequired();
66 
67  b.Property<string>("Tag");
68 
69  b.HasKey("Id");
70 
71  b.HasIndex("ChatSettingsId", "DiscordChannelId")
72  .IsUnique();
73 
74  b.HasIndex("ChatSettingsId", "IrcChannel")
75  .IsUnique();
76 
77  b.ToTable("ChatChannels");
78  });
79 
80  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
81  {
82  b.Property<long>("Id")
83  .ValueGeneratedOnAdd();
84 
85  b.Property<string>("ByondVersion")
86  .IsRequired();
87 
88  b.Property<Guid?>("DirectoryName")
89  .IsRequired();
90 
91  b.Property<string>("DmeName")
92  .IsRequired();
93 
94  b.Property<long>("JobId");
95 
96  b.Property<int>("MinimumSecurityLevel");
97 
98  b.Property<string>("Output")
99  .IsRequired();
100 
101  b.Property<long>("RevisionInformationId");
102 
103  b.HasKey("Id");
104 
105  b.HasIndex("DirectoryName");
106 
107  b.HasIndex("JobId")
108  .IsUnique();
109 
110  b.HasIndex("RevisionInformationId");
111 
112  b.ToTable("CompileJobs");
113  });
114 
115  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
116  {
117  b.Property<long>("Id")
118  .ValueGeneratedOnAdd();
119 
120  b.Property<string>("AccessToken");
121 
122  b.Property<bool?>("AllowWebClient")
123  .IsRequired();
124 
125  b.Property<bool?>("AutoStart")
126  .IsRequired();
127 
128  b.Property<long>("InstanceId");
129 
130  b.Property<ushort?>("PrimaryPort")
131  .IsRequired();
132 
133  b.Property<int?>("ProcessId");
134 
135  b.Property<ushort?>("SecondaryPort")
136  .IsRequired();
137 
138  b.Property<int>("SecurityLevel");
139 
140  b.Property<bool?>("SoftRestart")
141  .IsRequired();
142 
143  b.Property<bool?>("SoftShutdown")
144  .IsRequired();
145 
146  b.Property<uint?>("StartupTimeout")
147  .IsRequired();
148 
149  b.HasKey("Id");
150 
151  b.HasIndex("InstanceId")
152  .IsUnique();
153 
154  b.ToTable("DreamDaemonSettings");
155  });
156 
157  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
158  {
159  b.Property<long>("Id")
160  .ValueGeneratedOnAdd();
161 
162  b.Property<ushort?>("ApiValidationPort")
163  .IsRequired();
164 
165  b.Property<int>("ApiValidationSecurityLevel");
166 
167  b.Property<long>("InstanceId");
168 
169  b.Property<string>("ProjectName");
170 
171  b.HasKey("Id");
172 
173  b.HasIndex("InstanceId")
174  .IsUnique();
175 
176  b.ToTable("DreamMakerSettings");
177  });
178 
179  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
180  {
181  b.Property<long>("Id")
182  .ValueGeneratedOnAdd();
183 
184  b.Property<uint?>("AutoUpdateInterval")
185  .IsRequired();
186 
187  b.Property<int>("ConfigurationType");
188 
189  b.Property<string>("Name")
190  .IsRequired();
191 
192  b.Property<bool?>("Online")
193  .IsRequired();
194 
195  b.Property<string>("Path")
196  .IsRequired();
197 
198  b.HasKey("Id");
199 
200  b.HasIndex("Path")
201  .IsUnique();
202 
203  b.ToTable("Instances");
204  });
205 
206  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
207  {
208  b.Property<long>("Id")
209  .ValueGeneratedOnAdd();
210 
211  b.Property<ulong>("ByondRights");
212 
213  b.Property<ulong>("ChatBotRights");
214 
215  b.Property<ulong>("ConfigurationRights");
216 
217  b.Property<ulong>("DreamDaemonRights");
218 
219  b.Property<ulong>("DreamMakerRights");
220 
221  b.Property<long>("InstanceId");
222 
223  b.Property<ulong>("InstanceUserRights");
224 
225  b.Property<ulong>("RepositoryRights");
226 
227  b.Property<long?>("UserId")
228  .IsRequired();
229 
230  b.HasKey("Id");
231 
232  b.HasIndex("InstanceId");
233 
234  b.HasIndex("UserId", "InstanceId")
235  .IsUnique();
236 
237  b.ToTable("InstanceUsers");
238  });
239 
240  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
241  {
242  b.Property<long>("Id")
243  .ValueGeneratedOnAdd();
244 
245  b.Property<ulong?>("CancelRight");
246 
247  b.Property<ulong?>("CancelRightsType");
248 
249  b.Property<bool?>("Cancelled")
250  .IsRequired();
251 
252  b.Property<long?>("CancelledById");
253 
254  b.Property<string>("Description")
255  .IsRequired();
256 
257  b.Property<string>("ExceptionDetails");
258 
259  b.Property<long>("InstanceId");
260 
261  b.Property<DateTimeOffset?>("StartedAt")
262  .IsRequired();
263 
264  b.Property<long>("StartedById");
265 
266  b.Property<DateTimeOffset?>("StoppedAt");
267 
268  b.HasKey("Id");
269 
270  b.HasIndex("CancelledById");
271 
272  b.HasIndex("InstanceId");
273 
274  b.HasIndex("StartedById");
275 
276  b.ToTable("Jobs");
277  });
278 
279  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
280  {
281  b.Property<long>("Id")
282  .ValueGeneratedOnAdd();
283 
284  b.Property<string>("AccessIdentifier")
285  .IsRequired();
286 
287  b.Property<string>("ChatChannelsJson")
288  .IsRequired();
289 
290  b.Property<string>("ChatCommandsJson")
291  .IsRequired();
292 
293  b.Property<long?>("CompileJobId");
294 
295  b.Property<bool>("IsPrimary");
296 
297  b.Property<ushort>("Port");
298 
299  b.Property<int>("ProcessId");
300 
301  b.Property<int>("RebootState");
302 
303  b.Property<string>("ServerCommandsJson")
304  .IsRequired();
305 
306  b.HasKey("Id");
307 
308  b.HasIndex("CompileJobId");
309 
310  b.ToTable("ReattachInformations");
311  });
312 
313  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
314  {
315  b.Property<long>("Id")
316  .ValueGeneratedOnAdd();
317 
318  b.Property<string>("AccessToken");
319 
320  b.Property<string>("AccessUser");
321 
322  b.Property<bool?>("AutoUpdatesKeepTestMerges")
323  .IsRequired();
324 
325  b.Property<bool?>("AutoUpdatesSynchronize")
326  .IsRequired();
327 
328  b.Property<string>("CommitterEmail")
329  .IsRequired();
330 
331  b.Property<string>("CommitterName")
332  .IsRequired();
333 
334  b.Property<long>("InstanceId");
335 
336  b.Property<bool?>("PushTestMergeCommits")
337  .IsRequired();
338 
339  b.Property<bool?>("ShowTestMergeCommitters")
340  .IsRequired();
341 
342  b.HasKey("Id");
343 
344  b.HasIndex("InstanceId")
345  .IsUnique();
346 
347  b.ToTable("RepositorySettings");
348  });
349 
350  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
351  {
352  b.Property<long>("Id")
353  .ValueGeneratedOnAdd();
354 
355  b.Property<long>("RevisionInformationId");
356 
357  b.Property<long>("TestMergeId");
358 
359  b.HasKey("Id");
360 
361  b.HasIndex("RevisionInformationId");
362 
363  b.HasIndex("TestMergeId");
364 
365  b.ToTable("RevInfoTestMerges");
366  });
367 
368  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
369  {
370  b.Property<long>("Id")
371  .ValueGeneratedOnAdd();
372 
373  b.Property<string>("CommitSha")
374  .IsRequired()
375  .HasMaxLength(40);
376 
377  b.Property<long>("InstanceId");
378 
379  b.Property<string>("OriginCommitSha")
380  .IsRequired()
381  .HasMaxLength(40);
382 
383  b.HasKey("Id");
384 
385  b.HasIndex("CommitSha")
386  .IsUnique();
387 
388  b.HasIndex("InstanceId");
389 
390  b.ToTable("RevisionInformations");
391  });
392 
393  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
394  {
395  b.Property<long>("Id")
396  .ValueGeneratedOnAdd();
397 
398  b.Property<string>("Author")
399  .IsRequired();
400 
401  b.Property<string>("BodyAtMerge")
402  .IsRequired();
403 
404  b.Property<string>("Comment");
405 
406  b.Property<DateTimeOffset>("MergedAt");
407 
408  b.Property<long>("MergedById");
409 
410  b.Property<int?>("Number")
411  .IsRequired();
412 
413  b.Property<long?>("PrimaryRevisionInformationId")
414  .IsRequired();
415 
416  b.Property<string>("PullRequestRevision")
417  .IsRequired();
418 
419  b.Property<string>("TitleAtMerge")
420  .IsRequired();
421 
422  b.Property<string>("Url")
423  .IsRequired();
424 
425  b.HasKey("Id");
426 
427  b.HasIndex("MergedById");
428 
429  b.HasIndex("PrimaryRevisionInformationId")
430  .IsUnique();
431 
432  b.ToTable("TestMerges");
433  });
434 
435  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
436  {
437  b.Property<long>("Id")
438  .ValueGeneratedOnAdd();
439 
440  b.Property<ulong>("AdministrationRights");
441 
442  b.Property<string>("CanonicalName")
443  .IsRequired();
444 
445  b.Property<DateTimeOffset?>("CreatedAt")
446  .IsRequired();
447 
448  b.Property<long?>("CreatedById");
449 
450  b.Property<bool?>("Enabled")
451  .IsRequired();
452 
453  b.Property<ulong>("InstanceManagerRights");
454 
455  b.Property<DateTimeOffset?>("LastPasswordUpdate");
456 
457  b.Property<string>("Name")
458  .IsRequired();
459 
460  b.Property<string>("PasswordHash");
461 
462  b.Property<string>("SystemIdentifier");
463 
464  b.HasKey("Id");
465 
466  b.HasIndex("CanonicalName")
467  .IsUnique();
468 
469  b.HasIndex("CreatedById");
470 
471  b.ToTable("Users");
472  });
473 
474  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
475  {
476  b.Property<long>("Id")
477  .ValueGeneratedOnAdd();
478 
479  b.Property<long?>("AlphaId");
480 
481  b.Property<bool>("AlphaIsActive");
482 
483  b.Property<long?>("BravoId");
484 
485  b.Property<long>("InstanceId");
486 
487  b.HasKey("Id");
488 
489  b.HasIndex("AlphaId");
490 
491  b.HasIndex("BravoId");
492 
493  b.HasIndex("InstanceId")
494  .IsUnique();
495 
496  b.ToTable("WatchdogReattachInformations");
497  });
498 
499  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
500  {
501  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
502  .WithMany("ChatSettings")
503  .HasForeignKey("InstanceId")
504  .OnDelete(DeleteBehavior.Cascade);
505  });
506 
507  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
508  {
509  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
510  .WithMany("Channels")
511  .HasForeignKey("ChatSettingsId")
512  .OnDelete(DeleteBehavior.Cascade);
513  });
514 
515  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
516  {
517  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
518  .WithOne()
519  .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
520  .OnDelete(DeleteBehavior.Restrict);
521 
522  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
523  .WithMany("CompileJobs")
524  .HasForeignKey("RevisionInformationId")
525  .OnDelete(DeleteBehavior.Cascade);
526  });
527 
528  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
529  {
530  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
531  .WithOne("DreamDaemonSettings")
532  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
533  .OnDelete(DeleteBehavior.Cascade);
534  });
535 
536  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
537  {
538  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
539  .WithOne("DreamMakerSettings")
540  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
541  .OnDelete(DeleteBehavior.Cascade);
542  });
543 
544  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
545  {
546  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
547  .WithMany("InstanceUsers")
548  .HasForeignKey("InstanceId")
549  .OnDelete(DeleteBehavior.Cascade);
550 
551  b.HasOne("Tgstation.Server.Host.Models.User")
552  .WithMany("InstanceUsers")
553  .HasForeignKey("UserId")
554  .OnDelete(DeleteBehavior.Cascade);
555  });
556 
557  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
558  {
559  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
560  .WithMany()
561  .HasForeignKey("CancelledById");
562 
563  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
564  .WithMany("Jobs")
565  .HasForeignKey("InstanceId")
566  .OnDelete(DeleteBehavior.Cascade);
567 
568  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
569  .WithMany()
570  .HasForeignKey("StartedById")
571  .OnDelete(DeleteBehavior.Cascade);
572  });
573 
574  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
575  {
576  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
577  .WithMany()
578  .HasForeignKey("CompileJobId");
579  });
580 
581  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
582  {
583  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
584  .WithOne("RepositorySettings")
585  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
586  .OnDelete(DeleteBehavior.Cascade);
587  });
588 
589  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
590  {
591  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
592  .WithMany("ActiveTestMerges")
593  .HasForeignKey("RevisionInformationId")
594  .OnDelete(DeleteBehavior.Cascade);
595 
596  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
597  .WithMany("RevisonInformations")
598  .HasForeignKey("TestMergeId")
599  .OnDelete(DeleteBehavior.Cascade);
600  });
601 
602  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
603  {
604  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
605  .WithMany("RevisionInformations")
606  .HasForeignKey("InstanceId")
607  .OnDelete(DeleteBehavior.Cascade);
608  });
609 
610  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
611  {
612  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
613  .WithMany("TestMerges")
614  .HasForeignKey("MergedById")
615  .OnDelete(DeleteBehavior.Restrict);
616 
617  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
618  .WithOne("PrimaryTestMerge")
619  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
620  .OnDelete(DeleteBehavior.Restrict);
621  });
622 
623  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
624  {
625  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
626  .WithMany("CreatedUsers")
627  .HasForeignKey("CreatedById");
628  });
629 
630  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
631  {
632  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
633  .WithMany()
634  .HasForeignKey("AlphaId");
635 
636  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
637  .WithMany()
638  .HasForeignKey("BravoId");
639 
640  b.HasOne("Tgstation.Server.Host.Models.Instance")
641  .WithOne("WatchdogReattachInformation")
642  .HasForeignKey("Tgstation.Server.Host.Models.WatchdogReattachInformation", "InstanceId")
643  .OnDelete(DeleteBehavior.Cascade);
644  });
645 #pragma warning restore 612, 618
646  }
647  }
648 }
+ -
DatabaseContext<TParentContext> for MySQL
diff --git a/_my_sql_design_time_db_context_factory_8cs_source.html b/_my_sql_design_time_db_context_factory_8cs_source.html index 1c68f1295a..7800dc5342 100644 --- a/_my_sql_design_time_db_context_factory_8cs_source.html +++ b/_my_sql_design_time_db_context_factory_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
MySqlDesignTimeDbContextFactory.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Identity;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.EntityFrameworkCore.Design;
4 using Microsoft.Extensions.Logging;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
10  sealed class MySqlDesignTimeDbContextFactory : IDesignTimeDbContextFactory<MySqlDatabaseContext>
11  {
13  public MySqlDatabaseContext CreateDbContext(string[] args) => new MySqlDatabaseContext(new DbContextOptions<MySqlDatabaseContext>(), DesignTimeDbContextFactoryHelpers.GetDbContextOptions(), new DatabaseSeeder(new CryptographySuite(new PasswordHasher<User>())), new LoggerFactory().CreateLogger<MySqlDatabaseContext>());
14  }
15 }
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Identity;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.EntityFrameworkCore.Design;
4 using Microsoft.Extensions.Logging;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
10  sealed class MySqlDesignTimeDbContextFactory : IDesignTimeDbContextFactory<MySqlDatabaseContext>
11  {
13  public MySqlDatabaseContext CreateDbContext(string[] args) => new MySqlDatabaseContext(new DbContextOptions<MySqlDatabaseContext>(), DesignTimeDbContextFactoryHelpers.GetDbContextOptions(), new DatabaseSeeder(new CryptographySuite(new PasswordHasher<User>())), new LoggerFactory().CreateLogger<MySqlDatabaseContext>());
14  }
15 }
+
Contains helpers for creating design time Models.DatabaseContext<TParentContext>s ...
- diff --git a/_native_methods_8cs_source.html b/_native_methods_8cs_source.html index 55511829cb..61708e85e1 100644 --- a/_native_methods_8cs_source.html +++ b/_native_methods_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
NativeMethods.cs
-Go to the documentation of this file.
1 using System;
2 using System.Runtime.InteropServices;
3 
4 namespace Tgstation.Server.Host
5 {
9  static class NativeMethods
10  {
14  [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
15  public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);
16 
20  [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
21  public static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags);
22  }
23 }
- +Go to the documentation of this file.
1 using System;
2 using System.Runtime.InteropServices;
3 
4 namespace Tgstation.Server.Host
5 {
9  static class NativeMethods
10  {
14  [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
15  public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);
16 
20  [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
21  public static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags);
22  }
23 }
+
Native methods used by the code
Definition: NativeMethods.cs:9
diff --git a/_posix_byond_installer_8cs_source.html b/_posix_byond_installer_8cs_source.html index 047f2e6834..234fd64b5d 100644 --- a/_posix_byond_installer_8cs_source.html +++ b/_posix_byond_installer_8cs_source.html @@ -92,8 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
PosixByondInstaller.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Globalization;
4 using System.Net;
5 using System.Text;
6 using System.Threading;
7 using System.Threading.Tasks;
9 using Tgstation.Server.Host.IO;
10 
11 namespace Tgstation.Server.Host.Components.Byond
12 {
17  {
21  const string ByondRevisionsURL = "https://secure.byond.com/download/build/{0}/{0}.{1}_byond_linux.zip";
25  const string ByondCachePath = "~/.byond/cache";
26 
28  public string DreamDaemonName => "DreamDaemon.sh";
29 
31  public string DreamMakerName => "DreamMaker.sh";
32 
37 
42 
46  readonly ILogger<PosixByondInstaller> logger;
47 
54  public PosixByondInstaller(IIOManager ioManager, IPostWriteHandler postWriteHandler, ILogger<PosixByondInstaller> logger)
55  {
56  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
57  this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler));
58  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
59  }
60 
62  public async Task CleanCache(CancellationToken cancellationToken)
63  {
64  try
65  {
66  await ioManager.DeleteDirectory(ByondCachePath, cancellationToken).ConfigureAwait(false);
67  }
68  catch (Exception e)
69  {
70  logger.LogWarning("Error deleting BYOND cache! Exception: {0}", e);
71  }
72  }
73 
75  public async Task<byte[]> DownloadVersion(Version version, CancellationToken cancellationToken)
76  {
77  var ourVersion = version;
78  //lummox is annoying and doesn't like to post linux versions if nothing changed in DreamDaemon/DM
79  //if this for's exit condition ever triggers, i get to say I told you so
80  Exception lastException = null;
81  for (var I = 0; I < 5 && ourVersion.Minor >= 1; ++I, ourVersion = new Version(ourVersion.Major, ourVersion.Minor))
82  {
83  try
84  {
85  var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsURL, ourVersion.Major, ourVersion.Minor);
86 
87  return await ioManager.DownloadFile(new Uri(url), cancellationToken).ConfigureAwait(false);
88  }
89  catch (WebException e)
90  {
91  if (!(e.Status == WebExceptionStatus.ProtocolError && e.Response is HttpWebResponse response && response.StatusCode == HttpStatusCode.NotFound))
92  throw;
93  lastException = e;
94  }
95  }
96  throw lastException;
97  }
98 
100  public Task InstallByond(string path, Version version, CancellationToken cancellationToken)
101  {
102  //write the scripts for running the ting
103  //need to add $ORIGIN to LD_LIBRARY_PATH
104  const string StandardScript = "#!/bin/sh\nexport LD_LIBRARY_PATH=\"\\$ORIGIN:$LD_LIBRARY_PATH\"\nBASEDIR=$(dirname \"$0\")\nexec \"$BASEDIR/{0}\" \"$@\"\n";
105 
106  const string DreamDaemonExecutableName = "DreamDaemon";
107  const string DreamMakerExecutableName = "DreamMaker";
108 
109  var dreamDaemonScript = String.Format(CultureInfo.InvariantCulture, StandardScript, DreamDaemonExecutableName);
110  var dreamMakerScript = String.Format(CultureInfo.InvariantCulture, StandardScript, DreamMakerExecutableName);
111 
112  async Task WriteAndMakeExecutable(string fullPath, string script)
113  {
114  await ioManager.WriteAllBytes(fullPath, Encoding.ASCII.GetBytes(script), cancellationToken).ConfigureAwait(false);
115  postWriteHandler.HandleWrite(fullPath);
116  }
117 
118  var basePath = ioManager.ConcatPath(path, ByondManager.BinPath);
119 
120  var task = Task.WhenAll(WriteAndMakeExecutable(ioManager.ConcatPath(basePath, DreamDaemonName), dreamDaemonScript), WriteAndMakeExecutable(ioManager.ConcatPath(basePath, DreamMakerName), dreamMakerScript));
121 
122  postWriteHandler.HandleWrite(ioManager.ConcatPath(basePath, DreamDaemonExecutableName));
123  postWriteHandler.HandleWrite(ioManager.ConcatPath(basePath, DreamMakerExecutableName));
124 
125  return task;
126  }
127  }
128 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Globalization;
4 using System.Net;
5 using System.Text;
6 using System.Threading;
7 using System.Threading.Tasks;
9 using Tgstation.Server.Host.IO;
10 
11 namespace Tgstation.Server.Host.Components.Byond
12 {
17  {
21  const string ByondRevisionsURL = "https://secure.byond.com/download/build/{0}/{0}.{1}_byond_linux.zip";
25  const string ByondCachePath = "~/.byond/cache";
26 
28  public string DreamDaemonName => "DreamDaemon.sh";
29 
31  public string DreamMakerName => "DreamMaker.sh";
32 
37 
42 
46  readonly ILogger<PosixByondInstaller> logger;
47 
54  public PosixByondInstaller(IIOManager ioManager, IPostWriteHandler postWriteHandler, ILogger<PosixByondInstaller> logger)
55  {
56  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
57  this.postWriteHandler = postWriteHandler ?? throw new ArgumentNullException(nameof(postWriteHandler));
58  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
59  }
60 
62  public async Task CleanCache(CancellationToken cancellationToken)
63  {
64  try
65  {
66  await ioManager.DeleteDirectory(ByondCachePath, cancellationToken).ConfigureAwait(false);
67  }
68  catch (Exception e)
69  {
70  logger.LogWarning("Error deleting BYOND cache! Exception: {0}", e);
71  }
72  }
73 
75  public async Task<byte[]> DownloadVersion(Version version, CancellationToken cancellationToken)
76  {
77  var ourVersion = version;
78  //lummox is annoying and doesn't like to post linux versions if nothing changed in DreamDaemon/DM
79  //if this for's exit condition ever triggers, i get to say I told you so
80  Exception lastException = null;
81  for (var I = 0; I < 5 && ourVersion.Minor >= 1; ++I, ourVersion = new Version(ourVersion.Major, ourVersion.Minor))
82  {
83  try
84  {
85  var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsURL, ourVersion.Major, ourVersion.Minor);
86 
87  return await ioManager.DownloadFile(new Uri(url), cancellationToken).ConfigureAwait(false);
88  }
89  catch (WebException e)
90  {
91  if (!(e.Status == WebExceptionStatus.ProtocolError && e.Response is HttpWebResponse response && response.StatusCode == HttpStatusCode.NotFound))
92  throw;
93  lastException = e;
94  }
95  }
96  throw lastException;
97  }
98 
100  public Task InstallByond(string path, Version version, CancellationToken cancellationToken)
101  {
102  //write the scripts for running the ting
103  //need to add $ORIGIN to LD_LIBRARY_PATH
104  const string StandardScript = "#!/bin/sh\nexport LD_LIBRARY_PATH=\"\\$ORIGIN:$LD_LIBRARY_PATH\"\nBASEDIR=$(dirname \"$0\")\nexec \"$BASEDIR/{0}\" \"$@\"\n";
105 
106  const string DreamDaemonExecutableName = "DreamDaemon";
107  const string DreamMakerExecutableName = "DreamMaker";
108 
109  var dreamDaemonScript = String.Format(CultureInfo.InvariantCulture, StandardScript, DreamDaemonExecutableName);
110  var dreamMakerScript = String.Format(CultureInfo.InvariantCulture, StandardScript, DreamMakerExecutableName);
111 
112  async Task WriteAndMakeExecutable(string fullPath, string script)
113  {
114  await ioManager.WriteAllBytes(fullPath, Encoding.ASCII.GetBytes(script), cancellationToken).ConfigureAwait(false);
115  postWriteHandler.HandleWrite(fullPath);
116  }
117 
118  var basePath = ioManager.ConcatPath(path, ByondManager.BinPath);
119 
120  var task = Task.WhenAll(WriteAndMakeExecutable(ioManager.ConcatPath(basePath, DreamDaemonName), dreamDaemonScript), WriteAndMakeExecutable(ioManager.ConcatPath(basePath, DreamMakerName), dreamMakerScript));
121 
122  postWriteHandler.HandleWrite(ioManager.ConcatPath(basePath, DreamDaemonExecutableName));
123  postWriteHandler.HandleWrite(ioManager.ConcatPath(basePath, DreamMakerExecutableName));
124 
125  return task;
126  }
127  }
128 }
PosixByondInstaller(IIOManager ioManager, IPostWriteHandler postWriteHandler, ILogger< PosixByondInstaller > logger)
Construct a WindowsByondInstaller
+
async Task< byte[]> DownloadVersion(Version version, CancellationToken cancellationToken)
Download a given BYOND version
@@ -105,7 +106,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly ILogger< PosixByondInstaller > logger
The ILogger for the PosixByondInstaller
readonly IIOManager ioManager
The IIOManager for the PosixByondInstaller
Task InstallByond(string path, Version version, CancellationToken cancellationToken)
Does actions necessary to get an extracted BYOND installation working
-
readonly IPostWriteHandler postWriteHandler
The IPostWriteHandler for the PosixByondInstaller
diff --git a/_posix_post_write_handler_8cs_source.html b/_posix_post_write_handler_8cs_source.html index f3f4b4c2fd..a085f46981 100644 --- a/_posix_post_write_handler_8cs_source.html +++ b/_posix_post_write_handler_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
PosixPostWriteHandler.cs
-Go to the documentation of this file.
1 using Mono.Unix;
2 using Mono.Unix.Native;
3 
4 namespace Tgstation.Server.Host.Core
5 {
10  {
12  public void HandleWrite(string filePath)
13  {
14  //set executable bit every time, don't want people calling me when their uploaded "sl" binary doesn't work
15  if (Syscall.stat(filePath, out var stat) != 0)
16  throw new UnixIOException(Stdlib.GetLastError());
17 
18  if (stat.st_mode.HasFlag(FilePermissions.S_IXUSR))
19  return;
20 
21  if (Syscall.chmod(filePath, stat.st_mode | FilePermissions.S_IXUSR) != 0)
22  throw new UnixIOException(Stdlib.GetLastError());
23  }
24  }
25 }
+Go to the documentation of this file.
1 using Mono.Unix;
2 using Mono.Unix.Native;
3 
4 namespace Tgstation.Server.Host.Core
5 {
10  {
12  public void HandleWrite(string filePath)
13  {
14  //set executable bit every time, don't want people calling me when their uploaded "sl" binary doesn't work
15  if (Syscall.stat(filePath, out var stat) != 0)
16  throw new UnixIOException(Stdlib.GetLastError());
17 
18  if (stat.st_mode.HasFlag(FilePermissions.S_IXUSR))
19  return;
20 
21  if (Syscall.chmod(filePath, stat.st_mode | FilePermissions.S_IXUSR) != 0)
22  throw new UnixIOException(Stdlib.GetLastError());
23  }
24  }
25 }
+
void HandleWrite(string filePath)
For handling system specific necessities after a write
IPostWriteHandler for POSIX systems
-
diff --git a/_posix_symlink_factory_8cs_source.html b/_posix_symlink_factory_8cs_source.html index 862de6516e..ee5cf99de8 100644 --- a/_posix_symlink_factory_8cs_source.html +++ b/_posix_symlink_factory_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
PosixSymlinkFactory.cs
-Go to the documentation of this file.
1 using Mono.Unix;
2 using System.IO;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.IO
7 {
12  {
14  public Task CreateSymbolicLink(string targetPath, string linkPath, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
15  {
16  UnixFileSystemInfo fsInfo;
17  var isFile = File.Exists(targetPath);
18  cancellationToken.ThrowIfCancellationRequested();
19  if (isFile)
20  fsInfo = new UnixFileInfo(targetPath);
21  else
22  fsInfo = new UnixDirectoryInfo(targetPath);
23  cancellationToken.ThrowIfCancellationRequested();
24  fsInfo.CreateSymbolicLink(linkPath);
25  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
26  }
27 }
+Go to the documentation of this file.
1 using Mono.Unix;
2 using System.IO;
3 using System.Threading;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.IO
7 {
12  {
14  public Task CreateSymbolicLink(string targetPath, string linkPath, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
15  {
16  UnixFileSystemInfo fsInfo;
17  var isFile = File.Exists(targetPath);
18  cancellationToken.ThrowIfCancellationRequested();
19  if (isFile)
20  fsInfo = new UnixFileInfo(targetPath);
21  else
22  fsInfo = new UnixDirectoryInfo(targetPath);
23  cancellationToken.ThrowIfCancellationRequested();
24  fsInfo.CreateSymbolicLink(linkPath);
25  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
26  }
27 }
+ -
diff --git a/_posix_system_identity_factory_8cs_source.html b/_posix_system_identity_factory_8cs_source.html index 37945d45f5..b77c62fe77 100644 --- a/_posix_system_identity_factory_8cs_source.html +++ b/_posix_system_identity_factory_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
PosixSystemIdentityFactory.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Security
7 {
13  {
15  public Task<ISystemIdentity> CreateSystemIdentity(User user, CancellationToken cancellationToken)
16  {
17  throw new NotImplementedException();
18  }
19 
21  public Task<ISystemIdentity> CreateSystemIdentity(string username, string password, CancellationToken cancellationToken)
22  {
23  throw new NotImplementedException();
24  }
25  }
26 }
Task< ISystemIdentity > CreateSystemIdentity(string username, string password, CancellationToken cancellationToken)
Create a ISystemIdentity for a given username and password
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Security
7 {
13  {
15  public Task<ISystemIdentity> CreateSystemIdentity(User user, CancellationToken cancellationToken)
16  {
17  throw new NotImplementedException();
18  }
19 
21  public Task<ISystemIdentity> CreateSystemIdentity(string username, string password, CancellationToken cancellationToken)
22  {
23  throw new NotImplementedException();
24  }
25  }
26 }
Task< ISystemIdentity > CreateSystemIdentity(string username, string password, CancellationToken cancellationToken)
Create a ISystemIdentity for a given username and password
Task< ISystemIdentity > CreateSystemIdentity(User user, CancellationToken cancellationToken)
Create a ISystemIdentity for a given user
+ - diff --git a/_process_8cs_source.html b/_process_8cs_source.html index 2aec27059d..e31f5c4488 100644 --- a/_process_8cs_source.html +++ b/_process_8cs_source.html @@ -92,16 +92,16 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Process.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Text;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Core
7 {
9  sealed class Process : IProcess
10  {
12  public int Id { get; }
13 
15  public Task Startup { get; }
16 
18  public Task<int> Lifetime { get; }
19 
20  readonly System.Diagnostics.Process handle;
21 
22  readonly StringBuilder outputStringBuilder;
23  readonly StringBuilder errorStringBuilder;
24  readonly StringBuilder combinedStringBuilder;
25 
29  readonly ILogger<Process> logger;
30 
31  public Process(System.Diagnostics.Process handle, Task<int> lifetime, StringBuilder outputStringBuilder, StringBuilder errorStringBuilder, StringBuilder combinedStringBuilder, ILogger<Process> logger)
32  {
33  this.handle = handle ?? throw new ArgumentNullException(nameof(handle));
34  Lifetime = lifetime ?? throw new ArgumentNullException(nameof(lifetime));
35 
36  this.outputStringBuilder = outputStringBuilder;
37  this.errorStringBuilder = errorStringBuilder;
38  this.combinedStringBuilder = combinedStringBuilder;
39 
40  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
41 
42  Id = handle.Id;
43  Startup = Task.Factory.StartNew(() =>
44  {
45  try
46  {
47  handle.WaitForInputIdle();
48  }
49  catch (InvalidOperationException) { }
50  }, default, TaskCreationOptions.LongRunning, TaskScheduler.Current);
51 
52  logger.LogTrace("Created proces ID: {0}", Id);
53  }
54 
56  public void Dispose() => handle.Dispose();
57 
59  public string GetCombinedOutput()
60  {
61  if (combinedStringBuilder == null)
62  throw new InvalidOperationException("Output/Error reading was not enabled!");
63  return combinedStringBuilder.ToString().TrimStart(Environment.NewLine.ToCharArray());
64  }
65 
67  public string GetErrorOutput()
68  {
69  if (errorStringBuilder == null)
70  throw new InvalidOperationException("Error reading was not enabled!");
71  return errorStringBuilder.ToString().TrimStart(Environment.NewLine.ToCharArray());
72  }
73 
75  public string GetStandardOutput()
76  {
77  if (outputStringBuilder == null)
78  throw new InvalidOperationException("Output reading was not enabled!");
79  return errorStringBuilder.ToString().TrimStart(Environment.NewLine.ToCharArray());
80  }
81 
83  public void Terminate()
84  {
85  if (handle.HasExited)
86  return;
87  try
88  {
89  logger.LogTrace("Terminating process...");
90  handle.Kill();
91  handle.WaitForExit();
92  }
93  catch (Exception e)
94  {
95  logger.LogDebug("Process termination exception: {0}", e);
96  }
97  }
98 
99  public void SetHighPriority()
100  {
101  try
102  {
103  handle.PriorityClass = System.Diagnostics.ProcessPriorityClass.AboveNormal;
104  logger.LogTrace("Set to above normal priority", handle.Id);
105  }
106  catch (Exception e)
107  {
108  logger.LogWarning("Unable to raise process priority! Exception: {0}", e);
109  }
110  }
111  }
112 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Text;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Core
7 {
9  sealed class Process : IProcess
10  {
12  public int Id { get; }
13 
15  public Task Startup { get; }
16 
18  public Task<int> Lifetime { get; }
19 
20  readonly System.Diagnostics.Process handle;
21 
22  readonly StringBuilder outputStringBuilder;
23  readonly StringBuilder errorStringBuilder;
24  readonly StringBuilder combinedStringBuilder;
25 
29  readonly ILogger<Process> logger;
30 
31  public Process(System.Diagnostics.Process handle, Task<int> lifetime, StringBuilder outputStringBuilder, StringBuilder errorStringBuilder, StringBuilder combinedStringBuilder, ILogger<Process> logger)
32  {
33  this.handle = handle ?? throw new ArgumentNullException(nameof(handle));
34  Lifetime = lifetime ?? throw new ArgumentNullException(nameof(lifetime));
35 
36  this.outputStringBuilder = outputStringBuilder;
37  this.errorStringBuilder = errorStringBuilder;
38  this.combinedStringBuilder = combinedStringBuilder;
39 
40  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
41 
42  Id = handle.Id;
43  Startup = Task.Factory.StartNew(() =>
44  {
45  try
46  {
47  handle.WaitForInputIdle();
48  }
49  catch (InvalidOperationException) { }
50  }, default, TaskCreationOptions.LongRunning, TaskScheduler.Current);
51 
52  logger.LogTrace("Created proces ID: {0}", Id);
53  }
54 
56  public void Dispose() => handle.Dispose();
57 
59  public string GetCombinedOutput()
60  {
61  if (combinedStringBuilder == null)
62  throw new InvalidOperationException("Output/Error reading was not enabled!");
63  return combinedStringBuilder.ToString().TrimStart(Environment.NewLine.ToCharArray());
64  }
65 
67  public string GetErrorOutput()
68  {
69  if (errorStringBuilder == null)
70  throw new InvalidOperationException("Error reading was not enabled!");
71  return errorStringBuilder.ToString().TrimStart(Environment.NewLine.ToCharArray());
72  }
73 
75  public string GetStandardOutput()
76  {
77  if (outputStringBuilder == null)
78  throw new InvalidOperationException("Output reading was not enabled!");
79  return errorStringBuilder.ToString().TrimStart(Environment.NewLine.ToCharArray());
80  }
81 
83  public void Terminate()
84  {
85  if (handle.HasExited)
86  return;
87  try
88  {
89  logger.LogTrace("Terminating process...");
90  handle.Kill();
91  handle.WaitForExit();
92  }
93  catch (Exception e)
94  {
95  logger.LogDebug("Process termination exception: {0}", e);
96  }
97  }
98 
99  public void SetHighPriority()
100  {
101  try
102  {
103  handle.PriorityClass = System.Diagnostics.ProcessPriorityClass.AboveNormal;
104  logger.LogTrace("Set to above normal priority", handle.Id);
105  }
106  catch (Exception e)
107  {
108  logger.LogWarning("Unable to raise process priority! Exception: {0}", e);
109  }
110  }
111  }
112 }
readonly StringBuilder errorStringBuilder
Definition: Process.cs:23
string GetErrorOutput()
Get the stderr output of the IProcess
Definition: Process.cs:67
+
readonly StringBuilder combinedStringBuilder
Definition: Process.cs:24
string GetStandardOutput()
Get the stdout output of the IProcess
Definition: Process.cs:75
string GetCombinedOutput()
Get the stderr and stdout output of the IProcess
Definition: Process.cs:59
Process(System.Diagnostics.Process handle, Task< int > lifetime, StringBuilder outputStringBuilder, StringBuilder errorStringBuilder, StringBuilder combinedStringBuilder, ILogger< Process > logger)
Definition: Process.cs:31
-
Abstraction over a System.Diagnostics.Process
Definition: IProcess.cs:9
void SetHighPriority()
Set&#39;s the owned System.Diagnostics.Process.PriorityClass to System.Diagnostics.ProcessPriorityClass.AboveNormal
Definition: Process.cs:99
readonly ILogger< Process > logger
The ILogger for the Process
Definition: Process.cs:29
diff --git a/_process_executor_8cs_source.html b/_process_executor_8cs_source.html index 5379adcaae..eb626c2d6f 100644 --- a/_process_executor_8cs_source.html +++ b/_process_executor_8cs_source.html @@ -92,15 +92,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ProcessExecutor.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Text;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Core
7 {
10  {
14  readonly ILogger<ProcessExecutor> logger;
15 
19  readonly ILoggerFactory loggerFactory;
20 
26  static Task<int> AttachExitHandler(System.Diagnostics.Process handle)
27  {
28  handle.EnableRaisingEvents = true;
29  var tcs = new TaskCompletionSource<int>();
30  handle.Exited += (a, b) =>
31  {
32  int exitCode;
33  try
34  {
35  exitCode = handle.ExitCode;
36  }
37  catch (InvalidOperationException)
38  {
39  return;
40  }
41  tcs.SetResult(exitCode);
42  };
43  return tcs.Task;
44  }
45 
51  public ProcessExecutor(ILogger<ProcessExecutor> logger, ILoggerFactory loggerFactory)
52  {
53  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
54  this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
55  }
56 
58  public IProcess GetProcess(int id)
59  {
60  logger.LogDebug("Attaching to process {0}...", id);
61  var handle = System.Diagnostics.Process.GetProcessById(id);
62  try
63  {
64  return new Process(handle, AttachExitHandler(handle), null, null, null, loggerFactory.CreateLogger<Process>());
65  }
66  catch
67  {
68  handle.Dispose();
69  throw;
70  }
71  }
72 
74  public IProcess LaunchProcess(string fileName, string workingDirectory, string arguments, bool readOutput, bool readError, bool noShellExecute)
75  {
76  logger.LogDebug("Launching process in {0}: {1} {2}", workingDirectory, fileName, arguments);
77  var handle = new System.Diagnostics.Process();
78  try
79  {
80  handle.StartInfo.FileName = fileName;
81  handle.StartInfo.Arguments = arguments;
82  handle.StartInfo.WorkingDirectory = workingDirectory;
83 
84  handle.StartInfo.UseShellExecute = !(noShellExecute || readOutput || readError);
85 
86  StringBuilder outputStringBuilder = null, errorStringBuilder = null, combinedStringBuilder = null;
87  if (readOutput || readError)
88  {
89  combinedStringBuilder = new StringBuilder();
90  if (readOutput)
91  {
92  outputStringBuilder = new StringBuilder();
93  handle.StartInfo.RedirectStandardOutput = true;
94  handle.OutputDataReceived += (sender, e) =>
95  {
96  combinedStringBuilder.Append(Environment.NewLine);
97  combinedStringBuilder.Append(e.Data);
98  outputStringBuilder.Append(Environment.NewLine);
99  outputStringBuilder.Append(e.Data);
100  };
101  }
102  if (readError)
103  {
104  errorStringBuilder = new StringBuilder();
105  handle.StartInfo.RedirectStandardError = true;
106  handle.ErrorDataReceived += (sender, e) =>
107  {
108  combinedStringBuilder.Append(Environment.NewLine);
109  combinedStringBuilder.Append(e.Data);
110  errorStringBuilder.Append(Environment.NewLine);
111  errorStringBuilder.Append(e.Data);
112  };
113  }
114  }
115 
116  var lifetimeTask = AttachExitHandler(handle);
117 
118  handle.Start();
119  try
120  {
121  if (readOutput)
122  handle.BeginOutputReadLine();
123  }
124  catch (InvalidOperationException) { }
125  try
126  {
127  if (readError)
128  handle.BeginErrorReadLine();
129  }
130  catch (InvalidOperationException) { }
131 
132  return new Process(handle, lifetimeTask, outputStringBuilder, errorStringBuilder, combinedStringBuilder, loggerFactory.CreateLogger<Process>());
133  }
134  catch
135  {
136  handle.Dispose();
137  throw;
138  }
139  }
140  }
141 }
readonly ILoggerFactory loggerFactory
The ILoggerFactory for the ProcessExecutor
+Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Text;
4 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Core
7 {
10  {
14  readonly ILogger<ProcessExecutor> logger;
15 
19  readonly ILoggerFactory loggerFactory;
20 
26  static Task<int> AttachExitHandler(System.Diagnostics.Process handle)
27  {
28  handle.EnableRaisingEvents = true;
29  var tcs = new TaskCompletionSource<int>();
30  handle.Exited += (a, b) =>
31  {
32  int exitCode;
33  try
34  {
35  exitCode = handle.ExitCode;
36  }
37  catch (InvalidOperationException)
38  {
39  return;
40  }
41  tcs.SetResult(exitCode);
42  };
43  return tcs.Task;
44  }
45 
51  public ProcessExecutor(ILogger<ProcessExecutor> logger, ILoggerFactory loggerFactory)
52  {
53  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
54  this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
55  }
56 
58  public IProcess GetProcess(int id)
59  {
60  logger.LogDebug("Attaching to process {0}...", id);
61  var handle = System.Diagnostics.Process.GetProcessById(id);
62  try
63  {
64  return new Process(handle, AttachExitHandler(handle), null, null, null, loggerFactory.CreateLogger<Process>());
65  }
66  catch
67  {
68  handle.Dispose();
69  throw;
70  }
71  }
72 
74  public IProcess LaunchProcess(string fileName, string workingDirectory, string arguments, bool readOutput, bool readError, bool noShellExecute)
75  {
76  logger.LogDebug("Launching process in {0}: {1} {2}", workingDirectory, fileName, arguments);
77  var handle = new System.Diagnostics.Process();
78  try
79  {
80  handle.StartInfo.FileName = fileName;
81  handle.StartInfo.Arguments = arguments;
82  handle.StartInfo.WorkingDirectory = workingDirectory;
83 
84  handle.StartInfo.UseShellExecute = !(noShellExecute || readOutput || readError);
85 
86  StringBuilder outputStringBuilder = null, errorStringBuilder = null, combinedStringBuilder = null;
87  if (readOutput || readError)
88  {
89  combinedStringBuilder = new StringBuilder();
90  if (readOutput)
91  {
92  outputStringBuilder = new StringBuilder();
93  handle.StartInfo.RedirectStandardOutput = true;
94  handle.OutputDataReceived += (sender, e) =>
95  {
96  combinedStringBuilder.Append(Environment.NewLine);
97  combinedStringBuilder.Append(e.Data);
98  outputStringBuilder.Append(Environment.NewLine);
99  outputStringBuilder.Append(e.Data);
100  };
101  }
102  if (readError)
103  {
104  errorStringBuilder = new StringBuilder();
105  handle.StartInfo.RedirectStandardError = true;
106  handle.ErrorDataReceived += (sender, e) =>
107  {
108  combinedStringBuilder.Append(Environment.NewLine);
109  combinedStringBuilder.Append(e.Data);
110  errorStringBuilder.Append(Environment.NewLine);
111  errorStringBuilder.Append(e.Data);
112  };
113  }
114  }
115 
116  var lifetimeTask = AttachExitHandler(handle);
117 
118  handle.Start();
119  try
120  {
121  if (readOutput)
122  handle.BeginOutputReadLine();
123  }
124  catch (InvalidOperationException) { }
125  try
126  {
127  if (readError)
128  handle.BeginErrorReadLine();
129  }
130  catch (InvalidOperationException) { }
131 
132  return new Process(handle, lifetimeTask, outputStringBuilder, errorStringBuilder, combinedStringBuilder, loggerFactory.CreateLogger<Process>());
133  }
134  catch
135  {
136  handle.Dispose();
137  throw;
138  }
139  }
140  }
141 }
readonly ILoggerFactory loggerFactory
The ILoggerFactory for the ProcessExecutor
+
IProcess LaunchProcess(string fileName, string workingDirectory, string arguments, bool readOutput, bool readError, bool noShellExecute)
Launch a IProcess
IProcess GetProcess(int id)
Get a IProcess by id
static Task< int > AttachExitHandler(System.Diagnostics.Process handle)
Create a Task<TResult> resulting in the exit code of a given handle
-
Abstraction over a System.Diagnostics.Process
Definition: IProcess.cs:9
ProcessExecutor(ILogger< ProcessExecutor > logger, ILoggerFactory loggerFactory)
Construct a ProcessExecutor
readonly ILogger< ProcessExecutor > logger
The ILogger for the ProcessExecutor
diff --git a/_program_8cs_source.html b/_program_8cs_source.html index 5bd5f45476..49ccb3c6d2 100644 --- a/_program_8cs_source.html +++ b/_program_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Program.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.IO;
5 using System.Threading;
6 using System.Threading.Tasks;
7 
8 namespace Tgstation.Server.Host
9 {
13  static class Program
14  {
18  internal static IServerFactory serverFactory = new ServerFactory();
19 
25  public static async Task<int> Main(string[] args)
26  {
27  var listArgs = new List<string>(args);
28  //first arg is 100% always the update path, starting it otherwise is solely for debugging purposes
29  string updatePath;
30  if (listArgs.Count > 0)
31  {
32  updatePath = listArgs[0];
33  listArgs.RemoveAt(0);
34  if (listArgs.Remove("--attach-debugger"))
35  Debugger.Launch();
36  }
37  else
38  updatePath = null;
39  try
40  {
41  using (var server = serverFactory.CreateServer(listArgs.ToArray(), updatePath))
42  {
43  try
44  {
45  using (var cts = new CancellationTokenSource())
46  {
47  void AppDomainHandler(object a, EventArgs b) => cts.Cancel();
48  AppDomain.CurrentDomain.ProcessExit += AppDomainHandler;
49  try
50  {
51  Console.CancelKeyPress += (a, b) =>
52  {
53  b.Cancel = true;
54  cts.Cancel();
55  };
56  await server.RunAsync(cts.Token).ConfigureAwait(false);
57  }
58  finally
59  {
60  AppDomain.CurrentDomain.ProcessExit -= AppDomainHandler;
61  }
62  }
63  }
64  catch (OperationCanceledException) { }
65  return server.RestartRequested ? 1 : 0;
66  }
67  }
68  catch (Exception e)
69  {
70  if (updatePath != null)
71  {
72  File.WriteAllText(updatePath, e.ToString());
73  return 2;
74  }
75  throw;
76  }
77  }
78  }
79 }
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.IO;
5 using System.Threading;
6 using System.Threading.Tasks;
7 
8 namespace Tgstation.Server.Host
9 {
13  static class Program
14  {
18  internal static IServerFactory serverFactory = new ServerFactory();
19 
25  public static async Task<int> Main(string[] args)
26  {
27  var listArgs = new List<string>(args);
28  //first arg is 100% always the update path, starting it otherwise is solely for debugging purposes
29  string updatePath;
30  if (listArgs.Count > 0)
31  {
32  updatePath = listArgs[0];
33  listArgs.RemoveAt(0);
34  if (listArgs.Remove("--attach-debugger"))
35  Debugger.Launch();
36  }
37  else
38  updatePath = null;
39  try
40  {
41  using (var server = serverFactory.CreateServer(listArgs.ToArray(), updatePath))
42  {
43  try
44  {
45  using (var cts = new CancellationTokenSource())
46  {
47  void AppDomainHandler(object a, EventArgs b) => cts.Cancel();
48  AppDomain.CurrentDomain.ProcessExit += AppDomainHandler;
49  try
50  {
51  Console.CancelKeyPress += (a, b) =>
52  {
53  b.Cancel = true;
54  cts.Cancel();
55  };
56  await server.RunAsync(cts.Token).ConfigureAwait(false);
57  }
58  finally
59  {
60  AppDomain.CurrentDomain.ProcessExit -= AppDomainHandler;
61  }
62  }
63  }
64  catch (OperationCanceledException) { }
65  return server.RestartRequested ? 1 : 0;
66  }
67  }
68  catch (Exception e)
69  {
70  if (updatePath != null)
71  {
72  File.WriteAllText(updatePath, e.ToString());
73  return 2;
74  }
75  throw;
76  }
77  }
78  }
79 }
+
Entrypoint for the Process
Definition: Program.cs:13
-
IServer CreateServer(string[] args, string updatePath)
Create a IServer
static async Task< int > Main(string[] args)
Entrypoint for the Program
Definition: Program.cs:25
diff --git a/_provider_8cs_source.html b/_provider_8cs_source.html index cde285f24d..0617a05a07 100644 --- a/_provider_8cs_source.html +++ b/_provider_8cs_source.html @@ -92,7 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Provider.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Components.Chat.Providers
7 {
9  abstract class Provider : IProvider
10  {
14  readonly Queue<Message> messageQueue;
15 
19  TaskCompletionSource<object> nextMessage;
20 
24  protected Provider()
25  {
26  messageQueue = new Queue<Message>();
27  nextMessage = new TaskCompletionSource<object>();
28  }
29 
31  public abstract bool Connected { get; }
32 
34  public abstract string BotMention { get; }
35 
40  protected void EnqueueMessage(Message message)
41  {
42  lock (messageQueue)
43  {
44  messageQueue.Enqueue(message);
45  nextMessage.TrySetResult(null);
46  }
47  }
48 
50  public abstract void Dispose();
51 
53  public abstract Task<bool> Connect(CancellationToken cancellationToken);
54 
56  public abstract Task Disconnect(CancellationToken cancellationToken);
57 
59  public abstract Task<IReadOnlyList<Channel>> MapChannels(IEnumerable<ChatChannel> channels, CancellationToken cancellationToken);
60 
62  public async Task<Message> NextMessage(CancellationToken cancellationToken)
63  {
64  var cancelTcs = new TaskCompletionSource<object>();
65  using (cancellationToken.Register(() => cancelTcs.SetCanceled()))
66  await Task.WhenAny(nextMessage.Task, cancelTcs.Task).ConfigureAwait(false);
67  cancellationToken.ThrowIfCancellationRequested();
68  lock (messageQueue)
69  {
70  var result = messageQueue.Dequeue();
71  if (messageQueue.Count == 0)
72  nextMessage = new TaskCompletionSource<object>();
73  return result;
74  }
75  }
76 
78  public abstract Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken);
79  }
80 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Components.Chat.Providers
7 {
9  abstract class Provider : IProvider
10  {
14  readonly Queue<Message> messageQueue;
15 
19  TaskCompletionSource<object> nextMessage;
20 
24  protected Provider()
25  {
26  messageQueue = new Queue<Message>();
27  nextMessage = new TaskCompletionSource<object>();
28  }
29 
31  public abstract bool Connected { get; }
32 
34  public abstract string BotMention { get; }
35 
40  protected void EnqueueMessage(Message message)
41  {
42  lock (messageQueue)
43  {
44  messageQueue.Enqueue(message);
45  nextMessage.TrySetResult(null);
46  }
47  }
48 
50  public abstract void Dispose();
51 
53  public abstract Task<bool> Connect(CancellationToken cancellationToken);
54 
56  public abstract Task Disconnect(CancellationToken cancellationToken);
57 
59  public abstract Task<IReadOnlyList<Channel>> MapChannels(IEnumerable<ChatChannel> channels, CancellationToken cancellationToken);
60 
62  public async Task<Message> NextMessage(CancellationToken cancellationToken)
63  {
64  var cancelTcs = new TaskCompletionSource<object>();
65  using (cancellationToken.Register(() => cancelTcs.SetCanceled()))
66  await Task.WhenAny(nextMessage.Task, cancelTcs.Task).ConfigureAwait(false);
67  cancellationToken.ThrowIfCancellationRequested();
68  lock (messageQueue)
69  {
70  var result = messageQueue.Dequeue();
71  if (messageQueue.Count == 0)
72  nextMessage = new TaskCompletionSource<object>();
73  return result;
74  }
75  }
76 
78  public abstract Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken);
79  }
80 }
+
Represents a message recieved by a IProvider
Definition: Message.cs:6
@@ -100,7 +101,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
async Task< Message > NextMessage(CancellationToken cancellationToken)
Get a Task<TResult> resulting in the next Message the IProvider recieves or on a disconnect ...
Definition: Provider.cs:62
TaskCompletionSource< object > nextMessage
TaskCompletionSource<TResult> that completes while messageQueue isn&#39;t empty
Definition: Provider.cs:19
-
readonly Queue< Message > messageQueue
Queue<T> of received Messages
Definition: Provider.cs:14
diff --git a/_provider_factory_8cs_source.html b/_provider_factory_8cs_source.html index 21376facc5..ff633a5b31 100644 --- a/_provider_factory_8cs_source.html +++ b/_provider_factory_8cs_source.html @@ -92,25 +92,26 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ProviderFactory.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Collections.Generic;
4 using System.Globalization;
8 
9 namespace Tgstation.Server.Host.Components.Chat
10 {
13  {
17  readonly ILoggerFactory loggerFactory;
18 
23 
29  public ProviderFactory(ILoggerFactory loggerFactory, IApplication application)
30  {
31  this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
32  this.application = application ?? throw new ArgumentNullException(nameof(application));
33  }
34 
36  public IProvider CreateProvider(Api.Models.Internal.ChatBot settings)
37  {
38  if (settings == null)
39  throw new ArgumentNullException(nameof(settings));
40  switch (settings.Provider)
41  {
42  case ChatProvider.Irc:
43  //Connection string semicolon delimited until the password field
44  if (settings.ConnectionString == null)
45  throw new InvalidOperationException("ConnectionString cannot be null!");
46  var splits = settings.ConnectionString.Split(';');
47  if (splits.Length < 4)
48  throw new InvalidOperationException("Invalid connection string!");
49 
50  var address = splits[0];
51  if (!UInt16.TryParse(splits[1], out var port))
52  throw new InvalidOperationException("Unable to parse port!");
53  var nick = splits[2];
54  if (!Int32.TryParse(splits[3], out var intSsl))
55  throw new InvalidOperationException("Unable to parse ssl option!");
56 
57  IrcPasswordType? passwordType = null;
58  string password = null;
59  if (splits.Length > 4)
60  {
61  if (splits.Length < 6)
62  throw new InvalidOperationException("Invalid connection string!");
63  if (!Int32.TryParse(splits[4], out var intPasswordType))
64  throw new InvalidOperationException("Unable to parse password type!");
65 
66  passwordType = (IrcPasswordType)intPasswordType;
67  switch (passwordType)
68  {
69  case IrcPasswordType.NickServ:
70  case IrcPasswordType.Sasl:
71  case IrcPasswordType.Server:
72  break;
73  default:
74  throw new InvalidOperationException("Invalid password type!");
75  }
76 
77  var rest = new List<string>(splits);
78  rest.RemoveRange(0, 5);
79  password = String.Join(";", rest);
80  }
81 
82  return new IrcProvider(loggerFactory.CreateLogger<IrcProvider>(), application, address, port, nick, password, passwordType, intSsl != 0);
83  case ChatProvider.Discord:
84  //discord is just the bot token
85  return new DiscordProvider(loggerFactory.CreateLogger<DiscordProvider>(), settings.ConnectionString);
86  default:
87  throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid ChatProvider: {0}", settings.Provider));
88  }
89  }
90  }
91 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Collections.Generic;
4 using System.Globalization;
8 
9 namespace Tgstation.Server.Host.Components.Chat
10 {
13  {
17  readonly ILoggerFactory loggerFactory;
18 
23 
29  public ProviderFactory(ILoggerFactory loggerFactory, IApplication application)
30  {
31  this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
32  this.application = application ?? throw new ArgumentNullException(nameof(application));
33  }
34 
36  public IProvider CreateProvider(Api.Models.Internal.ChatBot settings)
37  {
38  if (settings == null)
39  throw new ArgumentNullException(nameof(settings));
40  var builder = settings.ConnectionStringBuilder;
41  if (builder == null || !builder.Valid)
42  throw new InvalidOperationException("Invalid ChatConnectionStringBuilder!");
43  switch (settings.Provider)
44  {
45  case ChatProvider.Irc:
46  var ircBuilder = (IrcConnectionStringBuilder)builder;
47  return new IrcProvider(loggerFactory.CreateLogger<IrcProvider>(), application, ircBuilder.Address, ircBuilder.Port.Value, ircBuilder.Nickname, ircBuilder.Password, ircBuilder.PasswordType, ircBuilder.UseSsl.Value);
48  case ChatProvider.Discord:
49  var discordBuilder = (DiscordConnectionStringBuilder)builder;
50  return new DiscordProvider(loggerFactory.CreateLogger<DiscordProvider>(), discordBuilder.BotToken);
51  default:
52  throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid ChatProvider: {0}", settings.Provider));
53  }
54  }
55  }
56 }
readonly IApplication application
The IApplication for the ProviderFactory
Configures the ASP.NET Core web application
Definition: IApplication.cs:8
+ +
ChatConnectionStringBuilder for ChatProvider.Discord
ProviderFactory(ILoggerFactory loggerFactory, IApplication application)
Construct a ProviderFactory
For interacting with a chat service
Definition: IProvider.cs:11
-
IrcPasswordType
Represents the type of a password passed to the constructor of IrcProvider
-
readonly ILoggerFactory loggerFactory
The ILoggerFactory for the ProviderFactory
+
ChatConnectionStringBuilder for ChatProvider.Irc
ChatProvider
Represents a chat service provider
Definition: ChatProvider.cs:6
diff --git a/_pull_requests_command_8cs_source.html b/_pull_requests_command_8cs_source.html index ed1203de20..cac33ae37a 100644 --- a/_pull_requests_command_8cs_source.html +++ b/_pull_requests_command_8cs_source.html @@ -92,8 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
PullRequestsCommand.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using System;
3 using System.Collections.Generic;
4 using System.Globalization;
5 using System.Linq;
6 using System.Threading;
7 using System.Threading.Tasks;
11 
12 namespace Tgstation.Server.Host.Components.Chat.Commands
13 {
18  {
20  public string Name => "prs";
21 
23  public string HelpText => "Display live test merge pull request numbers. Add --repo to view repository test merges";
24 
26  public bool AdminOnly => false;
27 
31  readonly IWatchdog watchdog;
32 
37 
42 
46  readonly Models.Instance instance;
47 
55  public PullRequestsCommand(IWatchdog watchdog, IRepositoryManager repositoryManager, IDatabaseContextFactory databaseContextFactory, Models.Instance instance)
56  {
57  this.watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog));
58  this.repositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager));
59  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
60  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
61  }
62 
64  public async Task<string> Invoke(string arguments, User user, CancellationToken cancellationToken)
65  {
66  IEnumerable<Models.TestMerge> results = null;
67  if (arguments.Split(' ').Any(x => x.ToUpperInvariant() == "--REPO"))
68  {
69  string head;
70  using (var repo = await repositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
71  {
72  if (repo == null)
73  return "Repository unavailable!";
74  head = repo.Head;
75  }
76  await databaseContextFactory.UseContext(async db => results = await db.RevisionInformations.Where(x => x.Instance.Id == instance.Id && x.CommitSha == head)
77  .SelectMany(x => x.ActiveTestMerges)
78  .Select(x => x.TestMerge)
79  .Select(x => new Models.TestMerge
80  {
81  Number = x.Number,
82  PullRequestRevision = x.PullRequestRevision
83  }).ToListAsync(cancellationToken).ConfigureAwait(false)).ConfigureAwait(false);
84  }
85  else
86  {
87  if (!watchdog.Running)
88  return "Server offline!";
89  results = watchdog.ActiveCompileJob?.RevisionInformation.ActiveTestMerges.Select(x => x.TestMerge).ToList() ?? new List<Models.TestMerge>();
90  }
91 
92  if (!results.Any())
93  return "None!";
94 
95  return String.Join(", ", results.Select(x => String.Format(CultureInfo.InvariantCulture, "#{0} at {1}", x.Number, x.PullRequestRevision.Substring(0, 7))));
96  }
97  }
98 }
readonly IDatabaseContextFactory databaseContextFactory
The IDatabaseContextFactory for the PullRequestsCommand
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using System;
3 using System.Collections.Generic;
4 using System.Globalization;
5 using System.Linq;
6 using System.Threading;
7 using System.Threading.Tasks;
11 
12 namespace Tgstation.Server.Host.Components.Chat.Commands
13 {
18  {
20  public string Name => "prs";
21 
23  public string HelpText => "Display live test merge pull request numbers. Add --repo to view repository test merges";
24 
26  public bool AdminOnly => false;
27 
31  readonly IWatchdog watchdog;
32 
37 
42 
46  readonly Models.Instance instance;
47 
55  public PullRequestsCommand(IWatchdog watchdog, IRepositoryManager repositoryManager, IDatabaseContextFactory databaseContextFactory, Models.Instance instance)
56  {
57  this.watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog));
58  this.repositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager));
59  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
60  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
61  }
62 
64  public async Task<string> Invoke(string arguments, User user, CancellationToken cancellationToken)
65  {
66  IEnumerable<Models.TestMerge> results = null;
67  if (arguments.Split(' ').Any(x => x.ToUpperInvariant() == "--REPO"))
68  {
69  string head;
70  using (var repo = await repositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
71  {
72  if (repo == null)
73  return "Repository unavailable!";
74  head = repo.Head;
75  }
76  await databaseContextFactory.UseContext(async db => results = await db.RevisionInformations.Where(x => x.Instance.Id == instance.Id && x.CommitSha == head)
77  .SelectMany(x => x.ActiveTestMerges)
78  .Select(x => x.TestMerge)
79  .Select(x => new Models.TestMerge
80  {
81  Number = x.Number,
82  PullRequestRevision = x.PullRequestRevision
83  }).ToListAsync(cancellationToken).ConfigureAwait(false)).ConfigureAwait(false);
84  }
85  else
86  {
87  if (!watchdog.Running)
88  return "Server offline!";
89  results = watchdog.ActiveCompileJob?.RevisionInformation.ActiveTestMerges.Select(x => x.TestMerge).ToList() ?? new List<Models.TestMerge>();
90  }
91 
92  if (!results.Any())
93  return "None!";
94 
95  return String.Join(", ", results.Select(x => String.Format(CultureInfo.InvariantCulture, "#{0} at {1}", x.Number, x.PullRequestRevision.Substring(0, 7))));
96  }
97  }
98 }
readonly IDatabaseContextFactory databaseContextFactory
The IDatabaseContextFactory for the PullRequestsCommand
+
readonly IRepositoryManager repositoryManager
The IRepositoryManager for the PullRequestsCommand
Factory for scoping usage of IDatabaseContexts. Meant for use by Components
@@ -101,7 +102,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly Models.Instance instance
The Models.Instance for the PullRequestsCommand
Command for reading the active Api.Models.TestMerges
-
readonly IWatchdog watchdog
The IWatchdog for the PullRequestsCommand
Represents a tgs_chat_user datum
Definition: User.cs:10
diff --git a/_rate_limit_exception_8cs_source.html b/_rate_limit_exception_8cs_source.html index e7ae69cc84..1c8236cb7b 100644 --- a/_rate_limit_exception_8cs_source.html +++ b/_rate_limit_exception_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RateLimitException.cs
-Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
10  public sealed class RateLimitException : ClientException
11  {
12  DateTimeOffset RetryAfter { get; }
13 
17  public RateLimitException() { }
18 
23  public RateLimitException(string secondsString) : base(new ErrorMessage
24  {
25  Message = "GitHub rate limit reached!",
26  SeverApiVersion = null
27  }, (HttpStatusCode)429)
28  {
29  if (Int32.TryParse(secondsString, out var seconds) && seconds >= 0)
30  RetryAfter = DateTimeOffset.Now.AddSeconds(seconds);
31  else
32  RetryAfter = DateTimeOffset.Now;
33  }
34 
40  public RateLimitException(string message, Exception innerException) : base(message, innerException) { }
41  }
42 }
+Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
10  public sealed class RateLimitException : ClientException
11  {
12  DateTimeOffset RetryAfter { get; }
13 
17  public RateLimitException() { }
18 
23  public RateLimitException(string secondsString) : base(new ErrorMessage
24  {
25  Message = "GitHub rate limit reached!",
26  SeverApiVersion = null
27  }, (HttpStatusCode)429)
28  {
29  if (Int32.TryParse(secondsString, out var seconds) && seconds >= 0)
30  RetryAfter = DateTimeOffset.Now.AddSeconds(seconds);
31  else
32  RetryAfter = DateTimeOffset.Now;
33  }
34 
40  public RateLimitException(string message, Exception innerException) : base(message, innerException) { }
41  }
42 }
+
Occurs when a GitHub rate limit occurs
RateLimitException(string secondsString)
Construct an RateLimitException with a secondsString
-
RateLimitException()
Construct an RateLimitException
Represents an error message returned by the server
Definition: ErrorMessage.cs:8
diff --git a/_reattach_info_handler_8cs_source.html b/_reattach_info_handler_8cs_source.html index 400aba27e2..ae586e657f 100644 --- a/_reattach_info_handler_8cs_source.html +++ b/_reattach_info_handler_8cs_source.html @@ -92,11 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ReattachInfoHandler.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Linq;
5 using System.Threading;
6 using System.Threading.Tasks;
10 
11 namespace Tgstation.Server.Host.Components
12 {
15  {
20 
25 
29  readonly ILogger<ReattachInfoHandler> logger;
30 
34  readonly Api.Models.Instance metadata;
35 
43  public ReattachInfoHandler(IDatabaseContextFactory databaseContextFactory, IDmbFactory dmbFactory, ILogger<ReattachInfoHandler> logger, Api.Models.Instance metadata)
44  {
45  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
46  this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory));
47  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
48  this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
49  }
50 
52  public Task Save(WatchdogReattachInformation reattachInformation, CancellationToken cancellationToken) => databaseContextFactory.UseContext(async (db) =>
53  {
54  if (reattachInformation == null)
55  throw new ArgumentNullException(nameof(reattachInformation));
56 
57  logger.LogDebug("Saving reattach information: {0}...", reattachInformation);
58 
59  var instance = new Models.Instance { Id = metadata.Id };
60  db.Instances.Attach(instance);
61 
62  Models.ReattachInformation ConvertReattachInfo(ReattachInformation wdInfo)
63  {
64  if (wdInfo == null)
65  return null;
66  db.CompileJobs.Attach(wdInfo.Dmb.CompileJob);
67  return new Models.ReattachInformation
68  {
69  AccessIdentifier = wdInfo.AccessIdentifier,
70  ChatChannelsJson = wdInfo.ChatChannelsJson,
71  ChatCommandsJson = wdInfo.ChatCommandsJson,
72  ServerCommandsJson = wdInfo.ServerCommandsJson,
73  CompileJob = wdInfo.Dmb.CompileJob,
74  IsPrimary = wdInfo.IsPrimary,
75  Port = wdInfo.Port,
76  ProcessId = wdInfo.ProcessId,
77  RebootState = wdInfo.RebootState
78  };
79  }
80 
81  instance.WatchdogReattachInformation = new Models.WatchdogReattachInformation
82  {
83  Alpha = ConvertReattachInfo(reattachInformation.Alpha),
84  Bravo = ConvertReattachInfo(reattachInformation.Bravo),
85  AlphaIsActive = reattachInformation.AlphaIsActive,
86  };
87  await db.Save(cancellationToken).ConfigureAwait(false);
88  });
89 
91  public async Task<WatchdogReattachInformation> Load(CancellationToken cancellationToken)
92  {
93  Models.WatchdogReattachInformation result = null;
94  await databaseContextFactory.UseContext(async (db) =>
95  result = await db.Instances.Where(x => x.Id == metadata.Id).Select(x => x.WatchdogReattachInformation).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false)
96  ).ConfigureAwait(false);
97 
98  if (result == default)
99  {
100  logger.LogDebug("Reattach information not found!");
101  return null;
102  }
103 
104  var bravoDmbTask = dmbFactory.FromCompileJob(result.Bravo.CompileJob, cancellationToken);
105  var info = new WatchdogReattachInformation(result, await dmbFactory.FromCompileJob(result.Alpha.CompileJob, cancellationToken).ConfigureAwait(false), await bravoDmbTask.ConfigureAwait(false));
106  logger.LogDebug("Reattach information loaded: {0}", info);
107  return info;
108  }
109  }
110 }
string ServerCommandsJson
Path to the server commands json file
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Linq;
5 using System.Threading;
6 using System.Threading.Tasks;
10 
11 namespace Tgstation.Server.Host.Components
12 {
15  {
20 
25 
29  readonly ILogger<ReattachInfoHandler> logger;
30 
34  readonly Api.Models.Instance metadata;
35 
43  public ReattachInfoHandler(IDatabaseContextFactory databaseContextFactory, IDmbFactory dmbFactory, ILogger<ReattachInfoHandler> logger, Api.Models.Instance metadata)
44  {
45  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
46  this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory));
47  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
48  this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
49  }
50 
52  public Task Save(WatchdogReattachInformation reattachInformation, CancellationToken cancellationToken) => databaseContextFactory.UseContext(async (db) =>
53  {
54  if (reattachInformation == null)
55  throw new ArgumentNullException(nameof(reattachInformation));
56 
57  logger.LogDebug("Saving reattach information: {0}...", reattachInformation);
58 
59  var instance = new Models.Instance { Id = metadata.Id };
60  db.Instances.Attach(instance);
61 
62  Models.ReattachInformation ConvertReattachInfo(ReattachInformation wdInfo)
63  {
64  if (wdInfo == null)
65  return null;
66  db.CompileJobs.Attach(wdInfo.Dmb.CompileJob);
67  return new Models.ReattachInformation
68  {
69  AccessIdentifier = wdInfo.AccessIdentifier,
70  ChatChannelsJson = wdInfo.ChatChannelsJson,
71  ChatCommandsJson = wdInfo.ChatCommandsJson,
72  ServerCommandsJson = wdInfo.ServerCommandsJson,
73  CompileJob = wdInfo.Dmb.CompileJob,
74  IsPrimary = wdInfo.IsPrimary,
75  Port = wdInfo.Port,
76  ProcessId = wdInfo.ProcessId,
77  RebootState = wdInfo.RebootState
78  };
79  }
80 
81  instance.WatchdogReattachInformation = new Models.WatchdogReattachInformation
82  {
83  Alpha = ConvertReattachInfo(reattachInformation.Alpha),
84  Bravo = ConvertReattachInfo(reattachInformation.Bravo),
85  AlphaIsActive = reattachInformation.AlphaIsActive,
86  };
87  await db.Save(cancellationToken).ConfigureAwait(false);
88  });
89 
91  public async Task<WatchdogReattachInformation> Load(CancellationToken cancellationToken)
92  {
93  Models.WatchdogReattachInformation result = null;
94  await databaseContextFactory.UseContext(async (db) =>
95  result = await db.Instances.Where(x => x.Id == metadata.Id).Select(x => x.WatchdogReattachInformation).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false)
96  ).ConfigureAwait(false);
97 
98  if (result == default)
99  {
100  logger.LogDebug("Reattach information not found!");
101  return null;
102  }
103 
104  var bravoDmbTask = dmbFactory.FromCompileJob(result.Bravo.CompileJob, cancellationToken);
105  var info = new WatchdogReattachInformation(result, await dmbFactory.FromCompileJob(result.Alpha.CompileJob, cancellationToken).ConfigureAwait(false), await bravoDmbTask.ConfigureAwait(false));
106  logger.LogDebug("Reattach information loaded: {0}", info);
107  return info;
108  }
109  }
110 }
string ServerCommandsJson
Path to the server commands json file
readonly IDatabaseContextFactory databaseContextFactory
The IDatabaseContextFactory for the ReattachInfoHandler
CompileJob CompileJob
The CompileJob of the .dmb
Definition: IDmbProvider.cs:29
+
string ChatCommandsJson
Path to the chat commands json file
@@ -109,7 +110,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ushort Port
The port DreamDaemon was last listening on
IDmbProvider Dmb
The IDmbProvider used by DreamDaemon
-
readonly Api.Models.Instance metadata
The Api.Models.Instance for the ReattachInfoHandler
readonly IDmbFactory dmbFactory
The IDmbFactory for the ReattachInfoHandler
ReattachInfoHandler(IDatabaseContextFactory databaseContextFactory, IDmbFactory dmbFactory, ILogger< ReattachInfoHandler > logger, Api.Models.Instance metadata)
Construct a ReattachInfoHandler
diff --git a/_reattach_information_base_8cs_source.html b/_reattach_information_base_8cs_source.html index 972e432fb0..a3ab24010e 100644 --- a/_reattach_information_base_8cs_source.html +++ b/_reattach_information_base_8cs_source.html @@ -92,14 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ReattachInformationBase.cs
-Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 using System.Globalization;
6 
7 namespace Tgstation.Server.Host.Models
8 {
12  public abstract class ReattachInformationBase : JsonSubFileList
13  {
17  [Required]
18  public string AccessIdentifier { get; set; }
19 
23  public int ProcessId { get; set; }
24 
28  public bool IsPrimary { get; set; }
29 
33  public ushort Port { get; set; }
34 
38  public RebootState RebootState { get; set; }
39 
43  protected ReattachInformationBase() { }
44 
49  protected ReattachInformationBase(ReattachInformationBase copy) : base(copy)
50  {
51  if (copy == null)
52  throw new ArgumentNullException(nameof(copy));
53  AccessIdentifier = copy.AccessIdentifier;
54  IsPrimary = copy.IsPrimary;
55  Port = copy.Port;
56  ProcessId = copy.ProcessId;
57  RebootState = copy.RebootState;
58  }
59 
61  public override string ToString() => String.Format(CultureInfo.InvariantCulture, "Process ID: {3}, Access Identifier {4}, Primary: {0}, RebootState: {1}, Port: {2}", IsPrimary, RebootState, Port, ProcessId, AccessIdentifier);
62  }
63 }
ReattachInformationBase()
Construct a ReattachInformationBase
+Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 using System.Globalization;
6 
7 namespace Tgstation.Server.Host.Models
8 {
12  public abstract class ReattachInformationBase : JsonSubFileList
13  {
17  [Required]
18  public string AccessIdentifier { get; set; }
19 
23  public int ProcessId { get; set; }
24 
28  public bool IsPrimary { get; set; }
29 
33  public ushort Port { get; set; }
34 
38  public RebootState RebootState { get; set; }
39 
43  protected ReattachInformationBase() { }
44 
49  protected ReattachInformationBase(ReattachInformationBase copy) : base(copy)
50  {
51  if (copy == null)
52  throw new ArgumentNullException(nameof(copy));
53  AccessIdentifier = copy.AccessIdentifier;
54  IsPrimary = copy.IsPrimary;
55  Port = copy.Port;
56  ProcessId = copy.ProcessId;
57  RebootState = copy.RebootState;
58  }
59 
61  public override string ToString() => String.Format(CultureInfo.InvariantCulture, "Process ID: {3}, Access Identifier {4}, Primary: {0}, RebootState: {1}, Port: {2}", IsPrimary, RebootState, Port, ProcessId, AccessIdentifier);
62  }
63 }
ReattachInformationBase()
Construct a ReattachInformationBase
ReattachInformationBase(ReattachInformationBase copy)
Construct a ReattachInformationBase from a given copy
+
Information used in for reattaching and interop
ushort Port
The port DreamDaemon was last listening on
-
bool IsPrimary
If the Components.IDmbProvider.PrimaryDirectory of the associated dmb is being used ...
diff --git a/_reboot_state_8cs_source.html b/_reboot_state_8cs_source.html index 11f82df93c..210b910d69 100644 --- a/_reboot_state_8cs_source.html +++ b/_reboot_state_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RebootState.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Components.Watchdog
2 {
6  public enum RebootState : int
7  {
11  Normal = 0,
15  Shutdown = 1,
19  Restart = 2
20  }
21 }
Run DreamDaemon&#39;s normal reboot process
- +Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Components.Watchdog
2 {
6  public enum RebootState : int
7  {
11  Normal = 0,
15  Shutdown = 1,
19  Restart = 2
20  }
21 }
Run DreamDaemon&#39;s normal reboot process
+
RebootState
Represents the action to take when /world/Reboot() is called
Definition: RebootState.cs:6
diff --git a/_repository_client_8cs_source.html b/_repository_client_8cs_source.html index fb25a59df9..4747defb67 100644 --- a/_repository_client_8cs_source.html +++ b/_repository_client_8cs_source.html @@ -92,7 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RepositoryClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using Tgstation.Server.Api;
6 
7 namespace Tgstation.Server.Client.Components
8 {
11  {
19  readonly Instance instance;
20 
26  public RepositoryClient(IApiClient apiClient, Instance instance)
27  {
28  this.apiClient = apiClient;
29  this.instance = instance;
30  }
31 
33  public Task<Repository> Clone(Repository repository, CancellationToken cancellationToken) => apiClient.Create<Repository, Repository>(Routes.Repository, repository, instance.Id, cancellationToken);
34 
36  public Task<Repository> Delete(CancellationToken cancellationToken) => apiClient.Delete<Repository>(Routes.Repository, instance.Id, cancellationToken);
37 
39  public Task<Repository> Read(CancellationToken cancellationToken) => apiClient.Read<Repository>(Routes.Repository, instance.Id, cancellationToken);
40 
42  public Task<Repository> Update(Repository repository, CancellationToken cancellationToken) => apiClient.Update<Repository, Repository>(Routes.Repository, repository ?? throw new ArgumentNullException(nameof(repository)), instance.Id, cancellationToken);
43  }
44 }
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using Tgstation.Server.Api;
6 
7 namespace Tgstation.Server.Client.Components
8 {
11  {
19  readonly Instance instance;
20 
26  public RepositoryClient(IApiClient apiClient, Instance instance)
27  {
28  this.apiClient = apiClient;
29  this.instance = instance;
30  }
31 
33  public Task<Repository> Clone(Repository repository, CancellationToken cancellationToken) => apiClient.Create<Repository, Repository>(Routes.Repository, repository, instance.Id, cancellationToken);
34 
36  public Task<Repository> Delete(CancellationToken cancellationToken) => apiClient.Delete<Repository>(Routes.Repository, instance.Id, cancellationToken);
37 
39  public Task<Repository> Read(CancellationToken cancellationToken) => apiClient.Read<Repository>(Routes.Repository, instance.Id, cancellationToken);
40 
42  public Task<Repository> Update(Repository repository, CancellationToken cancellationToken) => apiClient.Update<Repository, Repository>(Routes.Repository, repository ?? throw new ArgumentNullException(nameof(repository)), instance.Id, cancellationToken);
43  }
44 }
+
readonly Instance instance
The Instance for the RepositoryClient
@@ -101,7 +102,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Represents a git repository
Definition: Repository.cs:8
const string Repository
The Models.Repository controller
Definition: Routes.cs:39
-
Metadata about a server instance
Definition: Instance.cs:9
readonly IApiClient apiClient
The IApiClient for the RepositoryClient
diff --git a/_repository_controller_8cs_source.html b/_repository_controller_8cs_source.html index 38e8788cb2..9a006bdef5 100644 --- a/_repository_controller_8cs_source.html +++ b/_repository_controller_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RepositoryController.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using Microsoft.Extensions.Options;
5 using System;
6 using System.Collections.Generic;
7 using System.Globalization;
8 using System.Linq;
9 using System.Linq.Expressions;
10 using System.Net;
11 using System.Reflection;
12 using System.Threading;
13 using System.Threading.Tasks;
14 using Tgstation.Server.Api;
22 
23 namespace Tgstation.Server.Host.Controllers
24 {
28  [Route(Routes.Repository)]
29  public sealed class RepositoryController : ModelController<Repository>
30  {
35 
40 
45 
50 
61  public RepositoryController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, IGitHubClientFactory gitHubClientFactory, IJobManager jobManager, ILogger<RepositoryController> logger, IOptions<GeneralConfiguration> generalConfigurationOptions) : base(databaseContext, authenticationContextFactory, logger, true)
62  {
63  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
64  this.gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory));
65  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
66  generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
67  }
68 
69  async Task<bool> LoadRevisionInformation(Components.Repository.IRepository repository, IDatabaseContext databaseContext, Models.Instance instance, string lastOriginCommitSha, Action<Models.RevisionInformation> revInfoSink, CancellationToken cancellationToken)
70  {
71  var repoSha = repository.Head;
72 
73  IQueryable<Models.RevisionInformation> queryTarget = databaseContext.RevisionInformations;
74 
75  var revisionInfo = await databaseContext.RevisionInformations.Where(x => x.CommitSha == repoSha && x.Instance.Id == instance.Id)
76  .Include(x => x.CompileJobs)
77  .Include(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge) //minimal info, they can query the rest if they're allowed
78  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); //search every rev info because LOL SHA COLLISIONS
79 
80  if (revisionInfo == default)
81  revisionInfo = databaseContext.RevisionInformations.Local.Where(x => x.CommitSha == repoSha).FirstOrDefault();
82 
83  var needsDbUpdate = revisionInfo == default;
84  if (needsDbUpdate)
85  {
86  //needs insertion
87  revisionInfo = new Models.RevisionInformation
88  {
89  Instance = instance,
90  CommitSha = repoSha,
91  CompileJobs = new List<Models.CompileJob>(),
92  ActiveTestMerges = new List<RevInfoTestMerge>() //non null vals for api returns
93  };
94 
95  lock (databaseContext) //cleaner this way
96  databaseContext.RevisionInformations.Add(revisionInfo);
97  }
98  revisionInfo.OriginCommitSha = revisionInfo.OriginCommitSha ?? lastOriginCommitSha;
99  if (revisionInfo.OriginCommitSha == null)
100  {
101  revisionInfo.OriginCommitSha = repoSha;
102  Logger.LogWarning(Components.Repository.Repository.OriginTrackingErrorTemplate, repoSha);
103  }
104  revInfoSink?.Invoke(revisionInfo);
105  return needsDbUpdate;
106  }
107 
108  async Task<bool> PopulateApi(Repository model, Components.Repository.IRepository repository, IDatabaseContext databaseContext, Models.Instance instance, CancellationToken cancellationToken)
109  {
110  if (repository.IsGitHubRepository)
111  {
112  model.GitHubOwner = repository.GitHubOwner;
113  model.GitHubName = repository.GitHubRepoName;
114  }
115  model.Origin = repository.Origin;
116  model.Reference = repository.Reference;
117 
118  //rev info stuff
119  Models.RevisionInformation revisionInfo = null;
120  var needsDbUpdate = await LoadRevisionInformation(repository, databaseContext, instance, null, x => revisionInfo = x, cancellationToken).ConfigureAwait(false);
121  model.RevisionInformation = revisionInfo.ToApi();
122  return needsDbUpdate;
123  }
124 
126  [TgsAuthorize(RepositoryRights.SetOrigin)]
127  public override async Task<IActionResult> Create([FromBody] Repository model, CancellationToken cancellationToken)
128  {
129  if (model == null)
130  throw new ArgumentNullException(nameof(model));
131 
132  if (model.Origin == null)
133  return BadRequest(new ErrorMessage { Message = "Missing repo origin!" });
134 
135  if (model.AccessUser == null ^ model.AccessToken == null)
136  return BadRequest(new ErrorMessage { Message = "Either both accessToken and accessUser must be present or neither!" });
137 
138  var currentModel = await DatabaseContext.RepositorySettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
139 
140  if (currentModel == default)
141  return StatusCode((int)HttpStatusCode.Gone);
142 
143  //normalize github urls
144  const string BadGitHubUrl = "://www.github.com/";
145  var uiOrigin = model.Origin.ToUpperInvariant();
146  var uiBad = BadGitHubUrl.ToUpperInvariant();
147  var uiGitHub = Components.Repository.Repository.GitHubUrl.ToUpperInvariant();
148  if (uiOrigin.Contains(uiBad, StringComparison.Ordinal))
149  model.Origin = uiOrigin.Replace(uiBad, uiGitHub, StringComparison.Ordinal);
150 
151  currentModel.AccessToken = model.AccessToken;
152  currentModel.AccessUser = model.AccessUser; //intentionally only these fields, user not allowed to change anything else atm
153  var cloneBranch = model.Reference;
154  var origin = model.Origin;
155 
156  var repoManager = instanceManager.GetInstance(Instance).RepositoryManager;
157 
158  if (repoManager.CloneInProgress)
159  return Conflict(new ErrorMessage
160  {
161  Message = "A clone operation is in progress!"
162  });
163 
164  if(repoManager.InUse)
165  return Conflict(new ErrorMessage
166  {
167  Message = "The repo is busy!"
168  });
169 
170  using (var repo = await repoManager.LoadRepository(cancellationToken).ConfigureAwait(false))
171  {
172  if (repo != null)
173  //clone conflict
174  return Conflict(new ErrorMessage
175  {
176  Message = "The repository already exists!"
177  });
178 
179  var job = new Models.Job
180  {
181  Description = String.Format(CultureInfo.InvariantCulture, "Clone branch {1} of repository {0}", origin, cloneBranch ?? "master"),
182  StartedBy = AuthenticationContext.User,
183  CancelRightsType = RightsType.Repository,
184  CancelRight = (ulong)RepositoryRights.CancelClone,
186  };
187  var api = currentModel.ToApi();
188  await jobManager.RegisterOperation(job, async (paramJob, databaseContext, progressReporter, ct) =>
189  {
190  using (var repos = await repoManager.CloneRepository(new Uri(origin), cloneBranch, currentModel.AccessUser, currentModel.AccessToken, progressReporter, ct).ConfigureAwait(false))
191  {
192  if (repos == null)
193  throw new JobException("Filesystem conflict while cloning repository!");
194  var instance = new Models.Instance
195  {
196  Id = Instance.Id
197  };
198  databaseContext.Instances.Attach(instance);
199  if (await PopulateApi(api, repos, databaseContext, instance, ct).ConfigureAwait(false))
200  await databaseContext.Save(ct).ConfigureAwait(false);
201  }
202  }, cancellationToken).ConfigureAwait(false);
203 
204  api.Origin = model.Origin;
205  api.Reference = model.Reference;
206  api.ActiveJob = job.ToApi();
207 
208  return StatusCode((int)HttpStatusCode.Created, api);
209  }
210  }
211 
217  [TgsAuthorize(RepositoryRights.Delete)]
218  public async Task<IActionResult> Delete(CancellationToken cancellationToken)
219  {
220  var currentModel = await DatabaseContext.RepositorySettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
221 
222  if (currentModel == default)
223  return StatusCode((int)HttpStatusCode.Gone);
224 
225  currentModel.AccessToken = null;
226  currentModel.AccessUser = null;
227 
228  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
229 
230  Logger.LogInformation("Instance {0} repository delete initiated by user {1}", Instance.Id, AuthenticationContext.User.Id);
231 
232  var job = new Models.Job
233  {
234  Description = "Delete repository",
235  StartedBy = AuthenticationContext.User,
237  };
238  var api = currentModel.ToApi();
239  await jobManager.RegisterOperation(job, (paramJob, databaseContext, progressReporter, ct) => instanceManager.GetInstance(Instance).RepositoryManager.DeleteRepository(cancellationToken), cancellationToken).ConfigureAwait(false);
240  api.ActiveJob = job.ToApi();
241  return Accepted(api);
242  }
243 
245  [TgsAuthorize(RepositoryRights.Read)]
246  public override async Task<IActionResult> Read(CancellationToken cancellationToken)
247  {
248  var currentModel = await DatabaseContext.RepositorySettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
249 
250  if (currentModel == default)
251  return StatusCode((int)HttpStatusCode.Gone);
252 
253  var api = currentModel.ToApi();
254  var repoManager = instanceManager.GetInstance(Instance).RepositoryManager;
255 
256  if (repoManager.CloneInProgress)
257  return Conflict(new ErrorMessage
258  {
259  Message = "A clone operation is in progress!"
260  });
261 
262  if (repoManager.InUse)
263  return Conflict(new ErrorMessage
264  {
265  Message = "The repo is busy!"
266  });
267 
268  using (var repo = await repoManager.LoadRepository(cancellationToken).ConfigureAwait(false))
269  {
270  if (repo != null && await PopulateApi(api, repo, DatabaseContext, Instance, cancellationToken).ConfigureAwait(false))
271  {
272  //user may have fucked with the repo without telling us, do what we can
273  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
274  return StatusCode((int)HttpStatusCode.Created, api);
275  }
276  return Json(api);
277  }
278  }
279 
281  [TgsAuthorize(RepositoryRights.ChangeAutoUpdateSettings | RepositoryRights.ChangeCommitter | RepositoryRights.ChangeCredentials | RepositoryRights.ChangeTestMergeCommits | RepositoryRights.MergePullRequest | RepositoryRights.SetReference | RepositoryRights.SetSha | RepositoryRights.UpdateBranch)]
282  public override async Task<IActionResult> Update([FromBody]Repository model, CancellationToken cancellationToken)
283  {
284  if (model == null)
285  throw new ArgumentNullException(nameof(model));
286 
287  if (model.AccessUser == null ^ model.AccessToken == null)
288  return BadRequest(new ErrorMessage { Message = "Either both accessToken and accessUser must be present or neither!" });
289 
290  if (model.CheckoutSha != null && model.Reference != null)
291  return BadRequest(new ErrorMessage { Message = "Only one of sha or reference may be specified!" });
292 
293  if (model.CheckoutSha != null && model.UpdateFromOrigin == true)
294  return BadRequest(new ErrorMessage { Message = "Cannot update a reference when checking out a sha!" });
295 
296  if (model.Origin != null)
297  return BadRequest(new ErrorMessage { Message = "origin cannot be modified without deleting the repository!" });
298 
299  if (model.NewTestMerges?.Any(x => !x.Number.HasValue) == true)
300  return BadRequest(new ErrorMessage { Message = "All new test merges must provide a number!" });
301 
302  if (model.NewTestMerges?.Any(x => model.NewTestMerges.Any(y => x != y && x.Number == y.Number)) == true)
303  return BadRequest(new ErrorMessage { Message = "Cannot test merge the same PR twice in one job!" });
304 
305  if (model.CommitterName?.Length == 0)
306  return BadRequest(new ErrorMessage { Message = "Cannot set empty committer name!" });
307 
308  if (model.CommitterEmail?.Length == 0)
309  return BadRequest(new ErrorMessage { Message = "Cannot set empty committer e=mail!" });
310 
311  var newTestMerges = model.NewTestMerges != null && model.NewTestMerges.Count > 0;
312  var userRights = (RepositoryRights)AuthenticationContext.GetRight(RightsType.Repository);
313  if (newTestMerges && !userRights.HasFlag(RepositoryRights.MergePullRequest))
314  return Forbid();
315 
316  var currentModel = await DatabaseContext.RepositorySettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
317 
318  if (currentModel == default)
319  return StatusCode((int)HttpStatusCode.Gone);
320 
321  bool CheckModified<T>(Expression<Func<Api.Models.Internal.RepositorySettings, T>> expression, RepositoryRights requiredRight)
322  {
323  var memberSelectorExpression = (MemberExpression)expression.Body;
324  var property = (PropertyInfo)memberSelectorExpression.Member;
325 
326  var newVal = property.GetValue(model);
327  if (newVal == null)
328  return false;
329  if (!userRights.HasFlag(requiredRight) && property.GetValue(currentModel) != newVal)
330  return true;
331 
332  property.SetValue(currentModel, newVal);
333  return false;
334  };
335 
336  if (CheckModified(x => x.AccessToken, RepositoryRights.ChangeCredentials)
337  || CheckModified(x => x.AccessUser, RepositoryRights.ChangeCredentials)
338  || CheckModified(x => x.AutoUpdatesKeepTestMerges, RepositoryRights.ChangeAutoUpdateSettings)
339  || CheckModified(x => x.AutoUpdatesSynchronize, RepositoryRights.ChangeAutoUpdateSettings)
340  || CheckModified(x => x.CommitterEmail, RepositoryRights.ChangeCommitter)
341  || CheckModified(x => x.CommitterName, RepositoryRights.ChangeCommitter)
342  || CheckModified(x => x.PushTestMergeCommits, RepositoryRights.ChangeTestMergeCommits)
343  || CheckModified(x => x.ShowTestMergeCommitters, RepositoryRights.ChangeTestMergeCommits)
344  || (model.UpdateFromOrigin == true && !userRights.HasFlag(RepositoryRights.UpdateBranch)))
345  return Forbid();
346 
347  if (currentModel.AccessToken?.Length == 0 && currentModel.AccessUser?.Length == 0)
348  {
349  //setting an empty string clears everything
350  currentModel.AccessUser = null;
351  currentModel.AccessToken = null;
352  }
353 
354  var canRead = userRights.HasFlag(RepositoryRights.Read);
355 
356  var api = canRead ? currentModel.ToApi() : new Repository();
357  var repoManager = instanceManager.GetInstance(Instance).RepositoryManager;
358 
359  if (canRead)
360  {
361  if (repoManager.CloneInProgress)
362  return Conflict(new ErrorMessage
363  {
364  Message = "A clone operation is in progress!"
365  });
366 
367  if (repoManager.InUse)
368  return Conflict(new ErrorMessage
369  {
370  Message = "The repo is busy!"
371  });
372 
373  using (var repo = await repoManager.LoadRepository(cancellationToken).ConfigureAwait(false))
374  {
375  if (repo == null)
376  return Conflict(new ErrorMessage
377  {
378  Message = "Repository could not be loaded!"
379  });
380  await PopulateApi(api, repo, DatabaseContext, Instance, cancellationToken).ConfigureAwait(false);
381  }
382  }
383 
384  //this is just db stuf so stow it away
385  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
386 
387  //format the job description
388  string description = null;
389  if (model.UpdateFromOrigin == true)
390  if (model.Reference != null)
391  description = String.Format(CultureInfo.InvariantCulture, "Fetch and hard reset repository to origin/{0}", model.Reference);
392  else if (model.CheckoutSha != null)
393  description = String.Format(CultureInfo.InvariantCulture, "Fetch and checkout {0} in repository", model.CheckoutSha);
394  else
395  description = "Pull current repository reference";
396  else if (model.Reference != null || model.CheckoutSha != null)
397  description = String.Format(CultureInfo.InvariantCulture, "Checkout repository {0} {1}", model.Reference != null ? "reference" : "SHA", model.Reference ?? model.CheckoutSha);
398 
399  if (newTestMerges)
400  description = String.Format(CultureInfo.InvariantCulture, "{0}est merge pull request(s) {1}{2}",
401  description != null ? String.Format(CultureInfo.InvariantCulture, "{0} and t", description) : "T",
402  String.Join(", ", model.NewTestMerges.Select(x =>
403  String.Format(CultureInfo.InvariantCulture, "#{0}{1}", x.Number,
404  x.PullRequestRevision != null ? String.Format(CultureInfo.InvariantCulture, " at {0}", x.PullRequestRevision.Substring(0, 7)) : String.Empty))),
405  description != null ? String.Empty : " in repository");
406 
407  if (description == null)
408  //no git changes
409  return Json(api);
410 
411  var job = new Models.Job
412  {
413  Description = description,
414  StartedBy = AuthenticationContext.User,
415  Instance = Instance,
416  CancelRightsType = RightsType.Repository,
417  CancelRight = (ulong)RepositoryRights.CancelPendingChanges,
418  };
419 
420  await jobManager.RegisterOperation(job, async (paramJob, databaseContext, progressReporter, ct) =>
421  {
422  using (var repo = await repoManager.LoadRepository(ct).ConfigureAwait(false))
423  {
424  if (repo == null)
425  throw new JobException("Repository could not be loaded!");
426 
427  var modelHasShaOrReference = model.CheckoutSha != null || model.Reference != null;
428 
429  var startReference = repo.Reference;
430  var startSha = repo.Head;
431  string postUpdateSha = null;
432 
433  if (newTestMerges && !repo.IsGitHubRepository)
434  throw new JobException("Cannot test merge on a non GitHub based repository!");
435 
436  var committerName = currentModel.ShowTestMergeCommitters.Value ? AuthenticationContext.User.Name : currentModel.CommitterName;
437 
438  var hardResettingToOriginReference = model.UpdateFromOrigin == true && model.Reference != null;
439 
440  var numSteps = (model.NewTestMerges?.Count ?? 0) + (model.UpdateFromOrigin == true ? 1 : 0) + (!modelHasShaOrReference ? 2 : (hardResettingToOriginReference ? 3 : 1));
441  var doneSteps = 0;
442 
443  Action<int> NextProgressReporter()
444  {
445  var tmpDoneSteps = doneSteps;
446  ++doneSteps;
447  return progress => progressReporter((progress + 100 * tmpDoneSteps) / numSteps);
448  };
449 
450  progressReporter(0);
451 
452  //get a base line for where we are
453  Models.RevisionInformation lastRevisionInfo = null;
454 
455  var attachedInstance = new Models.Instance
456  {
457  Id = Instance.Id
458  };
459  databaseContext.Instances.Attach(attachedInstance);
460 
461  await LoadRevisionInformation(repo, databaseContext, attachedInstance, null, x => lastRevisionInfo = x, ct).ConfigureAwait(false);
462 
463  //apply new rev info, tracking applied test merges
464  async Task UpdateRevInfo()
465  {
466  var last = lastRevisionInfo;
467  await LoadRevisionInformation(repo, databaseContext, attachedInstance, last.OriginCommitSha, x => lastRevisionInfo = x, ct).ConfigureAwait(false);
468  lastRevisionInfo.ActiveTestMerges.AddRange(last.ActiveTestMerges);
469  };
470 
471  try
472  {
473  //fetch/pull
474  if (model.UpdateFromOrigin == true)
475  {
476  if (!repo.Tracking)
477  throw new JobException("Not on an updatable reference!");
478  await repo.FetchOrigin(currentModel.AccessUser, currentModel.AccessToken, NextProgressReporter(), ct).ConfigureAwait(false);
479  doneSteps = 1;
480  if (!modelHasShaOrReference)
481  {
482  var fastForward = await repo.MergeOrigin(committerName, currentModel.CommitterEmail, NextProgressReporter(), ct).ConfigureAwait(false);
483  if (!fastForward.HasValue)
484  throw new JobException("Merge conflict occurred during origin update!");
485  await UpdateRevInfo().ConfigureAwait(false);
486  if (fastForward.Value)
487  {
488  lastRevisionInfo.OriginCommitSha = repo.Head;
489  await repo.Sychronize(currentModel.AccessUser, currentModel.AccessToken, currentModel.CommitterName, currentModel.CommitterEmail, NextProgressReporter(), true, ct).ConfigureAwait(false);
490  postUpdateSha = repo.Head;
491  }
492  else
493  NextProgressReporter()(100);
494  }
495  }
496 
497  //checkout/hard reset
498  if (modelHasShaOrReference)
499  {
500  if ((model.CheckoutSha != null && repo.Head.ToUpperInvariant().StartsWith(model.CheckoutSha.ToUpperInvariant(), StringComparison.Ordinal))
501  || (model.Reference != null && repo.Reference.ToUpperInvariant() != model.Reference.ToUpperInvariant()))
502  {
503  var committish = model.CheckoutSha ?? model.Reference;
504  var isSha = await repo.IsSha(committish, cancellationToken).ConfigureAwait(false);
505 
506  if ((isSha && model.Reference != null) || (!isSha && model.CheckoutSha != null))
507  throw new JobException("Attempted to checkout a SHA or reference that was actually the opposite!");
508 
509  await repo.CheckoutObject(committish, NextProgressReporter(), ct).ConfigureAwait(false);
510  await LoadRevisionInformation(repo, databaseContext, attachedInstance, null, x => lastRevisionInfo = x, ct).ConfigureAwait(false); //we've either seen origin before or what we're checking out is on origin
511  }
512  else
513  NextProgressReporter()(100);
514 
515  if (hardResettingToOriginReference)
516  {
517  if (!repo.Tracking)
518  throw new JobException("Checked out reference does not track a remote object!");
519  await repo.ResetToOrigin(NextProgressReporter(), ct).ConfigureAwait(false);
520  await repo.Sychronize(currentModel.AccessUser, currentModel.AccessToken, currentModel.CommitterName, currentModel.CommitterEmail, NextProgressReporter(), true, ct).ConfigureAwait(false);
521  await LoadRevisionInformation(repo, databaseContext, attachedInstance, null, x => lastRevisionInfo = x, ct).ConfigureAwait(false);
522  //repo head is on origin so force this
523  //will update the db if necessary
524  lastRevisionInfo.OriginCommitSha = repo.Head;
525  }
526  }
527 
528  Dictionary<int, Octokit.PullRequest> prMap = null;
529  //test merging
530  if (newTestMerges)
531  {
532  //bit of sanitization
533  foreach (var I in model.NewTestMerges.Where(x => String.IsNullOrWhiteSpace(x.PullRequestRevision)))
534  I.PullRequestRevision = null;
535 
536  var gitHubClient = currentModel.AccessToken != null
537  ? gitHubClientFactory.CreateClient(currentModel.AccessToken)
538  : (String.IsNullOrEmpty(generalConfiguration.GitHubAccessToken)
539  ? gitHubClientFactory.CreateClient()
540  : gitHubClientFactory.CreateClient(generalConfiguration.GitHubAccessToken));
541 
542  var repoOwner = repo.GitHubOwner;
543  var repoName = repo.GitHubRepoName;
544 
545  Models.RevisionInformation revInfoWereLookingFor = null;
546  bool needToApplyRemainingPrs = true;
547  //optimization: if we've already merged these exact same commits in this fashion before, just find the rev info for it and check it out
548  if (lastRevisionInfo.OriginCommitSha == lastRevisionInfo.CommitSha)
549  {
550  //In order for this to work though we need the shas of all the commits
551  if (model.NewTestMerges.Any(x => x.PullRequestRevision == null))
552  prMap = new Dictionary<int, Octokit.PullRequest>();
553 
554  bool cantSearch = false;
555  foreach (var I in model.NewTestMerges)
556  {
557  if (I.PullRequestRevision != null)
558  //normalize the shas to lowercase ala libgit2
559 #pragma warning disable CA1308 // Normalize strings to uppercase
560  I.PullRequestRevision = I.PullRequestRevision?.ToLowerInvariant();
561 #pragma warning restore CA1308 // Normalize strings to uppercase
562  else
563  //retrieve the latest sha
564  try
565  {
566  var pr = await gitHubClient.PullRequest.Get(repoOwner, repoName, I.Number.Value).ConfigureAwait(false);
567  prMap.Add(I.Number.Value, pr);
568  I.PullRequestRevision = pr.Head.Sha;
569  }
570  catch
571  {
572  cantSearch = true;
573  break;
574  }
575  }
576 
577  if (!cantSearch)
578  {
579  var dbPull = await databaseContext.RevisionInformations
580  .Where(x => x.Instance.Id == Instance.Id
581  && x.OriginCommitSha == lastRevisionInfo.OriginCommitSha
582  && x.ActiveTestMerges.Count <= model.NewTestMerges.Count
583  && x.ActiveTestMerges.Count > 0)
584  .Include(x => x.ActiveTestMerges)
585  .ThenInclude(x => x.TestMerge)
586  .ToListAsync(cancellationToken).ConfigureAwait(false);
587 
588  //split here cause this bit has to be done locally
589  revInfoWereLookingFor = dbPull
590  .Where(x => x.ActiveTestMerges.Count == model.NewTestMerges.Count
591  && x.ActiveTestMerges.Select(y => y.TestMerge)
592  .All(y => model.NewTestMerges.Any(z =>
593  y.Number == z.Number
594  && y.PullRequestRevision.StartsWith(z.PullRequestRevision, StringComparison.Ordinal)
595  && (y.Comment?.Trim().ToUpperInvariant() == z.Comment?.Trim().ToUpperInvariant() || z.Comment == null))))
596  .FirstOrDefault();
597 
598  if (revInfoWereLookingFor == null && model.NewTestMerges.Count > 1)
599  {
600  //okay try to add at least SOME prs we've seen before
601  var search = model.NewTestMerges.ToList();
602 
603  var appliedTestMergeIds = new List<long>();
604 
605  Models.RevisionInformation lastGoodRevInfo = null;
606  do
607  {
608  foreach (var I in search)
609  {
610  revInfoWereLookingFor = dbPull
611  .Where(x => model.NewTestMerges.Any(z =>
612  x.PrimaryTestMerge.Number == z.Number
613  && x.PrimaryTestMerge.PullRequestRevision.StartsWith(z.PullRequestRevision, StringComparison.Ordinal)
614  && (x.PrimaryTestMerge.Comment?.Trim().ToUpperInvariant() == z.Comment?.Trim().ToUpperInvariant() || z.Comment == null))
615  && x.ActiveTestMerges.Select(y => y.TestMerge).All(y => appliedTestMergeIds.Contains(y.Id)))
616  .FirstOrDefault();
617 
618  if (revInfoWereLookingFor != null)
619  {
620  lastGoodRevInfo = revInfoWereLookingFor;
621  appliedTestMergeIds.Add(revInfoWereLookingFor.PrimaryTestMerge.Id);
622  search.Remove(I);
623  break;
624  }
625  }
626  } while (revInfoWereLookingFor != null && search.Count > 0);
627 
628  revInfoWereLookingFor = lastGoodRevInfo;
629  needToApplyRemainingPrs = search.Count != 0;
630  if (needToApplyRemainingPrs)
631  model.NewTestMerges = search;
632  }
633  else if (revInfoWereLookingFor != null)
634  needToApplyRemainingPrs = false;
635  }
636  }
637 
638  if (revInfoWereLookingFor != null)
639  {
640  //goteem
641  await repo.ResetToSha(revInfoWereLookingFor.CommitSha, NextProgressReporter(), cancellationToken).ConfigureAwait(false);
642  lastRevisionInfo = revInfoWereLookingFor;
643  }
644 
645  if (needToApplyRemainingPrs)
646  {
647  var contextUser = new Models.User
648  {
650  };
651  databaseContext.Users.Attach(contextUser);
652 
653  foreach (var I in model.NewTestMerges)
654  {
655  Octokit.PullRequest pr = null;
656  string errorMessage = null;
657 
658  if (lastRevisionInfo.ActiveTestMerges.Any(x => x.TestMerge.Number == I.Number.Value))
659  throw new JobException("Cannot test merge the same PR twice in one HEAD!");
660 
661  try
662  {
663  //load from cache if possible
664  if (prMap == null || !prMap.TryGetValue(I.Number.Value, out pr))
665  pr = await gitHubClient.PullRequest.Get(repoOwner, repoName, I.Number.Value).ConfigureAwait(false);
666  }
667  catch (Octokit.RateLimitExceededException)
668  {
669  //you look at your anonymous access and sigh
670  errorMessage = "P.R.E. RATE LIMITED";
671  }
672  catch (Octokit.AuthorizationException)
673  {
674  errorMessage = "P.R.E. BAD CREDENTIALS";
675  }
676  catch (Octokit.NotFoundException)
677  {
678  //you look at your shithub and sigh
679  errorMessage = "P.R.E. NOT FOUND";
680  }
681 
682  //we want to take the earliest truth possible to prevent RCEs, if this fails AddTestMerge will set it
683  if (I.PullRequestRevision == null && pr != null)
684  I.PullRequestRevision = pr.Head.Sha;
685 
686  var mergeResult = await repo.AddTestMerge(I, committerName, currentModel.CommitterEmail, currentModel.AccessUser, currentModel.AccessToken, NextProgressReporter(), ct).ConfigureAwait(false);
687 
688  if (!mergeResult.HasValue) //conflict, we don't care, dd already knows
689  continue;
690 
691  ++doneSteps;
692 
693  var revInfoUpdateTask = UpdateRevInfo();
694 
695  var tm = new Models.TestMerge
696  {
697  Author = pr?.User.Login ?? errorMessage,
698  BodyAtMerge = pr?.Body ?? errorMessage ?? String.Empty,
699  MergedAt = DateTimeOffset.Now,
700  TitleAtMerge = pr?.Title ?? errorMessage ?? String.Empty,
701  Comment = I.Comment,
702  Number = I.Number,
703  MergedBy = contextUser,
704  PullRequestRevision = I.PullRequestRevision,
705  Url = pr?.HtmlUrl ?? errorMessage
706  };
707 
708  await revInfoUpdateTask.ConfigureAwait(false);
709 
710  lastRevisionInfo.PrimaryTestMerge = tm;
711  lastRevisionInfo.ActiveTestMerges.Add(new RevInfoTestMerge
712  {
713  TestMerge = tm
714  });
715  }
716  }
717  }
718 
719  var currentHead = repo.Head;
720  if (startSha != currentHead || (postUpdateSha != null && postUpdateSha != currentHead))
721  {
722  await repo.Sychronize(currentModel.AccessUser, currentModel.AccessToken, currentModel.CommitterName, currentModel.CommitterEmail, NextProgressReporter(), false, ct).ConfigureAwait(false);
723  await UpdateRevInfo().ConfigureAwait(false);
724  }
725  await databaseContext.Save(ct).ConfigureAwait(false);
726  }
727  catch
728  {
729  doneSteps = 0;
730  numSteps = 2;
731  //the stuff didn't make it into the db, forget what we've done and abort
732  await repo.CheckoutObject(startReference ?? startSha, NextProgressReporter(), default).ConfigureAwait(false);
733  if (startReference != null && repo.Head != startSha)
734  await repo.ResetToSha(startSha, NextProgressReporter(), default).ConfigureAwait(false);
735  else
736  progressReporter(100);
737  throw;
738  }
739  }
740  }, cancellationToken).ConfigureAwait(false);
741 
742  api.ActiveJob = job.ToApi();
743  return Accepted(api);
744  }
745  }
746 }
string Name
The name of the User
Definition: User.cs:38
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using Microsoft.Extensions.Options;
5 using System;
6 using System.Collections.Generic;
7 using System.Globalization;
8 using System.Linq;
9 using System.Linq.Expressions;
10 using System.Net;
11 using System.Reflection;
12 using System.Threading;
13 using System.Threading.Tasks;
14 using Tgstation.Server.Api;
22 
23 namespace Tgstation.Server.Host.Controllers
24 {
28  [Route(Routes.Repository)]
29  public sealed class RepositoryController : ModelController<Repository>
30  {
35 
40 
45 
50 
61  public RepositoryController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, IGitHubClientFactory gitHubClientFactory, IJobManager jobManager, ILogger<RepositoryController> logger, IOptions<GeneralConfiguration> generalConfigurationOptions) : base(databaseContext, authenticationContextFactory, logger, true)
62  {
63  this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager));
64  this.gitHubClientFactory = gitHubClientFactory ?? throw new ArgumentNullException(nameof(gitHubClientFactory));
65  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
66  generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
67  }
68 
69  async Task<bool> LoadRevisionInformation(Components.Repository.IRepository repository, IDatabaseContext databaseContext, Models.Instance instance, string lastOriginCommitSha, Action<Models.RevisionInformation> revInfoSink, CancellationToken cancellationToken)
70  {
71  var repoSha = repository.Head;
72 
73  IQueryable<Models.RevisionInformation> queryTarget = databaseContext.RevisionInformations;
74 
75  var revisionInfo = await databaseContext.RevisionInformations.Where(x => x.CommitSha == repoSha && x.Instance.Id == instance.Id)
76  .Include(x => x.CompileJobs)
77  .Include(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge) //minimal info, they can query the rest if they're allowed
78  .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); //search every rev info because LOL SHA COLLISIONS
79 
80  if (revisionInfo == default)
81  revisionInfo = databaseContext.RevisionInformations.Local.Where(x => x.CommitSha == repoSha).FirstOrDefault();
82 
83  var needsDbUpdate = revisionInfo == default;
84  if (needsDbUpdate)
85  {
86  //needs insertion
87  revisionInfo = new Models.RevisionInformation
88  {
89  Instance = instance,
90  CommitSha = repoSha,
91  CompileJobs = new List<Models.CompileJob>(),
92  ActiveTestMerges = new List<RevInfoTestMerge>() //non null vals for api returns
93  };
94 
95  lock (databaseContext) //cleaner this way
96  databaseContext.RevisionInformations.Add(revisionInfo);
97  }
98  revisionInfo.OriginCommitSha = revisionInfo.OriginCommitSha ?? lastOriginCommitSha;
99  if (revisionInfo.OriginCommitSha == null)
100  {
101  revisionInfo.OriginCommitSha = repoSha;
102  Logger.LogWarning(Components.Repository.Repository.OriginTrackingErrorTemplate, repoSha);
103  }
104  revInfoSink?.Invoke(revisionInfo);
105  return needsDbUpdate;
106  }
107 
108  async Task<bool> PopulateApi(Repository model, Components.Repository.IRepository repository, IDatabaseContext databaseContext, Models.Instance instance, CancellationToken cancellationToken)
109  {
110  if (repository.IsGitHubRepository)
111  {
112  model.GitHubOwner = repository.GitHubOwner;
113  model.GitHubName = repository.GitHubRepoName;
114  }
115  model.Origin = repository.Origin;
116  model.Reference = repository.Reference;
117 
118  //rev info stuff
119  Models.RevisionInformation revisionInfo = null;
120  var needsDbUpdate = await LoadRevisionInformation(repository, databaseContext, instance, null, x => revisionInfo = x, cancellationToken).ConfigureAwait(false);
121  model.RevisionInformation = revisionInfo.ToApi();
122  return needsDbUpdate;
123  }
124 
126  [TgsAuthorize(RepositoryRights.SetOrigin)]
127  public override async Task<IActionResult> Create([FromBody] Repository model, CancellationToken cancellationToken)
128  {
129  if (model == null)
130  throw new ArgumentNullException(nameof(model));
131 
132  if (model.Origin == null)
133  return BadRequest(new ErrorMessage { Message = "Missing repo origin!" });
134 
135  if (model.AccessUser == null ^ model.AccessToken == null)
136  return BadRequest(new ErrorMessage { Message = "Either both accessToken and accessUser must be present or neither!" });
137 
138  var currentModel = await DatabaseContext.RepositorySettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
139 
140  if (currentModel == default)
141  return StatusCode((int)HttpStatusCode.Gone);
142 
143  //normalize github urls
144  const string BadGitHubUrl = "://www.github.com/";
145  var uiOrigin = model.Origin.ToUpperInvariant();
146  var uiBad = BadGitHubUrl.ToUpperInvariant();
147  var uiGitHub = Components.Repository.Repository.GitHubUrl.ToUpperInvariant();
148  if (uiOrigin.Contains(uiBad, StringComparison.Ordinal))
149  model.Origin = uiOrigin.Replace(uiBad, uiGitHub, StringComparison.Ordinal);
150 
151  currentModel.AccessToken = model.AccessToken;
152  currentModel.AccessUser = model.AccessUser; //intentionally only these fields, user not allowed to change anything else atm
153  var cloneBranch = model.Reference;
154  var origin = model.Origin;
155 
156  var repoManager = instanceManager.GetInstance(Instance).RepositoryManager;
157 
158  if (repoManager.CloneInProgress)
159  return Conflict(new ErrorMessage
160  {
161  Message = "A clone operation is in progress!"
162  });
163 
164  if(repoManager.InUse)
165  return Conflict(new ErrorMessage
166  {
167  Message = "The repo is busy!"
168  });
169 
170  using (var repo = await repoManager.LoadRepository(cancellationToken).ConfigureAwait(false))
171  {
172  if (repo != null)
173  //clone conflict
174  return Conflict(new ErrorMessage
175  {
176  Message = "The repository already exists!"
177  });
178 
179  var job = new Models.Job
180  {
181  Description = String.Format(CultureInfo.InvariantCulture, "Clone branch {1} of repository {0}", origin, cloneBranch ?? "master"),
182  StartedBy = AuthenticationContext.User,
183  CancelRightsType = RightsType.Repository,
184  CancelRight = (ulong)RepositoryRights.CancelClone,
186  };
187  var api = currentModel.ToApi();
188  await jobManager.RegisterOperation(job, async (paramJob, databaseContext, progressReporter, ct) =>
189  {
190  using (var repos = await repoManager.CloneRepository(new Uri(origin), cloneBranch, currentModel.AccessUser, currentModel.AccessToken, progressReporter, ct).ConfigureAwait(false))
191  {
192  if (repos == null)
193  throw new JobException("Filesystem conflict while cloning repository!");
194  var instance = new Models.Instance
195  {
196  Id = Instance.Id
197  };
198  databaseContext.Instances.Attach(instance);
199  if (await PopulateApi(api, repos, databaseContext, instance, ct).ConfigureAwait(false))
200  await databaseContext.Save(ct).ConfigureAwait(false);
201  }
202  }, cancellationToken).ConfigureAwait(false);
203 
204  api.Origin = model.Origin;
205  api.Reference = model.Reference;
206  api.ActiveJob = job.ToApi();
207 
208  return StatusCode((int)HttpStatusCode.Created, api);
209  }
210  }
211 
217  [TgsAuthorize(RepositoryRights.Delete)]
218  public async Task<IActionResult> Delete(CancellationToken cancellationToken)
219  {
220  var currentModel = await DatabaseContext.RepositorySettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
221 
222  if (currentModel == default)
223  return StatusCode((int)HttpStatusCode.Gone);
224 
225  currentModel.AccessToken = null;
226  currentModel.AccessUser = null;
227 
228  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
229 
230  Logger.LogInformation("Instance {0} repository delete initiated by user {1}", Instance.Id, AuthenticationContext.User.Id);
231 
232  var job = new Models.Job
233  {
234  Description = "Delete repository",
235  StartedBy = AuthenticationContext.User,
237  };
238  var api = currentModel.ToApi();
239  await jobManager.RegisterOperation(job, (paramJob, databaseContext, progressReporter, ct) => instanceManager.GetInstance(Instance).RepositoryManager.DeleteRepository(cancellationToken), cancellationToken).ConfigureAwait(false);
240  api.ActiveJob = job.ToApi();
241  return Accepted(api);
242  }
243 
245  [TgsAuthorize(RepositoryRights.Read)]
246  public override async Task<IActionResult> Read(CancellationToken cancellationToken)
247  {
248  var currentModel = await DatabaseContext.RepositorySettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
249 
250  if (currentModel == default)
251  return StatusCode((int)HttpStatusCode.Gone);
252 
253  var api = currentModel.ToApi();
254  var repoManager = instanceManager.GetInstance(Instance).RepositoryManager;
255 
256  if (repoManager.CloneInProgress)
257  return Conflict(new ErrorMessage
258  {
259  Message = "A clone operation is in progress!"
260  });
261 
262  if (repoManager.InUse)
263  return Conflict(new ErrorMessage
264  {
265  Message = "The repo is busy!"
266  });
267 
268  using (var repo = await repoManager.LoadRepository(cancellationToken).ConfigureAwait(false))
269  {
270  if (repo != null && await PopulateApi(api, repo, DatabaseContext, Instance, cancellationToken).ConfigureAwait(false))
271  {
272  //user may have fucked with the repo without telling us, do what we can
273  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
274  return StatusCode((int)HttpStatusCode.Created, api);
275  }
276  return Json(api);
277  }
278  }
279 
281  [TgsAuthorize(RepositoryRights.ChangeAutoUpdateSettings | RepositoryRights.ChangeCommitter | RepositoryRights.ChangeCredentials | RepositoryRights.ChangeTestMergeCommits | RepositoryRights.MergePullRequest | RepositoryRights.SetReference | RepositoryRights.SetSha | RepositoryRights.UpdateBranch)]
282  public override async Task<IActionResult> Update([FromBody]Repository model, CancellationToken cancellationToken)
283  {
284  if (model == null)
285  throw new ArgumentNullException(nameof(model));
286 
287  if (model.AccessUser == null ^ model.AccessToken == null)
288  return BadRequest(new ErrorMessage { Message = "Either both accessToken and accessUser must be present or neither!" });
289 
290  if (model.CheckoutSha != null && model.Reference != null)
291  return BadRequest(new ErrorMessage { Message = "Only one of sha or reference may be specified!" });
292 
293  if (model.CheckoutSha != null && model.UpdateFromOrigin == true)
294  return BadRequest(new ErrorMessage { Message = "Cannot update a reference when checking out a sha!" });
295 
296  if (model.Origin != null)
297  return BadRequest(new ErrorMessage { Message = "origin cannot be modified without deleting the repository!" });
298 
299  if (model.NewTestMerges?.Any(x => !x.Number.HasValue) == true)
300  return BadRequest(new ErrorMessage { Message = "All new test merges must provide a number!" });
301 
302  if (model.NewTestMerges?.Any(x => model.NewTestMerges.Any(y => x != y && x.Number == y.Number)) == true)
303  return BadRequest(new ErrorMessage { Message = "Cannot test merge the same PR twice in one job!" });
304 
305  if (model.CommitterName?.Length == 0)
306  return BadRequest(new ErrorMessage { Message = "Cannot set empty committer name!" });
307 
308  if (model.CommitterEmail?.Length == 0)
309  return BadRequest(new ErrorMessage { Message = "Cannot set empty committer e=mail!" });
310 
311  var newTestMerges = model.NewTestMerges != null && model.NewTestMerges.Count > 0;
312  var userRights = (RepositoryRights)AuthenticationContext.GetRight(RightsType.Repository);
313  if (newTestMerges && !userRights.HasFlag(RepositoryRights.MergePullRequest))
314  return Forbid();
315 
316  var currentModel = await DatabaseContext.RepositorySettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
317 
318  if (currentModel == default)
319  return StatusCode((int)HttpStatusCode.Gone);
320 
321  bool CheckModified<T>(Expression<Func<Api.Models.Internal.RepositorySettings, T>> expression, RepositoryRights requiredRight)
322  {
323  var memberSelectorExpression = (MemberExpression)expression.Body;
324  var property = (PropertyInfo)memberSelectorExpression.Member;
325 
326  var newVal = property.GetValue(model);
327  if (newVal == null)
328  return false;
329  if (!userRights.HasFlag(requiredRight) && property.GetValue(currentModel) != newVal)
330  return true;
331 
332  property.SetValue(currentModel, newVal);
333  return false;
334  };
335 
336  if (CheckModified(x => x.AccessToken, RepositoryRights.ChangeCredentials)
337  || CheckModified(x => x.AccessUser, RepositoryRights.ChangeCredentials)
338  || CheckModified(x => x.AutoUpdatesKeepTestMerges, RepositoryRights.ChangeAutoUpdateSettings)
339  || CheckModified(x => x.AutoUpdatesSynchronize, RepositoryRights.ChangeAutoUpdateSettings)
340  || CheckModified(x => x.CommitterEmail, RepositoryRights.ChangeCommitter)
341  || CheckModified(x => x.CommitterName, RepositoryRights.ChangeCommitter)
342  || CheckModified(x => x.PushTestMergeCommits, RepositoryRights.ChangeTestMergeCommits)
343  || CheckModified(x => x.ShowTestMergeCommitters, RepositoryRights.ChangeTestMergeCommits)
344  || (model.UpdateFromOrigin == true && !userRights.HasFlag(RepositoryRights.UpdateBranch)))
345  return Forbid();
346 
347  if (currentModel.AccessToken?.Length == 0 && currentModel.AccessUser?.Length == 0)
348  {
349  //setting an empty string clears everything
350  currentModel.AccessUser = null;
351  currentModel.AccessToken = null;
352  }
353 
354  var canRead = userRights.HasFlag(RepositoryRights.Read);
355 
356  var api = canRead ? currentModel.ToApi() : new Repository();
357  var repoManager = instanceManager.GetInstance(Instance).RepositoryManager;
358 
359  if (canRead)
360  {
361  if (repoManager.CloneInProgress)
362  return Conflict(new ErrorMessage
363  {
364  Message = "A clone operation is in progress!"
365  });
366 
367  if (repoManager.InUse)
368  return Conflict(new ErrorMessage
369  {
370  Message = "The repo is busy!"
371  });
372 
373  using (var repo = await repoManager.LoadRepository(cancellationToken).ConfigureAwait(false))
374  {
375  if (repo == null)
376  return Conflict(new ErrorMessage
377  {
378  Message = "Repository could not be loaded!"
379  });
380  await PopulateApi(api, repo, DatabaseContext, Instance, cancellationToken).ConfigureAwait(false);
381  }
382  }
383 
384  //this is just db stuf so stow it away
385  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
386 
387  //format the job description
388  string description = null;
389  if (model.UpdateFromOrigin == true)
390  if (model.Reference != null)
391  description = String.Format(CultureInfo.InvariantCulture, "Fetch and hard reset repository to origin/{0}", model.Reference);
392  else if (model.CheckoutSha != null)
393  description = String.Format(CultureInfo.InvariantCulture, "Fetch and checkout {0} in repository", model.CheckoutSha);
394  else
395  description = "Pull current repository reference";
396  else if (model.Reference != null || model.CheckoutSha != null)
397  description = String.Format(CultureInfo.InvariantCulture, "Checkout repository {0} {1}", model.Reference != null ? "reference" : "SHA", model.Reference ?? model.CheckoutSha);
398 
399  if (newTestMerges)
400  description = String.Format(CultureInfo.InvariantCulture, "{0}est merge pull request(s) {1}{2}",
401  description != null ? String.Format(CultureInfo.InvariantCulture, "{0} and t", description) : "T",
402  String.Join(", ", model.NewTestMerges.Select(x =>
403  String.Format(CultureInfo.InvariantCulture, "#{0}{1}", x.Number,
404  x.PullRequestRevision != null ? String.Format(CultureInfo.InvariantCulture, " at {0}", x.PullRequestRevision.Substring(0, 7)) : String.Empty))),
405  description != null ? String.Empty : " in repository");
406 
407  if (description == null)
408  //no git changes
409  return Json(api);
410 
411  var job = new Models.Job
412  {
413  Description = description,
414  StartedBy = AuthenticationContext.User,
415  Instance = Instance,
416  CancelRightsType = RightsType.Repository,
417  CancelRight = (ulong)RepositoryRights.CancelPendingChanges,
418  };
419 
420  await jobManager.RegisterOperation(job, async (paramJob, databaseContext, progressReporter, ct) =>
421  {
422  using (var repo = await repoManager.LoadRepository(ct).ConfigureAwait(false))
423  {
424  if (repo == null)
425  throw new JobException("Repository could not be loaded!");
426 
427  var modelHasShaOrReference = model.CheckoutSha != null || model.Reference != null;
428 
429  var startReference = repo.Reference;
430  var startSha = repo.Head;
431  string postUpdateSha = null;
432 
433  if (newTestMerges && !repo.IsGitHubRepository)
434  throw new JobException("Cannot test merge on a non GitHub based repository!");
435 
436  var committerName = currentModel.ShowTestMergeCommitters.Value ? AuthenticationContext.User.Name : currentModel.CommitterName;
437 
438  var hardResettingToOriginReference = model.UpdateFromOrigin == true && model.Reference != null;
439 
440  var numSteps = (model.NewTestMerges?.Count ?? 0) + (model.UpdateFromOrigin == true ? 1 : 0) + (!modelHasShaOrReference ? 2 : (hardResettingToOriginReference ? 3 : 1));
441  var doneSteps = 0;
442 
443  Action<int> NextProgressReporter()
444  {
445  var tmpDoneSteps = doneSteps;
446  ++doneSteps;
447  return progress => progressReporter((progress + 100 * tmpDoneSteps) / numSteps);
448  };
449 
450  progressReporter(0);
451 
452  //get a base line for where we are
453  Models.RevisionInformation lastRevisionInfo = null;
454 
455  var attachedInstance = new Models.Instance
456  {
457  Id = Instance.Id
458  };
459  databaseContext.Instances.Attach(attachedInstance);
460 
461  await LoadRevisionInformation(repo, databaseContext, attachedInstance, null, x => lastRevisionInfo = x, ct).ConfigureAwait(false);
462 
463  //apply new rev info, tracking applied test merges
464  async Task UpdateRevInfo()
465  {
466  var last = lastRevisionInfo;
467  await LoadRevisionInformation(repo, databaseContext, attachedInstance, last.OriginCommitSha, x => lastRevisionInfo = x, ct).ConfigureAwait(false);
468  lastRevisionInfo.ActiveTestMerges.AddRange(last.ActiveTestMerges);
469  };
470 
471  try
472  {
473  //fetch/pull
474  if (model.UpdateFromOrigin == true)
475  {
476  if (!repo.Tracking)
477  throw new JobException("Not on an updatable reference!");
478  await repo.FetchOrigin(currentModel.AccessUser, currentModel.AccessToken, NextProgressReporter(), ct).ConfigureAwait(false);
479  doneSteps = 1;
480  if (!modelHasShaOrReference)
481  {
482  var fastForward = await repo.MergeOrigin(committerName, currentModel.CommitterEmail, NextProgressReporter(), ct).ConfigureAwait(false);
483  if (!fastForward.HasValue)
484  throw new JobException("Merge conflict occurred during origin update!");
485  await UpdateRevInfo().ConfigureAwait(false);
486  if (fastForward.Value)
487  {
488  lastRevisionInfo.OriginCommitSha = repo.Head;
489  await repo.Sychronize(currentModel.AccessUser, currentModel.AccessToken, currentModel.CommitterName, currentModel.CommitterEmail, NextProgressReporter(), true, ct).ConfigureAwait(false);
490  postUpdateSha = repo.Head;
491  }
492  else
493  NextProgressReporter()(100);
494  }
495  }
496 
497  //checkout/hard reset
498  if (modelHasShaOrReference)
499  {
500  if ((model.CheckoutSha != null && repo.Head.ToUpperInvariant().StartsWith(model.CheckoutSha.ToUpperInvariant(), StringComparison.Ordinal))
501  || (model.Reference != null && repo.Reference.ToUpperInvariant() != model.Reference.ToUpperInvariant()))
502  {
503  var committish = model.CheckoutSha ?? model.Reference;
504  var isSha = await repo.IsSha(committish, cancellationToken).ConfigureAwait(false);
505 
506  if ((isSha && model.Reference != null) || (!isSha && model.CheckoutSha != null))
507  throw new JobException("Attempted to checkout a SHA or reference that was actually the opposite!");
508 
509  await repo.CheckoutObject(committish, NextProgressReporter(), ct).ConfigureAwait(false);
510  await LoadRevisionInformation(repo, databaseContext, attachedInstance, null, x => lastRevisionInfo = x, ct).ConfigureAwait(false); //we've either seen origin before or what we're checking out is on origin
511  }
512  else
513  NextProgressReporter()(100);
514 
515  if (hardResettingToOriginReference)
516  {
517  if (!repo.Tracking)
518  throw new JobException("Checked out reference does not track a remote object!");
519  await repo.ResetToOrigin(NextProgressReporter(), ct).ConfigureAwait(false);
520  await repo.Sychronize(currentModel.AccessUser, currentModel.AccessToken, currentModel.CommitterName, currentModel.CommitterEmail, NextProgressReporter(), true, ct).ConfigureAwait(false);
521  await LoadRevisionInformation(repo, databaseContext, attachedInstance, null, x => lastRevisionInfo = x, ct).ConfigureAwait(false);
522  //repo head is on origin so force this
523  //will update the db if necessary
524  lastRevisionInfo.OriginCommitSha = repo.Head;
525  }
526  }
527 
528  Dictionary<int, Octokit.PullRequest> prMap = null;
529  //test merging
530  if (newTestMerges)
531  {
532  //bit of sanitization
533  foreach (var I in model.NewTestMerges.Where(x => String.IsNullOrWhiteSpace(x.PullRequestRevision)))
534  I.PullRequestRevision = null;
535 
536  var gitHubClient = currentModel.AccessToken != null
537  ? gitHubClientFactory.CreateClient(currentModel.AccessToken)
538  : (String.IsNullOrEmpty(generalConfiguration.GitHubAccessToken)
539  ? gitHubClientFactory.CreateClient()
540  : gitHubClientFactory.CreateClient(generalConfiguration.GitHubAccessToken));
541 
542  var repoOwner = repo.GitHubOwner;
543  var repoName = repo.GitHubRepoName;
544 
545  Models.RevisionInformation revInfoWereLookingFor = null;
546  bool needToApplyRemainingPrs = true;
547  //optimization: if we've already merged these exact same commits in this fashion before, just find the rev info for it and check it out
548  if (lastRevisionInfo.OriginCommitSha == lastRevisionInfo.CommitSha)
549  {
550  //In order for this to work though we need the shas of all the commits
551  if (model.NewTestMerges.Any(x => x.PullRequestRevision == null))
552  prMap = new Dictionary<int, Octokit.PullRequest>();
553 
554  bool cantSearch = false;
555  foreach (var I in model.NewTestMerges)
556  {
557  if (I.PullRequestRevision != null)
558  //normalize the shas to lowercase ala libgit2
559 #pragma warning disable CA1308 // Normalize strings to uppercase
560  I.PullRequestRevision = I.PullRequestRevision?.ToLowerInvariant();
561 #pragma warning restore CA1308 // Normalize strings to uppercase
562  else
563  //retrieve the latest sha
564  try
565  {
566  var pr = await gitHubClient.PullRequest.Get(repoOwner, repoName, I.Number.Value).ConfigureAwait(false);
567  prMap.Add(I.Number.Value, pr);
568  I.PullRequestRevision = pr.Head.Sha;
569  }
570  catch
571  {
572  cantSearch = true;
573  break;
574  }
575  }
576 
577  if (!cantSearch)
578  {
579  var dbPull = await databaseContext.RevisionInformations
580  .Where(x => x.Instance.Id == Instance.Id
581  && x.OriginCommitSha == lastRevisionInfo.OriginCommitSha
582  && x.ActiveTestMerges.Count <= model.NewTestMerges.Count
583  && x.ActiveTestMerges.Count > 0)
584  .Include(x => x.ActiveTestMerges)
585  .ThenInclude(x => x.TestMerge)
586  .ToListAsync(cancellationToken).ConfigureAwait(false);
587 
588  //split here cause this bit has to be done locally
589  revInfoWereLookingFor = dbPull
590  .Where(x => x.ActiveTestMerges.Count == model.NewTestMerges.Count
591  && x.ActiveTestMerges.Select(y => y.TestMerge)
592  .All(y => model.NewTestMerges.Any(z =>
593  y.Number == z.Number
594  && y.PullRequestRevision.StartsWith(z.PullRequestRevision, StringComparison.Ordinal)
595  && (y.Comment?.Trim().ToUpperInvariant() == z.Comment?.Trim().ToUpperInvariant() || z.Comment == null))))
596  .FirstOrDefault();
597 
598  if (revInfoWereLookingFor == null && model.NewTestMerges.Count > 1)
599  {
600  //okay try to add at least SOME prs we've seen before
601  var search = model.NewTestMerges.ToList();
602 
603  var appliedTestMergeIds = new List<long>();
604 
605  Models.RevisionInformation lastGoodRevInfo = null;
606  do
607  {
608  foreach (var I in search)
609  {
610  revInfoWereLookingFor = dbPull
611  .Where(x => model.NewTestMerges.Any(z =>
612  x.PrimaryTestMerge.Number == z.Number
613  && x.PrimaryTestMerge.PullRequestRevision.StartsWith(z.PullRequestRevision, StringComparison.Ordinal)
614  && (x.PrimaryTestMerge.Comment?.Trim().ToUpperInvariant() == z.Comment?.Trim().ToUpperInvariant() || z.Comment == null))
615  && x.ActiveTestMerges.Select(y => y.TestMerge).All(y => appliedTestMergeIds.Contains(y.Id)))
616  .FirstOrDefault();
617 
618  if (revInfoWereLookingFor != null)
619  {
620  lastGoodRevInfo = revInfoWereLookingFor;
621  appliedTestMergeIds.Add(revInfoWereLookingFor.PrimaryTestMerge.Id);
622  search.Remove(I);
623  break;
624  }
625  }
626  } while (revInfoWereLookingFor != null && search.Count > 0);
627 
628  revInfoWereLookingFor = lastGoodRevInfo;
629  needToApplyRemainingPrs = search.Count != 0;
630  if (needToApplyRemainingPrs)
631  model.NewTestMerges = search;
632  }
633  else if (revInfoWereLookingFor != null)
634  needToApplyRemainingPrs = false;
635  }
636  }
637 
638  if (revInfoWereLookingFor != null)
639  {
640  //goteem
641  await repo.ResetToSha(revInfoWereLookingFor.CommitSha, NextProgressReporter(), cancellationToken).ConfigureAwait(false);
642  lastRevisionInfo = revInfoWereLookingFor;
643  }
644 
645  if (needToApplyRemainingPrs)
646  {
647  var contextUser = new Models.User
648  {
650  };
651  databaseContext.Users.Attach(contextUser);
652 
653  foreach (var I in model.NewTestMerges)
654  {
655  Octokit.PullRequest pr = null;
656  string errorMessage = null;
657 
658  if (lastRevisionInfo.ActiveTestMerges.Any(x => x.TestMerge.Number == I.Number.Value))
659  throw new JobException("Cannot test merge the same PR twice in one HEAD!");
660 
661  try
662  {
663  //load from cache if possible
664  if (prMap == null || !prMap.TryGetValue(I.Number.Value, out pr))
665  pr = await gitHubClient.PullRequest.Get(repoOwner, repoName, I.Number.Value).ConfigureAwait(false);
666  }
667  catch (Octokit.RateLimitExceededException)
668  {
669  //you look at your anonymous access and sigh
670  errorMessage = "P.R.E. RATE LIMITED";
671  }
672  catch (Octokit.AuthorizationException)
673  {
674  errorMessage = "P.R.E. BAD CREDENTIALS";
675  }
676  catch (Octokit.NotFoundException)
677  {
678  //you look at your shithub and sigh
679  errorMessage = "P.R.E. NOT FOUND";
680  }
681 
682  //we want to take the earliest truth possible to prevent RCEs, if this fails AddTestMerge will set it
683  if (I.PullRequestRevision == null && pr != null)
684  I.PullRequestRevision = pr.Head.Sha;
685 
686  var mergeResult = await repo.AddTestMerge(I, committerName, currentModel.CommitterEmail, currentModel.AccessUser, currentModel.AccessToken, NextProgressReporter(), ct).ConfigureAwait(false);
687 
688  if (!mergeResult.HasValue) //conflict, we don't care, dd already knows
689  continue;
690 
691  ++doneSteps;
692 
693  var revInfoUpdateTask = UpdateRevInfo();
694 
695  var tm = new Models.TestMerge
696  {
697  Author = pr?.User.Login ?? errorMessage,
698  BodyAtMerge = pr?.Body ?? errorMessage ?? String.Empty,
699  MergedAt = DateTimeOffset.Now,
700  TitleAtMerge = pr?.Title ?? errorMessage ?? String.Empty,
701  Comment = I.Comment,
702  Number = I.Number,
703  MergedBy = contextUser,
704  PullRequestRevision = I.PullRequestRevision,
705  Url = pr?.HtmlUrl ?? errorMessage
706  };
707 
708  await revInfoUpdateTask.ConfigureAwait(false);
709 
710  lastRevisionInfo.PrimaryTestMerge = tm;
711  lastRevisionInfo.ActiveTestMerges.Add(new RevInfoTestMerge
712  {
713  TestMerge = tm
714  });
715  }
716  }
717  }
718 
719  var currentHead = repo.Head;
720  if (startSha != currentHead || (postUpdateSha != null && postUpdateSha != currentHead))
721  {
722  await repo.Sychronize(currentModel.AccessUser, currentModel.AccessToken, currentModel.CommitterName, currentModel.CommitterEmail, NextProgressReporter(), false, ct).ConfigureAwait(false);
723  await UpdateRevInfo().ConfigureAwait(false);
724  }
725  await databaseContext.Save(ct).ConfigureAwait(false);
726  }
727  catch
728  {
729  doneSteps = 0;
730  numSteps = 2;
731  //the stuff didn't make it into the db, forget what we've done and abort
732  await repo.CheckoutObject(startReference ?? startSha, NextProgressReporter(), default).ConfigureAwait(false);
733  if (startReference != null && repo.Head != startSha)
734  await repo.ResetToSha(startSha, NextProgressReporter(), default).ConfigureAwait(false);
735  else
736  progressReporter(100);
737  throw;
738  }
739  }
740  }, cancellationToken).ConfigureAwait(false);
741 
742  api.ActiveJob = job.ToApi();
743  return Accepted(api);
744  }
745  }
746 }
string Name
The name of the User
Definition: User.cs:38
Manages the runtime of Jobs
Definition: IJobManager.cs:12
@@ -103,6 +103,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
string GitHubOwner
If the repository was cloned from GitHub.com this will be set with the owner of the repository ...
Definition: Repository.cs:28
+ @@ -122,7 +123,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Represents a git repository
Definition: Repository.cs:8
const string Repository
The Models.Repository controller
Definition: Routes.cs:39
RevisionInformation RevisionInformation
The current Models.RevisionInformation for the Repository
Definition: Repository.cs:23
-
DbSet< RepositorySettings > RepositorySettings
RepositoryRights
Rights for a Models.Repository
diff --git a/_repository_manager_8cs_source.html b/_repository_manager_8cs_source.html index d31cfa077b..cbf8478056 100644 --- a/_repository_manager_8cs_source.html +++ b/_repository_manager_8cs_source.html @@ -92,10 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RepositoryManager.cs
-Go to the documentation of this file.
1 using LibGit2Sharp;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Threading;
5 using System.Threading.Tasks;
8 using Tgstation.Server.Host.IO;
9 
10 namespace Tgstation.Server.Host.Components.Repository
11 {
14  {
16  public bool InUse => semaphore.CurrentCount == 0;
17 
19  public bool CloneInProgress { get; private set; }
20 
25 
30 
35 
39  readonly ILogger<Repository> repositoryLogger;
40 
44  readonly ILogger<RepositoryManager> logger;
45 
50 
54  readonly SemaphoreSlim semaphore;
55 
65  public RepositoryManager(RepositorySettings repositorySettings, IIOManager ioManager, IEventConsumer eventConsumer, ICredentialsProvider credentialsProvider, ILogger<Repository> repositoryLogger, ILogger<RepositoryManager> logger)
66  {
67  this.repositorySettings = repositorySettings ?? throw new ArgumentNullException(nameof(repositorySettings));
68  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
69  this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
70  this.credentialsProvider = credentialsProvider ?? throw new ArgumentNullException(nameof(credentialsProvider));
71  this.repositoryLogger = repositoryLogger ?? throw new ArgumentNullException(nameof(repositoryLogger));
72  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
73  semaphore = new SemaphoreSlim(1);
74  }
75 
77  public void Dispose()
78  {
79  logger.LogTrace("Disposing...");
80  semaphore.Dispose();
81  }
82 
84  public async Task<IRepository> CloneRepository(Uri url, string initialBranch, string username, string password, Action<int> progressReporter, CancellationToken cancellationToken)
85  {
86  if (url == null)
87  throw new ArgumentNullException(nameof(url));
88  if (progressReporter == null)
89  throw new ArgumentNullException(nameof(progressReporter));
90 
91  logger.LogInformation("Begin clone {0} (Branch: {1})", url, initialBranch);
92  lock (this)
93  {
94  if (CloneInProgress)
95  throw new InvalidOperationException("The repository is already being cloned!");
96  CloneInProgress = true;
97  }
98  try
99  {
100  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
101  {
102  logger.LogTrace("Semaphore acquired");
103  if (!await ioManager.DirectoryExists(".", cancellationToken).ConfigureAwait(false))
104  try
105  {
106  await Task.Factory.StartNew(() =>
107  {
108  string path = null;
109  try
110  {
111  path = LibGit2Sharp.Repository.Clone(url.ToString(), ioManager.ResolvePath("."), new CloneOptions
112  {
113  OnProgress = (a) => !cancellationToken.IsCancellationRequested,
114  OnTransferProgress = (a) =>
115  {
116  var percentage = 100 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2));
117  progressReporter((int)percentage);
118  return !cancellationToken.IsCancellationRequested;
119  },
120  RecurseSubmodules = true,
121  OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested,
122  RepositoryOperationStarting = (a) => !cancellationToken.IsCancellationRequested,
123  BranchName = initialBranch,
124  CredentialsProvider = credentialsProvider.GenerateHandler(username, password)
125  });
126  }
127  catch (UserCancelledException) { }
128  cancellationToken.ThrowIfCancellationRequested();
129  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
130  }
131  catch
132  {
133  try
134  {
135  logger.LogTrace("Deleting partially cloned repository...");
136  await ioManager.DeleteDirectory(".", default).ConfigureAwait(false);
137  }
138  catch (Exception e)
139  {
140  logger.LogDebug("Error deleting partially cloned repository! Exception: {0}", e);
141  }
142  throw;
143  }
144  else
145  {
146  logger.LogDebug("Repository exists, clone aborted!");
147  return null;
148  }
149  }
150  logger.LogInformation("Clone complete!");
151  }
152  finally
153  {
154  CloneInProgress = false;
155  }
156  return await LoadRepository(cancellationToken).ConfigureAwait(false);
157  }
158 
160  public async Task<IRepository> LoadRepository(CancellationToken cancellationToken)
161  {
162  logger.LogTrace("Begin LoadRepository...");
163  lock (this)
164  if (CloneInProgress)
165  throw new InvalidOperationException("The repository is being cloned!");
166  await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
167  LibGit2Sharp.Repository repo = null;
168  await Task.Factory.StartNew(() =>
169  {
170  try
171  {
172  logger.LogTrace("Creating LibGit2Sharp.Repository...");
173  repo = new LibGit2Sharp.Repository(ioManager.ResolvePath("."));
174  }
175  catch (RepositoryNotFoundException e)
176  {
177  logger.LogDebug("Repository not found!");
178  logger.LogTrace("Exception: {0}", e);
179  }
180  catch
181  {
182  semaphore.Release();
183  throw;
184  }
185  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
186  if (repo == null)
187  {
188  semaphore.Release();
189  return null;
190  }
191  return new Repository(repo, ioManager, eventConsumer, credentialsProvider, repositoryLogger, () =>
192  {
193  logger.LogTrace("Releasing semaphore due to Repository disposal...");
194  semaphore.Release();
195  });
196  }
197 
199  public async Task DeleteRepository(CancellationToken cancellationToken)
200  {
201  logger.LogInformation("Deleting repository...");
202  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
203  {
204  logger.LogTrace("Semaphore acquired, deleting Repository directory...");
205  await ioManager.DeleteDirectory(".", cancellationToken).ConfigureAwait(false);
206  }
207  }
208  }
209 }
+Go to the documentation of this file.
1 using LibGit2Sharp;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Threading;
5 using System.Threading.Tasks;
8 using Tgstation.Server.Host.IO;
9 
10 namespace Tgstation.Server.Host.Components.Repository
11 {
14  {
16  public bool InUse => semaphore.CurrentCount == 0;
17 
19  public bool CloneInProgress { get; private set; }
20 
25 
30 
35 
39  readonly ILogger<Repository> repositoryLogger;
40 
44  readonly ILogger<RepositoryManager> logger;
45 
50 
54  readonly SemaphoreSlim semaphore;
55 
65  public RepositoryManager(RepositorySettings repositorySettings, IIOManager ioManager, IEventConsumer eventConsumer, ICredentialsProvider credentialsProvider, ILogger<Repository> repositoryLogger, ILogger<RepositoryManager> logger)
66  {
67  this.repositorySettings = repositorySettings ?? throw new ArgumentNullException(nameof(repositorySettings));
68  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
69  this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
70  this.credentialsProvider = credentialsProvider ?? throw new ArgumentNullException(nameof(credentialsProvider));
71  this.repositoryLogger = repositoryLogger ?? throw new ArgumentNullException(nameof(repositoryLogger));
72  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
73  semaphore = new SemaphoreSlim(1);
74  }
75 
77  public void Dispose()
78  {
79  logger.LogTrace("Disposing...");
80  semaphore.Dispose();
81  }
82 
84  public async Task<IRepository> CloneRepository(Uri url, string initialBranch, string username, string password, Action<int> progressReporter, CancellationToken cancellationToken)
85  {
86  if (url == null)
87  throw new ArgumentNullException(nameof(url));
88  if (progressReporter == null)
89  throw new ArgumentNullException(nameof(progressReporter));
90 
91  logger.LogInformation("Begin clone {0} (Branch: {1})", url, initialBranch);
92  lock (this)
93  {
94  if (CloneInProgress)
95  throw new InvalidOperationException("The repository is already being cloned!");
96  CloneInProgress = true;
97  }
98  try
99  {
100  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
101  {
102  logger.LogTrace("Semaphore acquired");
103  if (!await ioManager.DirectoryExists(".", cancellationToken).ConfigureAwait(false))
104  try
105  {
106  await Task.Factory.StartNew(() =>
107  {
108  string path = null;
109  try
110  {
111  path = LibGit2Sharp.Repository.Clone(url.ToString(), ioManager.ResolvePath("."), new CloneOptions
112  {
113  OnProgress = (a) => !cancellationToken.IsCancellationRequested,
114  OnTransferProgress = (a) =>
115  {
116  var percentage = 100 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2));
117  progressReporter((int)percentage);
118  return !cancellationToken.IsCancellationRequested;
119  },
120  RecurseSubmodules = true,
121  OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested,
122  RepositoryOperationStarting = (a) => !cancellationToken.IsCancellationRequested,
123  BranchName = initialBranch,
124  CredentialsProvider = credentialsProvider.GenerateHandler(username, password)
125  });
126  }
127  catch (UserCancelledException) { }
128  cancellationToken.ThrowIfCancellationRequested();
129  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
130  }
131  catch
132  {
133  try
134  {
135  logger.LogTrace("Deleting partially cloned repository...");
136  await ioManager.DeleteDirectory(".", default).ConfigureAwait(false);
137  }
138  catch (Exception e)
139  {
140  logger.LogDebug("Error deleting partially cloned repository! Exception: {0}", e);
141  }
142  throw;
143  }
144  else
145  {
146  logger.LogDebug("Repository exists, clone aborted!");
147  return null;
148  }
149  }
150  logger.LogInformation("Clone complete!");
151  }
152  finally
153  {
154  CloneInProgress = false;
155  }
156  return await LoadRepository(cancellationToken).ConfigureAwait(false);
157  }
158 
160  public async Task<IRepository> LoadRepository(CancellationToken cancellationToken)
161  {
162  logger.LogTrace("Begin LoadRepository...");
163  lock (this)
164  if (CloneInProgress)
165  throw new InvalidOperationException("The repository is being cloned!");
166  await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
167  LibGit2Sharp.Repository repo = null;
168  await Task.Factory.StartNew(() =>
169  {
170  try
171  {
172  logger.LogTrace("Creating LibGit2Sharp.Repository...");
173  repo = new LibGit2Sharp.Repository(ioManager.ResolvePath("."));
174  }
175  catch (RepositoryNotFoundException e)
176  {
177  logger.LogDebug("Repository not found!");
178  logger.LogTrace("Exception: {0}", e);
179  }
180  catch
181  {
182  semaphore.Release();
183  throw;
184  }
185  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
186  if (repo == null)
187  {
188  semaphore.Release();
189  return null;
190  }
191  return new Repository(repo, ioManager, eventConsumer, credentialsProvider, repositoryLogger, () =>
192  {
193  logger.LogTrace("Releasing semaphore due to Repository disposal...");
194  semaphore.Release();
195  });
196  }
197 
199  public async Task DeleteRepository(CancellationToken cancellationToken)
200  {
201  logger.LogInformation("Deleting repository...");
202  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
203  {
204  logger.LogTrace("Semaphore acquired, deleting Repository directory...");
205  await ioManager.DeleteDirectory(".", cancellationToken).ConfigureAwait(false);
206  }
207  }
208  }
209 }
Represents configurable settings for a Repository
Consumes EventTypes and takes the appropriate actions
+
RepositoryManager(RepositorySettings repositorySettings, IIOManager ioManager, IEventConsumer eventConsumer, ICredentialsProvider credentialsProvider, ILogger< Repository > repositoryLogger, ILogger< RepositoryManager > logger)
Construct a RepositoryManager
CredentialsHandler GenerateHandler(string username, string password)
Generate a CredentialsHandler from a given username and password
readonly ILogger< RepositoryManager > logger
The ILogger for the RepositoryManager
@@ -109,8 +110,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly ICredentialsProvider credentialsProvider
The ICredentialsProvider for the RepositoryManager
readonly IIOManager ioManager
The IIOManager for the RepositoryManager
- - +
Factory for creating and loading IRepositorys
diff --git a/_repository_rights_8cs_source.html b/_repository_rights_8cs_source.html index 022b465036..4f0224ee5e 100644 --- a/_repository_rights_8cs_source.html +++ b/_repository_rights_8cs_source.html @@ -92,15 +92,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RepositoryRights.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum RepositoryRights : ulong
10  {
14  None = 0,
22  SetOrigin = 2,
26  SetSha = 4,
30  MergePullRequest = 8,
34  UpdateBranch = 16,
38  ChangeCommitter = 32,
46  ChangeCredentials = 128,
50  SetReference = 256,
54  Read = 512,
62  Delete = 2048,
66  CancelClone = 4096
67  }
68 }
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Rights
4 {
8  [Flags]
9  public enum RepositoryRights : ulong
10  {
14  None = 0,
22  SetOrigin = 2,
26  SetSha = 4,
30  MergePullRequest = 8,
34  UpdateBranch = 16,
38  ChangeCommitter = 32,
46  ChangeCredentials = 128,
50  SetReference = 256,
54  Read = 512,
62  Delete = 2048,
66  CancelClone = 4096
67  }
68 }
User may change Models.Internal.RepositorySettings.AutoUpdatesKeepTestMerges and Models.Internal.RepositorySettings.AutoUpdatesSynchronize
+
User may fetch and merge GitHub pull requests
User may change Models.Internal.RepositorySettings.PushTestMergeCommits
-
RepositoryRights
Rights for a Models.Repository
User may directly set the sha the Models.Repository&#39;s HEAD points to
User may set Models.Repository.Reference to another git reference (not a SHA)
diff --git a/_request_timeout_exception_8cs_source.html b/_request_timeout_exception_8cs_source.html index 4bfe68714e..6a599cdf1e 100644 --- a/_request_timeout_exception_8cs_source.html +++ b/_request_timeout_exception_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RequestTimeoutException.cs
-Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
11  {
16  {
17  Message = "The request timed out!",
18  SeverApiVersion = null
19  }, HttpStatusCode.RequestTimeout)
20  { }
21 
26  public RequestTimeoutException(string message) : base(message) { }
27 
33  public RequestTimeoutException(string message, Exception innerException) : base(message, innerException) { }
34  }
35 }
+Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
11  {
16  {
17  Message = "The request timed out!",
18  SeverApiVersion = null
19  }, HttpStatusCode.RequestTimeout)
20  { }
21 
26  public RequestTimeoutException(string message) : base(message) { }
27 
33  public RequestTimeoutException(string message, Exception innerException) : base(message, innerException) { }
34  }
35 }
RequestTimeoutException()
Construct an RequestTimeoutException
+
RequestTimeoutException(string message, Exception innerException)
Construct an RequestTimeoutException with a message and innerException
-
Occurs when the client provides invalid credentials
RequestTimeoutException(string message)
Construct an RequestTimeoutException with a message
diff --git a/_resolving_i_o_manager_8cs_source.html b/_resolving_i_o_manager_8cs_source.html index 1a94741b1a..72fc51252e 100644 --- a/_resolving_i_o_manager_8cs_source.html +++ b/_resolving_i_o_manager_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ResolvingIOManager.cs
-Go to the documentation of this file.
1 using System;
2 using System.IO;
3 
4 namespace Tgstation.Server.Host.IO
5 {
10  {
14  readonly string subdirectory;
15 
21  public ResolvingIOManager(IIOManager parent, string _subdirectory)
22  {
23  if (parent == null)
24  throw new ArgumentNullException(nameof(parent));
25  if (_subdirectory == null)
26  throw new ArgumentNullException(nameof(_subdirectory));
27 
28  subdirectory = ConcatPath(parent.ResolvePath("."), _subdirectory);
29  }
30 
32  public override string ResolvePath(string path)
33  {
34  if (!Path.IsPathRooted(path))
35  return base.ResolvePath(ConcatPath(subdirectory, path));
36  return path;
37  }
38  }
39 }
An IIOManager that resolve relative paths from another IIOManager to a subdirectory of that ...
+Go to the documentation of this file.
1 using System;
2 using System.IO;
3 
4 namespace Tgstation.Server.Host.IO
5 {
10  {
14  readonly string subdirectory;
15 
21  public ResolvingIOManager(IIOManager parent, string _subdirectory)
22  {
23  if (parent == null)
24  throw new ArgumentNullException(nameof(parent));
25  if (_subdirectory == null)
26  throw new ArgumentNullException(nameof(_subdirectory));
27 
28  subdirectory = ConcatPath(parent.ResolvePath("."), _subdirectory);
29  }
30 
32  public override string ResolvePath(string path)
33  {
34  if (!Path.IsPathRooted(path))
35  return base.ResolvePath(ConcatPath(subdirectory, path));
36  return path;
37  }
38  }
39 }
An IIOManager that resolve relative paths from another IIOManager to a subdirectory of that ...
+
string ResolvePath(string path)
Retrieve the full path of some path given a relative path. Must be used before passing relative path...
-
override string ResolvePath(string path)
Retrieve the full path of some path given a relative path. Must be used before passing relative path...
IIOManager that resolves paths to Environment.CurrentDirectory
Interface for using filesystems
Definition: IIOManager.cs:11
diff --git a/_response_8cs_source.html b/_response_8cs_source.html index b12f718361..04f23dd36c 100644 --- a/_response_8cs_source.html +++ b/_response_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Response.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 
3 namespace Tgstation.Server.Host.Components.Chat
4 {
8  sealed class Response
9  {
13  public string Message { get; set; }
14 
18  public List<ulong> ChannelIds { get; set; }
19  }
20 }
- +Go to the documentation of this file.
1 using System.Collections.Generic;
2 
3 namespace Tgstation.Server.Host.Components.Chat
4 {
8  sealed class Response
9  {
13  public string Message { get; set; }
14 
18  public List<ulong> ChannelIds { get; set; }
19  }
20 }
+
Represents a chat message requested by DD
Definition: Response.cs:8
diff --git a/_rev_info_test_merge_8cs_source.html b/_rev_info_test_merge_8cs_source.html index db10a052e2..caffe4db2d 100644 --- a/_rev_info_test_merge_8cs_source.html +++ b/_rev_info_test_merge_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RevInfoTestMerge.cs
-Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Host.Models
4 {
8  public sealed class RevInfoTestMerge
9  {
13  public long Id { get; set; }
14 
18  [Required]
19  public TestMerge TestMerge { get; set; }
20 
24  [Required]
26  }
27 }
+Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Host.Models
4 {
8  public sealed class RevInfoTestMerge
9  {
13  public long Id { get; set; }
14 
18  [Required]
19  public TestMerge TestMerge { get; set; }
20 
24  [Required]
26  }
27 }
+
Many to many relationship for Models.RevisionInformation and Models.TestMerge
-
diff --git a/_revision_command_8cs_source.html b/_revision_command_8cs_source.html index b373a89c32..e8aad65946 100644 --- a/_revision_command_8cs_source.html +++ b/_revision_command_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RevisionCommand.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Globalization;
4 using System.Linq;
5 using System.Threading;
6 using System.Threading.Tasks;
9 
10 namespace Tgstation.Server.Host.Components.Chat.Commands
11 {
15  sealed class RevisionCommand : ICommand
16  {
18  public string Name => "revision";
19 
21  public string HelpText => "Display live commit sha. Add --repo to view repository revision";
22 
24  public bool AdminOnly => false;
25 
29  readonly IWatchdog watchdog;
30 
35 
39  readonly Models.Instance instance;
40 
47  public RevisionCommand(IWatchdog watchdog, IRepositoryManager repositoryManager, Models.Instance instance)
48  {
49  this.watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog));
50  this.repositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager));
51  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
52  }
53 
55  public async Task<string> Invoke(string arguments, User user, CancellationToken cancellationToken)
56  {
57  string result;
58  if (arguments.Split(' ').Any(x => x.ToUpperInvariant() == "--REPO"))
59  using (var repo = await repositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
60  {
61  if (repo == null)
62  return "Repository unavailable!";
63  result = repo.Head;
64  }
65  else
66  {
67  if (!watchdog.Running)
68  return "Server offline!";
69  result = watchdog.ActiveCompileJob?.RevisionInformation.CommitSha;
70  }
71 
72  return String.Format(CultureInfo.InvariantCulture, "^{0}", result);
73  }
74  }
75 }
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Globalization;
4 using System.Linq;
5 using System.Threading;
6 using System.Threading.Tasks;
9 
10 namespace Tgstation.Server.Host.Components.Chat.Commands
11 {
15  sealed class RevisionCommand : ICommand
16  {
18  public string Name => "revision";
19 
21  public string HelpText => "Display live commit sha. Add --repo to view repository revision";
22 
24  public bool AdminOnly => false;
25 
29  readonly IWatchdog watchdog;
30 
35 
39  readonly Models.Instance instance;
40 
47  public RevisionCommand(IWatchdog watchdog, IRepositoryManager repositoryManager, Models.Instance instance)
48  {
49  this.watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog));
50  this.repositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager));
51  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
52  }
53 
55  public async Task<string> Invoke(string arguments, User user, CancellationToken cancellationToken)
56  {
57  string result;
58  if (arguments.Split(' ').Any(x => x.ToUpperInvariant() == "--REPO"))
59  using (var repo = await repositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
60  {
61  if (repo == null)
62  return "Repository unavailable!";
63  result = repo.Head;
64  }
65  else
66  {
67  if (!watchdog.Running)
68  return "Server offline!";
69  result = watchdog.ActiveCompileJob?.RevisionInformation.CommitSha;
70  }
71 
72  return String.Format(CultureInfo.InvariantCulture, "^{0}", result);
73  }
74  }
75 }
+
readonly IRepositoryManager repositoryManager
The IRepositoryManager for the PullRequestsCommand
async Task< string > Invoke(string arguments, User user, CancellationToken cancellationToken)
Invoke the ICommand
readonly IWatchdog watchdog
The IWatchdog for the PullRequestsCommand
-
readonly Models.Instance instance
The Models.Instance for the PullRequestsCommand
RevisionCommand(IWatchdog watchdog, IRepositoryManager repositoryManager, Models.Instance instance)
Construct a RevisionCommand
For displaying Api.Models.Internal.RevisionInformation
diff --git a/_rights_helper_8cs_source.html b/_rights_helper_8cs_source.html index 96ffc05272..cb65635b7d 100644 --- a/_rights_helper_8cs_source.html +++ b/_rights_helper_8cs_source.html @@ -92,17 +92,17 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RightsHelper.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Globalization;
4 
5 namespace Tgstation.Server.Api.Rights
6 {
10  public static class RightsHelper
11  {
15  static readonly IReadOnlyDictionary<RightsType, Type> typeMap = new Dictionary<RightsType, Type>
16  {
17  { RightsType.Administration, typeof(AdministrationRights) },
18  { RightsType.InstanceManager, typeof(InstanceManagerRights) },
19 
20  { RightsType.Repository, typeof(RepositoryRights) },
21  { RightsType.Byond, typeof(ByondRights) },
22  { RightsType.DreamMaker, typeof(DreamMakerRights) },
23  { RightsType.DreamDaemon, typeof(DreamDaemonRights) },
24  { RightsType.ChatBots, typeof(ChatBotRights) },
25  { RightsType.Configuration, typeof(ConfigurationRights) },
26  { RightsType.InstanceUser, typeof(InstanceUserRights) }
27  };
28 
34  public static Type RightToType(RightsType rightsType) => typeMap[rightsType];
35 
42  public static string RoleNames<TRight>(TRight right) where TRight : Enum
43  {
44  var flags = new List<string>();
45  IEnumerable<string> GetRoleNames()
46  {
47  foreach (Enum J in Enum.GetValues(right.GetType()))
48  if (Convert.ToInt32(J, CultureInfo.InvariantCulture) != 0 && right.HasFlag(J))
49  yield return String.Concat(typeof(TRight).Name, '.', J.ToString());
50  };
51  var names = GetRoleNames();
52  return String.Join(",", names);
53  }
54 
61  public static string RoleName(RightsType rightsType, Enum right)
62  {
63  var enumType = typeMap[rightsType];
64  return String.Concat(enumType.Name, '.', Enum.GetName(enumType, right));
65  }
66 
72  public static bool IsInstanceRight(RightsType rightsType) => !(rightsType == RightsType.Administration || rightsType == RightsType.InstanceManager);
73  }
74 }
ConfigurationRights
Rights for Models.ConfigurationFile
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Globalization;
4 
5 namespace Tgstation.Server.Api.Rights
6 {
10  public static class RightsHelper
11  {
15  static readonly IReadOnlyDictionary<RightsType, Type> typeMap = new Dictionary<RightsType, Type>
16  {
17  { RightsType.Administration, typeof(AdministrationRights) },
18  { RightsType.InstanceManager, typeof(InstanceManagerRights) },
19 
20  { RightsType.Repository, typeof(RepositoryRights) },
21  { RightsType.Byond, typeof(ByondRights) },
22  { RightsType.DreamMaker, typeof(DreamMakerRights) },
23  { RightsType.DreamDaemon, typeof(DreamDaemonRights) },
24  { RightsType.ChatBots, typeof(ChatBotRights) },
25  { RightsType.Configuration, typeof(ConfigurationRights) },
26  { RightsType.InstanceUser, typeof(InstanceUserRights) }
27  };
28 
34  public static Type RightToType(RightsType rightsType) => typeMap[rightsType];
35 
42  public static string RoleNames<TRight>(TRight right) where TRight : Enum
43  {
44  var flags = new List<string>();
45  IEnumerable<string> GetRoleNames()
46  {
47  foreach (Enum J in Enum.GetValues(right.GetType()))
48  if (Convert.ToInt32(J, CultureInfo.InvariantCulture) != 0 && right.HasFlag(J))
49  yield return String.Concat(typeof(TRight).Name, '.', J.ToString());
50  };
51  var names = GetRoleNames();
52  return String.Join(",", names);
53  }
54 
61  public static string RoleName(RightsType rightsType, Enum right)
62  {
63  var enumType = typeMap[rightsType];
64  return String.Concat(enumType.Name, '.', Enum.GetName(enumType, right));
65  }
66 
72  public static bool IsInstanceRight(RightsType rightsType) => !(rightsType == RightsType.Administration || rightsType == RightsType.InstanceManager);
73  }
74 }
ConfigurationRights
Rights for Models.ConfigurationFile
ByondRights
Rights for Models.Byond
Definition: ByondRights.cs:9
RightsType
The type of rights a model uses
Definition: RightsType.cs:6
InstanceUserRights
Rights for an Models.Instance
+
AdministrationRights
Rights for Models.Administration
DreamDaemonRights
Rights for Models.DreamDaemon
InstanceManagerRights
Rights for managing Models.Instances
DreamMakerRights
Rights for Models.DreamMaker
-
RepositoryRights
Rights for a Models.Repository
ChatBotRights
Rights for Models.ChatBot
Definition: ChatBotRights.cs:9
static string RoleName(RightsType rightsType, Enum right)
Gets the role claim name used for a given rightsType and right
Definition: RightsHelper.cs:61
diff --git a/_rights_type_8cs_source.html b/_rights_type_8cs_source.html index df86a3be7c..197cfb78ef 100644 --- a/_rights_type_8cs_source.html +++ b/_rights_type_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RightsType.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Rights
2 {
6  public enum RightsType : ulong
7  {
19  Repository,
23  Byond,
27  DreamMaker,
35  ChatBots,
44  }
45 }
RightsType
The type of rights a model uses
Definition: RightsType.cs:6
+Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Rights
2 {
6  public enum RightsType : ulong
7  {
19  Repository,
23  Byond,
27  DreamMaker,
35  ChatBots,
44  }
45 }
RightsType
The type of rights a model uses
Definition: RightsType.cs:6
+ - diff --git a/_routes_8cs_source.html b/_routes_8cs_source.html index 1488ea5275..00de494472 100644 --- a/_routes_8cs_source.html +++ b/_routes_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Routes.cs
-Go to the documentation of this file.
1 using System;
2 using System.Globalization;
3 
4 namespace Tgstation.Server.Api
5 {
9  public static class Routes
10  {
14  public const string Root = "/";
15 
19  public const string Administration = Root + nameof(Models.Administration);
20 
24  public const string User = Root + nameof(Models.User);
25 
29  public const string InstanceManager = Root + nameof(Models.Instance);
30 
34  public const string Byond = Root + nameof(Models.Byond);
35 
39  public const string Repository = Root + nameof(Models.Repository);
40 
44  public const string DreamDaemon = Root + nameof(Models.DreamDaemon);
45 
49  public const string Configuration = Root + "Config";
50 
54  public const string InstanceUser = Root + nameof(Models.InstanceUser);
55 
59  public const string Chat = Root + "Chat";
60 
64  public const string DreamMaker = Root + nameof(Models.DreamMaker);
65 
69  public const string Jobs = Root + nameof(Models.Job);
70 
77  public static string SetID(string route, long id) => String.Format(CultureInfo.InvariantCulture, "{0}/{1}", route, id);
78 
84  public static string List(string route) => String.Format(CultureInfo.InvariantCulture, "{0}/List", route);
85  }
86 }
+Go to the documentation of this file.
1 using System;
2 using System.Globalization;
3 
4 namespace Tgstation.Server.Api
5 {
9  public static class Routes
10  {
14  public const string Root = "/";
15 
19  public const string Administration = Root + nameof(Models.Administration);
20 
24  public const string User = Root + nameof(Models.User);
25 
29  public const string InstanceManager = Root + nameof(Models.Instance);
30 
34  public const string Byond = Root + nameof(Models.Byond);
35 
39  public const string Repository = Root + nameof(Models.Repository);
40 
44  public const string DreamDaemon = Root + nameof(Models.DreamDaemon);
45 
49  public const string Configuration = Root + "Config";
50 
54  public const string InstanceUser = Root + nameof(Models.InstanceUser);
55 
59  public const string Chat = Root + "Chat";
60 
64  public const string DreamMaker = Root + nameof(Models.DreamMaker);
65 
69  public const string Jobs = Root + nameof(Models.Job);
70 
77  public static string SetID(string route, long id) => String.Format(CultureInfo.InvariantCulture, "{0}/{1}", route, id);
78 
84  public static string List(string route) => String.Format(CultureInfo.InvariantCulture, "{0}/List", route);
85  }
86 }
+
Routes to a server actions
Definition: Routes.cs:9
-
diff --git a/_semaphore_slim_context_8cs_source.html b/_semaphore_slim_context_8cs_source.html index 3eef49154f..3d5ad71763 100644 --- a/_semaphore_slim_context_8cs_source.html +++ b/_semaphore_slim_context_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
SemaphoreSlimContext.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Core
6 {
10  public sealed class SemaphoreSlimContext : IDisposable
11  {
18  public static async Task<SemaphoreSlimContext> Lock(SemaphoreSlim semaphore, CancellationToken cancellationToken)
19  {
20  if (semaphore == null)
21  throw new ArgumentNullException(nameof(semaphore));
22  await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
23  cancellationToken.ThrowIfCancellationRequested();
24  return new SemaphoreSlimContext(semaphore);
25  }
26 
30  readonly SemaphoreSlim lockedSemaphore;
31 
35  bool disposed;
36 
41  SemaphoreSlimContext(SemaphoreSlim lockedSemaphore) => this.lockedSemaphore = lockedSemaphore;
42 
46 #pragma warning disable CA1821 // Remove empty Finalizers //TODO remove this when https://github.com/dotnet/roslyn-analyzers/issues/1241 is fixed
47  ~SemaphoreSlimContext() => Dispose();
48 #pragma warning restore CA1821 // Remove empty Finalizers
49 
53  public void Dispose()
54  {
55  lock (this)
56  {
57  if (disposed)
58  return;
59  disposed = true;
60  }
61  GC.SuppressFinalize(this);
62  lockedSemaphore.Release();
63  }
64  }
65 }
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 
5 namespace Tgstation.Server.Host.Core
6 {
10  public sealed class SemaphoreSlimContext : IDisposable
11  {
18  public static async Task<SemaphoreSlimContext> Lock(SemaphoreSlim semaphore, CancellationToken cancellationToken)
19  {
20  if (semaphore == null)
21  throw new ArgumentNullException(nameof(semaphore));
22  await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
23  cancellationToken.ThrowIfCancellationRequested();
24  return new SemaphoreSlimContext(semaphore);
25  }
26 
30  readonly SemaphoreSlim lockedSemaphore;
31 
35  bool disposed;
36 
41  SemaphoreSlimContext(SemaphoreSlim lockedSemaphore) => this.lockedSemaphore = lockedSemaphore;
42 
46 #pragma warning disable CA1821 // Remove empty Finalizers //TODO remove this when https://github.com/dotnet/roslyn-analyzers/issues/1241 is fixed
47  ~SemaphoreSlimContext() => Dispose();
48 #pragma warning restore CA1821 // Remove empty Finalizers
49 
53  public void Dispose()
54  {
55  lock (this)
56  {
57  if (disposed)
58  return;
59  disposed = true;
60  }
61  GC.SuppressFinalize(this);
62  lockedSemaphore.Release();
63  }
64  }
65 }
+
readonly SemaphoreSlim lockedSemaphore
The locked SemaphoreSlim
-
void Dispose()
Release the lock on lockedSemaphore
static async Task< SemaphoreSlimContext > Lock(SemaphoreSlim semaphore, CancellationToken cancellationToken)
Asyncronously locks a semaphore
diff --git a/_server_8cs_source.html b/_server_8cs_source.html index 4a65b9ed24..865eac39ae 100644 --- a/_server_8cs_source.html +++ b/_server_8cs_source.html @@ -92,9 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Server.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Hosting;
2 using Microsoft.Extensions.DependencyInjection;
3 using System;
4 using System.IO;
5 using System.Diagnostics.CodeAnalysis;
6 using System.Threading;
7 using System.Threading.Tasks;
9 using Tgstation.Server.Host.IO;
10 
11 namespace Tgstation.Server.Host
12 {
14  sealed class Server : IServer, IServerControl
15  {
17  public bool RestartRequested { get; private set; }
18 
22  readonly IWebHostBuilder webHostBuilder;
23 
27  readonly SemaphoreSlim semaphore;
28 
32  readonly string updatePath;
33 
37  bool updated;
38 
42  CancellationTokenSource cancellationTokenSource;
43 
49  public Server(IWebHostBuilder webHostBuilder, string updatePath)
50  {
51  this.webHostBuilder = webHostBuilder ?? throw new ArgumentNullException(nameof(webHostBuilder));
52  this.updatePath = updatePath;
53 
54  semaphore = new SemaphoreSlim(1);
55  updated = false;
56  RestartRequested = false;
57  }
58 
60  public void Dispose() => semaphore.Dispose();
61 
63  [ExcludeFromCodeCoverage]
64  public async Task RunAsync(CancellationToken cancellationToken)
65  {
66  using (cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
67  using (var fsWatcher = updatePath != null ? new FileSystemWatcher(Path.GetDirectoryName(updatePath)) : null)
68  {
69  if (fsWatcher != null)
70  {
71  fsWatcher.Created += (a, b) =>
72  {
73  if (b.FullPath == updatePath && File.Exists(b.FullPath))
74  cancellationTokenSource.Cancel();
75  };
76  fsWatcher.EnableRaisingEvents = true;
77  }
78  using (var webHost = webHostBuilder
79  .UseStartup<Application>()
80  .ConfigureServices((serviceCollection) => serviceCollection.AddSingleton<IServerControl>(this))
81  .Build()
82  )
83  await webHost.RunAsync(cancellationTokenSource.Token).ConfigureAwait(false);
84  }
85  }
86 
88  public async Task<bool> ApplyUpdate(byte[] updateZipData, IIOManager ioManager, CancellationToken cancellationToken)
89  {
90  if (updatePath == null)
91  return false;
92  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
93  {
94  if (updated)
95  throw new InvalidOperationException("ApplyUpdate has already been called!");
96  updated = true;
97  try
98  {
99  await ioManager.ZipToDirectory(updatePath, updateZipData, cancellationToken).ConfigureAwait(false);
100  }
101  catch
102  {
103  try
104  {
105  //important to not leave this directory around if possible
106  await ioManager.DeleteDirectory(updatePath, default).ConfigureAwait(false);
107  }
108  catch { }
109  updated = false;
110  throw;
111  }
112  Restart();
113  return true;
114  }
115  }
116 
118  public void RegisterForRestart(Action action)
119  {
120  if (action == null)
121  throw new ArgumentNullException(nameof(action));
122  if (cancellationTokenSource == null)
123  throw new InvalidOperationException("Tried to register an update action on a non-running Server!");
124  cancellationTokenSource.Token.Register(() =>
125  {
126  if (RestartRequested)
127  action();
128  });
129  }
130 
132  public bool Restart()
133  {
134  if (updatePath == null)
135  return false;
136  if (cancellationTokenSource == null)
137  throw new InvalidOperationException("Tried to restart a non-running Server!");
138  RestartRequested = true;
139  cancellationTokenSource.Cancel();
140  return true;
141  }
142  }
143 }
Task DeleteDirectory(string path, CancellationToken cancellationToken)
Recursively delete a directory
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Hosting;
2 using Microsoft.Extensions.DependencyInjection;
3 using System;
4 using System.IO;
5 using System.Diagnostics.CodeAnalysis;
6 using System.Threading;
7 using System.Threading.Tasks;
9 using Tgstation.Server.Host.IO;
10 
11 namespace Tgstation.Server.Host
12 {
14  sealed class Server : IServer, IServerControl
15  {
17  public bool RestartRequested { get; private set; }
18 
22  readonly IWebHostBuilder webHostBuilder;
23 
27  readonly SemaphoreSlim semaphore;
28 
32  readonly string updatePath;
33 
37  bool updated;
38 
42  CancellationTokenSource cancellationTokenSource;
43 
49  public Server(IWebHostBuilder webHostBuilder, string updatePath)
50  {
51  this.webHostBuilder = webHostBuilder ?? throw new ArgumentNullException(nameof(webHostBuilder));
52  this.updatePath = updatePath;
53 
54  semaphore = new SemaphoreSlim(1);
55  updated = false;
56  RestartRequested = false;
57  }
58 
60  public void Dispose() => semaphore.Dispose();
61 
63  [ExcludeFromCodeCoverage]
64  public async Task RunAsync(CancellationToken cancellationToken)
65  {
66  using (cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
67  using (var fsWatcher = updatePath != null ? new FileSystemWatcher(Path.GetDirectoryName(updatePath)) : null)
68  {
69  if (fsWatcher != null)
70  {
71  fsWatcher.Created += (a, b) =>
72  {
73  if (b.FullPath == updatePath && File.Exists(b.FullPath))
74  cancellationTokenSource.Cancel();
75  };
76  fsWatcher.EnableRaisingEvents = true;
77  }
78  using (var webHost = webHostBuilder
79  .UseStartup<Application>()
80  .ConfigureServices((serviceCollection) => serviceCollection.AddSingleton<IServerControl>(this))
81  .Build()
82  )
83  await webHost.RunAsync(cancellationTokenSource.Token).ConfigureAwait(false);
84  }
85  }
86 
88  public async Task<bool> ApplyUpdate(byte[] updateZipData, IIOManager ioManager, CancellationToken cancellationToken)
89  {
90  if (updatePath == null)
91  return false;
92  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
93  {
94  if (updated)
95  throw new InvalidOperationException("ApplyUpdate has already been called!");
96  updated = true;
97  try
98  {
99  await ioManager.ZipToDirectory(updatePath, updateZipData, cancellationToken).ConfigureAwait(false);
100  }
101  catch
102  {
103  try
104  {
105  //important to not leave this directory around if possible
106  await ioManager.DeleteDirectory(updatePath, default).ConfigureAwait(false);
107  }
108  catch { }
109  updated = false;
110  throw;
111  }
112  Restart();
113  return true;
114  }
115  }
116 
118  public void RegisterForRestart(Action action)
119  {
120  if (action == null)
121  throw new ArgumentNullException(nameof(action));
122  if (cancellationTokenSource == null)
123  throw new InvalidOperationException("Tried to register an update action on a non-running Server!");
124  cancellationTokenSource.Token.Register(() =>
125  {
126  if (RestartRequested)
127  action();
128  });
129  }
130 
132  public bool Restart()
133  {
134  if (updatePath == null)
135  return false;
136  if (cancellationTokenSource == null)
137  throw new InvalidOperationException("Tried to restart a non-running Server!");
138  RestartRequested = true;
139  cancellationTokenSource.Cancel();
140  return true;
141  }
142  }
143 }
Task DeleteDirectory(string path, CancellationToken cancellationToken)
Recursively delete a directory
async Task RunAsync(CancellationToken cancellationToken)
Runs the IServer
Definition: Server.cs:64
void RegisterForRestart(Action action)
Register a given action to run before stopping the server for a restart
Definition: Server.cs:118
+
Task ZipToDirectory(string path, byte[] zipFileBytes, CancellationToken cancellationToken)
Extract a set of zipFileBytes to a given path
@@ -102,7 +103,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
bool Restart()
Restarts the Host
Definition: Server.cs:132
readonly string updatePath
The absolute path to install updates to
Definition: Server.cs:32
Represents the host
Definition: IServer.cs:10
-
Server(IWebHostBuilder webHostBuilder, string updatePath)
Construct a Server
Definition: Server.cs:49
CancellationTokenSource cancellationTokenSource
The cancellationTokenSource for the Server
Definition: Server.cs:42
bool updated
If a server update has been applied
Definition: Server.cs:37
diff --git a/_server_client_8cs_source.html b/_server_client_8cs_source.html index bc2b7d79e8..0ceaba669b 100644 --- a/_server_client_8cs_source.html +++ b/_server_client_8cs_source.html @@ -92,10 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ServerClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using Tgstation.Server.Api;
6 
7 namespace Tgstation.Server.Client
8 {
10  sealed class ServerClient : IServerClient
11  {
13  public Uri Url => apiClient.Url;
14 
16  public Token Token { get; }
17 
19  public TimeSpan Timeout
20  {
21  get => apiClient.Timeout;
22  set => apiClient.Timeout = value;
23  }
24 
26  public IInstanceManagerClient Instances { get; }
27 
30 
32  public IUsersClient Users { get; }
33 
38 
44  public ServerClient(IApiClient apiClient, Token token)
45  {
46  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
47  Token = token ?? throw new ArgumentNullException(nameof(token));
48 
49  if (Token.Bearer != apiClient.Headers.Token)
50  throw new ArgumentOutOfRangeException(nameof(token), token, "Provided token does not match apiClient headers!");
51 
52  Instances = new InstanceManagerClient(apiClient);
53  Users = new UsersClient(apiClient);
54  Administration = new AdministrationClient(apiClient);
55  }
56 
58  public void Dispose() => apiClient.Dispose();
59 
61  public Task<ServerInformation> Version(CancellationToken cancellationToken) => apiClient.Read<ServerInformation>(Routes.Root, cancellationToken);
62 
64  public void AddRequestLogger(IRequestLogger requestLogger) => apiClient.AddRequestLogger(requestLogger);
65  }
66 }
ServerClient(IApiClient apiClient, Token token)
Construct a ServerClient
Definition: ServerClient.cs:44
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using Tgstation.Server.Api;
6 
7 namespace Tgstation.Server.Client
8 {
10  sealed class ServerClient : IServerClient
11  {
13  public Uri Url => apiClient.Url;
14 
16  public Token Token { get; }
17 
19  public TimeSpan Timeout
20  {
21  get => apiClient.Timeout;
22  set => apiClient.Timeout = value;
23  }
24 
26  public IInstanceManagerClient Instances { get; }
27 
30 
32  public IUsersClient Users { get; }
33 
38 
44  public ServerClient(IApiClient apiClient, Token token)
45  {
46  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
47  Token = token ?? throw new ArgumentNullException(nameof(token));
48 
49  if (Token.Bearer != apiClient.Headers.Token)
50  throw new ArgumentOutOfRangeException(nameof(token), token, "Provided token does not match apiClient headers!");
51 
52  Instances = new InstanceManagerClient(apiClient);
53  Users = new UsersClient(apiClient);
54  Administration = new AdministrationClient(apiClient);
55  }
56 
58  public void Dispose() => apiClient.Dispose();
59 
61  public Task<ServerInformation> Version(CancellationToken cancellationToken) => apiClient.Read<ServerInformation>(Routes.Root, cancellationToken);
62 
64  public void AddRequestLogger(IRequestLogger requestLogger) => apiClient.AddRequestLogger(requestLogger);
65  }
66 }
ServerClient(IApiClient apiClient, Token token)
Construct a ServerClient
Definition: ServerClient.cs:44
+ @@ -108,7 +109,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Main client for communicating with a server
-
const string Root
The root controller
Definition: Routes.cs:14
Represents a JWT returned by the API
Definition: Token.cs:8
diff --git a/_server_client_factory_8cs_source.html b/_server_client_factory_8cs_source.html index 4c9f06191e..b5ab9c4e47 100644 --- a/_server_client_factory_8cs_source.html +++ b/_server_client_factory_8cs_source.html @@ -92,15 +92,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ServerClientFactory.cs
-Go to the documentation of this file.
1 using System;
2 using System.Net.Http.Headers;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client
9 {
12  {
16  static readonly IApiClientFactory apiClientFactory = new ApiClientFactory();
17 
21  readonly ProductHeaderValue productHeaderValue;
22 
27  public ServerClientFactory(ProductHeaderValue productHeaderValue)
28  {
29  this.productHeaderValue = productHeaderValue ?? throw new ArgumentNullException(nameof(productHeaderValue));
30  }
31 
33  public async Task<IServerClient> CreateServerClient(Uri host, string username, string password, TimeSpan timeout, CancellationToken cancellationToken)
34  {
35  if (host == null)
36  throw new ArgumentNullException(nameof(host));
37  if (username == null)
38  throw new ArgumentNullException(nameof(username));
39  if (password == null)
40  throw new ArgumentNullException(nameof(password));
41 
42  Token token;
43  using (var api = apiClientFactory.CreateApiClient(host, new ApiHeaders(productHeaderValue, username, password)))
44  {
45  if (timeout != default)
46  api.Timeout = timeout;
47  token = await api.Update<Token>(Routes.Root, cancellationToken).ConfigureAwait(false);
48  }
49  return CreateServerClient(host, token, timeout);
50  }
51 
53  public IServerClient CreateServerClient(Uri host, Token token, TimeSpan timeout)
54  {
55  if (host == null)
56  throw new ArgumentNullException(nameof(host));
57  if (token == null)
58  throw new ArgumentNullException(nameof(token));
59  var result = new ServerClient(apiClientFactory.CreateApiClient(host, new ApiHeaders(productHeaderValue, token.Bearer)), token);
60  if (timeout != default)
61  result.Timeout = timeout;
62  return result;
63  }
64  }
65 }
+Go to the documentation of this file.
1 using System;
2 using System.Net.Http.Headers;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client
9 {
12  {
16  static readonly IApiClientFactory apiClientFactory = new ApiClientFactory();
17 
21  readonly ProductHeaderValue productHeaderValue;
22 
27  public ServerClientFactory(ProductHeaderValue productHeaderValue)
28  {
29  this.productHeaderValue = productHeaderValue ?? throw new ArgumentNullException(nameof(productHeaderValue));
30  }
31 
33  public async Task<IServerClient> CreateServerClient(Uri host, string username, string password, TimeSpan timeout, CancellationToken cancellationToken)
34  {
35  if (host == null)
36  throw new ArgumentNullException(nameof(host));
37  if (username == null)
38  throw new ArgumentNullException(nameof(username));
39  if (password == null)
40  throw new ArgumentNullException(nameof(password));
41 
42  Token token;
43  using (var api = apiClientFactory.CreateApiClient(host, new ApiHeaders(productHeaderValue, username, password)))
44  {
45  if (timeout != default)
46  api.Timeout = timeout;
47  token = await api.Update<Token>(Routes.Root, cancellationToken).ConfigureAwait(false);
48  }
49  return CreateServerClient(host, token, timeout);
50  }
51 
53  public IServerClient CreateServerClient(Uri host, Token token, TimeSpan timeout)
54  {
55  if (host == null)
56  throw new ArgumentNullException(nameof(host));
57  if (token == null)
58  throw new ArgumentNullException(nameof(token));
59  var result = new ServerClient(apiClientFactory.CreateApiClient(host, new ApiHeaders(productHeaderValue, token.Bearer)), token);
60  if (timeout != default)
61  result.Timeout = timeout;
62  return result;
63  }
64  }
65 }
+
Represents the header that must be present for every server request
Definition: ApiHeaders.cs:16
Routes to a server actions
Definition: Routes.cs:9
Main client for communicating with a server
-
const string Root
The root controller
Definition: Routes.cs:14
Represents a JWT returned by the API
Definition: Token.cs:8
Factory for creating IServerClients
diff --git a/_server_error_exception_8cs_source.html b/_server_error_exception_8cs_source.html index 951656efff..d4ced35b44 100644 --- a/_server_error_exception_8cs_source.html +++ b/_server_error_exception_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ServerErrorException.cs
-Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
10  public sealed class ServerErrorException : ClientException
11  {
15  public string Html { get; }
16 
20  public ServerErrorException() { }
21 
26  public ServerErrorException(string html) : base(new ErrorMessage
27  {
28  Message = "An internal server error occurred!",
29  SeverApiVersion = null
30  }, HttpStatusCode.InternalServerError)
31  {
32  Html = html;
33  }
34 
40  public ServerErrorException(string message, Exception innerException) : base(message, innerException) { }
41  }
42 }
+Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
10  public sealed class ServerErrorException : ClientException
11  {
15  public string Html { get; }
16 
20  public ServerErrorException() { }
21 
26  public ServerErrorException(string html) : base(new ErrorMessage
27  {
28  Message = "An internal server error occurred!",
29  SeverApiVersion = null
30  }, HttpStatusCode.InternalServerError)
31  {
32  Html = html;
33  }
34 
40  public ServerErrorException(string message, Exception innerException) : base(message, innerException) { }
41  }
42 }
+
ServerErrorException(string message, Exception innerException)
Construct an ServerErrorException with a message and innerException
Occurs when an error occurs in the server
-
ServerErrorException(string html)
Construct an ServerErrorException with html
Represents an error message returned by the server
Definition: ErrorMessage.cs:8
diff --git a/_server_factory_8cs_source.html b/_server_factory_8cs_source.html index 2cdeed3f1c..b5999e6a48 100644 --- a/_server_factory_8cs_source.html +++ b/_server_factory_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ServerFactory.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore;
2 
3 namespace Tgstation.Server.Host
4 {
6  public sealed class ServerFactory : IServerFactory
7  {
9  public IServer CreateServer(string[] args, string updatePath) => new Server(WebHost.CreateDefaultBuilder(args), updatePath);
10  }
11 }
+Go to the documentation of this file.
1 using Microsoft.AspNetCore;
2 
3 namespace Tgstation.Server.Host
4 {
6  public sealed class ServerFactory : IServerFactory
7  {
9  public IServer CreateServer(string[] args, string updatePath) => new Server(WebHost.CreateDefaultBuilder(args), updatePath);
10  }
11 }
+
Represents the host
Definition: IServer.cs:10
-
diff --git a/_server_information_8cs_source.html b/_server_information_8cs_source.html index 2934ed26fc..b7ba37a7c3 100644 --- a/_server_information_8cs_source.html +++ b/_server_information_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ServerInformation.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public sealed class ServerInformation
9  {
13  public Version Version { get; set; }
14 
18  public Version ApiVersion { get; set; }
19  }
20 }
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public sealed class ServerInformation
9  {
13  public Version Version { get; set; }
14 
18  public Version ApiVersion { get; set; }
19  }
20 }
+
Represents basic server information
-
diff --git a/_server_service_8cs_source.html b/_server_service_8cs_source.html index 68c61e63af..698a5b3d95 100644 --- a/_server_service_8cs_source.html +++ b/_server_service_8cs_source.html @@ -92,7 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ServerService.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using Microsoft.Extensions.Logging.EventLog;
3 using Microsoft.Extensions.Logging.EventLog.Internal;
4 using System;
5 using System.Diagnostics;
6 using System.Diagnostics.CodeAnalysis;
7 using System.ServiceProcess;
8 using System.Threading;
9 using System.Threading.Tasks;
11 
12 namespace Tgstation.Server.Host.Service
13 {
18  {
22  public const string Name = "tgstation-server-4";
23 
27  readonly IWatchdog watchdog;
28 
33 
37  CancellationTokenSource cancellationTokenSource;
38 
44  public ServerService(IWatchdogFactory watchdogFactory, ILoggerFactory loggerFactory)
45  {
46  if (watchdogFactory == null)
47  throw new ArgumentNullException(nameof(watchdogFactory));
48  if (loggerFactory == null)
49  throw new ArgumentNullException(nameof(loggerFactory));
50 
51  loggerFactory.AddEventLog(new EventLogSettings
52  {
53  EventLog = this
54  });
55 
56  ServiceName = Name;
57  watchdog = watchdogFactory.CreateWatchdog(loggerFactory);
58  }
59 
61  public int MaxMessageSize => (int)EventLog.MaximumKilobytes * 1024;
62 
64  public void WriteEntry(string message, EventLogEntryType type, int eventID, short category) => EventLog.WriteEntry(message, type, eventID, category);
65 
67  [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "cancellationTokenSource")]
68  protected override void Dispose(bool disposing)
69  {
70  cancellationTokenSource?.Dispose();
71  base.Dispose(disposing);
72  }
73 
75  protected override void OnStart(string[] args)
76  {
77  cancellationTokenSource?.Dispose();
78  cancellationTokenSource = new CancellationTokenSource();
79  watchdogTask = watchdog.RunAsync(args, cancellationTokenSource.Token);
80  }
81 
83  protected override void OnStop()
84  {
85  cancellationTokenSource.Cancel();
86  watchdogTask.GetAwaiter().GetResult();
87  }
88  }
89 }
IWatchdog CreateWatchdog(ILoggerFactory loggerFactory)
Create a IWatchdog
+Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using Microsoft.Extensions.Logging.EventLog;
3 using Microsoft.Extensions.Logging.EventLog.Internal;
4 using System;
5 using System.Diagnostics;
6 using System.Diagnostics.CodeAnalysis;
7 using System.ServiceProcess;
8 using System.Threading;
9 using System.Threading.Tasks;
11 
12 namespace Tgstation.Server.Host.Service
13 {
18  {
22  public const string Name = "tgstation-server-4";
23 
27  readonly IWatchdog watchdog;
28 
33 
37  CancellationTokenSource cancellationTokenSource;
38 
44  public ServerService(IWatchdogFactory watchdogFactory, ILoggerFactory loggerFactory)
45  {
46  if (watchdogFactory == null)
47  throw new ArgumentNullException(nameof(watchdogFactory));
48  if (loggerFactory == null)
49  throw new ArgumentNullException(nameof(loggerFactory));
50 
51  loggerFactory.AddEventLog(new EventLogSettings
52  {
53  EventLog = this
54  });
55 
56  ServiceName = Name;
57  watchdog = watchdogFactory.CreateWatchdog(loggerFactory);
58  }
59 
61  public int MaxMessageSize => (int)EventLog.MaximumKilobytes * 1024;
62 
64  public void WriteEntry(string message, EventLogEntryType type, int eventID, short category) => EventLog.WriteEntry(message, type, eventID, category);
65 
67  [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "cancellationTokenSource")]
68  protected override void Dispose(bool disposing)
69  {
70  cancellationTokenSource?.Dispose();
71  base.Dispose(disposing);
72  }
73 
75  protected override void OnStart(string[] args)
76  {
77  cancellationTokenSource?.Dispose();
78  cancellationTokenSource = new CancellationTokenSource();
79  watchdogTask = watchdog.RunAsync(args, cancellationTokenSource.Token);
80  }
81 
83  protected override void OnStop()
84  {
85  cancellationTokenSource.Cancel();
86  watchdogTask.GetAwaiter().GetResult();
87  }
88  }
89 }
IWatchdog CreateWatchdog(ILoggerFactory loggerFactory)
Create a IWatchdog
+
override void OnStart(string[] args)
Task watchdogTask
The Task recieved from IWatchdog.RunAsync(string[], CancellationToken) of watchdog ...
@@ -100,7 +101,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ServerService(IWatchdogFactory watchdogFactory, ILoggerFactory loggerFactory)
Construct a ServerService
-
The watchdog for a Host
Definition: IWatchdog.cs:9
readonly IWatchdog watchdog
The IWatchdog for the ServerService
diff --git a/_server_side_modifications_8cs_source.html b/_server_side_modifications_8cs_source.html index c0fec69361..a5e3c126bb 100644 --- a/_server_side_modifications_8cs_source.html +++ b/_server_side_modifications_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ServerSideModifications.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Components.StaticFiles
2 {
6  public sealed class ServerSideModifications
7  {
11  public bool TotalDmeOverwrite { get; }
12 
16  public string HeadIncludeLine { get; }
17 
21  public string TailIncludeLine { get; }
22 
29  public ServerSideModifications(string headIncludeLine, string tailIncludeLine, bool totalDmeOverwrite)
30  {
31  HeadIncludeLine = headIncludeLine;
32  TailIncludeLine = tailIncludeLine;
33  TotalDmeOverwrite = totalDmeOverwrite;
34  }
35  }
36 }
ServerSideModifications(string headIncludeLine, string tailIncludeLine, bool totalDmeOverwrite)
Construct ServerSideModifications
- +Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Components.StaticFiles
2 {
6  public sealed class ServerSideModifications
7  {
11  public bool TotalDmeOverwrite { get; }
12 
16  public string HeadIncludeLine { get; }
17 
21  public string TailIncludeLine { get; }
22 
29  public ServerSideModifications(string headIncludeLine, string tailIncludeLine, bool totalDmeOverwrite)
30  {
31  HeadIncludeLine = headIncludeLine;
32  TailIncludeLine = tailIncludeLine;
33  TotalDmeOverwrite = totalDmeOverwrite;
34  }
35  }
36 }
+
ServerSideModifications(string headIncludeLine, string tailIncludeLine, bool totalDmeOverwrite)
Construct ServerSideModifications
diff --git a/_service_unavailable_exception_8cs_source.html b/_service_unavailable_exception_8cs_source.html index 33e18abff1..9308fa3e3c 100644 --- a/_service_unavailable_exception_8cs_source.html +++ b/_service_unavailable_exception_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ServiceUnavailableException.cs
-Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
11  {
16  {
17  Message = "The server is currently starting or stopping and is unable to process the request!",
18  SeverApiVersion = null
19  }, HttpStatusCode.ServiceUnavailable)
20  { }
21 
26  public ServiceUnavailableException(string message) : base(message) { }
27 
33  public ServiceUnavailableException(string message, Exception innerException) : base(message, innerException) { }
34  }
35 }
+Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
11  {
16  {
17  Message = "The server is currently starting or stopping and is unable to process the request!",
18  SeverApiVersion = null
19  }, HttpStatusCode.ServiceUnavailable)
20  { }
21 
26  public ServiceUnavailableException(string message) : base(message) { }
27 
33  public ServiceUnavailableException(string message, Exception innerException) : base(message, innerException) { }
34  }
35 }
+
Occurs when the client makes a request while the server is starting or stopping
ServiceUnavailableException(string message)
Construct an ServiceUnavailableException with a message
-
ServiceUnavailableException(string message, Exception innerException)
Construct an ServiceUnavailableException with a message and innerException
ServiceUnavailableException()
Construct an ServiceUnavailableException
diff --git a/_session_controller_8cs_source.html b/_session_controller_8cs_source.html index 5f7a00b229..aa8c909d13 100644 --- a/_session_controller_8cs_source.html +++ b/_session_controller_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
SessionController.cs
-Go to the documentation of this file.
1 using Byond.TopicSender;
2 using Microsoft.Extensions.Logging;
3 using Newtonsoft.Json;
4 using System;
5 using System.Collections.Generic;
6 using System.Globalization;
7 using System.Net;
8 using System.Threading;
9 using System.Threading.Tasks;
15 
16 namespace Tgstation.Server.Host.Components.Watchdog
17 {
20  {
22  public bool IsPrimary
23  {
24  get
25  {
26  CheckDisposed();
27  return reattachInformation.IsPrimary;
28  }
29  }
30 
33  {
34  get
35  {
36  if (!Lifetime.IsCompleted)
37  throw new InvalidOperationException("ApiValidated cannot be checked while Lifetime is incomplete!");
38  return apiValidationStatus;
39  }
40  }
41 
43  public IDmbProvider Dmb
44  {
45  get
46  {
47  CheckDisposed();
48  return reattachInformation.Dmb;
49  }
50  }
51 
53  public ushort? Port
54  {
55  get
56  {
57  CheckDisposed();
58  if (portClosedForReboot)
59  return null;
60  return reattachInformation.Port;
61  }
62  }
63 
66  {
67  get
68  {
69  CheckDisposed();
70  return reattachInformation.RebootState;
71  }
72  }
73 
75  public bool ClosePortOnReboot { get; set; }
76 
78  public bool TerminationWasRequested { get; private set; }
79 
81  public Task<LaunchResult> LaunchResult { get; }
82 
84  public Task<int> Lifetime => process.Lifetime;
85 
87  public Task OnReboot => rebootTcs.Task;
88 
93 
97  readonly IByondTopicSender byondTopicSender;
98 
103 
107  readonly IProcess process;
108 
113 
118 
122  readonly IChat chat;
123 
127  readonly ILogger<SessionController> logger;
128 
133 
137  TaskCompletionSource<bool> portAssignmentTcs;
141  ushort? nextPort;
142 
146  TaskCompletionSource<object> rebootTcs;
147 
152 
156  bool disposed;
157 
162 
166  bool released;
167 
181  public SessionController(ReattachInformation reattachInformation, IProcess process, IByondExecutableLock byondLock, IByondTopicSender byondTopicSender, IJsonTrackingContext chatJsonTrackingContext, ICommContext interopContext, IChat chat, ILogger<SessionController> logger, DreamDaemonSecurity? launchSecurityLevel, uint? startupTimeout)
182  {
183  this.chatJsonTrackingContext = chatJsonTrackingContext; //null valid
184  this.reattachInformation = reattachInformation ?? throw new ArgumentNullException(nameof(reattachInformation));
185  this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender));
186  this.process = process ?? throw new ArgumentNullException(nameof(process));
187  this.byondLock = byondLock ?? throw new ArgumentNullException(nameof(byondLock));
188  this.interopContext = interopContext ?? throw new ArgumentNullException(nameof(interopContext));
189  this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
190  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
191 
192  this.launchSecurityLevel = launchSecurityLevel;
193 
194  interopContext.RegisterHandler(this);
195 
196  portClosedForReboot = false;
197  disposed = false;
198  apiValidationStatus = ApiValidationStatus.NeverValidated;
199  released = false;
200 
201  rebootTcs = new TaskCompletionSource<object>();
202 
203  async Task<LaunchResult> GetLaunchResult()
204  {
205  var startTime = DateTimeOffset.Now;
206  Task toAwait = process.Startup;
207 
208  if (startupTimeout.HasValue)
209  toAwait = Task.WhenAny(process.Startup, Task.Delay(startTime.AddSeconds(startupTimeout.Value) - startTime));
210 
211  await toAwait.ConfigureAwait(false);
212 
213  var result = new LaunchResult
214  {
215  ExitCode = process.Lifetime.IsCompleted ? (int?)await process.Lifetime.ConfigureAwait(false) : null,
216  StartupTime = process.Startup.IsCompleted ? (TimeSpan?)(DateTimeOffset.Now - startTime) : null
217  };
218  return result;
219  };
220  LaunchResult = GetLaunchResult();
221  }
222 
227 #pragma warning disable CA1821 // Remove empty Finalizers //TODO remove this when https://github.com/dotnet/roslyn-analyzers/issues/1241 is fixed
228  ~SessionController() => Dispose(false);
229 #pragma warning restore CA1821 // Remove empty Finalizers
230 
232  public void Dispose()
233  {
234  Dispose(true);
235  GC.SuppressFinalize(this);
236  }
237 
239  void Dispose(bool disposing)
240  {
241  lock (this)
242  {
243  if (disposed)
244  return;
245  if (disposing)
246  {
247  if (!released)
248  {
249  process.Terminate();
250  byondLock.Dispose();
251  }
252  process.Dispose();
253  interopContext.Dispose();
254  Dmb?.Dispose(); //will be null when released
255  chatJsonTrackingContext.Dispose();
256  disposed = true;
257  }
258  else
259  {
260  if (logger != null)
261  logger.LogError("Being disposed via finalizer!");
262  if (!released)
263  if (process != null)
264  process.Terminate();
265  else if (logger != null)
266  logger.LogCritical("Unable to terminate active DreamDaemon session due to finalizer ordering!");
267  }
268  }
269  }
270 
272  public async Task HandleInterop(CommCommand command, CancellationToken cancellationToken)
273  {
274  if (command == null)
275  throw new ArgumentNullException(nameof(command));
276 
277  var query = command.Parameters;
278 
279  object content;
280  Action postRespond = null;
281  if (query.TryGetValue(Constants.DMParameterCommand, out var method))
282  {
283  content = new object();
284  switch (method)
285  {
287  //currently unused, maybe in the future
288  break;
290  TerminationWasRequested = true;
291  process.Terminate();
292  return;
294  lock (this)
295  {
296  if (!query.TryGetValue(Constants.DMParameterData, out var stringPort) || !UInt16.TryParse(stringPort, out var currentPort))
297  {
299  logger.LogWarning("DreamDaemon sent new port command without providing it's own!");
300  content = new ErrorMessage { Message = "Missing stringified port as data parameter!" };
301  break;
302  }
303 
304  if (!nextPort.HasValue)
305  //not ready yet, so what we'll do is accept the random port DD opened on for now and change it later when we decide to
306  reattachInformation.Port = currentPort;
307  else
308  {
309  //nextPort is ready, tell DD to switch to that
310  //if it fails it'll kill itself
311  content = new Dictionary<string, ushort> { { Constants.DMParameterData, nextPort.Value } };
312  reattachInformation.Port = nextPort.Value;
313  nextPort = null;
314 
315  //we'll also get here from SetPort so complete that task
316  var tmpTcs = portAssignmentTcs;
317  portAssignmentTcs = null;
318  if (tmpTcs != null)
319  postRespond = () => tmpTcs.SetResult(true);
320  }
321 
322  portClosedForReboot = false;
323  }
324  break;
326  if (!launchSecurityLevel.HasValue)
327  {
328  logger.LogWarning("DreamDaemon requested API validation but no intial security level was passed to the session controller!");
329  apiValidationStatus = ApiValidationStatus.UnaskedValidationRequest;
330  content = new ErrorMessage { Message = "Invalid API validation request!" };
331  break;
332  }
333  if (!query.TryGetValue(Constants.DMParameterData, out var stringMinimumSecurityLevel) || !Enum.TryParse<DreamDaemonSecurity>(stringMinimumSecurityLevel, out var minimumSecurityLevel))
334  apiValidationStatus = ApiValidationStatus.BadValidationRequest;
335  else
336  switch (minimumSecurityLevel)
337  {
338  case DreamDaemonSecurity.Safe:
339  apiValidationStatus = ApiValidationStatus.RequiresSafe;
340  break;
341  case DreamDaemonSecurity.Ultrasafe:
342  apiValidationStatus = ApiValidationStatus.RequiresUltrasafe;
343  break;
344  case DreamDaemonSecurity.Trusted:
345  apiValidationStatus = ApiValidationStatus.RequiresTrusted;
346  break;
347  default:
348  throw new InvalidOperationException("Enum.TryParse failed to validate the DreamDaemonSecurity range!");
349  }
350  break;
352  if (ClosePortOnReboot)
353  {
354  content = new Dictionary<string, int> { { Constants.DMParameterData, 0 } };
355  portClosedForReboot = true;
356  }
357  var oldTcs = rebootTcs;
358  rebootTcs = new TaskCompletionSource<object>();
359  postRespond = () => oldTcs.SetResult(null);
360  break;
361  default:
362  content = new ErrorMessage { Message = "Requested command not supported!" };
363  break;
364  }
365  }
366  else
367  content = new ErrorMessage { Message = "Missing command parameter!" };
368 
369  var json = JsonConvert.SerializeObject(content);
370  var response = await SendCommand(String.Format(CultureInfo.InvariantCulture, "{0}&{1}={2}", byondTopicSender.SanitizeString(Constants.DMTopicInteropResponse), byondTopicSender.SanitizeString(Constants.DMParameterData), byondTopicSender.SanitizeString(json)), cancellationToken).ConfigureAwait(false);
371 
372  if (response != Constants.DMResponseSuccess)
373  logger.LogWarning("Recieved error response while responding to interop: {0}", response);
374 
375  postRespond?.Invoke();
376  }
377 
382  {
383  if (disposed)
384  throw new ObjectDisposedException(nameof(SessionController));
385  }
386 
389  {
390  CheckDisposed();
391  //we still don't want to dispose the dmb yet, even though we're keeping it alive
392  var tmpProvider = reattachInformation.Dmb;
393  reattachInformation.Dmb = null;
394  released = true;
395  Dispose();
396  Dmb.KeepAlive();
397  reattachInformation.Dmb = tmpProvider;
398  return reattachInformation;
399  }
400 
402  public async Task<string> SendCommand(string command, CancellationToken cancellationToken)
403  {
404  try
405  {
406  var commandString = String.Format(CultureInfo.InvariantCulture,
407  "?{0}={1}&{2}={3}",
408  byondTopicSender.SanitizeString(Constants.DMInteropAccessIdentifier),
409  byondTopicSender.SanitizeString(reattachInformation.AccessIdentifier),
410  byondTopicSender.SanitizeString(Constants.DMParameterCommand),
411  //intentionally don't sanitize command, that's up to the caller
412  command);
413 
414  logger.LogTrace("Export to :{0}. Query: {1}", reattachInformation.Port, commandString);
415 
416  return await byondTopicSender.SendTopic(
417  new IPEndPoint(IPAddress.Loopback, reattachInformation.Port),
418  commandString,
419  cancellationToken).ConfigureAwait(false);
420  }
421  catch (Exception e)
422  {
423  logger.LogInformation("Send command exception:{0}{1}", Environment.NewLine, e.Message);
424  return null;
425  }
426  }
427 
429  public Task<bool> SetPort(ushort port, CancellationToken cancellationToken)
430  {
431  CheckDisposed();
432 
433  if (port == 0)
434  throw new ArgumentOutOfRangeException(nameof(port), port, "port must not be zero!");
435 
436  async Task<bool> ImmediateTopicPortChange()
437  {
438  var commandResult = await SendCommand(String.Format(CultureInfo.InvariantCulture, "{0}&{1}={2}", byondTopicSender.SanitizeString(Constants.DMTopicChangePort), byondTopicSender.SanitizeString(Constants.DMParameterData), byondTopicSender.SanitizeString(port.ToString(CultureInfo.InvariantCulture))), cancellationToken).ConfigureAwait(false);
439 
440  if (commandResult != Constants.DMResponseSuccess)
441  {
442  logger.LogWarning("Failed port change! DD says: {0}", commandResult);
443  return false;
444  }
445 
446  return true;
447  }
448 
449  lock (this)
450  if (portClosedForReboot)
451  {
452  if (portAssignmentTcs != null)
453  throw new InvalidOperationException("A port change operation is already in progress!");
454  nextPort = port;
455  portAssignmentTcs = new TaskCompletionSource<bool>();
456  return portAssignmentTcs.Task;
457  }
458  else
459  return ImmediateTopicPortChange();
460  }
461 
463  public async Task<bool> SetRebootState(RebootState newRebootState, CancellationToken cancellationToken)
464  {
465  if (RebootState == newRebootState)
466  return true;
467  reattachInformation.RebootState = newRebootState;
468  return await SendCommand(String.Format(CultureInfo.InvariantCulture, "{0}&{1}={2}", byondTopicSender.SanitizeString(Constants.DMTopicChangeReboot), byondTopicSender.SanitizeString(Constants.DMParameterData), (int)newRebootState), cancellationToken).ConfigureAwait(false) == Constants.DMResponseSuccess;
469  }
470 
472  public void ResetRebootState()
473  {
474  CheckDisposed();
475  reattachInformation.RebootState = RebootState.Normal;
476  }
477 
479  public void SetHighPriority() => process.SetHighPriority();
480  }
481 }
+Go to the documentation of this file.
1 using Byond.TopicSender;
2 using Microsoft.Extensions.Logging;
3 using Newtonsoft.Json;
4 using System;
5 using System.Collections.Generic;
6 using System.Globalization;
7 using System.Net;
8 using System.Threading;
9 using System.Threading.Tasks;
15 
16 namespace Tgstation.Server.Host.Components.Watchdog
17 {
20  {
22  public bool IsPrimary
23  {
24  get
25  {
26  CheckDisposed();
27  return reattachInformation.IsPrimary;
28  }
29  }
30 
33  {
34  get
35  {
36  if (!Lifetime.IsCompleted)
37  throw new InvalidOperationException("ApiValidated cannot be checked while Lifetime is incomplete!");
38  return apiValidationStatus;
39  }
40  }
41 
43  public IDmbProvider Dmb
44  {
45  get
46  {
47  CheckDisposed();
48  return reattachInformation.Dmb;
49  }
50  }
51 
53  public ushort? Port
54  {
55  get
56  {
57  CheckDisposed();
58  if (portClosedForReboot)
59  return null;
60  return reattachInformation.Port;
61  }
62  }
63 
66  {
67  get
68  {
69  CheckDisposed();
70  return reattachInformation.RebootState;
71  }
72  }
73 
75  public bool ClosePortOnReboot { get; set; }
76 
78  public bool TerminationWasRequested { get; private set; }
79 
81  public Task<LaunchResult> LaunchResult { get; }
82 
84  public Task<int> Lifetime => process.Lifetime;
85 
87  public Task OnReboot => rebootTcs.Task;
88 
93 
97  readonly IByondTopicSender byondTopicSender;
98 
103 
107  readonly IProcess process;
108 
113 
118 
122  readonly IChat chat;
123 
127  readonly ILogger<SessionController> logger;
128 
133 
137  TaskCompletionSource<bool> portAssignmentTcs;
141  ushort? nextPort;
142 
146  TaskCompletionSource<object> rebootTcs;
147 
152 
156  bool disposed;
157 
162 
166  bool released;
167 
181  public SessionController(ReattachInformation reattachInformation, IProcess process, IByondExecutableLock byondLock, IByondTopicSender byondTopicSender, IJsonTrackingContext chatJsonTrackingContext, ICommContext interopContext, IChat chat, ILogger<SessionController> logger, DreamDaemonSecurity? launchSecurityLevel, uint? startupTimeout)
182  {
183  this.chatJsonTrackingContext = chatJsonTrackingContext; //null valid
184  this.reattachInformation = reattachInformation ?? throw new ArgumentNullException(nameof(reattachInformation));
185  this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender));
186  this.process = process ?? throw new ArgumentNullException(nameof(process));
187  this.byondLock = byondLock ?? throw new ArgumentNullException(nameof(byondLock));
188  this.interopContext = interopContext ?? throw new ArgumentNullException(nameof(interopContext));
189  this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
190  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
191 
192  this.launchSecurityLevel = launchSecurityLevel;
193 
194  interopContext.RegisterHandler(this);
195 
196  portClosedForReboot = false;
197  disposed = false;
198  apiValidationStatus = ApiValidationStatus.NeverValidated;
199  released = false;
200 
201  rebootTcs = new TaskCompletionSource<object>();
202 
203  async Task<LaunchResult> GetLaunchResult()
204  {
205  var startTime = DateTimeOffset.Now;
206  Task toAwait = process.Startup;
207 
208  if (startupTimeout.HasValue)
209  toAwait = Task.WhenAny(process.Startup, Task.Delay(startTime.AddSeconds(startupTimeout.Value) - startTime));
210 
211  await toAwait.ConfigureAwait(false);
212 
213  var result = new LaunchResult
214  {
215  ExitCode = process.Lifetime.IsCompleted ? (int?)await process.Lifetime.ConfigureAwait(false) : null,
216  StartupTime = process.Startup.IsCompleted ? (TimeSpan?)(DateTimeOffset.Now - startTime) : null
217  };
218  return result;
219  };
220  LaunchResult = GetLaunchResult();
221  }
222 
227 #pragma warning disable CA1821 // Remove empty Finalizers //TODO remove this when https://github.com/dotnet/roslyn-analyzers/issues/1241 is fixed
228  ~SessionController() => Dispose(false);
229 #pragma warning restore CA1821 // Remove empty Finalizers
230 
232  public void Dispose()
233  {
234  Dispose(true);
235  GC.SuppressFinalize(this);
236  }
237 
239  void Dispose(bool disposing)
240  {
241  lock (this)
242  {
243  if (disposed)
244  return;
245  if (disposing)
246  {
247  if (!released)
248  {
249  process.Terminate();
250  byondLock.Dispose();
251  }
252  process.Dispose();
253  interopContext.Dispose();
254  Dmb?.Dispose(); //will be null when released
255  chatJsonTrackingContext.Dispose();
256  disposed = true;
257  }
258  else
259  {
260  if (logger != null)
261  logger.LogError("Being disposed via finalizer!");
262  if (!released)
263  if (process != null)
264  process.Terminate();
265  else if (logger != null)
266  logger.LogCritical("Unable to terminate active DreamDaemon session due to finalizer ordering!");
267  }
268  }
269  }
270 
272  public async Task HandleInterop(CommCommand command, CancellationToken cancellationToken)
273  {
274  if (command == null)
275  throw new ArgumentNullException(nameof(command));
276 
277  var query = command.Parameters;
278 
279  object content;
280  Action postRespond = null;
281  if (query.TryGetValue(Constants.DMParameterCommand, out var method))
282  {
283  content = new object();
284  switch (method)
285  {
287  //currently unused, maybe in the future
288  break;
290  TerminationWasRequested = true;
291  process.Terminate();
292  return;
294  lock (this)
295  {
296  if (!query.TryGetValue(Constants.DMParameterData, out var stringPort) || !UInt16.TryParse(stringPort, out var currentPort))
297  {
299  logger.LogWarning("DreamDaemon sent new port command without providing it's own!");
300  content = new ErrorMessage { Message = "Missing stringified port as data parameter!" };
301  break;
302  }
303 
304  if (!nextPort.HasValue)
305  //not ready yet, so what we'll do is accept the random port DD opened on for now and change it later when we decide to
306  reattachInformation.Port = currentPort;
307  else
308  {
309  //nextPort is ready, tell DD to switch to that
310  //if it fails it'll kill itself
311  content = new Dictionary<string, ushort> { { Constants.DMParameterData, nextPort.Value } };
312  reattachInformation.Port = nextPort.Value;
313  nextPort = null;
314 
315  //we'll also get here from SetPort so complete that task
316  var tmpTcs = portAssignmentTcs;
317  portAssignmentTcs = null;
318  if (tmpTcs != null)
319  postRespond = () => tmpTcs.SetResult(true);
320  }
321 
322  portClosedForReboot = false;
323  }
324  break;
326  if (!launchSecurityLevel.HasValue)
327  {
328  logger.LogWarning("DreamDaemon requested API validation but no intial security level was passed to the session controller!");
329  apiValidationStatus = ApiValidationStatus.UnaskedValidationRequest;
330  content = new ErrorMessage { Message = "Invalid API validation request!" };
331  break;
332  }
333  if (!query.TryGetValue(Constants.DMParameterData, out var stringMinimumSecurityLevel) || !Enum.TryParse<DreamDaemonSecurity>(stringMinimumSecurityLevel, out var minimumSecurityLevel))
334  apiValidationStatus = ApiValidationStatus.BadValidationRequest;
335  else
336  switch (minimumSecurityLevel)
337  {
338  case DreamDaemonSecurity.Safe:
339  apiValidationStatus = ApiValidationStatus.RequiresSafe;
340  break;
341  case DreamDaemonSecurity.Ultrasafe:
342  apiValidationStatus = ApiValidationStatus.RequiresUltrasafe;
343  break;
344  case DreamDaemonSecurity.Trusted:
345  apiValidationStatus = ApiValidationStatus.RequiresTrusted;
346  break;
347  default:
348  throw new InvalidOperationException("Enum.TryParse failed to validate the DreamDaemonSecurity range!");
349  }
350  break;
352  if (ClosePortOnReboot)
353  {
354  content = new Dictionary<string, int> { { Constants.DMParameterData, 0 } };
355  portClosedForReboot = true;
356  }
357  var oldTcs = rebootTcs;
358  rebootTcs = new TaskCompletionSource<object>();
359  postRespond = () => oldTcs.SetResult(null);
360  break;
361  default:
362  content = new ErrorMessage { Message = "Requested command not supported!" };
363  break;
364  }
365  }
366  else
367  content = new ErrorMessage { Message = "Missing command parameter!" };
368 
369  var json = JsonConvert.SerializeObject(content);
370  var response = await SendCommand(String.Format(CultureInfo.InvariantCulture, "{0}&{1}={2}", byondTopicSender.SanitizeString(Constants.DMTopicInteropResponse), byondTopicSender.SanitizeString(Constants.DMParameterData), byondTopicSender.SanitizeString(json)), cancellationToken).ConfigureAwait(false);
371 
372  if (response != Constants.DMResponseSuccess)
373  logger.LogWarning("Recieved error response while responding to interop: {0}", response);
374 
375  postRespond?.Invoke();
376  }
377 
382  {
383  if (disposed)
384  throw new ObjectDisposedException(nameof(SessionController));
385  }
386 
389  {
390  CheckDisposed();
391  //we still don't want to dispose the dmb yet, even though we're keeping it alive
392  var tmpProvider = reattachInformation.Dmb;
393  reattachInformation.Dmb = null;
394  released = true;
395  Dispose();
396  Dmb.KeepAlive();
397  reattachInformation.Dmb = tmpProvider;
398  return reattachInformation;
399  }
400 
402  public async Task<string> SendCommand(string command, CancellationToken cancellationToken)
403  {
404  try
405  {
406  var commandString = String.Format(CultureInfo.InvariantCulture,
407  "?{0}={1}&{2}={3}",
408  byondTopicSender.SanitizeString(Constants.DMInteropAccessIdentifier),
409  byondTopicSender.SanitizeString(reattachInformation.AccessIdentifier),
410  byondTopicSender.SanitizeString(Constants.DMParameterCommand),
411  //intentionally don't sanitize command, that's up to the caller
412  command);
413 
414  logger.LogTrace("Export to :{0}. Query: {1}", reattachInformation.Port, commandString);
415 
416  return await byondTopicSender.SendTopic(
417  new IPEndPoint(IPAddress.Loopback, reattachInformation.Port),
418  commandString,
419  cancellationToken).ConfigureAwait(false);
420  }
421  catch (Exception e)
422  {
423  logger.LogInformation("Send command exception:{0}{1}", Environment.NewLine, e.Message);
424  return null;
425  }
426  }
427 
429  public Task<bool> SetPort(ushort port, CancellationToken cancellationToken)
430  {
431  CheckDisposed();
432 
433  if (port == 0)
434  throw new ArgumentOutOfRangeException(nameof(port), port, "port must not be zero!");
435 
436  async Task<bool> ImmediateTopicPortChange()
437  {
438  var commandResult = await SendCommand(String.Format(CultureInfo.InvariantCulture, "{0}&{1}={2}", byondTopicSender.SanitizeString(Constants.DMTopicChangePort), byondTopicSender.SanitizeString(Constants.DMParameterData), byondTopicSender.SanitizeString(port.ToString(CultureInfo.InvariantCulture))), cancellationToken).ConfigureAwait(false);
439 
440  if (commandResult != Constants.DMResponseSuccess)
441  {
442  logger.LogWarning("Failed port change! DD says: {0}", commandResult);
443  return false;
444  }
445 
446  return true;
447  }
448 
449  lock (this)
450  if (portClosedForReboot)
451  {
452  if (portAssignmentTcs != null)
453  throw new InvalidOperationException("A port change operation is already in progress!");
454  nextPort = port;
455  portAssignmentTcs = new TaskCompletionSource<bool>();
456  return portAssignmentTcs.Task;
457  }
458  else
459  return ImmediateTopicPortChange();
460  }
461 
463  public async Task<bool> SetRebootState(RebootState newRebootState, CancellationToken cancellationToken)
464  {
465  if (RebootState == newRebootState)
466  return true;
467  reattachInformation.RebootState = newRebootState;
468  return await SendCommand(String.Format(CultureInfo.InvariantCulture, "{0}&{1}={2}", byondTopicSender.SanitizeString(Constants.DMTopicChangeReboot), byondTopicSender.SanitizeString(Constants.DMParameterData), (int)newRebootState), cancellationToken).ConfigureAwait(false) == Constants.DMResponseSuccess;
469  }
470 
472  public void ResetRebootState()
473  {
474  CheckDisposed();
475  reattachInformation.RebootState = RebootState.Normal;
476  }
477 
479  public void SetHighPriority() => process.SetHighPriority();
480  }
481 }
bool disposed
If the SessionController has been disposed
@@ -103,6 +103,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Task< bool > SetPort(ushort port, CancellationToken cancellationToken)
Causes the world to start listening on a newPort
IReadOnlyDictionary< string, string > Parameters
The raw JSON decond of the CommCommand
Definition: CommCommand.cs:13
+
async Task< string > SendCommand(string command, CancellationToken cancellationToken)
Sends a command to DreamDaemon through /world/Topic()
readonly IProcess process
The IProcess for the SessionController
@@ -132,7 +133,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ushort nextPort
The port to assign DreamDaemon when it queries for it
-
readonly ILogger< SessionController > logger
The ILogger for the SessionController
TaskCompletionSource< object > rebootTcs
The TaskCompletionSource<TResult> that completes when DD tells us about a reboot
diff --git a/_session_controller_factory_8cs_source.html b/_session_controller_factory_8cs_source.html index 206a4080dd..51156ed647 100644 --- a/_session_controller_factory_8cs_source.html +++ b/_session_controller_factory_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
SessionControllerFactory.cs
-Go to the documentation of this file.
1 using Byond.TopicSender;
2 using Microsoft.Extensions.Logging;
3 using Newtonsoft.Json;
4 using Newtonsoft.Json.Serialization;
5 using System;
6 using System.Globalization;
7 using System.Linq;
8 using System.Text;
9 using System.Threading;
10 using System.Threading.Tasks;
17 using Tgstation.Server.Host.IO;
19 
20 namespace Tgstation.Server.Host.Components.Watchdog
21 {
24  {
29 
33  readonly IByondManager byond;
34 
38  readonly IByondTopicSender byondTopicSender;
39 
44 
49 
54 
58  readonly IChat chat;
59 
63  readonly ILoggerFactory loggerFactory;
64 
68  readonly Api.Models.Instance instance;
69 
75  static string SecurityWord(DreamDaemonSecurity securityLevel)
76  {
77  switch (securityLevel)
78  {
79  case DreamDaemonSecurity.Safe:
80  return "safe";
81  case DreamDaemonSecurity.Trusted:
82  return "trusted";
83  case DreamDaemonSecurity.Ultrasafe:
84  return "ultrasafe";
85  default:
86  throw new ArgumentOutOfRangeException(nameof(securityLevel), securityLevel, String.Format(CultureInfo.InvariantCulture, "Bad DreamDaemon security level: {0}", securityLevel));
87  }
88  }
89 
102  public SessionControllerFactory(IProcessExecutor processExecutor, IByondManager byond, IByondTopicSender byondTopicSender, ICryptographySuite cryptographySuite, IApplication application, IIOManager ioManager, IChat chat, ILoggerFactory loggerFactory, Api.Models.Instance instance)
103  {
104  this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor));
105  this.byond = byond ?? throw new ArgumentNullException(nameof(byond));
106  this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender));
107  this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
108  this.application = application ?? throw new ArgumentNullException(nameof(application));
109  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
110  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
111  this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
112  this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
113  }
114 
116  public async Task<ISessionController> LaunchNew(DreamDaemonLaunchParameters launchParameters, IDmbProvider dmbProvider, IByondExecutableLock currentByondLock, bool primaryPort, bool primaryDirectory, bool apiValidate, CancellationToken cancellationToken)
117  {
118  var portToUse = primaryPort ? launchParameters.PrimaryPort : launchParameters.SecondaryPort;
119  if (!portToUse.HasValue)
120  throw new InvalidOperationException("Given port is null!");
121  var accessIdentifier = cryptographySuite.GetSecureString();
122 
123  const string JsonPostfix = "tgs.json";
124 
125  var basePath = primaryDirectory ? dmbProvider.PrimaryDirectory : dmbProvider.SecondaryDirectory;
126  //delete all previous tgs json files
127  var files = await ioManager.GetFilesWithExtension(basePath, JsonPostfix, cancellationToken).ConfigureAwait(false);
128 
129  await Task.WhenAll(files.Select(x => ioManager.DeleteFile(x, cancellationToken))).ConfigureAwait(false);
130 
131  //i changed this back from guids, hopefully i don't regret that
132  string JsonFile(string name) => String.Format(CultureInfo.InvariantCulture, "{0}.{1}", name, JsonPostfix);
133 
134  var securityLevelToUse = launchParameters.SecurityLevel.Value;
135  switch (dmbProvider.CompileJob.MinimumSecurityLevel)
136  {
137  case DreamDaemonSecurity.Ultrasafe:
138  break;
139  case DreamDaemonSecurity.Safe:
140  if (securityLevelToUse == DreamDaemonSecurity.Ultrasafe)
141  securityLevelToUse = DreamDaemonSecurity.Safe;
142  break;
143  case DreamDaemonSecurity.Trusted:
144  securityLevelToUse = DreamDaemonSecurity.Trusted;
145  break;
146  default:
147  throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid DreamDaemonSecurity value: {0}", dmbProvider.CompileJob.MinimumSecurityLevel));
148  }
149 
150  //setup interop files
151  var interopInfo = new JsonFile
152  {
153  AccessIdentifier = accessIdentifier,
154  ApiValidateOnly = apiValidate,
155  ChatChannelsJson = JsonFile("chat_channels"),
156  ChatCommandsJson = JsonFile("chat_commands"),
157  ServerCommandsJson = JsonFile("server_commands"),
158  InstanceName = instance.Name,
159  SecurityLevel = securityLevelToUse,
160  Revision = new Api.Models.Internal.RevisionInformation
161  {
162  CommitSha = dmbProvider.CompileJob.RevisionInformation.CommitSha,
163  OriginCommitSha = dmbProvider.CompileJob.RevisionInformation.OriginCommitSha
164  }
165  };
166 
167  interopInfo.TestMerges.AddRange(dmbProvider.CompileJob.RevisionInformation.ActiveTestMerges.Select(x => x.TestMerge).Select(x => new Interop.TestMerge(x, interopInfo.Revision)));
168 
169  var interopJsonFile = JsonFile("interop");
170 
171  var interopJson = JsonConvert.SerializeObject(interopInfo, Formatting.Indented, new JsonSerializerSettings
172  {
173  ContractResolver = new CamelCasePropertyNamesContractResolver(),
174  ReferenceLoopHandling = ReferenceLoopHandling.Ignore
175  });
176 
177  var localIoManager = new ResolvingIOManager(ioManager, basePath);
178 
179  var chatJsonTrackingTask = chat.TrackJsons(basePath, interopInfo.ChatChannelsJson, interopInfo.ChatCommandsJson, cancellationToken);
180 
181  await localIoManager.WriteAllBytes(interopJsonFile, Encoding.UTF8.GetBytes(interopJson), cancellationToken).ConfigureAwait(false);
182  var chatJsonTrackingContext = await chatJsonTrackingTask.ConfigureAwait(false);
183  try
184  {
185  //get the byond lock
186  var byondLock = currentByondLock ?? await byond.UseExecutables(Version.Parse(dmbProvider.CompileJob.ByondVersion), cancellationToken).ConfigureAwait(false);
187  try
188  {
189  //create interop context
190  var context = new CommContext(ioManager, loggerFactory.CreateLogger<CommContext>(), basePath, interopInfo.ServerCommandsJson);
191  try
192  {
193  //set command line options
194  //more sanitization here cause it uses the same scheme
195  var parameters = String.Format(CultureInfo.InvariantCulture, "{2}={0}&{3}={1}", byondTopicSender.SanitizeString(application.Version.ToString()), byondTopicSender.SanitizeString(interopJsonFile), byondTopicSender.SanitizeString(Constants.DMParamHostVersion), byondTopicSender.SanitizeString(Constants.DMParamInfoJson));
196 
197  //important to run on all ports to allow port changing
198  var arguments = String.Format(CultureInfo.InvariantCulture, "{0} -port {1} -ports 1-65535 {2}-close -{3} -verbose -public -params \"{4}\"",
199  dmbProvider.DmbName,
200  primaryPort ? launchParameters.PrimaryPort : launchParameters.SecondaryPort,
201  launchParameters.AllowWebClient.Value ? "-webclient " : String.Empty,
202  SecurityWord(securityLevelToUse),
203  parameters);
204 
205  //launch dd
206  var process = processExecutor.LaunchProcess(byondLock.DreamDaemonPath, basePath, arguments, noShellExecute: true);
207  try
208  {
209  //return the session controller for it
210  var result = new SessionController(new ReattachInformation
211  {
212  AccessIdentifier = accessIdentifier,
213  Dmb = dmbProvider,
214  IsPrimary = primaryDirectory,
215  Port = portToUse.Value,
216  ProcessId = process.Id,
217  ChatChannelsJson = interopInfo.ChatChannelsJson,
218  ChatCommandsJson = interopInfo.ChatCommandsJson,
219  ServerCommandsJson = interopInfo.ServerCommandsJson,
220  }, process, byondLock, byondTopicSender, chatJsonTrackingContext, context, chat, loggerFactory.CreateLogger<SessionController>(), launchParameters.SecurityLevel, launchParameters.StartupTimeout);
221 
222  //writeback launch parameter's fixed security level
223  launchParameters.SecurityLevel = securityLevelToUse;
224 
225  return result;
226  }
227  catch
228  {
229  process.Dispose();
230  throw;
231  }
232  }
233  catch
234  {
235  context.Dispose();
236  throw;
237  }
238  }
239  catch
240  {
241  if (currentByondLock == null)
242  byondLock.Dispose();
243  throw;
244  }
245  }
246  catch
247  {
248  chatJsonTrackingContext.Dispose();
249  throw;
250  }
251  }
252 
254  public async Task<ISessionController> Reattach(ReattachInformation reattachInformation, CancellationToken cancellationToken)
255  {
256  if (reattachInformation == null)
257  throw new ArgumentNullException(nameof(reattachInformation));
258 
259  var basePath = reattachInformation.IsPrimary ? reattachInformation.Dmb.PrimaryDirectory : reattachInformation.Dmb.SecondaryDirectory;
260  var chatJsonTrackingContext = await chat.TrackJsons(basePath, reattachInformation.ChatChannelsJson, reattachInformation.ChatCommandsJson, cancellationToken).ConfigureAwait(false);
261  try
262  {
263  var byondLock = await byond.UseExecutables(Version.Parse(reattachInformation.Dmb.CompileJob.ByondVersion), cancellationToken).ConfigureAwait(false);
264  try
265  {
266  var context = new CommContext(ioManager, loggerFactory.CreateLogger<CommContext>(), basePath, reattachInformation.ServerCommandsJson);
267  try
268  {
269  var process = processExecutor.GetProcess(reattachInformation.ProcessId);
270  try
271  {
272  return new SessionController(reattachInformation, process, byondLock, byondTopicSender, chatJsonTrackingContext, context, chat, loggerFactory.CreateLogger<SessionController>(), null, null);
273  }
274  catch
275  {
276  process.Dispose();
277  throw;
278  }
279  }
280  catch
281  {
282  context.Dispose();
283  throw;
284  }
285  }
286  catch
287  {
288  byondLock.Dispose();
289  throw;
290  }
291  }
292  catch
293  {
294  chatJsonTrackingContext.Dispose();
295  throw;
296  }
297  }
298  }
299 }
string ServerCommandsJson
Path to the server commands json file
+Go to the documentation of this file.
1 using Byond.TopicSender;
2 using Microsoft.Extensions.Logging;
3 using Newtonsoft.Json;
4 using Newtonsoft.Json.Serialization;
5 using System;
6 using System.Globalization;
7 using System.Linq;
8 using System.Text;
9 using System.Threading;
10 using System.Threading.Tasks;
17 using Tgstation.Server.Host.IO;
19 
20 namespace Tgstation.Server.Host.Components.Watchdog
21 {
24  {
29 
33  readonly IByondManager byond;
34 
38  readonly IByondTopicSender byondTopicSender;
39 
44 
49 
54 
58  readonly IChat chat;
59 
63  readonly ILoggerFactory loggerFactory;
64 
68  readonly Api.Models.Instance instance;
69 
75  static string SecurityWord(DreamDaemonSecurity securityLevel)
76  {
77  switch (securityLevel)
78  {
79  case DreamDaemonSecurity.Safe:
80  return "safe";
81  case DreamDaemonSecurity.Trusted:
82  return "trusted";
83  case DreamDaemonSecurity.Ultrasafe:
84  return "ultrasafe";
85  default:
86  throw new ArgumentOutOfRangeException(nameof(securityLevel), securityLevel, String.Format(CultureInfo.InvariantCulture, "Bad DreamDaemon security level: {0}", securityLevel));
87  }
88  }
89 
102  public SessionControllerFactory(IProcessExecutor processExecutor, IByondManager byond, IByondTopicSender byondTopicSender, ICryptographySuite cryptographySuite, IApplication application, IIOManager ioManager, IChat chat, ILoggerFactory loggerFactory, Api.Models.Instance instance)
103  {
104  this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor));
105  this.byond = byond ?? throw new ArgumentNullException(nameof(byond));
106  this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender));
107  this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
108  this.application = application ?? throw new ArgumentNullException(nameof(application));
109  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
110  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
111  this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
112  this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
113  }
114 
116  public async Task<ISessionController> LaunchNew(DreamDaemonLaunchParameters launchParameters, IDmbProvider dmbProvider, IByondExecutableLock currentByondLock, bool primaryPort, bool primaryDirectory, bool apiValidate, CancellationToken cancellationToken)
117  {
118  var portToUse = primaryPort ? launchParameters.PrimaryPort : launchParameters.SecondaryPort;
119  if (!portToUse.HasValue)
120  throw new InvalidOperationException("Given port is null!");
121  var accessIdentifier = cryptographySuite.GetSecureString();
122 
123  const string JsonPostfix = "tgs.json";
124 
125  var basePath = primaryDirectory ? dmbProvider.PrimaryDirectory : dmbProvider.SecondaryDirectory;
126  //delete all previous tgs json files
127  var files = await ioManager.GetFilesWithExtension(basePath, JsonPostfix, cancellationToken).ConfigureAwait(false);
128 
129  await Task.WhenAll(files.Select(x => ioManager.DeleteFile(x, cancellationToken))).ConfigureAwait(false);
130 
131  //i changed this back from guids, hopefully i don't regret that
132  string JsonFile(string name) => String.Format(CultureInfo.InvariantCulture, "{0}.{1}", name, JsonPostfix);
133 
134  var securityLevelToUse = launchParameters.SecurityLevel.Value;
135  switch (dmbProvider.CompileJob.MinimumSecurityLevel)
136  {
137  case DreamDaemonSecurity.Ultrasafe:
138  break;
139  case DreamDaemonSecurity.Safe:
140  if (securityLevelToUse == DreamDaemonSecurity.Ultrasafe)
141  securityLevelToUse = DreamDaemonSecurity.Safe;
142  break;
143  case DreamDaemonSecurity.Trusted:
144  securityLevelToUse = DreamDaemonSecurity.Trusted;
145  break;
146  default:
147  throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid DreamDaemonSecurity value: {0}", dmbProvider.CompileJob.MinimumSecurityLevel));
148  }
149 
150  //setup interop files
151  var interopInfo = new JsonFile
152  {
153  AccessIdentifier = accessIdentifier,
154  ApiValidateOnly = apiValidate,
155  ChatChannelsJson = JsonFile("chat_channels"),
156  ChatCommandsJson = JsonFile("chat_commands"),
157  ServerCommandsJson = JsonFile("server_commands"),
158  InstanceName = instance.Name,
159  SecurityLevel = securityLevelToUse,
160  Revision = new Api.Models.Internal.RevisionInformation
161  {
162  CommitSha = dmbProvider.CompileJob.RevisionInformation.CommitSha,
163  OriginCommitSha = dmbProvider.CompileJob.RevisionInformation.OriginCommitSha
164  }
165  };
166 
167  interopInfo.TestMerges.AddRange(dmbProvider.CompileJob.RevisionInformation.ActiveTestMerges.Select(x => x.TestMerge).Select(x => new Interop.TestMerge(x, interopInfo.Revision)));
168 
169  var interopJsonFile = JsonFile("interop");
170 
171  var interopJson = JsonConvert.SerializeObject(interopInfo, Formatting.Indented, new JsonSerializerSettings
172  {
173  ContractResolver = new CamelCasePropertyNamesContractResolver(),
174  ReferenceLoopHandling = ReferenceLoopHandling.Ignore
175  });
176 
177  var localIoManager = new ResolvingIOManager(ioManager, basePath);
178 
179  var chatJsonTrackingTask = chat.TrackJsons(basePath, interopInfo.ChatChannelsJson, interopInfo.ChatCommandsJson, cancellationToken);
180 
181  await localIoManager.WriteAllBytes(interopJsonFile, Encoding.UTF8.GetBytes(interopJson), cancellationToken).ConfigureAwait(false);
182  var chatJsonTrackingContext = await chatJsonTrackingTask.ConfigureAwait(false);
183  try
184  {
185  //get the byond lock
186  var byondLock = currentByondLock ?? await byond.UseExecutables(Version.Parse(dmbProvider.CompileJob.ByondVersion), cancellationToken).ConfigureAwait(false);
187  try
188  {
189  //create interop context
190  var context = new CommContext(ioManager, loggerFactory.CreateLogger<CommContext>(), basePath, interopInfo.ServerCommandsJson);
191  try
192  {
193  //set command line options
194  //more sanitization here cause it uses the same scheme
195  var parameters = String.Format(CultureInfo.InvariantCulture, "{2}={0}&{3}={1}", byondTopicSender.SanitizeString(application.Version.ToString()), byondTopicSender.SanitizeString(interopJsonFile), byondTopicSender.SanitizeString(Constants.DMParamHostVersion), byondTopicSender.SanitizeString(Constants.DMParamInfoJson));
196 
197  //important to run on all ports to allow port changing
198  var arguments = String.Format(CultureInfo.InvariantCulture, "{0} -port {1} -ports 1-65535 {2}-close -{3} -verbose -public -params \"{4}\"",
199  dmbProvider.DmbName,
200  primaryPort ? launchParameters.PrimaryPort : launchParameters.SecondaryPort,
201  launchParameters.AllowWebClient.Value ? "-webclient " : String.Empty,
202  SecurityWord(securityLevelToUse),
203  parameters);
204 
205  //launch dd
206  var process = processExecutor.LaunchProcess(byondLock.DreamDaemonPath, basePath, arguments, noShellExecute: true);
207  try
208  {
209  //return the session controller for it
210  var result = new SessionController(new ReattachInformation
211  {
212  AccessIdentifier = accessIdentifier,
213  Dmb = dmbProvider,
214  IsPrimary = primaryDirectory,
215  Port = portToUse.Value,
216  ProcessId = process.Id,
217  ChatChannelsJson = interopInfo.ChatChannelsJson,
218  ChatCommandsJson = interopInfo.ChatCommandsJson,
219  ServerCommandsJson = interopInfo.ServerCommandsJson,
220  }, process, byondLock, byondTopicSender, chatJsonTrackingContext, context, chat, loggerFactory.CreateLogger<SessionController>(), launchParameters.SecurityLevel, launchParameters.StartupTimeout);
221 
222  //writeback launch parameter's fixed security level
223  launchParameters.SecurityLevel = securityLevelToUse;
224 
225  return result;
226  }
227  catch
228  {
229  process.Dispose();
230  throw;
231  }
232  }
233  catch
234  {
235  context.Dispose();
236  throw;
237  }
238  }
239  catch
240  {
241  if (currentByondLock == null)
242  byondLock.Dispose();
243  throw;
244  }
245  }
246  catch
247  {
248  chatJsonTrackingContext.Dispose();
249  throw;
250  }
251  }
252 
254  public async Task<ISessionController> Reattach(ReattachInformation reattachInformation, CancellationToken cancellationToken)
255  {
256  if (reattachInformation == null)
257  throw new ArgumentNullException(nameof(reattachInformation));
258 
259  var basePath = reattachInformation.IsPrimary ? reattachInformation.Dmb.PrimaryDirectory : reattachInformation.Dmb.SecondaryDirectory;
260  var chatJsonTrackingContext = await chat.TrackJsons(basePath, reattachInformation.ChatChannelsJson, reattachInformation.ChatCommandsJson, cancellationToken).ConfigureAwait(false);
261  try
262  {
263  var byondLock = await byond.UseExecutables(Version.Parse(reattachInformation.Dmb.CompileJob.ByondVersion), cancellationToken).ConfigureAwait(false);
264  try
265  {
266  var context = new CommContext(ioManager, loggerFactory.CreateLogger<CommContext>(), basePath, reattachInformation.ServerCommandsJson);
267  try
268  {
269  var process = processExecutor.GetProcess(reattachInformation.ProcessId);
270  try
271  {
272  return new SessionController(reattachInformation, process, byondLock, byondTopicSender, chatJsonTrackingContext, context, chat, loggerFactory.CreateLogger<SessionController>(), null, null);
273  }
274  catch
275  {
276  process.Dispose();
277  throw;
278  }
279  }
280  catch
281  {
282  context.Dispose();
283  throw;
284  }
285  }
286  catch
287  {
288  byondLock.Dispose();
289  throw;
290  }
291  }
292  catch
293  {
294  chatJsonTrackingContext.Dispose();
295  throw;
296  }
297  }
298  }
299 }
string ServerCommandsJson
Path to the server commands json file
readonly ILoggerFactory loggerFactory
The ILoggerFactory for the SessionControllerFactory
readonly IIOManager ioManager
The IIOManager for the SessionControllerFactory
@@ -105,6 +105,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly Api.Models.Instance instance
The Api.Models.Instance for the SessionControllerFactory
readonly ICryptographySuite cryptographySuite
The ICryptographySuite for the SessionControllerFactory
Configures the ASP.NET Core web application
Definition: IApplication.cs:8
+
string SecondaryDirectory
The secondary game directory with a trailing directory separator
Definition: IDmbProvider.cs:24
RevisionInformation RevisionInformation
Git revision the compiler ran on. Not modifiable
Definition: CompileJob.cs:16
@@ -130,9 +131,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly IByondTopicSender byondTopicSender
The IByondTopicSender for the SessionControllerFactory
async Task< ISessionController > Reattach(ReattachInformation reattachInformation, CancellationToken cancellationToken)
Create a ISessionController from an existing DreamDaemon instance
IDmbProvider Dmb
The IDmbProvider used by DreamDaemon
- - +
For managing the BYOND installation
bool IsPrimary
If the Components.IDmbProvider.PrimaryDirectory of the associated dmb is being used ...
diff --git a/_sql_server_database_context_8cs_source.html b/_sql_server_database_context_8cs_source.html index dd8c9ce055..5518f3f950 100644 --- a/_sql_server_database_context_8cs_source.html +++ b/_sql_server_database_context_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
SqlServerDatabaseContext.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using Microsoft.Extensions.Options;
5 
6 namespace Tgstation.Server.Host.Models
7 {
11  sealed class SqlServerDatabaseContext : DatabaseContext<SqlServerDatabaseContext>
12  {
20  public SqlServerDatabaseContext(DbContextOptions<SqlServerDatabaseContext> dbContextOptions, IOptions<DatabaseConfiguration> databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger<SqlServerDatabaseContext> logger) : base(dbContextOptions, databaseConfiguration, databaseSeeder, logger)
21  { }
22 
24  protected override void OnConfiguring(DbContextOptionsBuilder options)
25  {
26  base.OnConfiguring(options);
27  options.UseSqlServer(DatabaseConfiguration.ConnectionString);
28  }
29  }
30 }
string ConnectionString
The connection string for the database
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using Microsoft.Extensions.Options;
5 
6 namespace Tgstation.Server.Host.Models
7 {
11  sealed class SqlServerDatabaseContext : DatabaseContext<SqlServerDatabaseContext>
12  {
20  public SqlServerDatabaseContext(DbContextOptions<SqlServerDatabaseContext> dbContextOptions, IOptions<DatabaseConfiguration> databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger<SqlServerDatabaseContext> logger) : base(dbContextOptions, databaseConfiguration, databaseSeeder, logger)
21  { }
22 
24  protected override void OnConfiguring(DbContextOptionsBuilder options)
25  {
26  base.OnConfiguring(options);
27  options.UseSqlServer(DatabaseConfiguration.ConnectionString);
28  }
29  }
30 }
string ConnectionString
The connection string for the database
override void OnConfiguring(DbContextOptionsBuilder options)
+
For initially seeding a database
DatabaseContext<TParentContext> for Sqlserver
SqlServerDatabaseContext(DbContextOptions< SqlServerDatabaseContext > dbContextOptions, IOptions< DatabaseConfiguration > databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger< SqlServerDatabaseContext > logger)
Construct a SqlServerDatabaseContext
-
Configuration options for the Models.DatabaseContext<TParentContext>
diff --git a/_sql_server_database_context_model_snapshot_8cs_source.html b/_sql_server_database_context_model_snapshot_8cs_source.html index 5234f0e650..4f2732fe16 100644 --- a/_sql_server_database_context_model_snapshot_8cs_source.html +++ b/_sql_server_database_context_model_snapshot_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
SqlServerDatabaseContextModelSnapshot.cs
-Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Metadata;
6 using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 
9 namespace Tgstation.Server.Host.Models.Migrations
10 {
13  {
14  protected override void BuildModel(ModelBuilder modelBuilder)
15  {
16 #pragma warning disable 612, 618
17  modelBuilder
18  .HasAnnotation("ProductVersion", "2.1.3-rtm-32065")
19  .HasAnnotation("Relational:MaxIdentifierLength", 128)
20  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
21 
22  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
23  {
24  b.Property<long>("Id")
25  .ValueGeneratedOnAdd()
26  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
27 
28  b.Property<string>("ConnectionString")
29  .IsRequired();
30 
31  b.Property<bool?>("Enabled");
32 
33  b.Property<long>("InstanceId");
34 
35  b.Property<string>("Name")
36  .IsRequired();
37 
38  b.Property<int?>("Provider");
39 
40  b.HasKey("Id");
41 
42  b.HasIndex("InstanceId");
43 
44  b.HasIndex("Name")
45  .IsUnique();
46 
47  b.ToTable("ChatBots");
48  });
49 
50  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
51  {
52  b.Property<long>("Id")
53  .ValueGeneratedOnAdd()
54  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
55 
56  b.Property<long>("ChatSettingsId");
57 
58  b.Property<decimal?>("DiscordChannelId")
59  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
60 
61  b.Property<string>("IrcChannel");
62 
63  b.Property<bool?>("IsAdminChannel")
64  .IsRequired();
65 
66  b.Property<bool?>("IsUpdatesChannel")
67  .IsRequired();
68 
69  b.Property<bool?>("IsWatchdogChannel")
70  .IsRequired();
71 
72  b.Property<string>("Tag");
73 
74  b.HasKey("Id");
75 
76  b.HasIndex("ChatSettingsId", "DiscordChannelId")
77  .IsUnique()
78  .HasFilter("[DiscordChannelId] IS NOT NULL");
79 
80  b.HasIndex("ChatSettingsId", "IrcChannel")
81  .IsUnique()
82  .HasFilter("[IrcChannel] IS NOT NULL");
83 
84  b.ToTable("ChatChannels");
85  });
86 
87  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
88  {
89  b.Property<long>("Id")
90  .ValueGeneratedOnAdd()
91  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
92 
93  b.Property<string>("ByondVersion")
94  .IsRequired();
95 
96  b.Property<Guid?>("DirectoryName")
97  .IsRequired();
98 
99  b.Property<string>("DmeName")
100  .IsRequired();
101 
102  b.Property<long>("JobId");
103 
104  b.Property<int>("MinimumSecurityLevel");
105 
106  b.Property<string>("Output")
107  .IsRequired();
108 
109  b.Property<long>("RevisionInformationId");
110 
111  b.HasKey("Id");
112 
113  b.HasIndex("DirectoryName");
114 
115  b.HasIndex("JobId")
116  .IsUnique();
117 
118  b.HasIndex("RevisionInformationId");
119 
120  b.ToTable("CompileJobs");
121  });
122 
123  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
124  {
125  b.Property<long>("Id")
126  .ValueGeneratedOnAdd()
127  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
128 
129  b.Property<string>("AccessToken");
130 
131  b.Property<bool?>("AllowWebClient")
132  .IsRequired();
133 
134  b.Property<bool?>("AutoStart")
135  .IsRequired();
136 
137  b.Property<long>("InstanceId");
138 
139  b.Property<int>("PrimaryPort");
140 
141  b.Property<int?>("ProcessId");
142 
143  b.Property<int>("SecondaryPort");
144 
145  b.Property<int>("SecurityLevel");
146 
147  b.Property<bool?>("SoftRestart")
148  .IsRequired();
149 
150  b.Property<bool?>("SoftShutdown")
151  .IsRequired();
152 
153  b.Property<long>("StartupTimeout");
154 
155  b.HasKey("Id");
156 
157  b.HasIndex("InstanceId")
158  .IsUnique();
159 
160  b.ToTable("DreamDaemonSettings");
161  });
162 
163  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
164  {
165  b.Property<long>("Id")
166  .ValueGeneratedOnAdd()
167  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
168 
169  b.Property<int>("ApiValidationPort");
170 
171  b.Property<int>("ApiValidationSecurityLevel");
172 
173  b.Property<long>("InstanceId");
174 
175  b.Property<string>("ProjectName");
176 
177  b.HasKey("Id");
178 
179  b.HasIndex("InstanceId")
180  .IsUnique();
181 
182  b.ToTable("DreamMakerSettings");
183  });
184 
185  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
186  {
187  b.Property<long>("Id")
188  .ValueGeneratedOnAdd()
189  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
190 
191  b.Property<long>("AutoUpdateInterval");
192 
193  b.Property<int>("ConfigurationType");
194 
195  b.Property<string>("Name")
196  .IsRequired();
197 
198  b.Property<bool?>("Online")
199  .IsRequired();
200 
201  b.Property<string>("Path")
202  .IsRequired();
203 
204  b.HasKey("Id");
205 
206  b.HasIndex("Path")
207  .IsUnique();
208 
209  b.ToTable("Instances");
210  });
211 
212  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
213  {
214  b.Property<long>("Id")
215  .ValueGeneratedOnAdd()
216  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
217 
218  b.Property<decimal>("ByondRights")
219  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
220 
221  b.Property<decimal>("ChatBotRights")
222  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
223 
224  b.Property<decimal>("ConfigurationRights")
225  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
226 
227  b.Property<decimal>("DreamDaemonRights")
228  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
229 
230  b.Property<decimal>("DreamMakerRights")
231  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
232 
233  b.Property<long>("InstanceId");
234 
235  b.Property<decimal>("InstanceUserRights")
236  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
237 
238  b.Property<decimal>("RepositoryRights")
239  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
240 
241  b.Property<long?>("UserId")
242  .IsRequired();
243 
244  b.HasKey("Id");
245 
246  b.HasIndex("InstanceId");
247 
248  b.HasIndex("UserId", "InstanceId")
249  .IsUnique();
250 
251  b.ToTable("InstanceUsers");
252  });
253 
254  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
255  {
256  b.Property<long>("Id")
257  .ValueGeneratedOnAdd()
258  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
259 
260  b.Property<decimal?>("CancelRight")
261  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
262 
263  b.Property<decimal?>("CancelRightsType")
264  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
265 
266  b.Property<bool?>("Cancelled")
267  .IsRequired();
268 
269  b.Property<long?>("CancelledById");
270 
271  b.Property<string>("Description")
272  .IsRequired();
273 
274  b.Property<string>("ExceptionDetails");
275 
276  b.Property<long>("InstanceId");
277 
278  b.Property<DateTimeOffset?>("StartedAt")
279  .IsRequired();
280 
281  b.Property<long>("StartedById");
282 
283  b.Property<DateTimeOffset?>("StoppedAt");
284 
285  b.HasKey("Id");
286 
287  b.HasIndex("CancelledById");
288 
289  b.HasIndex("InstanceId");
290 
291  b.HasIndex("StartedById");
292 
293  b.ToTable("Jobs");
294  });
295 
296  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
297  {
298  b.Property<long>("Id")
299  .ValueGeneratedOnAdd()
300  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
301 
302  b.Property<string>("AccessIdentifier")
303  .IsRequired();
304 
305  b.Property<string>("ChatChannelsJson")
306  .IsRequired();
307 
308  b.Property<string>("ChatCommandsJson")
309  .IsRequired();
310 
311  b.Property<long?>("CompileJobId");
312 
313  b.Property<bool>("IsPrimary");
314 
315  b.Property<int>("Port");
316 
317  b.Property<int>("ProcessId");
318 
319  b.Property<int>("RebootState");
320 
321  b.Property<string>("ServerCommandsJson")
322  .IsRequired();
323 
324  b.HasKey("Id");
325 
326  b.HasIndex("CompileJobId");
327 
328  b.ToTable("ReattachInformations");
329  });
330 
331  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
332  {
333  b.Property<long>("Id")
334  .ValueGeneratedOnAdd()
335  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
336 
337  b.Property<string>("AccessToken");
338 
339  b.Property<string>("AccessUser");
340 
341  b.Property<bool?>("AutoUpdatesKeepTestMerges")
342  .IsRequired();
343 
344  b.Property<bool?>("AutoUpdatesSynchronize")
345  .IsRequired();
346 
347  b.Property<string>("CommitterEmail")
348  .IsRequired();
349 
350  b.Property<string>("CommitterName")
351  .IsRequired();
352 
353  b.Property<long>("InstanceId");
354 
355  b.Property<bool?>("PushTestMergeCommits")
356  .IsRequired();
357 
358  b.Property<bool?>("ShowTestMergeCommitters")
359  .IsRequired();
360 
361  b.HasKey("Id");
362 
363  b.HasIndex("InstanceId")
364  .IsUnique();
365 
366  b.ToTable("RepositorySettings");
367  });
368 
369  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
370  {
371  b.Property<long>("Id")
372  .ValueGeneratedOnAdd()
373  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
374 
375  b.Property<long>("RevisionInformationId");
376 
377  b.Property<long>("TestMergeId");
378 
379  b.HasKey("Id");
380 
381  b.HasIndex("RevisionInformationId");
382 
383  b.HasIndex("TestMergeId");
384 
385  b.ToTable("RevInfoTestMerges");
386  });
387 
388  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
389  {
390  b.Property<long>("Id")
391  .ValueGeneratedOnAdd()
392  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
393 
394  b.Property<string>("CommitSha")
395  .IsRequired()
396  .HasMaxLength(40);
397 
398  b.Property<long>("InstanceId");
399 
400  b.Property<string>("OriginCommitSha")
401  .IsRequired()
402  .HasMaxLength(40);
403 
404  b.HasKey("Id");
405 
406  b.HasIndex("CommitSha")
407  .IsUnique();
408 
409  b.HasIndex("InstanceId");
410 
411  b.ToTable("RevisionInformations");
412  });
413 
414  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
415  {
416  b.Property<long>("Id")
417  .ValueGeneratedOnAdd()
418  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
419 
420  b.Property<string>("Author")
421  .IsRequired();
422 
423  b.Property<string>("BodyAtMerge")
424  .IsRequired();
425 
426  b.Property<string>("Comment");
427 
428  b.Property<DateTimeOffset>("MergedAt");
429 
430  b.Property<long>("MergedById");
431 
432  b.Property<int?>("Number")
433  .IsRequired();
434 
435  b.Property<long?>("PrimaryRevisionInformationId")
436  .IsRequired();
437 
438  b.Property<string>("PullRequestRevision")
439  .IsRequired();
440 
441  b.Property<string>("TitleAtMerge")
442  .IsRequired();
443 
444  b.Property<string>("Url")
445  .IsRequired();
446 
447  b.HasKey("Id");
448 
449  b.HasIndex("MergedById");
450 
451  b.HasIndex("PrimaryRevisionInformationId")
452  .IsUnique();
453 
454  b.ToTable("TestMerges");
455  });
456 
457  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
458  {
459  b.Property<long>("Id")
460  .ValueGeneratedOnAdd()
461  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
462 
463  b.Property<decimal>("AdministrationRights")
464  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
465 
466  b.Property<string>("CanonicalName")
467  .IsRequired();
468 
469  b.Property<DateTimeOffset?>("CreatedAt")
470  .IsRequired();
471 
472  b.Property<long?>("CreatedById");
473 
474  b.Property<bool?>("Enabled")
475  .IsRequired();
476 
477  b.Property<decimal>("InstanceManagerRights")
478  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
479 
480  b.Property<DateTimeOffset?>("LastPasswordUpdate");
481 
482  b.Property<string>("Name")
483  .IsRequired();
484 
485  b.Property<string>("PasswordHash");
486 
487  b.Property<string>("SystemIdentifier");
488 
489  b.HasKey("Id");
490 
491  b.HasIndex("CanonicalName")
492  .IsUnique();
493 
494  b.HasIndex("CreatedById");
495 
496  b.ToTable("Users");
497  });
498 
499  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
500  {
501  b.Property<long>("Id")
502  .ValueGeneratedOnAdd()
503  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
504 
505  b.Property<long?>("AlphaId");
506 
507  b.Property<bool>("AlphaIsActive");
508 
509  b.Property<long?>("BravoId");
510 
511  b.Property<long>("InstanceId");
512 
513  b.HasKey("Id");
514 
515  b.HasIndex("AlphaId");
516 
517  b.HasIndex("BravoId");
518 
519  b.HasIndex("InstanceId")
520  .IsUnique();
521 
522  b.ToTable("WatchdogReattachInformations");
523  });
524 
525  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
526  {
527  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
528  .WithMany("ChatSettings")
529  .HasForeignKey("InstanceId")
530  .OnDelete(DeleteBehavior.Cascade);
531  });
532 
533  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
534  {
535  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
536  .WithMany("Channels")
537  .HasForeignKey("ChatSettingsId")
538  .OnDelete(DeleteBehavior.Cascade);
539  });
540 
541  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
542  {
543  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
544  .WithOne()
545  .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
546  .OnDelete(DeleteBehavior.Restrict);
547 
548  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
549  .WithMany("CompileJobs")
550  .HasForeignKey("RevisionInformationId")
551  .OnDelete(DeleteBehavior.Cascade);
552  });
553 
554  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
555  {
556  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
557  .WithOne("DreamDaemonSettings")
558  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
559  .OnDelete(DeleteBehavior.Cascade);
560  });
561 
562  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
563  {
564  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
565  .WithOne("DreamMakerSettings")
566  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
567  .OnDelete(DeleteBehavior.Cascade);
568  });
569 
570  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
571  {
572  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
573  .WithMany("InstanceUsers")
574  .HasForeignKey("InstanceId")
575  .OnDelete(DeleteBehavior.Cascade);
576 
577  b.HasOne("Tgstation.Server.Host.Models.User")
578  .WithMany("InstanceUsers")
579  .HasForeignKey("UserId")
580  .OnDelete(DeleteBehavior.Cascade);
581  });
582 
583  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
584  {
585  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
586  .WithMany()
587  .HasForeignKey("CancelledById");
588 
589  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
590  .WithMany("Jobs")
591  .HasForeignKey("InstanceId")
592  .OnDelete(DeleteBehavior.Cascade);
593 
594  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
595  .WithMany()
596  .HasForeignKey("StartedById")
597  .OnDelete(DeleteBehavior.Cascade);
598  });
599 
600  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
601  {
602  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
603  .WithMany()
604  .HasForeignKey("CompileJobId");
605  });
606 
607  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
608  {
609  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
610  .WithOne("RepositorySettings")
611  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
612  .OnDelete(DeleteBehavior.Cascade);
613  });
614 
615  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
616  {
617  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
618  .WithMany("ActiveTestMerges")
619  .HasForeignKey("RevisionInformationId")
620  .OnDelete(DeleteBehavior.Cascade);
621 
622  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
623  .WithMany("RevisonInformations")
624  .HasForeignKey("TestMergeId")
625  .OnDelete(DeleteBehavior.Cascade);
626  });
627 
628  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
629  {
630  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
631  .WithMany("RevisionInformations")
632  .HasForeignKey("InstanceId")
633  .OnDelete(DeleteBehavior.Cascade);
634  });
635 
636  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
637  {
638  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
639  .WithMany("TestMerges")
640  .HasForeignKey("MergedById")
641  .OnDelete(DeleteBehavior.Restrict);
642 
643  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
644  .WithOne("PrimaryTestMerge")
645  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
646  .OnDelete(DeleteBehavior.Restrict);
647  });
648 
649  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
650  {
651  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
652  .WithMany("CreatedUsers")
653  .HasForeignKey("CreatedById");
654  });
655 
656  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
657  {
658  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
659  .WithMany()
660  .HasForeignKey("AlphaId");
661 
662  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
663  .WithMany()
664  .HasForeignKey("BravoId");
665 
666  b.HasOne("Tgstation.Server.Host.Models.Instance")
667  .WithOne("WatchdogReattachInformation")
668  .HasForeignKey("Tgstation.Server.Host.Models.WatchdogReattachInformation", "InstanceId")
669  .OnDelete(DeleteBehavior.Cascade);
670  });
671 #pragma warning restore 612, 618
672  }
673  }
674 }
+Go to the documentation of this file.
1 // <auto-generated />
2 using System;
3 using Microsoft.EntityFrameworkCore;
4 using Microsoft.EntityFrameworkCore.Infrastructure;
5 using Microsoft.EntityFrameworkCore.Metadata;
6 using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
8 
9 namespace Tgstation.Server.Host.Models.Migrations
10 {
13  {
14  protected override void BuildModel(ModelBuilder modelBuilder)
15  {
16 #pragma warning disable 612, 618
17  modelBuilder
18  .HasAnnotation("ProductVersion", "2.1.3-rtm-32065")
19  .HasAnnotation("Relational:MaxIdentifierLength", 128)
20  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
21 
22  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
23  {
24  b.Property<long>("Id")
25  .ValueGeneratedOnAdd()
26  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
27 
28  b.Property<string>("ConnectionString")
29  .IsRequired();
30 
31  b.Property<bool?>("Enabled");
32 
33  b.Property<long>("InstanceId");
34 
35  b.Property<string>("Name")
36  .IsRequired();
37 
38  b.Property<int?>("Provider");
39 
40  b.HasKey("Id");
41 
42  b.HasIndex("InstanceId");
43 
44  b.HasIndex("Name")
45  .IsUnique();
46 
47  b.ToTable("ChatBots");
48  });
49 
50  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
51  {
52  b.Property<long>("Id")
53  .ValueGeneratedOnAdd()
54  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
55 
56  b.Property<long>("ChatSettingsId");
57 
58  b.Property<decimal?>("DiscordChannelId")
59  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
60 
61  b.Property<string>("IrcChannel");
62 
63  b.Property<bool?>("IsAdminChannel")
64  .IsRequired();
65 
66  b.Property<bool?>("IsUpdatesChannel")
67  .IsRequired();
68 
69  b.Property<bool?>("IsWatchdogChannel")
70  .IsRequired();
71 
72  b.Property<string>("Tag");
73 
74  b.HasKey("Id");
75 
76  b.HasIndex("ChatSettingsId", "DiscordChannelId")
77  .IsUnique()
78  .HasFilter("[DiscordChannelId] IS NOT NULL");
79 
80  b.HasIndex("ChatSettingsId", "IrcChannel")
81  .IsUnique()
82  .HasFilter("[IrcChannel] IS NOT NULL");
83 
84  b.ToTable("ChatChannels");
85  });
86 
87  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
88  {
89  b.Property<long>("Id")
90  .ValueGeneratedOnAdd()
91  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
92 
93  b.Property<string>("ByondVersion")
94  .IsRequired();
95 
96  b.Property<Guid?>("DirectoryName")
97  .IsRequired();
98 
99  b.Property<string>("DmeName")
100  .IsRequired();
101 
102  b.Property<long>("JobId");
103 
104  b.Property<int>("MinimumSecurityLevel");
105 
106  b.Property<string>("Output")
107  .IsRequired();
108 
109  b.Property<long>("RevisionInformationId");
110 
111  b.HasKey("Id");
112 
113  b.HasIndex("DirectoryName");
114 
115  b.HasIndex("JobId")
116  .IsUnique();
117 
118  b.HasIndex("RevisionInformationId");
119 
120  b.ToTable("CompileJobs");
121  });
122 
123  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
124  {
125  b.Property<long>("Id")
126  .ValueGeneratedOnAdd()
127  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
128 
129  b.Property<string>("AccessToken");
130 
131  b.Property<bool?>("AllowWebClient")
132  .IsRequired();
133 
134  b.Property<bool?>("AutoStart")
135  .IsRequired();
136 
137  b.Property<long>("InstanceId");
138 
139  b.Property<int>("PrimaryPort");
140 
141  b.Property<int?>("ProcessId");
142 
143  b.Property<int>("SecondaryPort");
144 
145  b.Property<int>("SecurityLevel");
146 
147  b.Property<bool?>("SoftRestart")
148  .IsRequired();
149 
150  b.Property<bool?>("SoftShutdown")
151  .IsRequired();
152 
153  b.Property<long>("StartupTimeout");
154 
155  b.HasKey("Id");
156 
157  b.HasIndex("InstanceId")
158  .IsUnique();
159 
160  b.ToTable("DreamDaemonSettings");
161  });
162 
163  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
164  {
165  b.Property<long>("Id")
166  .ValueGeneratedOnAdd()
167  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
168 
169  b.Property<int>("ApiValidationPort");
170 
171  b.Property<int>("ApiValidationSecurityLevel");
172 
173  b.Property<long>("InstanceId");
174 
175  b.Property<string>("ProjectName");
176 
177  b.HasKey("Id");
178 
179  b.HasIndex("InstanceId")
180  .IsUnique();
181 
182  b.ToTable("DreamMakerSettings");
183  });
184 
185  modelBuilder.Entity("Tgstation.Server.Host.Models.Instance", b =>
186  {
187  b.Property<long>("Id")
188  .ValueGeneratedOnAdd()
189  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
190 
191  b.Property<long>("AutoUpdateInterval");
192 
193  b.Property<int>("ConfigurationType");
194 
195  b.Property<string>("Name")
196  .IsRequired();
197 
198  b.Property<bool?>("Online")
199  .IsRequired();
200 
201  b.Property<string>("Path")
202  .IsRequired();
203 
204  b.HasKey("Id");
205 
206  b.HasIndex("Path")
207  .IsUnique();
208 
209  b.ToTable("Instances");
210  });
211 
212  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
213  {
214  b.Property<long>("Id")
215  .ValueGeneratedOnAdd()
216  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
217 
218  b.Property<decimal>("ByondRights")
219  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
220 
221  b.Property<decimal>("ChatBotRights")
222  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
223 
224  b.Property<decimal>("ConfigurationRights")
225  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
226 
227  b.Property<decimal>("DreamDaemonRights")
228  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
229 
230  b.Property<decimal>("DreamMakerRights")
231  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
232 
233  b.Property<long>("InstanceId");
234 
235  b.Property<decimal>("InstanceUserRights")
236  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
237 
238  b.Property<decimal>("RepositoryRights")
239  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
240 
241  b.Property<long?>("UserId")
242  .IsRequired();
243 
244  b.HasKey("Id");
245 
246  b.HasIndex("InstanceId");
247 
248  b.HasIndex("UserId", "InstanceId")
249  .IsUnique();
250 
251  b.ToTable("InstanceUsers");
252  });
253 
254  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
255  {
256  b.Property<long>("Id")
257  .ValueGeneratedOnAdd()
258  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
259 
260  b.Property<decimal?>("CancelRight")
261  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
262 
263  b.Property<decimal?>("CancelRightsType")
264  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
265 
266  b.Property<bool?>("Cancelled")
267  .IsRequired();
268 
269  b.Property<long?>("CancelledById");
270 
271  b.Property<string>("Description")
272  .IsRequired();
273 
274  b.Property<string>("ExceptionDetails");
275 
276  b.Property<long>("InstanceId");
277 
278  b.Property<DateTimeOffset?>("StartedAt")
279  .IsRequired();
280 
281  b.Property<long>("StartedById");
282 
283  b.Property<DateTimeOffset?>("StoppedAt");
284 
285  b.HasKey("Id");
286 
287  b.HasIndex("CancelledById");
288 
289  b.HasIndex("InstanceId");
290 
291  b.HasIndex("StartedById");
292 
293  b.ToTable("Jobs");
294  });
295 
296  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
297  {
298  b.Property<long>("Id")
299  .ValueGeneratedOnAdd()
300  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
301 
302  b.Property<string>("AccessIdentifier")
303  .IsRequired();
304 
305  b.Property<string>("ChatChannelsJson")
306  .IsRequired();
307 
308  b.Property<string>("ChatCommandsJson")
309  .IsRequired();
310 
311  b.Property<long?>("CompileJobId");
312 
313  b.Property<bool>("IsPrimary");
314 
315  b.Property<int>("Port");
316 
317  b.Property<int>("ProcessId");
318 
319  b.Property<int>("RebootState");
320 
321  b.Property<string>("ServerCommandsJson")
322  .IsRequired();
323 
324  b.HasKey("Id");
325 
326  b.HasIndex("CompileJobId");
327 
328  b.ToTable("ReattachInformations");
329  });
330 
331  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
332  {
333  b.Property<long>("Id")
334  .ValueGeneratedOnAdd()
335  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
336 
337  b.Property<string>("AccessToken");
338 
339  b.Property<string>("AccessUser");
340 
341  b.Property<bool?>("AutoUpdatesKeepTestMerges")
342  .IsRequired();
343 
344  b.Property<bool?>("AutoUpdatesSynchronize")
345  .IsRequired();
346 
347  b.Property<string>("CommitterEmail")
348  .IsRequired();
349 
350  b.Property<string>("CommitterName")
351  .IsRequired();
352 
353  b.Property<long>("InstanceId");
354 
355  b.Property<bool?>("PushTestMergeCommits")
356  .IsRequired();
357 
358  b.Property<bool?>("ShowTestMergeCommitters")
359  .IsRequired();
360 
361  b.HasKey("Id");
362 
363  b.HasIndex("InstanceId")
364  .IsUnique();
365 
366  b.ToTable("RepositorySettings");
367  });
368 
369  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
370  {
371  b.Property<long>("Id")
372  .ValueGeneratedOnAdd()
373  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
374 
375  b.Property<long>("RevisionInformationId");
376 
377  b.Property<long>("TestMergeId");
378 
379  b.HasKey("Id");
380 
381  b.HasIndex("RevisionInformationId");
382 
383  b.HasIndex("TestMergeId");
384 
385  b.ToTable("RevInfoTestMerges");
386  });
387 
388  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
389  {
390  b.Property<long>("Id")
391  .ValueGeneratedOnAdd()
392  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
393 
394  b.Property<string>("CommitSha")
395  .IsRequired()
396  .HasMaxLength(40);
397 
398  b.Property<long>("InstanceId");
399 
400  b.Property<string>("OriginCommitSha")
401  .IsRequired()
402  .HasMaxLength(40);
403 
404  b.HasKey("Id");
405 
406  b.HasIndex("CommitSha")
407  .IsUnique();
408 
409  b.HasIndex("InstanceId");
410 
411  b.ToTable("RevisionInformations");
412  });
413 
414  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
415  {
416  b.Property<long>("Id")
417  .ValueGeneratedOnAdd()
418  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
419 
420  b.Property<string>("Author")
421  .IsRequired();
422 
423  b.Property<string>("BodyAtMerge")
424  .IsRequired();
425 
426  b.Property<string>("Comment");
427 
428  b.Property<DateTimeOffset>("MergedAt");
429 
430  b.Property<long>("MergedById");
431 
432  b.Property<int?>("Number")
433  .IsRequired();
434 
435  b.Property<long?>("PrimaryRevisionInformationId")
436  .IsRequired();
437 
438  b.Property<string>("PullRequestRevision")
439  .IsRequired();
440 
441  b.Property<string>("TitleAtMerge")
442  .IsRequired();
443 
444  b.Property<string>("Url")
445  .IsRequired();
446 
447  b.HasKey("Id");
448 
449  b.HasIndex("MergedById");
450 
451  b.HasIndex("PrimaryRevisionInformationId")
452  .IsUnique();
453 
454  b.ToTable("TestMerges");
455  });
456 
457  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
458  {
459  b.Property<long>("Id")
460  .ValueGeneratedOnAdd()
461  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
462 
463  b.Property<decimal>("AdministrationRights")
464  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
465 
466  b.Property<string>("CanonicalName")
467  .IsRequired();
468 
469  b.Property<DateTimeOffset?>("CreatedAt")
470  .IsRequired();
471 
472  b.Property<long?>("CreatedById");
473 
474  b.Property<bool?>("Enabled")
475  .IsRequired();
476 
477  b.Property<decimal>("InstanceManagerRights")
478  .HasConversion(new ValueConverter<decimal, decimal>(v => default(decimal), v => default(decimal), new ConverterMappingHints(precision: 20, scale: 0)));
479 
480  b.Property<DateTimeOffset?>("LastPasswordUpdate");
481 
482  b.Property<string>("Name")
483  .IsRequired();
484 
485  b.Property<string>("PasswordHash");
486 
487  b.Property<string>("SystemIdentifier");
488 
489  b.HasKey("Id");
490 
491  b.HasIndex("CanonicalName")
492  .IsUnique();
493 
494  b.HasIndex("CreatedById");
495 
496  b.ToTable("Users");
497  });
498 
499  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
500  {
501  b.Property<long>("Id")
502  .ValueGeneratedOnAdd()
503  .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
504 
505  b.Property<long?>("AlphaId");
506 
507  b.Property<bool>("AlphaIsActive");
508 
509  b.Property<long?>("BravoId");
510 
511  b.Property<long>("InstanceId");
512 
513  b.HasKey("Id");
514 
515  b.HasIndex("AlphaId");
516 
517  b.HasIndex("BravoId");
518 
519  b.HasIndex("InstanceId")
520  .IsUnique();
521 
522  b.ToTable("WatchdogReattachInformations");
523  });
524 
525  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatBot", b =>
526  {
527  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
528  .WithMany("ChatSettings")
529  .HasForeignKey("InstanceId")
530  .OnDelete(DeleteBehavior.Cascade);
531  });
532 
533  modelBuilder.Entity("Tgstation.Server.Host.Models.ChatChannel", b =>
534  {
535  b.HasOne("Tgstation.Server.Host.Models.ChatBot", "ChatSettings")
536  .WithMany("Channels")
537  .HasForeignKey("ChatSettingsId")
538  .OnDelete(DeleteBehavior.Cascade);
539  });
540 
541  modelBuilder.Entity("Tgstation.Server.Host.Models.CompileJob", b =>
542  {
543  b.HasOne("Tgstation.Server.Host.Models.Job", "Job")
544  .WithOne()
545  .HasForeignKey("Tgstation.Server.Host.Models.CompileJob", "JobId")
546  .OnDelete(DeleteBehavior.Restrict);
547 
548  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
549  .WithMany("CompileJobs")
550  .HasForeignKey("RevisionInformationId")
551  .OnDelete(DeleteBehavior.Cascade);
552  });
553 
554  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamDaemonSettings", b =>
555  {
556  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
557  .WithOne("DreamDaemonSettings")
558  .HasForeignKey("Tgstation.Server.Host.Models.DreamDaemonSettings", "InstanceId")
559  .OnDelete(DeleteBehavior.Cascade);
560  });
561 
562  modelBuilder.Entity("Tgstation.Server.Host.Models.DreamMakerSettings", b =>
563  {
564  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
565  .WithOne("DreamMakerSettings")
566  .HasForeignKey("Tgstation.Server.Host.Models.DreamMakerSettings", "InstanceId")
567  .OnDelete(DeleteBehavior.Cascade);
568  });
569 
570  modelBuilder.Entity("Tgstation.Server.Host.Models.InstanceUser", b =>
571  {
572  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
573  .WithMany("InstanceUsers")
574  .HasForeignKey("InstanceId")
575  .OnDelete(DeleteBehavior.Cascade);
576 
577  b.HasOne("Tgstation.Server.Host.Models.User")
578  .WithMany("InstanceUsers")
579  .HasForeignKey("UserId")
580  .OnDelete(DeleteBehavior.Cascade);
581  });
582 
583  modelBuilder.Entity("Tgstation.Server.Host.Models.Job", b =>
584  {
585  b.HasOne("Tgstation.Server.Host.Models.User", "CancelledBy")
586  .WithMany()
587  .HasForeignKey("CancelledById");
588 
589  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
590  .WithMany("Jobs")
591  .HasForeignKey("InstanceId")
592  .OnDelete(DeleteBehavior.Cascade);
593 
594  b.HasOne("Tgstation.Server.Host.Models.User", "StartedBy")
595  .WithMany()
596  .HasForeignKey("StartedById")
597  .OnDelete(DeleteBehavior.Cascade);
598  });
599 
600  modelBuilder.Entity("Tgstation.Server.Host.Models.ReattachInformation", b =>
601  {
602  b.HasOne("Tgstation.Server.Host.Models.CompileJob", "CompileJob")
603  .WithMany()
604  .HasForeignKey("CompileJobId");
605  });
606 
607  modelBuilder.Entity("Tgstation.Server.Host.Models.RepositorySettings", b =>
608  {
609  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
610  .WithOne("RepositorySettings")
611  .HasForeignKey("Tgstation.Server.Host.Models.RepositorySettings", "InstanceId")
612  .OnDelete(DeleteBehavior.Cascade);
613  });
614 
615  modelBuilder.Entity("Tgstation.Server.Host.Models.RevInfoTestMerge", b =>
616  {
617  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "RevisionInformation")
618  .WithMany("ActiveTestMerges")
619  .HasForeignKey("RevisionInformationId")
620  .OnDelete(DeleteBehavior.Cascade);
621 
622  b.HasOne("Tgstation.Server.Host.Models.TestMerge", "TestMerge")
623  .WithMany("RevisonInformations")
624  .HasForeignKey("TestMergeId")
625  .OnDelete(DeleteBehavior.Cascade);
626  });
627 
628  modelBuilder.Entity("Tgstation.Server.Host.Models.RevisionInformation", b =>
629  {
630  b.HasOne("Tgstation.Server.Host.Models.Instance", "Instance")
631  .WithMany("RevisionInformations")
632  .HasForeignKey("InstanceId")
633  .OnDelete(DeleteBehavior.Cascade);
634  });
635 
636  modelBuilder.Entity("Tgstation.Server.Host.Models.TestMerge", b =>
637  {
638  b.HasOne("Tgstation.Server.Host.Models.User", "MergedBy")
639  .WithMany("TestMerges")
640  .HasForeignKey("MergedById")
641  .OnDelete(DeleteBehavior.Restrict);
642 
643  b.HasOne("Tgstation.Server.Host.Models.RevisionInformation", "PrimaryRevisionInformation")
644  .WithOne("PrimaryTestMerge")
645  .HasForeignKey("Tgstation.Server.Host.Models.TestMerge", "PrimaryRevisionInformationId")
646  .OnDelete(DeleteBehavior.Restrict);
647  });
648 
649  modelBuilder.Entity("Tgstation.Server.Host.Models.User", b =>
650  {
651  b.HasOne("Tgstation.Server.Host.Models.User", "CreatedBy")
652  .WithMany("CreatedUsers")
653  .HasForeignKey("CreatedById");
654  });
655 
656  modelBuilder.Entity("Tgstation.Server.Host.Models.WatchdogReattachInformation", b =>
657  {
658  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Alpha")
659  .WithMany()
660  .HasForeignKey("AlphaId");
661 
662  b.HasOne("Tgstation.Server.Host.Models.ReattachInformation", "Bravo")
663  .WithMany()
664  .HasForeignKey("BravoId");
665 
666  b.HasOne("Tgstation.Server.Host.Models.Instance")
667  .WithOne("WatchdogReattachInformation")
668  .HasForeignKey("Tgstation.Server.Host.Models.WatchdogReattachInformation", "InstanceId")
669  .OnDelete(DeleteBehavior.Cascade);
670  });
671 #pragma warning restore 612, 618
672  }
673  }
674 }
+
DatabaseContext<TParentContext> for Sqlserver
- diff --git a/_sql_server_design_time_db_context_factory_8cs_source.html b/_sql_server_design_time_db_context_factory_8cs_source.html index e5a3e2fbcd..997f7a6e49 100644 --- a/_sql_server_design_time_db_context_factory_8cs_source.html +++ b/_sql_server_design_time_db_context_factory_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
SqlServerDesignTimeDbContextFactory.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Identity;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.EntityFrameworkCore.Design;
4 using Microsoft.Extensions.Logging;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
10  sealed class SqlServerDesignTimeDbContextFactory : IDesignTimeDbContextFactory<SqlServerDatabaseContext>
11  {
13  public SqlServerDatabaseContext CreateDbContext(string[] args) => new SqlServerDatabaseContext(new DbContextOptions<SqlServerDatabaseContext>(), DesignTimeDbContextFactoryHelpers.GetDbContextOptions(), new DatabaseSeeder(new CryptographySuite(new PasswordHasher<User>())), new LoggerFactory().CreateLogger<SqlServerDatabaseContext>());
14  }
15 }
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Identity;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.EntityFrameworkCore.Design;
4 using Microsoft.Extensions.Logging;
6 
7 namespace Tgstation.Server.Host.Models.Migrations
8 {
10  sealed class SqlServerDesignTimeDbContextFactory : IDesignTimeDbContextFactory<SqlServerDatabaseContext>
11  {
13  public SqlServerDatabaseContext CreateDbContext(string[] args) => new SqlServerDatabaseContext(new DbContextOptions<SqlServerDatabaseContext>(), DesignTimeDbContextFactoryHelpers.GetDbContextOptions(), new DatabaseSeeder(new CryptographySuite(new PasswordHasher<User>())), new LoggerFactory().CreateLogger<SqlServerDatabaseContext>());
14  }
15 }
+
Contains helpers for creating design time Models.DatabaseContext<TParentContext>s ...
DatabaseContext<TParentContext> for Sqlserver
- diff --git a/_synchronous_i_o_manager_8cs_source.html b/_synchronous_i_o_manager_8cs_source.html index de21b1ad06..aa68527bd1 100644 --- a/_synchronous_i_o_manager_8cs_source.html +++ b/_synchronous_i_o_manager_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
SynchronousIOManager.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Globalization;
4 using System.IO;
5 using System.Linq;
6 using System.Security.Cryptography;
7 using System.Threading;
8 
9 namespace Tgstation.Server.Host.IO
10 {
13  {
15  public bool CreateDirectory(string path, CancellationToken cancellationToken)
16  {
17  if (IsDirectory(path))
18  return true;
19  cancellationToken.ThrowIfCancellationRequested();
20  Directory.CreateDirectory(path);
21  return false;
22  }
23 
25  public bool DeleteDirectory(string path)
26  {
27  if (File.Exists(path))
28  return false;
29 
30  if (!Directory.Exists(path))
31  return true;
32 
33  if (Directory.EnumerateFileSystemEntries(path).Any())
34  return false;
35 
36  Directory.Delete(path);
37  return true;
38  }
39 
41  public IEnumerable<string> GetDirectories(string path, CancellationToken cancellationToken)
42  {
43  foreach (var I in Directory.EnumerateDirectories(path))
44  {
45  yield return I;
46  cancellationToken.ThrowIfCancellationRequested();
47  }
48  }
49 
51  public IEnumerable<string> GetFiles(string path, CancellationToken cancellationToken)
52  {
53  foreach (var I in Directory.EnumerateFiles(path))
54  {
55  yield return I;
56  cancellationToken.ThrowIfCancellationRequested();
57  }
58  }
59 
61  public bool IsDirectory(string path)
62  {
63  if (path == null)
64  throw new ArgumentNullException(nameof(path));
65  return Directory.Exists(path);
66  }
67 
69  public byte[] ReadFile(string path)
70  {
71  if (path == null)
72  throw new ArgumentNullException(nameof(path));
73  return File.ReadAllBytes(path);
74  }
75 
77  public bool WriteFileChecked(string path, byte[] data, ref string sha1InOut, CancellationToken cancellationToken)
78  {
79  if (path == null)
80  throw new ArgumentNullException(nameof(path));
81  cancellationToken.ThrowIfCancellationRequested();
82  var directory = Path.GetDirectoryName(path);
83  Directory.CreateDirectory(directory);
84  cancellationToken.ThrowIfCancellationRequested();
85  using (var file = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
86  {
87  cancellationToken.ThrowIfCancellationRequested();
88 
89  //as nice as it would be to not have to arrayify the memory stream, we have to
90  //because, oddly enough sha1(memorystream) != sha1(memorystream.ToArray())
91  // vOv
92  byte[] originalBytes;
93  using (var readMs = new MemoryStream())
94  {
95  file.CopyTo(readMs);
96  originalBytes = readMs.ToArray();
97  }
98  if (originalBytes.Length != 0 && sha1InOut == null)
99  //no sha1? no write
100  return false;
101 
102  //suppressed due to only using for consistency checks
103 #pragma warning disable CA5350 // Do not use insecure cryptographic algorithm SHA1.
104  using (var sha1 = new SHA1Managed())
105 #pragma warning restore CA5350 // Do not use insecure cryptographic algorithm SHA1.
106  {
107  string GetSha1(byte[] dataToHash) => dataToHash != null && dataToHash.Length != 0 ? String.Join("", sha1.ComputeHash(dataToHash).Select(b => b.ToString("x2", CultureInfo.InvariantCulture))) : null;
108  var originalSha1 = GetSha1(originalBytes);
109  if (originalSha1 != sha1InOut)
110  {
111  sha1InOut = originalSha1;
112  return false;
113  }
114  sha1InOut = GetSha1(data);
115  }
116  cancellationToken.ThrowIfCancellationRequested();
117 
118  if (data != null)
119  {
120  file.Seek(0, SeekOrigin.Begin);
121 
122  cancellationToken.ThrowIfCancellationRequested();
123  file.SetLength(data.Length);
124  file.Write(data, 0, data.Length);
125  }
126  }
127  if (data == null)
128  File.Delete(path);
129  return true;
130  }
131  }
132 }
IEnumerable< string > GetDirectories(string path, CancellationToken cancellationToken)
Enumerate directories in a given path
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Globalization;
4 using System.IO;
5 using System.Linq;
6 using System.Security.Cryptography;
7 using System.Threading;
8 
9 namespace Tgstation.Server.Host.IO
10 {
13  {
15  public bool CreateDirectory(string path, CancellationToken cancellationToken)
16  {
17  if (IsDirectory(path))
18  return true;
19  cancellationToken.ThrowIfCancellationRequested();
20  Directory.CreateDirectory(path);
21  return false;
22  }
23 
25  public bool DeleteDirectory(string path)
26  {
27  if (File.Exists(path))
28  return false;
29 
30  if (!Directory.Exists(path))
31  return true;
32 
33  if (Directory.EnumerateFileSystemEntries(path).Any())
34  return false;
35 
36  Directory.Delete(path);
37  return true;
38  }
39 
41  public IEnumerable<string> GetDirectories(string path, CancellationToken cancellationToken)
42  {
43  foreach (var I in Directory.EnumerateDirectories(path))
44  {
45  yield return I;
46  cancellationToken.ThrowIfCancellationRequested();
47  }
48  }
49 
51  public IEnumerable<string> GetFiles(string path, CancellationToken cancellationToken)
52  {
53  foreach (var I in Directory.EnumerateFiles(path))
54  {
55  yield return I;
56  cancellationToken.ThrowIfCancellationRequested();
57  }
58  }
59 
61  public bool IsDirectory(string path)
62  {
63  if (path == null)
64  throw new ArgumentNullException(nameof(path));
65  return Directory.Exists(path);
66  }
67 
69  public byte[] ReadFile(string path)
70  {
71  if (path == null)
72  throw new ArgumentNullException(nameof(path));
73  return File.ReadAllBytes(path);
74  }
75 
77  public bool WriteFileChecked(string path, byte[] data, ref string sha1InOut, CancellationToken cancellationToken)
78  {
79  if (path == null)
80  throw new ArgumentNullException(nameof(path));
81  cancellationToken.ThrowIfCancellationRequested();
82  var directory = Path.GetDirectoryName(path);
83  Directory.CreateDirectory(directory);
84  cancellationToken.ThrowIfCancellationRequested();
85  using (var file = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
86  {
87  cancellationToken.ThrowIfCancellationRequested();
88 
89  //as nice as it would be to not have to arrayify the memory stream, we have to
90  //because, oddly enough sha1(memorystream) != sha1(memorystream.ToArray())
91  // vOv
92  byte[] originalBytes;
93  using (var readMs = new MemoryStream())
94  {
95  file.CopyTo(readMs);
96  originalBytes = readMs.ToArray();
97  }
98  if (originalBytes.Length != 0 && sha1InOut == null)
99  //no sha1? no write
100  return false;
101 
102  //suppressed due to only using for consistency checks
103 #pragma warning disable CA5350 // Do not use insecure cryptographic algorithm SHA1.
104  using (var sha1 = new SHA1Managed())
105 #pragma warning restore CA5350 // Do not use insecure cryptographic algorithm SHA1.
106  {
107  string GetSha1(byte[] dataToHash) => dataToHash != null && dataToHash.Length != 0 ? String.Join("", sha1.ComputeHash(dataToHash).Select(b => b.ToString("x2", CultureInfo.InvariantCulture))) : null;
108  var originalSha1 = GetSha1(originalBytes);
109  if (originalSha1 != sha1InOut)
110  {
111  sha1InOut = originalSha1;
112  return false;
113  }
114  sha1InOut = GetSha1(data);
115  }
116  cancellationToken.ThrowIfCancellationRequested();
117 
118  if (data != null)
119  {
120  file.Seek(0, SeekOrigin.Begin);
121 
122  cancellationToken.ThrowIfCancellationRequested();
123  file.SetLength(data.Length);
124  file.Write(data, 0, data.Length);
125  }
126  }
127  if (data == null)
128  File.Delete(path);
129  return true;
130  }
131  }
132 }
IEnumerable< string > GetDirectories(string path, CancellationToken cancellationToken)
Enumerate directories in a given path
bool CreateDirectory(string path, CancellationToken cancellationToken)
Create an empty directory at path
+
byte[] ReadFile(string path)
Read the bytes of a file at a given path
For accessing the disk in a synchronous manner
bool WriteFileChecked(string path, byte[] data, ref string sha1InOut, CancellationToken cancellationToken)
Write data to a file at a given path .
-
bool IsDirectory(string path)
Checks if a given path is a directory
bool DeleteDirectory(string path)
Deletes a directory at path if it&#39;s empty
diff --git a/_temporary_dmb_provider_8cs_source.html b/_temporary_dmb_provider_8cs_source.html index a0112b0fb8..542ed05ac2 100644 --- a/_temporary_dmb_provider_8cs_source.html +++ b/_temporary_dmb_provider_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
TemporaryDmbProvider.cs
-Go to the documentation of this file.
1 using System;
3 
4 namespace Tgstation.Server.Host.Components.Compiler
5 {
10  {
12  public string DmbName { get; }
13 
15  public string PrimaryDirectory { get; }
16 
18  public string SecondaryDirectory => throw new NotSupportedException();
19 
21  public CompileJob CompileJob { get; }
22 
29  public TemporaryDmbProvider(string directory, string dmb, CompileJob compileJob)
30  {
31  DmbName = dmb ?? throw new ArgumentNullException(nameof(dmb));
32  PrimaryDirectory = directory ?? throw new ArgumentNullException(nameof(directory));
33  CompileJob = compileJob ?? throw new ArgumentNullException(nameof(compileJob));
34  }
35 
37  public void Dispose() { }
38 
39  public void KeepAlive() => throw new NotSupportedException();
40  }
41 }
TemporaryDmbProvider(string directory, string dmb, CompileJob compileJob)
Construct a TemporaryDmbProvider
+Go to the documentation of this file.
1 using System;
3 
4 namespace Tgstation.Server.Host.Components.Compiler
5 {
10  {
12  public string DmbName { get; }
13 
15  public string PrimaryDirectory { get; }
16 
18  public string SecondaryDirectory => throw new NotSupportedException();
19 
21  public CompileJob CompileJob { get; }
22 
29  public TemporaryDmbProvider(string directory, string dmb, CompileJob compileJob)
30  {
31  DmbName = dmb ?? throw new ArgumentNullException(nameof(dmb));
32  PrimaryDirectory = directory ?? throw new ArgumentNullException(nameof(directory));
33  CompileJob = compileJob ?? throw new ArgumentNullException(nameof(compileJob));
34  }
35 
37  public void Dispose() { }
38 
39  public void KeepAlive() => throw new NotSupportedException();
40  }
41 }
TemporaryDmbProvider(string directory, string dmb, CompileJob compileJob)
Construct a TemporaryDmbProvider
+ - diff --git a/_test_api_client_8cs_source.html b/_test_api_client_8cs_source.html index 2cc741b9a0..6ce42285ef 100644 --- a/_test_api_client_8cs_source.html +++ b/_test_api_client_8cs_source.html @@ -92,10 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
TestApiClient.cs
-Go to the documentation of this file.
1 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using Moq;
3 using Newtonsoft.Json;
4 using Newtonsoft.Json.Converters;
5 using Newtonsoft.Json.Serialization;
6 using System;
7 using System.Net;
8 using System.Net.Http;
9 using System.Net.Http.Headers;
10 using System.Threading;
11 using System.Threading.Tasks;
12 using Tgstation.Server.Api;
14 
15 namespace Tgstation.Server.Client.Tests
16 {
17  [TestClass]
18  public sealed class TestApiClient
19  {
20  [TestMethod]
22  {
23  var sample = new Byond
24  {
25  Version = new Version(511, 1385)
26  };
27 
28  var sampleJson = JsonConvert.SerializeObject(sample, new JsonSerializerSettings
29  {
30  ContractResolver = new CamelCasePropertyNamesContractResolver(),
31  Converters = new[] { new VersionConverter() }
32  });
33 
34  var response = new HttpResponseMessage(HttpStatusCode.OK)
35  {
36  Content = new StringContent(sampleJson)
37  };
38 
39  var httpClient = new Mock<IHttpClient>();
40  httpClient.Setup(x => x.SendAsync(It.IsNotNull<HttpRequestMessage>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(response));
41 
42  var client = new ApiClient(httpClient.Object, new Uri("http://fake.com"), new ApiHeaders(new ProductHeaderValue("fake"), "fake"));
43 
44  var result = await client.Read<Byond>(Routes.Byond, default).ConfigureAwait(false);
45  Assert.AreEqual(sample.Version, result.Version);
46  }
47 
48  [TestMethod]
49  public async Task TestUnrecognizedResponse()
50  {
51  var sample = new Byond
52  {
53  Version = new Version(511, 1385)
54  };
55 
56  var fakeJson = "asdfasd <>F#(*)U*#JLI";
57 
58  var response = new HttpResponseMessage(HttpStatusCode.OK)
59  {
60  Content = new StringContent(fakeJson)
61  };
62 
63  var httpClient = new Mock<IHttpClient>();
64  httpClient.Setup(x => x.SendAsync(It.IsNotNull<HttpRequestMessage>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(response));
65 
66  var client = new ApiClient(httpClient.Object, new Uri("http://fake.com"), new ApiHeaders(new ProductHeaderValue("fake"), "fake"));
67 
68  await Assert.ThrowsExceptionAsync<UnrecognizedResponseException>(() => client.Read<Byond>(Routes.Byond, default)).ConfigureAwait(false);
69  }
70  }
71 }
const string Byond
The Models.Byond controller
Definition: Routes.cs:34
+Go to the documentation of this file.
1 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using Moq;
3 using Newtonsoft.Json;
4 using Newtonsoft.Json.Converters;
5 using Newtonsoft.Json.Serialization;
6 using System;
7 using System.Net;
8 using System.Net.Http;
9 using System.Net.Http.Headers;
10 using System.Threading;
11 using System.Threading.Tasks;
12 using Tgstation.Server.Api;
14 
15 namespace Tgstation.Server.Client.Tests
16 {
17  [TestClass]
18  public sealed class TestApiClient
19  {
20  [TestMethod]
22  {
23  var sample = new Byond
24  {
25  Version = new Version(511, 1385)
26  };
27 
28  var sampleJson = JsonConvert.SerializeObject(sample, new JsonSerializerSettings
29  {
30  ContractResolver = new CamelCasePropertyNamesContractResolver(),
31  Converters = new[] { new VersionConverter() }
32  });
33 
34  var response = new HttpResponseMessage(HttpStatusCode.OK)
35  {
36  Content = new StringContent(sampleJson)
37  };
38 
39  var httpClient = new Mock<IHttpClient>();
40  httpClient.Setup(x => x.SendAsync(It.IsNotNull<HttpRequestMessage>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(response));
41 
42  var client = new ApiClient(httpClient.Object, new Uri("http://fake.com"), new ApiHeaders(new ProductHeaderValue("fake"), "fake"));
43 
44  var result = await client.Read<Byond>(Routes.Byond, default).ConfigureAwait(false);
45  Assert.AreEqual(sample.Version, result.Version);
46  }
47 
48  [TestMethod]
49  public async Task TestUnrecognizedResponse()
50  {
51  var sample = new Byond
52  {
53  Version = new Version(511, 1385)
54  };
55 
56  var fakeJson = "asdfasd <>F#(*)U*#JLI";
57 
58  var response = new HttpResponseMessage(HttpStatusCode.OK)
59  {
60  Content = new StringContent(fakeJson)
61  };
62 
63  var httpClient = new Mock<IHttpClient>();
64  httpClient.Setup(x => x.SendAsync(It.IsNotNull<HttpRequestMessage>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(response));
65 
66  var client = new ApiClient(httpClient.Object, new Uri("http://fake.com"), new ApiHeaders(new ProductHeaderValue("fake"), "fake"));
67 
68  await Assert.ThrowsExceptionAsync<UnrecognizedResponseException>(() => client.Read<Byond>(Routes.Byond, default)).ConfigureAwait(false);
69  }
70  }
71 }
const string Byond
The Models.Byond controller
Definition: Routes.cs:34
+
Represents the header that must be present for every server request
Definition: ApiHeaders.cs:16
diff --git a/_test_dream_daemon_client_8cs_source.html b/_test_dream_daemon_client_8cs_source.html index 54085411e1..3cea74279a 100644 --- a/_test_dream_daemon_client_8cs_source.html +++ b/_test_dream_daemon_client_8cs_source.html @@ -92,7 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
TestDreamDaemonClient.cs
-Go to the documentation of this file.
1 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using Moq;
3 using System;
4 using System.Collections.Generic;
5 using System.Text;
6 using System.Threading;
7 using System.Threading.Tasks;
8 using Tgstation.Server.Api;
10 
11 namespace Tgstation.Server.Client.Components.Tests
12 {
13  [TestClass]
14  public sealed class TestDreamDaemonClient
15  {
16  [TestMethod]
17  public async Task TestStart()
18  {
19  var example = new Job
20  {
21  Id = 347,
22  StartedAt = DateTimeOffset.Now
23  };
24 
25  var inst = new Instance
26  {
27  Id = 4958
28  };
29 
30  var mockApiClient = new Mock<IApiClient>();
31  mockApiClient.Setup(x => x.Create<Job>(Routes.DreamDaemon, inst.Id, It.IsAny<CancellationToken>())).Returns(Task.FromResult(example));
32 
33  var client = new DreamDaemonClient(mockApiClient.Object, inst);
34 
35  var result = await client.Start(default).ConfigureAwait(false);
36  Assert.AreSame(example, result);
37  }
38  }
39 }
+Go to the documentation of this file.
1 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using Moq;
3 using System;
4 using System.Collections.Generic;
5 using System.Text;
6 using System.Threading;
7 using System.Threading.Tasks;
8 using Tgstation.Server.Api;
10 
11 namespace Tgstation.Server.Client.Components.Tests
12 {
13  [TestClass]
14  public sealed class TestDreamDaemonClient
15  {
16  [TestMethod]
17  public async Task TestStart()
18  {
19  var example = new Job
20  {
21  Id = 347,
22  StartedAt = DateTimeOffset.Now
23  };
24 
25  var inst = new Instance
26  {
27  Id = 4958
28  };
29 
30  var mockApiClient = new Mock<IApiClient>();
31  mockApiClient.Setup(x => x.Create<Job>(Routes.DreamDaemon, inst.Id, It.IsAny<CancellationToken>())).Returns(Task.FromResult(example));
32 
33  var client = new DreamDaemonClient(mockApiClient.Object, inst);
34 
35  var result = await client.Start(default).ConfigureAwait(false);
36  Assert.AreSame(example, result);
37  }
38  }
39 }
+
Routes to a server actions
Definition: Routes.cs:9
diff --git a/_test_merge_base_8cs_source.html b/_test_merge_base_8cs_source.html index 3d042e2dcb..e6093e2441 100644 --- a/_test_merge_base_8cs_source.html +++ b/_test_merge_base_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
TestMergeBase.cs
-Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 
4 namespace Tgstation.Server.Api.Models.Internal
5 {
9  public abstract class TestMergeBase : TestMergeParameters
10  {
14  [Required]
15  public string TitleAtMerge { get; set; }
16 
20  [Required]
21  public string BodyAtMerge { get; set; }
22 
26  [Required]
27 #pragma warning disable CA1056 // Uri properties should not be strings
28  public string Url { get; set; }
29 #pragma warning restore CA1056 // Uri properties should not be strings
30 
34  [Required]
35  public string Author { get; set; }
36 
40  protected TestMergeBase() { }
41 
46  protected TestMergeBase(TestMergeBase copy)
47  {
48  if (copy == null)
49  throw new ArgumentNullException(nameof(copy));
50  Author = copy.Author;
51  BodyAtMerge = copy.BodyAtMerge;
52  Comment = copy.Comment;
53  Number = copy.Number;
54  PullRequestRevision = copy.PullRequestRevision;
55  TitleAtMerge = copy.TitleAtMerge;
56  Url = copy.Url;
57  }
58  }
59 }
+Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 
4 namespace Tgstation.Server.Api.Models.Internal
5 {
9  public abstract class TestMergeBase : TestMergeParameters
10  {
14  [Required]
15  public string TitleAtMerge { get; set; }
16 
20  [Required]
21  public string BodyAtMerge { get; set; }
22 
26  [Required]
27 #pragma warning disable CA1056 // Uri properties should not be strings
28  public string Url { get; set; }
29 #pragma warning restore CA1056 // Uri properties should not be strings
30 
34  [Required]
35  public string Author { get; set; }
36 
40  protected TestMergeBase() { }
41 
46  protected TestMergeBase(TestMergeBase copy)
47  {
48  if (copy == null)
49  throw new ArgumentNullException(nameof(copy));
50  Author = copy.Author;
51  BodyAtMerge = copy.BodyAtMerge;
52  Comment = copy.Comment;
53  Number = copy.Number;
54  PullRequestRevision = copy.PullRequestRevision;
55  TitleAtMerge = copy.TitleAtMerge;
56  Url = copy.Url;
57  }
58  }
59 }
+
string TitleAtMerge
The title of the pull request
string Author
The author of the pull request
Layer of test merge data required internally
Definition: TestMergeBase.cs:9
int Number
The number of the pull request
string BodyAtMerge
The body of the pull request
-
Parameters for creating a TestMerge
string Url
The URL of the pull request
diff --git a/_test_merge_parameters_8cs_source.html b/_test_merge_parameters_8cs_source.html index 2a2b71a859..e29132fe19 100644 --- a/_test_merge_parameters_8cs_source.html +++ b/_test_merge_parameters_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
TestMergeParameters.cs
-Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public class TestMergeParameters
9  {
13  [Required]
14  public int? Number { get; set; }
15 
19  [Required]
20  public string PullRequestRevision { get; set; }
21 
25  public string Comment { get; set; }
26  }
27 }
- +Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public class TestMergeParameters
9  {
13  [Required]
14  public int? Number { get; set; }
15 
19  [Required]
20  public string PullRequestRevision { get; set; }
21 
25  public string Comment { get; set; }
26  }
27 }
+
Parameters for creating a TestMerge
diff --git a/_test_server_client_factory_8cs_source.html b/_test_server_client_factory_8cs_source.html index 1256f42f1a..560db30657 100644 --- a/_test_server_client_factory_8cs_source.html +++ b/_test_server_client_factory_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
TestServerClientFactory.cs
-Go to the documentation of this file.
1 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using System;
3 using System.Net.Http.Headers;
4 
5 namespace Tgstation.Server.Client.Tests
6 {
7  [TestClass]
8  public sealed class TestServerClientFactory
9  {
10  [TestMethod]
11  public void TestConstruction()
12  {
13  Assert.ThrowsException<ArgumentNullException>(() => new ServerClientFactory(null));
14  new ServerClientFactory(new ProductHeaderValue("Tgstation.Server.Client.Tests", GetType().Assembly.GetName().Version.ToString()));
15  }
16  }
17 }
+Go to the documentation of this file.
1 using Microsoft.VisualStudio.TestTools.UnitTesting;
2 using System;
3 using System.Net.Http.Headers;
4 
5 namespace Tgstation.Server.Client.Tests
6 {
7  [TestClass]
8  public sealed class TestServerClientFactory
9  {
10  [TestMethod]
11  public void TestConstruction()
12  {
13  Assert.ThrowsException<ArgumentNullException>(() => new ServerClientFactory(null));
14  new ServerClientFactory(new ProductHeaderValue("Tgstation.Server.Client.Tests", GetType().Assembly.GetName().Version.ToString()));
15  }
16  }
17 }
+ - diff --git a/_test_watchdog_factory_8cs_source.html b/_test_watchdog_factory_8cs_source.html index a23f42ad01..d6a4abcc4d 100644 --- a/_test_watchdog_factory_8cs_source.html +++ b/_test_watchdog_factory_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
TestWatchdogFactory.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using Microsoft.VisualStudio.TestTools.UnitTesting;
3 using Moq;
4 
5 namespace Tgstation.Server.Host.Watchdog.Tests
6 {
10  [TestClass]
11  public sealed class TestWatchdogFactory
12  {
13  [TestMethod]
14  public void TestCreateWatchdog()
15  {
16  var factory = new WatchdogFactory();
17  Assert.IsNotNull(factory.CreateWatchdog(new LoggerFactory()));
18  }
19  }
20 }
- +Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using Microsoft.VisualStudio.TestTools.UnitTesting;
3 using Moq;
4 
5 namespace Tgstation.Server.Host.Watchdog.Tests
6 {
10  [TestClass]
11  public sealed class TestWatchdogFactory
12  {
13  [TestMethod]
14  public void TestCreateWatchdog()
15  {
16  var factory = new WatchdogFactory();
17  Assert.IsNotNull(factory.CreateWatchdog(new LoggerFactory()));
18  }
19  }
20 }
+ diff --git a/_tgs_authorize_attribute_8cs_source.html b/_tgs_authorize_attribute_8cs_source.html index 21a86fdfc5..6bc17d17c3 100644 --- a/_tgs_authorize_attribute_8cs_source.html +++ b/_tgs_authorize_attribute_8cs_source.html @@ -92,11 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
TgsAuthorizeAttribute.cs
-Go to the documentation of this file.
1 using System;
2 using Microsoft.AspNetCore.Authorization;
4 
5 namespace Tgstation.Server.Host.Controllers
6 {
10  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
12  {
16  public TgsAuthorizeAttribute() { }
17 
22  public TgsAuthorizeAttribute(AdministrationRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
23 
28  public TgsAuthorizeAttribute(InstanceManagerRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
29 
34  public TgsAuthorizeAttribute(RepositoryRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
35 
40  public TgsAuthorizeAttribute(ByondRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
41 
46  public TgsAuthorizeAttribute(DreamMakerRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
47 
52  public TgsAuthorizeAttribute(DreamDaemonRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
53 
58  public TgsAuthorizeAttribute(ChatBotRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
59 
64  public TgsAuthorizeAttribute(ConfigurationRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
65 
70  public TgsAuthorizeAttribute(InstanceUserRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
71  }
72 }
ConfigurationRights
Rights for Models.ConfigurationFile
+Go to the documentation of this file.
1 using System;
2 using Microsoft.AspNetCore.Authorization;
4 
5 namespace Tgstation.Server.Host.Controllers
6 {
10  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
12  {
16  public TgsAuthorizeAttribute() { }
17 
22  public TgsAuthorizeAttribute(AdministrationRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
23 
28  public TgsAuthorizeAttribute(InstanceManagerRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
29 
34  public TgsAuthorizeAttribute(RepositoryRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
35 
40  public TgsAuthorizeAttribute(ByondRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
41 
46  public TgsAuthorizeAttribute(DreamMakerRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
47 
52  public TgsAuthorizeAttribute(DreamDaemonRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
53 
58  public TgsAuthorizeAttribute(ChatBotRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
59 
64  public TgsAuthorizeAttribute(ConfigurationRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
65 
70  public TgsAuthorizeAttribute(InstanceUserRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights);
71  }
72 }
ConfigurationRights
Rights for Models.ConfigurationFile
ByondRights
Rights for Models.Byond
Definition: ByondRights.cs:9
InstanceUserRights
Rights for an Models.Instance
+
Helper for using the AuthorizeAttribute with the Api.Rights system
AdministrationRights
Rights for Models.Administration
@@ -104,7 +105,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamDaemonRights
Rights for Models.DreamDaemon
InstanceManagerRights
Rights for managing Models.Instances
DreamMakerRights
Rights for Models.DreamMaker
-
RepositoryRights
Rights for a Models.Repository
diff --git a/_tgstation_8_server_8_api_2_models_2_chat_bot_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_chat_bot_8cs_source.html index 263bdd5c02..f623054d72 100644 --- a/_tgstation_8_server_8_api_2_models_2_chat_bot_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_chat_bot_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ChatBot.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 
5 namespace Tgstation.Server.Api.Models
6 {
8  public sealed class ChatBot : Internal.ChatBot
9  {
13  public List<ChatChannel> Channels { get; set; }
14 
20  {
21  switch (Provider)
22  {
23  case ChatProvider.Discord:
24  return Channels.Select(x => x.DiscordChannelId.HasValue && x.IrcChannel == null).All(x => x);
25  case ChatProvider.Irc:
26  return Channels.Select(x => !x.DiscordChannelId.HasValue && x.IrcChannel != null).All(x => x);
27  default:
28  throw new InvalidOperationException("Invalid provider type!");
29  }
30  }
31  }
32 }
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 
5 namespace Tgstation.Server.Api.Models
6 {
8  public sealed class ChatBot : Internal.ChatBot
9  {
13  public List<ChatChannel> Channels { get; set; }
14 
20  {
21  switch (Provider)
22  {
23  case ChatProvider.Discord:
24  return Channels?.Select(x => x.DiscordChannelId.HasValue && x.IrcChannel == null).All(x => x) ?? true;
25  case ChatProvider.Irc:
26  return Channels?.Select(x => !x.DiscordChannelId.HasValue && x.IrcChannel != null).All(x => x) ?? true;
27  default:
28  throw new InvalidOperationException("Invalid provider type!");
29  }
30  }
31  }
32 }
+ -
bool ValidateProviderChannelTypes()
Validates Channels are correct for the Internal.ChatBot.Provider
Definition: ChatBot.cs:19
ChatProvider
Represents a chat service provider
Definition: ChatProvider.cs:6
diff --git a/_tgstation_8_server_8_api_2_models_2_chat_channel_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_chat_channel_8cs_source.html index 63bbb6fa7c..5b842d78c3 100644 --- a/_tgstation_8_server_8_api_2_models_2_chat_channel_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_chat_channel_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ChatChannel.cs
-Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public class ChatChannel
9  {
13  public string IrcChannel { get; set; }
14 
18  public ulong? DiscordChannelId { get; set; }
19 
23  [Required]
24  public bool? IsAdminChannel { get; set; }
25 
29  [Required]
30  public bool? IsWatchdogChannel { get; set; }
31 
35  [Required]
36  public bool? IsUpdatesChannel { get; set; }
37 
41  public string Tag { get; set; }
42  }
43 }
Indicates a chat channel
Definition: ChatChannel.cs:8
+Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public class ChatChannel
9  {
13  public string IrcChannel { get; set; }
14 
18  public ulong? DiscordChannelId { get; set; }
19 
23  [Required]
24  public bool? IsAdminChannel { get; set; }
25 
29  [Required]
30  public bool? IsWatchdogChannel { get; set; }
31 
35  [Required]
36  public bool? IsUpdatesChannel { get; set; }
37 
41  public string Tag { get; set; }
42  }
43 }
Indicates a chat channel
Definition: ChatChannel.cs:8
+ -
diff --git a/_tgstation_8_server_8_api_2_models_2_compile_job_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_compile_job_8cs_source.html index efdf2d0850..271511987e 100644 --- a/_tgstation_8_server_8_api_2_models_2_compile_job_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_compile_job_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
CompileJob.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Models
4 {
6  public sealed class CompileJob : Internal.CompileJob
7  {
11  public Job Job { get; set; }
12 
17 
21  public Version ByondVersion { get; set; }
22  }
23 }
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Models
4 {
6  public sealed class CompileJob : Internal.CompileJob
7  {
11  public Job Job { get; set; }
12 
17 
21  public Version ByondVersion { get; set; }
22  }
23 }
+ -
Represents a long running job on the server. Model is read-only, updates attempt to cancel the job ...
Definition: Job.cs:6
diff --git a/_tgstation_8_server_8_api_2_models_2_dream_maker_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_dream_maker_8cs_source.html index c574ac2256..259eebb940 100644 --- a/_tgstation_8_server_8_api_2_models_2_dream_maker_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_dream_maker_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamMaker.cs
-Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public class DreamMaker
9  {
13  public string ProjectName { get; set; }
14 
18  [Required]
19  public ushort? ApiValidationPort { get; set; }
20 
24  [Required]
25  public DreamDaemonSecurity? ApiValidationSecurityLevel { get; set; }
26  }
27 }
Represents the state of the DreamMaker compiler. Create action starts a new compile. Delete action cancels the current compile
Definition: DreamMaker.cs:8
+Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public class DreamMaker
9  {
13  public string ProjectName { get; set; }
14 
18  [Required]
19  public ushort? ApiValidationPort { get; set; }
20 
24  [Required]
25  public DreamDaemonSecurity? ApiValidationSecurityLevel { get; set; }
26  }
27 }
Represents the state of the DreamMaker compiler. Create action starts a new compile. Delete action cancels the current compile
Definition: DreamMaker.cs:8
+ -
DreamDaemonSecurity
DreamDaemon&#39;s security level
diff --git a/_tgstation_8_server_8_api_2_models_2_instance_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_instance_8cs_source.html index 25641700b5..92ed8c34bc 100644 --- a/_tgstation_8_server_8_api_2_models_2_instance_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_instance_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Instance.cs
-Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 using System.ComponentModel.DataAnnotations.Schema;
3 
4 namespace Tgstation.Server.Api.Models
5 {
9  public class Instance
10  {
14  public long Id { get; set; }
15 
19  [Required]
20  public string Name { get; set; }
21 
25  [Required]
26  public string Path { get; set; }
27 
31  [Required]
32  public bool? Online { get; set; }
33 
37  [Required]
38  public ConfigurationType? ConfigurationType { get; set; }
39 
43  [Required]
44  public uint? AutoUpdateInterval { get; set; }
45 
50  [NotMapped]
51  public Job MoveJob { get; set; }
52 
54  public Instance CloneMetadata() => new Instance
55  {
56  Id = Id,
57  Name = Name,
58  Path = Path,
59  Online = Online,
61  AutoUpdateInterval = AutoUpdateInterval
62  };
63  }
64 }
+Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 using System.ComponentModel.DataAnnotations.Schema;
3 
4 namespace Tgstation.Server.Api.Models
5 {
9  public class Instance
10  {
14  public long Id { get; set; }
15 
19  [Required]
20  public string Name { get; set; }
21 
25  [Required]
26  public string Path { get; set; }
27 
31  [Required]
32  public bool? Online { get; set; }
33 
37  [Required]
38  public ConfigurationType? ConfigurationType { get; set; }
39 
43  [Required]
44  public uint? AutoUpdateInterval { get; set; }
45 
50  [NotMapped]
51  public Job MoveJob { get; set; }
52 
54  public Instance CloneMetadata() => new Instance
55  {
56  Id = Id,
57  Name = Name,
58  Path = Path,
59  Online = Online,
61  AutoUpdateInterval = AutoUpdateInterval
62  };
63  }
64 }
+
ConfigurationType
The type of configuration allowed on an Instance
-
Metadata about a server instance
Definition: Instance.cs:9
diff --git a/_tgstation_8_server_8_api_2_models_2_instance_user_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_instance_user_8cs_source.html index 9de5692908..0163c9b558 100644 --- a/_tgstation_8_server_8_api_2_models_2_instance_user_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_instance_user_8cs_source.html @@ -92,15 +92,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceUser.cs
-Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
3 
4 namespace Tgstation.Server.Api.Models
5 {
9  public class InstanceUser
10  {
14  [Required]
15  public long? UserId { get; set; }
16 
20  [Required]
21  public InstanceUserRights? InstanceUserRights { get; set; }
22 
26  [Required]
27  public ByondRights? ByondRights { get; set; }
28 
32  [Required]
33  public DreamDaemonRights? DreamDaemonRights { get; set; }
34 
38  [Required]
39  public DreamMakerRights? DreamMakerRights { get; set; }
40 
44  [Required]
45  public RepositoryRights? RepositoryRights { get; set; }
46 
50  [Required]
51  public ChatBotRights? ChatBotRights { get; set; }
52 
56  [Required]
58  }
59 }
ConfigurationRights
Rights for Models.ConfigurationFile
+Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
3 
4 namespace Tgstation.Server.Api.Models
5 {
9  public class InstanceUser
10  {
14  [Required]
15  public long? UserId { get; set; }
16 
20  [Required]
21  public InstanceUserRights? InstanceUserRights { get; set; }
22 
26  [Required]
27  public ByondRights? ByondRights { get; set; }
28 
32  [Required]
33  public DreamDaemonRights? DreamDaemonRights { get; set; }
34 
38  [Required]
39  public DreamMakerRights? DreamMakerRights { get; set; }
40 
44  [Required]
45  public RepositoryRights? RepositoryRights { get; set; }
46 
50  [Required]
51  public ChatBotRights? ChatBotRights { get; set; }
52 
56  [Required]
58  }
59 }
ConfigurationRights
Rights for Models.ConfigurationFile
ByondRights
Rights for Models.Byond
Definition: ByondRights.cs:9
InstanceUserRights
Rights for an Models.Instance
+
DreamDaemonRights
Rights for Models.DreamDaemon
Represents a Users permissions in an Instance
Definition: InstanceUser.cs:9
DreamMakerRights
Rights for Models.DreamMaker
-
RepositoryRights
Rights for a Models.Repository
diff --git a/_tgstation_8_server_8_api_2_models_2_internal_2_chat_bot_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_internal_2_chat_bot_8cs_source.html index 27a6fc1803..e3dd82762d 100644 --- a/_tgstation_8_server_8_api_2_models_2_internal_2_chat_bot_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_internal_2_chat_bot_8cs_source.html @@ -92,8 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ChatBot.cs
-Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Api.Models.Internal
4 {
8  public class ChatBot
9  {
13  public long Id { get; set; }
14 
18  [Required]
19  public string Name { get; set; }
20 
24  public bool? Enabled { get; set; }
25 
29  public ChatProvider? Provider { get; set; }
30 
34  [Required]
35  public string ConnectionString { get; set; }
36  }
37 }
-
Manage the server chat bots
Definition: ChatBot.cs:8
+Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 using System.ComponentModel.DataAnnotations.Schema;
4 
5 namespace Tgstation.Server.Api.Models.Internal
6 {
10  public class ChatBot
11  {
15  public long Id { get; set; }
16 
20  [Required]
21  public string Name { get; set; }
22 
26  public bool? Enabled { get; set; }
27 
31  public ChatProvider? Provider { get; set; }
32 
36  [Required]
37  public string ConnectionString { get; set; }
38 
42  [NotMapped]
43  public ChatConnectionStringBuilder ConnectionStringBuilder
44  {
45  get
46  {
47  if (ConnectionString == null)
48  return null;
49  switch (Provider)
50  {
51  case ChatProvider.Discord:
52  return new DiscordConnectionStringBuilder(ConnectionString);
53  case ChatProvider.Irc:
54  return new IrcConnectionStringBuilder(ConnectionString);
55  default:
56  throw new InvalidOperationException("Invalid Provider!");
57  }
58  }
59  set
60  {
61  if (value?.Valid == false)
62  throw new InvalidOperationException("Cannot set invalid ChatConnectionStringBuilder!");
63  ConnectionString = value?.ToString();
64  }
65  }
66  }
67 }
+ +
ChatConnectionStringBuilder for ChatProvider.Discord
+
Manage the server chat bots
Definition: ChatBot.cs:10
+ +
ChatConnectionStringBuilder for ChatProvider.Irc
ChatProvider
Represents a chat service provider
Definition: ChatProvider.cs:6
diff --git a/_tgstation_8_server_8_api_2_models_2_internal_2_compile_job_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_internal_2_compile_job_8cs_source.html index c248744a29..6a44e5d9f1 100644 --- a/_tgstation_8_server_8_api_2_models_2_internal_2_compile_job_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_internal_2_compile_job_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
CompileJob.cs
-Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 
4 namespace Tgstation.Server.Api.Models.Internal
5 {
9  public class CompileJob
10  {
14  public long Id { get; set; }
15 
19  [Required]
20  public string DmeName { get; set; }
21 
25  [Required]
26  public string Output { get; set; }
27 
31  [Required]
32  public Guid? DirectoryName { get; set; }
33 
37  [Required]
38  public DreamDaemonSecurity? MinimumSecurityLevel { get; set; }
39  }
40 }
+Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 
4 namespace Tgstation.Server.Api.Models.Internal
5 {
9  public class CompileJob
10  {
14  public long Id { get; set; }
15 
19  [Required]
20  public string DmeName { get; set; }
21 
25  [Required]
26  public string Output { get; set; }
27 
31  [Required]
32  public Guid? DirectoryName { get; set; }
33 
37  [Required]
38  public DreamDaemonSecurity? MinimumSecurityLevel { get; set; }
39  }
40 }
+
Represents a run of DreamMaker
Definition: CompileJob.cs:9
-
DreamDaemonSecurity
DreamDaemon&#39;s security level
diff --git a/_tgstation_8_server_8_api_2_models_2_internal_2_dream_daemon_settings_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_internal_2_dream_daemon_settings_8cs_source.html index 82e238edb4..2902ffbd74 100644 --- a/_tgstation_8_server_8_api_2_models_2_internal_2_dream_daemon_settings_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_internal_2_dream_daemon_settings_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamDaemonSettings.cs
-Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Api.Models.Internal
4 {
9  {
13  [Required]
14  public bool? AutoStart { get; set; }
15 
19  [Required]
20  public bool? SoftRestart { get; set; }
21 
25  [Required]
26  public bool? SoftShutdown { get; set; }
27  }
28 }
+Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Api.Models.Internal
4 {
9  {
13  [Required]
14  public bool? AutoStart { get; set; }
15 
19  [Required]
20  public bool? SoftRestart { get; set; }
21 
25  [Required]
26  public bool? SoftShutdown { get; set; }
27  }
28 }
+ -
diff --git a/_tgstation_8_server_8_api_2_models_2_internal_2_job_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_internal_2_job_8cs_source.html index e29c13965e..ce9e8f610c 100644 --- a/_tgstation_8_server_8_api_2_models_2_internal_2_job_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_internal_2_job_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Job.cs
-Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
4 
5 namespace Tgstation.Server.Api.Models.Internal
6 {
10  public class Job
11  {
15  public long Id { get; set; }
16 
20  [Required]
21  public string Description { get; set; }
22 
26  public string ExceptionDetails { get; set; }
27 
31  [Required]
32  public DateTimeOffset? StartedAt { get; set; }
33 
37  public DateTimeOffset? StoppedAt { get; set; }
38 
42  [Required]
43  public bool? Cancelled { get; set; }
44 
48  public RightsType? CancelRightsType { get; set; }
49 
53  public ulong? CancelRight { get; set; }
54  }
55 }
RightsType
The type of rights a model uses
Definition: RightsType.cs:6
+Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
4 
5 namespace Tgstation.Server.Api.Models.Internal
6 {
10  public class Job
11  {
15  public long Id { get; set; }
16 
20  [Required]
21  public string Description { get; set; }
22 
26  public string ExceptionDetails { get; set; }
27 
31  [Required]
32  public DateTimeOffset? StartedAt { get; set; }
33 
37  public DateTimeOffset? StoppedAt { get; set; }
38 
42  [Required]
43  public bool? Cancelled { get; set; }
44 
48  public RightsType? CancelRightsType { get; set; }
49 
53  public ulong? CancelRight { get; set; }
54  }
55 }
RightsType
The type of rights a model uses
Definition: RightsType.cs:6
+
Represents a long running job
Definition: Job.cs:10
- diff --git a/_tgstation_8_server_8_api_2_models_2_internal_2_repository_settings_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_internal_2_repository_settings_8cs_source.html index f664339107..e252fc7267 100644 --- a/_tgstation_8_server_8_api_2_models_2_internal_2_repository_settings_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_internal_2_repository_settings_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RepositorySettings.cs
-Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Api.Models.Internal
4 {
8  public class RepositorySettings
9  {
13  [Required]
14  public string CommitterName { get; set; }
15 
19  [Required]
20  public string CommitterEmail { get; set; }
21 
25  public string AccessUser { get; set; }
26 
30  public string AccessToken { get; set; }
31 
35  [Required]
36  public bool? PushTestMergeCommits { get; set; }
37 
41  [Required]
42  public bool? ShowTestMergeCommitters { get; set; }
43 
47  [Required]
48  public bool? AutoUpdatesKeepTestMerges { get; set; }
49 
53  [Required]
54  public bool? AutoUpdatesSynchronize { get; set; }
55  }
56 }
Represents configurable settings for a Repository
+Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Api.Models.Internal
4 {
8  public class RepositorySettings
9  {
13  [Required]
14  public string CommitterName { get; set; }
15 
19  [Required]
20  public string CommitterEmail { get; set; }
21 
25  public string AccessUser { get; set; }
26 
30  public string AccessToken { get; set; }
31 
35  [Required]
36  public bool? PushTestMergeCommits { get; set; }
37 
41  [Required]
42  public bool? ShowTestMergeCommitters { get; set; }
43 
47  [Required]
48  public bool? AutoUpdatesKeepTestMerges { get; set; }
49 
53  [Required]
54  public bool? AutoUpdatesSynchronize { get; set; }
55  }
56 }
Represents configurable settings for a Repository
+ -
diff --git a/_tgstation_8_server_8_api_2_models_2_internal_2_revision_information_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_internal_2_revision_information_8cs_source.html index 2c53457b08..16acfba64e 100644 --- a/_tgstation_8_server_8_api_2_models_2_internal_2_revision_information_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_internal_2_revision_information_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RevisionInformation.cs
-Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Api.Models.Internal
4 {
8  public class RevisionInformation
9  {
13  [Required, StringLength(40)]
14  public string CommitSha { get; set; }
15 
19  [Required, StringLength(40)]
20  public string OriginCommitSha { get; set; }
21  }
22 }
- +Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Api.Models.Internal
4 {
8  public class RevisionInformation
9  {
13  [Required, StringLength(40)]
14  public string CommitSha { get; set; }
15 
19  [Required, StringLength(40)]
20  public string OriginCommitSha { get; set; }
21  }
22 }
+
Represents information about a current git revison
diff --git a/_tgstation_8_server_8_api_2_models_2_internal_2_test_merge_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_internal_2_test_merge_8cs_source.html index 389b673dd6..19a405f8d6 100644 --- a/_tgstation_8_server_8_api_2_models_2_internal_2_test_merge_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_internal_2_test_merge_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
TestMerge.cs
-Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 
4 namespace Tgstation.Server.Api.Models.Internal
5 {
9  public class TestMerge : TestMergeBase
10  {
14  public long Id { get; set; }
15 
19  [Required]
20  public DateTimeOffset MergedAt { get; set; }
21  }
22 }
+Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 
4 namespace Tgstation.Server.Api.Models.Internal
5 {
9  public class TestMerge : TestMergeBase
10  {
14  public long Id { get; set; }
15 
19  [Required]
20  public DateTimeOffset MergedAt { get; set; }
21  }
22 }
+
Represents a merge of a GitHub pull request
Definition: TestMerge.cs:9
Layer of test merge data required internally
Definition: TestMergeBase.cs:9
-
diff --git a/_tgstation_8_server_8_api_2_models_2_internal_2_user_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_internal_2_user_8cs_source.html index 3bc380da24..5be0334d61 100644 --- a/_tgstation_8_server_8_api_2_models_2_internal_2_user_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_internal_2_user_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
User.cs
-Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
4 
5 namespace Tgstation.Server.Api.Models.Internal
6 {
10  public class User
11  {
15  public long Id { get; set; }
16 
20  [Required]
21  public bool? Enabled { get; set; }
22 
26  [Required]
27  public DateTimeOffset? CreatedAt { get; set; }
28 
32  public string SystemIdentifier { get; set; }
33 
37  [Required]
38  public string Name { get; set; }
39 
43  [Required]
45 
49  [Required]
51  }
52 }
+Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
4 
5 namespace Tgstation.Server.Api.Models.Internal
6 {
10  public class User
11  {
15  public long Id { get; set; }
16 
20  [Required]
21  public bool? Enabled { get; set; }
22 
26  [Required]
27  public DateTimeOffset? CreatedAt { get; set; }
28 
32  public string SystemIdentifier { get; set; }
33 
37  [Required]
38  public string Name { get; set; }
39 
43  [Required]
45 
49  [Required]
51  }
52 }
+
Represents a server User
Definition: User.cs:10
AdministrationRights
Rights for Models.Administration
InstanceManagerRights
Rights for managing Models.Instances
- diff --git a/_tgstation_8_server_8_api_2_models_2_job_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_job_8cs_source.html index 1d8049fac3..15455e754c 100644 --- a/_tgstation_8_server_8_api_2_models_2_job_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_job_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Job.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
6  public sealed class Job : Internal.Job
7  {
11  public User StartedBy { get; set; }
12 
16  public User CancelledBy { get; set; }
17 
21  public int? Progress { get; set; }
22  }
23 }
+Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
6  public sealed class Job : Internal.Job
7  {
11  public User StartedBy { get; set; }
12 
16  public User CancelledBy { get; set; }
17 
21  public int? Progress { get; set; }
22  }
23 }
Represents a long running job on the server. Model is read-only, updates attempt to cancel the job ...
Definition: Job.cs:6
diff --git a/_tgstation_8_server_8_api_2_models_2_repository_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_repository_8cs_source.html index b82078e674..d9da67fef5 100644 --- a/_tgstation_8_server_8_api_2_models_2_repository_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_repository_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Repository.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public sealed class Repository : Internal.RepositorySettings
9  {
13  public string Origin { get; set; }
14 
18  public string CheckoutSha { get; set; }
19 
24 
28  public string GitHubOwner { get; set; }
29 
33  public string GitHubName { get; set; }
34 
38  public Job ActiveJob { get; set; }
39 
43  public bool? UpdateFromOrigin { get; set; }
44 
48  public string Reference { get; set; }
49 
53  public List<TestMergeParameters> NewTestMerges { get; set; }
54  }
55 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public sealed class Repository : Internal.RepositorySettings
9  {
13  public string Origin { get; set; }
14 
18  public string CheckoutSha { get; set; }
19 
24 
28  public string GitHubOwner { get; set; }
29 
33  public string GitHubName { get; set; }
34 
38  public Job ActiveJob { get; set; }
39 
43  public bool? UpdateFromOrigin { get; set; }
44 
48  public string Reference { get; set; }
49 
53  public List<TestMergeParameters> NewTestMerges { get; set; }
54  }
55 }
+
Represents a git repository
Definition: Repository.cs:8
-
Represents a long running job on the server. Model is read-only, updates attempt to cancel the job ...
Definition: Job.cs:6
diff --git a/_tgstation_8_server_8_api_2_models_2_revision_information_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_revision_information_8cs_source.html index 568250a6d4..f9ba0b1738 100644 --- a/_tgstation_8_server_8_api_2_models_2_revision_information_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_revision_information_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RevisionInformation.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 
3 namespace Tgstation.Server.Api.Models
4 {
6  public sealed class RevisionInformation : Internal.RevisionInformation
7  {
11  public TestMerge PrimaryTestMerge { get; set; }
12 
16  public List<TestMerge> ActiveTestMerges { get; set; }
17 
21  public List<CompileJob> CompileJobs { get; set; }
22  }
23 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 
3 namespace Tgstation.Server.Api.Models
4 {
6  public sealed class RevisionInformation : Internal.RevisionInformation
7  {
11  public TestMerge PrimaryTestMerge { get; set; }
12 
16  public List<TestMerge> ActiveTestMerges { get; set; }
17 
21  public List<CompileJob> CompileJobs { get; set; }
22  }
23 }
+ -
diff --git a/_tgstation_8_server_8_api_2_models_2_test_merge_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_test_merge_8cs_source.html index 57299b877d..2301ae4fd3 100644 --- a/_tgstation_8_server_8_api_2_models_2_test_merge_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_test_merge_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
TestMerge.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
4  public sealed class TestMerge : Internal.TestMerge
5  {
9  public User MergedBy { get; set; }
10  }
11 }
- +Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
4  public sealed class TestMerge : Internal.TestMerge
5  {
9  public User MergedBy { get; set; }
10  }
11 }
+
diff --git a/_tgstation_8_server_8_api_2_models_2_user_8cs_source.html b/_tgstation_8_server_8_api_2_models_2_user_8cs_source.html index c6ba5519be..57e872a1c0 100644 --- a/_tgstation_8_server_8_api_2_models_2_user_8cs_source.html +++ b/_tgstation_8_server_8_api_2_models_2_user_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
User.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
4  public class User : Internal.User
5  {
9  public const string AdminName = "Admin";
10 
14  public const string DefaultAdminPassword = "ISolemlySwearToDeleteTheDataDirectory";
15 
19  public User CreatedBy { get; set; }
20  }
21 }
+Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
4  public class User : Internal.User
5  {
9  public const string AdminName = "Admin";
10 
14  public const string DefaultAdminPassword = "ISolemlySwearToDeleteTheDataDirectory";
15 
19  public User CreatedBy { get; set; }
20  }
21 }
diff --git a/_tgstation_8_server_8_api_8_assembly_info_8cs.html b/_tgstation_8_server_8_api_8_assembly_info_8cs.html index 88fc90fce7..823d805b2e 100644 --- a/_tgstation_8_server_8_api_8_assembly_info_8cs.html +++ b/_tgstation_8_server_8_api_8_assembly_info_8cs.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Api/obj/Release/netstandard2.0/Tgstation.Server.Api.AssemblyInfo.cs File Reference +tgstation-server: src/Tgstation.Server.Api/obj/Debug/netstandard2.0/Tgstation.Server.Api.AssemblyInfo.cs File Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/_tgstation_8_server_8_api_8_assembly_info_8cs_source.html b/_tgstation_8_server_8_api_8_assembly_info_8cs_source.html index 5d145268aa..2b24e63ff3 100644 --- a/_tgstation_8_server_8_api_8_assembly_info_8cs_source.html +++ b/_tgstation_8_server_8_api_8_assembly_info_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Api/obj/Release/netstandard2.0/Tgstation.Server.Api.AssemblyInfo.cs Source File +tgstation-server: src/Tgstation.Server.Api/obj/Debug/netstandard2.0/Tgstation.Server.Api.AssemblyInfo.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
@@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Tgstation.Server.Api.AssemblyInfo.cs
-Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("/tg/station")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
16 [assembly: System.Reflection.AssemblyCopyrightAttribute("2018")]
17 [assembly: System.Reflection.AssemblyDescriptionAttribute("API definitions for tgstation-server")]
18 [assembly: System.Reflection.AssemblyFileVersionAttribute("4.0.0.0")]
19 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("4.0.0.0-preview6004")]
20 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Api")]
21 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Api")]
22 [assembly: System.Reflection.AssemblyVersionAttribute("4.0.0.0")]
23 
24 // Generated by the MSBuild WriteCodeFragment class.
25 
+Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("/tg/station")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
16 [assembly: System.Reflection.AssemblyCopyrightAttribute("2018")]
17 [assembly: System.Reflection.AssemblyDescriptionAttribute("API definitions for tgstation-server")]
18 [assembly: System.Reflection.AssemblyFileVersionAttribute("4.0.0.0")]
19 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("4.0.0.0-preview6005")]
20 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Api")]
21 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Api")]
22 [assembly: System.Reflection.AssemblyVersionAttribute("4.0.0.0")]
23 
24 // Generated by the MSBuild WriteCodeFragment class.
25 
diff --git a/_tgstation_8_server_8_api_8_tests_8_assembly_info_8cs_source.html b/_tgstation_8_server_8_api_8_tests_8_assembly_info_8cs_source.html index f0cb13b64e..8aba9d97b3 100644 --- a/_tgstation_8_server_8_api_8_tests_8_assembly_info_8cs_source.html +++ b/_tgstation_8_server_8_api_8_tests_8_assembly_info_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Api.Tests/obj/Release/netcoreapp2.1/Tgstation.Server.Api.Tests.AssemblyInfo.cs Source File +tgstation-server: tests/Tgstation.Server.Api.Tests/obj/Debug/netcoreapp2.1/Tgstation.Server.Api.Tests.AssemblyInfo.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
@@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Tgstation.Server.Api.Tests.AssemblyInfo.cs
-Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Api.Tests")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Api.Tests")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Api.Tests")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
+Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Api.Tests")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Api.Tests")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Api.Tests")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
diff --git a/_tgstation_8_server_8_api_8_tests_8_program_8cs_source.html b/_tgstation_8_server_8_api_8_tests_8_program_8cs_source.html index f735cdd6bf..e0e4ccf475 100644 --- a/_tgstation_8_server_8_api_8_tests_8_program_8cs_source.html +++ b/_tgstation_8_server_8_api_8_tests_8_program_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Api.Tests/obj/Release/netcoreapp2.1/Tgstation.Server.Api.Tests.Program.cs Source File +tgstation-server: tests/Tgstation.Server.Api.Tests/obj/Debug/netcoreapp2.1/Tgstation.Server.Api.Tests.Program.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/_tgstation_8_server_8_client_8_assembly_info_8cs.html b/_tgstation_8_server_8_client_8_assembly_info_8cs.html index c1b2251f60..fb681e6583 100644 --- a/_tgstation_8_server_8_client_8_assembly_info_8cs.html +++ b/_tgstation_8_server_8_client_8_assembly_info_8cs.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Client/obj/Release/netstandard2.0/Tgstation.Server.Client.AssemblyInfo.cs File Reference +tgstation-server: src/Tgstation.Server.Client/obj/Debug/netstandard2.0/Tgstation.Server.Client.AssemblyInfo.cs File Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/_tgstation_8_server_8_client_8_assembly_info_8cs_source.html b/_tgstation_8_server_8_client_8_assembly_info_8cs_source.html index 6eba050809..3473aad3d1 100644 --- a/_tgstation_8_server_8_client_8_assembly_info_8cs_source.html +++ b/_tgstation_8_server_8_client_8_assembly_info_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Client/obj/Release/netstandard2.0/Tgstation.Server.Client.AssemblyInfo.cs Source File +tgstation-server: src/Tgstation.Server.Client/obj/Debug/netstandard2.0/Tgstation.Server.Client.AssemblyInfo.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
@@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Tgstation.Server.Client.AssemblyInfo.cs
-Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("/tg/station 13")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
16 [assembly: System.Reflection.AssemblyDescriptionAttribute("Client library for tgstation-server")]
17 [assembly: System.Reflection.AssemblyFileVersionAttribute("4.0.0.0")]
18 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("4.0.0.0-preview9111")]
19 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Client")]
20 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Client")]
21 [assembly: System.Reflection.AssemblyVersionAttribute("4.0.0.0")]
22 [assembly: System.Resources.NeutralResourcesLanguageAttribute("en-CA")]
23 
24 // Generated by the MSBuild WriteCodeFragment class.
25 
+Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("/tg/station 13")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
16 [assembly: System.Reflection.AssemblyDescriptionAttribute("Client library for tgstation-server")]
17 [assembly: System.Reflection.AssemblyFileVersionAttribute("4.0.0.0")]
18 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("4.0.0.0-preview9112")]
19 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Client")]
20 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Client")]
21 [assembly: System.Reflection.AssemblyVersionAttribute("4.0.0.0")]
22 [assembly: System.Resources.NeutralResourcesLanguageAttribute("en-CA")]
23 
24 // Generated by the MSBuild WriteCodeFragment class.
25 
diff --git a/_tgstation_8_server_8_client_8_tests_8_assembly_info_8cs_source.html b/_tgstation_8_server_8_client_8_tests_8_assembly_info_8cs_source.html index 3eef0f64ad..3c2a097db5 100644 --- a/_tgstation_8_server_8_client_8_tests_8_assembly_info_8cs_source.html +++ b/_tgstation_8_server_8_client_8_tests_8_assembly_info_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Client.Tests/obj/Release/netcoreapp2.1/Tgstation.Server.Client.Tests.AssemblyInfo.cs Source File +tgstation-server: tests/Tgstation.Server.Client.Tests/obj/Debug/netcoreapp2.1/Tgstation.Server.Client.Tests.AssemblyInfo.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
@@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Tgstation.Server.Client.Tests.AssemblyInfo.cs
-Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Client.Tests")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Client.Tests")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Client.Tests")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
+Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Client.Tests")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Client.Tests")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Client.Tests")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
diff --git a/_tgstation_8_server_8_client_8_tests_8_program_8cs_source.html b/_tgstation_8_server_8_client_8_tests_8_program_8cs_source.html index 0bb889fa5e..9ef26d4cd5 100644 --- a/_tgstation_8_server_8_client_8_tests_8_program_8cs_source.html +++ b/_tgstation_8_server_8_client_8_tests_8_program_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Client.Tests/obj/Release/netcoreapp2.1/Tgstation.Server.Client.Tests.Program.cs Source File +tgstation-server: tests/Tgstation.Server.Client.Tests/obj/Debug/netcoreapp2.1/Tgstation.Server.Client.Tests.Program.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/_tgstation_8_server_8_host_2_components_2_chat_2_user_8cs_source.html b/_tgstation_8_server_8_host_2_components_2_chat_2_user_8cs_source.html index d7dacabfab..5c59604492 100644 --- a/_tgstation_8_server_8_host_2_components_2_chat_2_user_8cs_source.html +++ b/_tgstation_8_server_8_host_2_components_2_chat_2_user_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
User.cs
-Go to the documentation of this file.
1 using Newtonsoft.Json;
2 using System;
3 using System.Globalization;
4 
5 namespace Tgstation.Server.Host.Components.Chat
6 {
10  public sealed class User
11  {
15  public string Id { get; set; }
16 
20  [JsonIgnore]
21  public ulong RealId
22  {
23  get => UInt64.Parse(Id, CultureInfo.InvariantCulture);
24  set => Id = value.ToString(CultureInfo.InvariantCulture);
25  }
26 
30  public string FriendlyName { get; set; }
31 
35  public string Mention { get; set; }
36 
40  public Channel Channel { get; set; }
41  }
42 }
- +Go to the documentation of this file.
1 using Newtonsoft.Json;
2 using System;
3 using System.Globalization;
4 
5 namespace Tgstation.Server.Host.Components.Chat
6 {
10  public sealed class User
11  {
15  public string Id { get; set; }
16 
20  [JsonIgnore]
21  public ulong RealId
22  {
23  get => UInt64.Parse(Id, CultureInfo.InvariantCulture);
24  set => Id = value.ToString(CultureInfo.InvariantCulture);
25  }
26 
30  public string FriendlyName { get; set; }
31 
35  public string Mention { get; set; }
36 
40  public Channel Channel { get; set; }
41  }
42 }
+
Represents a Providers.IProvider channel
Definition: Channel.cs:10
Represents a tgs_chat_user datum
Definition: User.cs:10
diff --git a/_tgstation_8_server_8_host_2_components_2_compiler_2_dream_maker_8cs_source.html b/_tgstation_8_server_8_host_2_components_2_compiler_2_dream_maker_8cs_source.html index 9b5b0cb19d..28e1859482 100644 --- a/_tgstation_8_server_8_host_2_components_2_compiler_2_dream_maker_8cs_source.html +++ b/_tgstation_8_server_8_host_2_components_2_compiler_2_dream_maker_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamMaker.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Collections.Generic;
4 using System.Globalization;
5 using System.Linq;
6 using System.Text;
7 using System.Threading;
8 using System.Threading.Tasks;
16 using Tgstation.Server.Host.IO;
17 
18 namespace Tgstation.Server.Host.Components.Compiler
19 {
21  sealed class DreamMaker : IDreamMaker
22  {
26  public const string ADirectoryName = "A";
30  public const string BDirectoryName = "B";
34  public const string DmbExtension = ".dmb";
38  const string DmeExtension = "dme";
39 
43  readonly IByondManager byond;
51  readonly StaticFiles.IConfiguration configuration;
71  readonly IChat chat;
79  readonly IWatchdog watchdog;
83  readonly ILogger<DreamMaker> logger;
84 
88  bool compiling;
89 
104  public DreamMaker(IByondManager byond, IIOManager ioManager, StaticFiles.IConfiguration configuration, ISessionControllerFactory sessionControllerFactory, ICompileJobConsumer compileJobConsumer, IApplication application, IEventConsumer eventConsumer, IChat chat, IProcessExecutor processExecutor, IWatchdog watchdog, ILogger<DreamMaker> logger)
105  {
106  this.byond = byond;
107  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
108  this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
109  this.sessionControllerFactory = sessionControllerFactory ?? throw new ArgumentNullException(nameof(sessionControllerFactory));
110  this.compileJobConsumer = compileJobConsumer ?? throw new ArgumentNullException(nameof(compileJobConsumer));
111  this.application = application ?? throw new ArgumentNullException(nameof(application));
112  this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
113  this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
114  this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor));
115  this.watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog));
116  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
117  }
118 
129  async Task VerifyApi(uint timeout, DreamDaemonSecurity securityLevel, Models.CompileJob job, IByondExecutableLock byondLock, ushort portToUse, CancellationToken cancellationToken)
130  {
131  logger.LogTrace("Verifying DMAPI...");
132  var launchParameters = new DreamDaemonLaunchParameters
133  {
134  AllowWebClient = false,
135  PrimaryPort = portToUse,
136  SecurityLevel = securityLevel, //all it needs to read the file and exit
137  StartupTimeout = timeout
138  };
139 
140  var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName);
141 
142  job.MinimumSecurityLevel = securityLevel; //needed for the TempDmbProvider
143  var provider = new TemporaryDmbProvider(ioManager.ResolvePath(dirA), String.Concat(job.DmeName, DmbExtension), job);
144 
145  var timeoutAt = DateTimeOffset.Now.AddSeconds(timeout);
146  using (var controller = await sessionControllerFactory.LaunchNew(launchParameters, provider, byondLock, true, true, true, cancellationToken).ConfigureAwait(false))
147  {
148  var launchResult = await controller.LaunchResult.ConfigureAwait(false);
149 
150  var now = DateTimeOffset.Now;
151  if (now < timeoutAt && launchResult.StartupTime.HasValue)
152  {
153  var timeoutTask = Task.Delay(timeoutAt - now, cancellationToken);
154 
155  await Task.WhenAny(controller.Lifetime, timeoutTask).ConfigureAwait(false);
156  cancellationToken.ThrowIfCancellationRequested();
157  }
158 
159  if (controller.Lifetime.IsCompleted)
160  {
161  var validationStatus = controller.ApiValidationStatus;
162  logger.LogTrace("API validation status: {0}", validationStatus);
163  switch (validationStatus)
164  {
165  case ApiValidationStatus.RequiresUltrasafe:
166  job.MinimumSecurityLevel = DreamDaemonSecurity.Ultrasafe;
167  return;
168  case ApiValidationStatus.RequiresSafe:
169  if (securityLevel == DreamDaemonSecurity.Ultrasafe)
170  throw new JobException("This game must be run with at least the 'Safe' DreamDaemon security level!");
171  job.MinimumSecurityLevel = DreamDaemonSecurity.Safe;
172  return;
173  case ApiValidationStatus.RequiresTrusted:
174  if (securityLevel != DreamDaemonSecurity.Trusted)
175  throw new JobException("This game must be run with at least the 'Trusted' DreamDaemon security level!");
176  job.MinimumSecurityLevel = DreamDaemonSecurity.Trusted;
177  return;
178  case ApiValidationStatus.NeverValidated:
179  break;
180  case ApiValidationStatus.BadValidationRequest:
181  throw new JobException("Recieved an unrecognized API validation request from DreamDaemon!");
182  case ApiValidationStatus.UnaskedValidationRequest:
183  default:
184  throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Session controller returned unexpected ApiValidationStatus: {0}", validationStatus));
185  }
186  }
187 
188  throw new JobException("DMAPI validation timed out!");
189  }
190  }
191 
199  async Task<int> RunDreamMaker(string dreamMakerPath, Models.CompileJob job, CancellationToken cancellationToken)
200  {
201  using (var dm = processExecutor.LaunchProcess(dreamMakerPath, ioManager.ResolvePath(ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName)), String.Format(CultureInfo.InvariantCulture, "-clean {0}.{1}", job.DmeName, DmeExtension), true, true))
202  {
203  int exitCode;
204  using (cancellationToken.Register(() => dm.Terminate()))
205  exitCode = await dm.Lifetime.ConfigureAwait(false);
206  cancellationToken.ThrowIfCancellationRequested();
207 
208  logger.LogDebug("DreamMaker exit code: {0}", exitCode);
209  job.Output = dm.GetCombinedOutput();
210  logger.LogTrace("DreamMaker output: {0}{1}", Environment.NewLine, job.Output);
211  return exitCode;
212  }
213  }
214 
221  async Task ModifyDme(Models.CompileJob job, CancellationToken cancellationToken)
222  {
223  var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName);
224  var dmePath = ioManager.ConcatPath(dirA, String.Join('.', job.DmeName, DmeExtension));
225  var dmeReadTask = ioManager.ReadAllBytes(dmePath, cancellationToken);
226 
227  var dmeModificationsTask = configuration.CopyDMFilesTo(dmePath, ioManager.ResolvePath(dirA), cancellationToken);
228 
229  var dmeBytes = await dmeReadTask.ConfigureAwait(false);
230  var dme = Encoding.UTF8.GetString(dmeBytes);
231 
232  var dmeModifications = await dmeModificationsTask.ConfigureAwait(false);
233 
234  if (dmeModifications == null || dmeModifications.TotalDmeOverwrite)
235  {
236  if (dmeModifications != null)
237  logger.LogDebug(".dme replacement configured!");
238  else
239  logger.LogTrace("No .dme modifications required.");
240  return;
241  }
242 
243  if (dmeModifications.HeadIncludeLine != null)
244  logger.LogDebug("Head .dme include line: {0}", dmeModifications.HeadIncludeLine);
245  if (dmeModifications.TailIncludeLine != null)
246  logger.LogDebug("Tail .dme include line: {0}", dmeModifications.TailIncludeLine);
247 
248  var dmeLines = new List<string>(dme.Split(new[] { Environment.NewLine }, StringSplitOptions.None));
249  for (var I = 0; I < dmeLines.Count; ++I)
250  {
251  var line = dmeLines[I];
252  if (line.Contains("BEGIN_INCLUDE", StringComparison.Ordinal) && dmeModifications.HeadIncludeLine != null)
253  {
254  dmeLines.Insert(I + 1, dmeModifications.HeadIncludeLine);
255  ++I;
256  }
257  else if (line.Contains("END_INCLUDE", StringComparison.Ordinal) && dmeModifications.TailIncludeLine != null)
258  {
259  dmeLines.Insert(I, dmeModifications.TailIncludeLine);
260  break;
261  }
262  }
263 
264  dmeBytes = Encoding.UTF8.GetBytes(String.Join(Environment.NewLine, dmeLines));
265  await ioManager.WriteAllBytes(dmePath, dmeBytes, cancellationToken).ConfigureAwait(false);
266  }
267 
269  public async Task<Models.CompileJob> Compile(Models.RevisionInformation revisionInformation, Api.Models.DreamMaker dreamMakerSettings, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken)
270  {
271  if (revisionInformation == null)
272  throw new ArgumentNullException(nameof(revisionInformation));
273 
274  if (dreamMakerSettings == null)
275  throw new ArgumentNullException(nameof(dreamMakerSettings));
276 
277  if (repository == null)
278  throw new ArgumentNullException(nameof(repository));
279 
280  if (dreamMakerSettings.ApiValidationSecurityLevel == DreamDaemonSecurity.Ultrasafe)
281  throw new ArgumentOutOfRangeException(nameof(dreamMakerSettings), dreamMakerSettings, "Cannot compile with ultrasafe security!");
282 
283  logger.LogTrace("Begin Compile");
284 
285  var job = new Models.CompileJob
286  {
287  DirectoryName = Guid.NewGuid(),
288  DmeName = dreamMakerSettings.ProjectName,
289  RevisionInformation = revisionInformation
290  };
291 
292  logger.LogTrace("Compile output GUID: {0}", job.DirectoryName);
293 
294  lock (this)
295  {
296  if (compiling)
297  throw new JobException("There is already a compile job in progress!");
298  compiling = true;
299  }
300 
301  try
302  {
303  var commitInsert = revisionInformation.CommitSha.Substring(0, 7);
304  string remoteCommitInsert;
305  if (revisionInformation.CommitSha == revisionInformation.OriginCommitSha)
306  {
307  commitInsert = String.Format(CultureInfo.InvariantCulture, "^{0}", commitInsert);
308  remoteCommitInsert = String.Empty;
309  }
310  else
311  remoteCommitInsert = String.Format(CultureInfo.InvariantCulture, ". Remote commit: ^{0}", revisionInformation.OriginCommitSha.Substring(0, 7));
312 
313  var testmergeInsert = revisionInformation.ActiveTestMerges.Count == 0 ? String.Empty : String.Format(CultureInfo.InvariantCulture, " (Test Merges: {0})",
314  String.Join(", ", revisionInformation.ActiveTestMerges.Select(x => x.TestMerge).Select(x =>
315  {
316  var result = String.Format(CultureInfo.InvariantCulture, "#{0} at {1}", x.Number, x.PullRequestRevision.Substring(0, 7));
317  if (x.Comment != null)
318  result += String.Format(CultureInfo.InvariantCulture, " ({0})", x.Comment);
319  return result;
320  })));
321 
322  using (var byondLock = await byond.UseExecutables(null, cancellationToken).ConfigureAwait(false))
323  {
324  await chat.SendUpdateMessage(String.Format(CultureInfo.InvariantCulture, "Deploying revision: {0}{1}{2} BYOND Version: {3}", commitInsert, testmergeInsert, remoteCommitInsert, byondLock.Version), cancellationToken).ConfigureAwait(false);
325 
326  async Task CleanupFailedCompile(bool cancelled)
327  {
328  logger.LogTrace("Cleaning compile directory...");
329  var chatTask = chat.SendUpdateMessage(cancelled ? "Deploy cancelled!" : "Deploy failed!", cancellationToken);
330  try
331  {
332  await ioManager.DeleteDirectory(job.DirectoryName.ToString(), CancellationToken.None).ConfigureAwait(false);
333  }
334  catch (Exception e)
335  {
336  logger.LogWarning("Error cleaning up compile directory {0}! Exception: {1}", ioManager.ResolvePath(job.DirectoryName.ToString()), e);
337  }
338 
339  await chatTask.ConfigureAwait(false);
340  };
341 
342  try
343  {
344  await ioManager.CreateDirectory(job.DirectoryName.ToString(), cancellationToken).ConfigureAwait(false);
345 
346  var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName);
347  var dirB = ioManager.ConcatPath(job.DirectoryName.ToString(), BDirectoryName);
348 
349  logger.LogTrace("Copying repository to game directory...");
350  //copy the repository
351  var fullDirA = ioManager.ResolvePath(dirA);
352  var repoOrigin = repository.Origin;
353  using (repository)
354  await repository.CopyTo(fullDirA, cancellationToken).ConfigureAwait(false);
355 
356  //run precompile scripts
357  var resolvedGameDirectory = ioManager.ResolvePath(ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName));
358  await eventConsumer.HandleEvent(EventType.CompileStart, new List<string> { resolvedGameDirectory, repoOrigin }, cancellationToken).ConfigureAwait(false);
359 
360  //determine the dme
361  if (job.DmeName == null)
362  {
363  logger.LogTrace("Searching for available .dmes...");
364  var path = (await ioManager.GetFilesWithExtension(dirA, DmeExtension, cancellationToken).ConfigureAwait(false)).FirstOrDefault();
365  if (path == default)
366  throw new JobException("Unable to find any .dme!");
367  var dmeWithExtension = ioManager.GetFileName(path);
368  job.DmeName = dmeWithExtension.Substring(0, dmeWithExtension.Length - DmeExtension.Length - 1);
369  }
370  else if (!await ioManager.FileExists(ioManager.ConcatPath(dirA, String.Join('.', job.DmeName, DmeExtension)), cancellationToken).ConfigureAwait(false))
371  throw new JobException("Unable to locate specified .dme!");
372 
373  logger.LogDebug("Selected {0}.dme for compilation!", job.DmeName);
374 
375  await ModifyDme(job, cancellationToken).ConfigureAwait(false);
376 
377  //run compiler, verify api
378  job.ByondVersion = byondLock.Version.ToString();
379 
380  var exitCode = await RunDreamMaker(byondLock.DreamMakerPath, job, cancellationToken).ConfigureAwait(false);
381 
382  try
383  {
384  if (exitCode != 0)
385  throw new JobException(String.Format(CultureInfo.InvariantCulture, "DM exited with a non-zero code: {0}{1}{2}", exitCode, Environment.NewLine, job.Output));
386 
387  await VerifyApi(apiValidateTimeout, dreamMakerSettings.ApiValidationSecurityLevel.Value, job, byondLock, dreamMakerSettings.ApiValidationPort.Value, cancellationToken).ConfigureAwait(false);
388  }
389  catch (JobException)
390  {
391  //server never validated or compile failed
392  await eventConsumer.HandleEvent(EventType.CompileFailure, new List<string> { resolvedGameDirectory, exitCode == 0 ? "1" : "0" }, cancellationToken).ConfigureAwait(false);
393  throw;
394  }
395 
396  logger.LogTrace("Running post compile event...");
397  await eventConsumer.HandleEvent(EventType.CompileComplete, new List<string> { ioManager.ResolvePath(ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName)) }, cancellationToken).ConfigureAwait(false);
398 
399  logger.LogTrace("Duplicating compiled game...");
400 
401  //duplicate the dmb et al
402  await ioManager.CopyDirectory(dirA, dirB, null, cancellationToken).ConfigureAwait(false);
403 
404  logger.LogTrace("Applying static game file symlinks...");
405 
406  //symlink in the static data
407  var symATask = configuration.SymlinkStaticFilesTo(fullDirA, cancellationToken);
408  var symBTask = configuration.SymlinkStaticFilesTo(ioManager.ResolvePath(dirB), cancellationToken);
409 
410  await Task.WhenAll(symATask, symBTask).ConfigureAwait(false);
411 
412  await chat.SendUpdateMessage(String.Format(CultureInfo.InvariantCulture, "Deployment complete!{0}", watchdog.Running ? " Changes will be applied on next server reboot." : String.Empty), cancellationToken).ConfigureAwait(false);
413 
414  logger.LogDebug("Compile complete!");
415  return job;
416  }
417  catch (Exception e)
418  {
419  await CleanupFailedCompile(e is OperationCanceledException).ConfigureAwait(false);
420  throw;
421  }
422  }
423  }
424  catch (OperationCanceledException)
425  {
426  await eventConsumer.HandleEvent(EventType.CompileCancelled, null, default).ConfigureAwait(false);
427  throw;
428  }
429  finally
430  {
431  compiling = false;
432  }
433  }
434  }
435 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Collections.Generic;
4 using System.Globalization;
5 using System.Linq;
6 using System.Text;
7 using System.Threading;
8 using System.Threading.Tasks;
16 using Tgstation.Server.Host.IO;
17 
18 namespace Tgstation.Server.Host.Components.Compiler
19 {
21  sealed class DreamMaker : IDreamMaker
22  {
26  public const string ADirectoryName = "A";
30  public const string BDirectoryName = "B";
34  public const string DmbExtension = ".dmb";
38  const string DmeExtension = "dme";
39 
43  readonly IByondManager byond;
51  readonly StaticFiles.IConfiguration configuration;
71  readonly IChat chat;
79  readonly IWatchdog watchdog;
83  readonly ILogger<DreamMaker> logger;
84 
88  bool compiling;
89 
104  public DreamMaker(IByondManager byond, IIOManager ioManager, StaticFiles.IConfiguration configuration, ISessionControllerFactory sessionControllerFactory, ICompileJobConsumer compileJobConsumer, IApplication application, IEventConsumer eventConsumer, IChat chat, IProcessExecutor processExecutor, IWatchdog watchdog, ILogger<DreamMaker> logger)
105  {
106  this.byond = byond;
107  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
108  this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
109  this.sessionControllerFactory = sessionControllerFactory ?? throw new ArgumentNullException(nameof(sessionControllerFactory));
110  this.compileJobConsumer = compileJobConsumer ?? throw new ArgumentNullException(nameof(compileJobConsumer));
111  this.application = application ?? throw new ArgumentNullException(nameof(application));
112  this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
113  this.chat = chat ?? throw new ArgumentNullException(nameof(chat));
114  this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor));
115  this.watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog));
116  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
117  }
118 
129  async Task VerifyApi(uint timeout, DreamDaemonSecurity securityLevel, Models.CompileJob job, IByondExecutableLock byondLock, ushort portToUse, CancellationToken cancellationToken)
130  {
131  logger.LogTrace("Verifying DMAPI...");
132  var launchParameters = new DreamDaemonLaunchParameters
133  {
134  AllowWebClient = false,
135  PrimaryPort = portToUse,
136  SecurityLevel = securityLevel, //all it needs to read the file and exit
137  StartupTimeout = timeout
138  };
139 
140  var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName);
141 
142  job.MinimumSecurityLevel = securityLevel; //needed for the TempDmbProvider
143  var provider = new TemporaryDmbProvider(ioManager.ResolvePath(dirA), String.Concat(job.DmeName, DmbExtension), job);
144 
145  var timeoutAt = DateTimeOffset.Now.AddSeconds(timeout);
146  using (var controller = await sessionControllerFactory.LaunchNew(launchParameters, provider, byondLock, true, true, true, cancellationToken).ConfigureAwait(false))
147  {
148  var launchResult = await controller.LaunchResult.ConfigureAwait(false);
149 
150  var now = DateTimeOffset.Now;
151  if (now < timeoutAt && launchResult.StartupTime.HasValue)
152  {
153  var timeoutTask = Task.Delay(timeoutAt - now, cancellationToken);
154 
155  await Task.WhenAny(controller.Lifetime, timeoutTask).ConfigureAwait(false);
156  cancellationToken.ThrowIfCancellationRequested();
157  }
158 
159  if (controller.Lifetime.IsCompleted)
160  {
161  var validationStatus = controller.ApiValidationStatus;
162  logger.LogTrace("API validation status: {0}", validationStatus);
163  switch (validationStatus)
164  {
165  case ApiValidationStatus.RequiresUltrasafe:
166  job.MinimumSecurityLevel = DreamDaemonSecurity.Ultrasafe;
167  return;
168  case ApiValidationStatus.RequiresSafe:
169  if (securityLevel == DreamDaemonSecurity.Ultrasafe)
170  throw new JobException("This game must be run with at least the 'Safe' DreamDaemon security level!");
171  job.MinimumSecurityLevel = DreamDaemonSecurity.Safe;
172  return;
173  case ApiValidationStatus.RequiresTrusted:
174  if (securityLevel != DreamDaemonSecurity.Trusted)
175  throw new JobException("This game must be run with at least the 'Trusted' DreamDaemon security level!");
176  job.MinimumSecurityLevel = DreamDaemonSecurity.Trusted;
177  return;
178  case ApiValidationStatus.NeverValidated:
179  break;
180  case ApiValidationStatus.BadValidationRequest:
181  throw new JobException("Recieved an unrecognized API validation request from DreamDaemon!");
182  case ApiValidationStatus.UnaskedValidationRequest:
183  default:
184  throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Session controller returned unexpected ApiValidationStatus: {0}", validationStatus));
185  }
186  }
187 
188  throw new JobException("DMAPI validation timed out!");
189  }
190  }
191 
199  async Task<int> RunDreamMaker(string dreamMakerPath, Models.CompileJob job, CancellationToken cancellationToken)
200  {
201  using (var dm = processExecutor.LaunchProcess(dreamMakerPath, ioManager.ResolvePath(ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName)), String.Format(CultureInfo.InvariantCulture, "-clean {0}.{1}", job.DmeName, DmeExtension), true, true))
202  {
203  int exitCode;
204  using (cancellationToken.Register(() => dm.Terminate()))
205  exitCode = await dm.Lifetime.ConfigureAwait(false);
206  cancellationToken.ThrowIfCancellationRequested();
207 
208  logger.LogDebug("DreamMaker exit code: {0}", exitCode);
209  job.Output = dm.GetCombinedOutput();
210  logger.LogTrace("DreamMaker output: {0}{1}", Environment.NewLine, job.Output);
211  return exitCode;
212  }
213  }
214 
221  async Task ModifyDme(Models.CompileJob job, CancellationToken cancellationToken)
222  {
223  var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName);
224  var dmePath = ioManager.ConcatPath(dirA, String.Join('.', job.DmeName, DmeExtension));
225  var dmeReadTask = ioManager.ReadAllBytes(dmePath, cancellationToken);
226 
227  var dmeModificationsTask = configuration.CopyDMFilesTo(dmePath, ioManager.ResolvePath(dirA), cancellationToken);
228 
229  var dmeBytes = await dmeReadTask.ConfigureAwait(false);
230  var dme = Encoding.UTF8.GetString(dmeBytes);
231 
232  var dmeModifications = await dmeModificationsTask.ConfigureAwait(false);
233 
234  if (dmeModifications == null || dmeModifications.TotalDmeOverwrite)
235  {
236  if (dmeModifications != null)
237  logger.LogDebug(".dme replacement configured!");
238  else
239  logger.LogTrace("No .dme modifications required.");
240  return;
241  }
242 
243  if (dmeModifications.HeadIncludeLine != null)
244  logger.LogDebug("Head .dme include line: {0}", dmeModifications.HeadIncludeLine);
245  if (dmeModifications.TailIncludeLine != null)
246  logger.LogDebug("Tail .dme include line: {0}", dmeModifications.TailIncludeLine);
247 
248  var dmeLines = new List<string>(dme.Split(new[] { Environment.NewLine }, StringSplitOptions.None));
249  for (var I = 0; I < dmeLines.Count; ++I)
250  {
251  var line = dmeLines[I];
252  if (line.Contains("BEGIN_INCLUDE", StringComparison.Ordinal) && dmeModifications.HeadIncludeLine != null)
253  {
254  dmeLines.Insert(I + 1, dmeModifications.HeadIncludeLine);
255  ++I;
256  }
257  else if (line.Contains("END_INCLUDE", StringComparison.Ordinal) && dmeModifications.TailIncludeLine != null)
258  {
259  dmeLines.Insert(I, dmeModifications.TailIncludeLine);
260  break;
261  }
262  }
263 
264  dmeBytes = Encoding.UTF8.GetBytes(String.Join(Environment.NewLine, dmeLines));
265  await ioManager.WriteAllBytes(dmePath, dmeBytes, cancellationToken).ConfigureAwait(false);
266  }
267 
269  public async Task<Models.CompileJob> Compile(Models.RevisionInformation revisionInformation, Api.Models.DreamMaker dreamMakerSettings, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken)
270  {
271  if (revisionInformation == null)
272  throw new ArgumentNullException(nameof(revisionInformation));
273 
274  if (dreamMakerSettings == null)
275  throw new ArgumentNullException(nameof(dreamMakerSettings));
276 
277  if (repository == null)
278  throw new ArgumentNullException(nameof(repository));
279 
280  if (dreamMakerSettings.ApiValidationSecurityLevel == DreamDaemonSecurity.Ultrasafe)
281  throw new ArgumentOutOfRangeException(nameof(dreamMakerSettings), dreamMakerSettings, "Cannot compile with ultrasafe security!");
282 
283  logger.LogTrace("Begin Compile");
284 
285  var job = new Models.CompileJob
286  {
287  DirectoryName = Guid.NewGuid(),
288  DmeName = dreamMakerSettings.ProjectName,
289  RevisionInformation = revisionInformation
290  };
291 
292  logger.LogTrace("Compile output GUID: {0}", job.DirectoryName);
293 
294  lock (this)
295  {
296  if (compiling)
297  throw new JobException("There is already a compile job in progress!");
298  compiling = true;
299  }
300 
301  try
302  {
303  var commitInsert = revisionInformation.CommitSha.Substring(0, 7);
304  string remoteCommitInsert;
305  if (revisionInformation.CommitSha == revisionInformation.OriginCommitSha)
306  {
307  commitInsert = String.Format(CultureInfo.InvariantCulture, "^{0}", commitInsert);
308  remoteCommitInsert = String.Empty;
309  }
310  else
311  remoteCommitInsert = String.Format(CultureInfo.InvariantCulture, ". Remote commit: ^{0}", revisionInformation.OriginCommitSha.Substring(0, 7));
312 
313  var testmergeInsert = revisionInformation.ActiveTestMerges.Count == 0 ? String.Empty : String.Format(CultureInfo.InvariantCulture, " (Test Merges: {0})",
314  String.Join(", ", revisionInformation.ActiveTestMerges.Select(x => x.TestMerge).Select(x =>
315  {
316  var result = String.Format(CultureInfo.InvariantCulture, "#{0} at {1}", x.Number, x.PullRequestRevision.Substring(0, 7));
317  if (x.Comment != null)
318  result += String.Format(CultureInfo.InvariantCulture, " ({0})", x.Comment);
319  return result;
320  })));
321 
322  using (var byondLock = await byond.UseExecutables(null, cancellationToken).ConfigureAwait(false))
323  {
324  await chat.SendUpdateMessage(String.Format(CultureInfo.InvariantCulture, "Deploying revision: {0}{1}{2} BYOND Version: {3}", commitInsert, testmergeInsert, remoteCommitInsert, byondLock.Version), cancellationToken).ConfigureAwait(false);
325 
326  async Task CleanupFailedCompile(bool cancelled)
327  {
328  logger.LogTrace("Cleaning compile directory...");
329  var chatTask = chat.SendUpdateMessage(cancelled ? "Deploy cancelled!" : "Deploy failed!", cancellationToken);
330  try
331  {
332  await ioManager.DeleteDirectory(job.DirectoryName.ToString(), CancellationToken.None).ConfigureAwait(false);
333  }
334  catch (Exception e)
335  {
336  logger.LogWarning("Error cleaning up compile directory {0}! Exception: {1}", ioManager.ResolvePath(job.DirectoryName.ToString()), e);
337  }
338 
339  await chatTask.ConfigureAwait(false);
340  };
341 
342  try
343  {
344  await ioManager.CreateDirectory(job.DirectoryName.ToString(), cancellationToken).ConfigureAwait(false);
345 
346  var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName);
347  var dirB = ioManager.ConcatPath(job.DirectoryName.ToString(), BDirectoryName);
348 
349  logger.LogTrace("Copying repository to game directory...");
350  //copy the repository
351  var fullDirA = ioManager.ResolvePath(dirA);
352  var repoOrigin = repository.Origin;
353  using (repository)
354  await repository.CopyTo(fullDirA, cancellationToken).ConfigureAwait(false);
355 
356  //run precompile scripts
357  var resolvedGameDirectory = ioManager.ResolvePath(ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName));
358  await eventConsumer.HandleEvent(EventType.CompileStart, new List<string> { resolvedGameDirectory, repoOrigin }, cancellationToken).ConfigureAwait(false);
359 
360  //determine the dme
361  if (job.DmeName == null)
362  {
363  logger.LogTrace("Searching for available .dmes...");
364  var path = (await ioManager.GetFilesWithExtension(dirA, DmeExtension, cancellationToken).ConfigureAwait(false)).FirstOrDefault();
365  if (path == default)
366  throw new JobException("Unable to find any .dme!");
367  var dmeWithExtension = ioManager.GetFileName(path);
368  job.DmeName = dmeWithExtension.Substring(0, dmeWithExtension.Length - DmeExtension.Length - 1);
369  }
370  else if (!await ioManager.FileExists(ioManager.ConcatPath(dirA, String.Join('.', job.DmeName, DmeExtension)), cancellationToken).ConfigureAwait(false))
371  throw new JobException("Unable to locate specified .dme!");
372 
373  logger.LogDebug("Selected {0}.dme for compilation!", job.DmeName);
374 
375  await ModifyDme(job, cancellationToken).ConfigureAwait(false);
376 
377  //run compiler, verify api
378  job.ByondVersion = byondLock.Version.ToString();
379 
380  var exitCode = await RunDreamMaker(byondLock.DreamMakerPath, job, cancellationToken).ConfigureAwait(false);
381 
382  try
383  {
384  if (exitCode != 0)
385  throw new JobException(String.Format(CultureInfo.InvariantCulture, "DM exited with a non-zero code: {0}{1}{2}", exitCode, Environment.NewLine, job.Output));
386 
387  await VerifyApi(apiValidateTimeout, dreamMakerSettings.ApiValidationSecurityLevel.Value, job, byondLock, dreamMakerSettings.ApiValidationPort.Value, cancellationToken).ConfigureAwait(false);
388  }
389  catch (JobException)
390  {
391  //server never validated or compile failed
392  await eventConsumer.HandleEvent(EventType.CompileFailure, new List<string> { resolvedGameDirectory, exitCode == 0 ? "1" : "0" }, cancellationToken).ConfigureAwait(false);
393  throw;
394  }
395 
396  logger.LogTrace("Running post compile event...");
397  await eventConsumer.HandleEvent(EventType.CompileComplete, new List<string> { ioManager.ResolvePath(ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName)) }, cancellationToken).ConfigureAwait(false);
398 
399  logger.LogTrace("Duplicating compiled game...");
400 
401  //duplicate the dmb et al
402  await ioManager.CopyDirectory(dirA, dirB, null, cancellationToken).ConfigureAwait(false);
403 
404  logger.LogTrace("Applying static game file symlinks...");
405 
406  //symlink in the static data
407  var symATask = configuration.SymlinkStaticFilesTo(fullDirA, cancellationToken);
408  var symBTask = configuration.SymlinkStaticFilesTo(ioManager.ResolvePath(dirB), cancellationToken);
409 
410  await Task.WhenAll(symATask, symBTask).ConfigureAwait(false);
411 
412  await chat.SendUpdateMessage(String.Format(CultureInfo.InvariantCulture, "Deployment complete!{0}", watchdog.Running ? " Changes will be applied on next server reboot." : String.Empty), cancellationToken).ConfigureAwait(false);
413 
414  logger.LogDebug("Compile complete!");
415  return job;
416  }
417  catch (Exception e)
418  {
419  await CleanupFailedCompile(e is OperationCanceledException).ConfigureAwait(false);
420  throw;
421  }
422  }
423  }
424  catch (OperationCanceledException)
425  {
426  await eventConsumer.HandleEvent(EventType.CompileCancelled, null, default).ConfigureAwait(false);
427  throw;
428  }
429  finally
430  {
431  compiling = false;
432  }
433  }
434  }
435 }
@@ -103,6 +103,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
readonly StaticFiles.IConfiguration configuration
The StaticFiles.IConfiguration for DreamMaker
Definition: DreamMaker.cs:51
Configures the ASP.NET Core web application
Definition: IApplication.cs:8
+ @@ -121,9 +122,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
async Task VerifyApi(uint timeout, DreamDaemonSecurity securityLevel, Models.CompileJob job, IByondExecutableLock byondLock, ushort portToUse, CancellationToken cancellationToken)
Run a quick DD instance to test the DMAPI is installed on the target code
Definition: DreamMaker.cs:129
string Origin
The current origin remote the IRepository is using
Definition: IRepository.cs:46
-
async Task< Models.CompileJob > Compile(Models.RevisionInformation revisionInformation, Api.Models.DreamMaker dreamMakerSettings, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken)
Definition: DreamMaker.cs:269
- +
For managing the BYOND installation
async Task ModifyDme(Models.CompileJob job, CancellationToken cancellationToken)
Adds server side includes to the .dme being compiled
Definition: DreamMaker.cs:221
readonly IChat chat
The IChat for DreamMaker
Definition: DreamMaker.cs:71
diff --git a/_tgstation_8_server_8_host_2_components_2_instance_8cs_source.html b/_tgstation_8_server_8_host_2_components_2_instance_8cs_source.html index 7e496e4012..161552a552 100644 --- a/_tgstation_8_server_8_host_2_components_2_instance_8cs_source.html +++ b/_tgstation_8_server_8_host_2_components_2_instance_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Instance.cs
-Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Linq;
5 using System.Threading;
6 using System.Threading.Tasks;
15 
16 namespace Tgstation.Server.Host.Components
17 {
19  sealed class Instance : IInstance
20  {
23 
25  public IByondManager ByondManager { get; }
26 
28  public IDreamMaker DreamMaker { get; }
29 
31  public IWatchdog Watchdog { get; }
32 
34  public IChat Chat { get; }
35 
37  public StaticFiles.IConfiguration Configuration { get; }
38 
40  public ICompileJobConsumer CompileJobConsumer { get; }
41 
46 
51 
56 
60  readonly ILogger<Instance> logger;
61 
65  readonly Api.Models.Instance metadata;
66 
70  Task timerTask;
74  CancellationTokenSource timerCts;
75 
91  public Instance(Api.Models.Instance metadata, IRepositoryManager repositoryManager, IByondManager byondManager, IDreamMaker dreamMaker, IWatchdog watchdog, IChat chat, StaticFiles.IConfiguration configuration, ICompileJobConsumer compileJobConsumer, IDatabaseContextFactory databaseContextFactory, IDmbFactory dmbFactory, IJobManager jobManager, ILogger<Instance> logger)
92  {
93  this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
94  RepositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager));
95  ByondManager = byondManager ?? throw new ArgumentNullException(nameof(byondManager));
96  DreamMaker = dreamMaker ?? throw new ArgumentNullException(nameof(dreamMaker));
97  Watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog));
98  Chat = chat ?? throw new ArgumentNullException(nameof(chat));
99  Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
100  CompileJobConsumer = compileJobConsumer ?? throw new ArgumentNullException(nameof(compileJobConsumer));
101  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
102  this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory));
103  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
104  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
105  }
106 
108  public void Dispose()
109  {
110  timerCts?.Dispose();
111  CompileJobConsumer.Dispose();
112  Configuration.Dispose();
113  Chat.Dispose();
114  Watchdog.Dispose();
116  }
117 
119  public async Task CompileProcess(Job job, IDatabaseContext databaseContext, Action<int> progressReporter, CancellationToken cancellationToken)
120  {
121  //DO NOT FOLLOW THE SUGGESTION FOR A THROW EXPRESSION HERE
122  if (job == null)
123  throw new ArgumentNullException(nameof(job));
124  if (databaseContext == null)
125  throw new ArgumentNullException(nameof(databaseContext));
126  if (progressReporter == null)
127  throw new ArgumentNullException(nameof(progressReporter));
128 
129  var ddSettingsTask = databaseContext.DreamDaemonSettings.Where(x => x.InstanceId == metadata.Id).Select(x => new DreamDaemonSettings
130  {
131  StartupTimeout = x.StartupTimeout,
132  }).FirstOrDefaultAsync(cancellationToken);
133 
134  var dreamMakerSettings = await databaseContext.DreamMakerSettings.Where(x => x.InstanceId == metadata.Id).FirstAsync(cancellationToken).ConfigureAwait(false);
135  if (dreamMakerSettings == default)
136  throw new JobException("Missing DreamMakerSettings in DB!");
137  var ddSettings = await ddSettingsTask.ConfigureAwait(false);
138  if (ddSettings == default)
139  throw new JobException("Missing DreamDaemonSettings in DB!");
140 
141  CompileJob compileJob;
142  RevisionInformation revInfo;
143  using (var repo = await RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
144  {
145  if (repo == null)
146  throw new JobException("Missing Repository!");
147 
148  var repoSha = repo.Head;
149  revInfo = await databaseContext.RevisionInformations.Where(x => x.CommitSha == repoSha).Include(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge).FirstOrDefaultAsync().ConfigureAwait(false);
150 
151  if (revInfo == default)
152  {
153  revInfo = new RevisionInformation
154  {
155  CommitSha = repoSha,
156  OriginCommitSha = repoSha,
157  Instance = new Models.Instance
158  {
159  Id = metadata.Id
160  }
161  };
162  logger.LogWarning(Repository.Repository.OriginTrackingErrorTemplate, repoSha);
163  databaseContext.Instances.Attach(revInfo.Instance);
164  }
165 
166  compileJob = await DreamMaker.Compile(revInfo, dreamMakerSettings, ddSettings.StartupTimeout.Value, repo, cancellationToken).ConfigureAwait(false);
167  }
168 
169  compileJob.Job = job;
170 
171  databaseContext.CompileJobs.Add(compileJob); //will be saved by job context
172 
173  job.PostComplete = ct => CompileJobConsumer.LoadCompileJob(compileJob, ct);
174  }
175 
182  async Task TimerLoop(uint minutes, CancellationToken cancellationToken)
183  {
184  while (true)
185  try
186  {
187  await Task.Delay(new TimeSpan(0, minutes > Int32.MaxValue ? Int32.MaxValue : (int)minutes, 0), cancellationToken).ConfigureAwait(false);
188 
189  try
190  {
191  Models.User user = null;
192  await databaseContextFactory.UseContext(async (db) => user = await db.Users.FirstAsync(cancellationToken).ConfigureAwait(false)).ConfigureAwait(false);
193  var repositoryUpdateJob = new Job
194  {
195  Instance = new Models.Instance
196  {
197  Id = metadata.Id
198  },
199  Description = "Scheduled repository update",
200  CancelRightsType = RightsType.Repository,
201  CancelRight = (ulong)RepositoryRights.CancelPendingChanges
202  };
203 
204  var noRepo = false;
205  await jobManager.RegisterOperation(repositoryUpdateJob, async (paramJob, databaseContext, progressReporter, jobCancellationToken) =>
206  {
207  var repositorySettingsTask = databaseContext.RepositorySettings.Where(x => x.InstanceId == metadata.Id).FirstAsync(jobCancellationToken);
208 
209  //assume 5 steps with synchronize
210  const int ProgressSections = 7;
211  const int ProgressStep = 100 / ProgressSections;
212 
213 
214  const int NumSteps = 3;
215  var doneSteps = 0;
216 
217  Action<int> NextProgressReporter()
218  {
219  var tmpDoneSteps = doneSteps;
220  ++doneSteps;
221  return progress => progressReporter((progress + 100 * tmpDoneSteps) / NumSteps);
222  };
223 
224  using (var repo = await RepositoryManager.LoadRepository(jobCancellationToken).ConfigureAwait(false))
225  {
226  if (repo == null)
227  {
228  //no repo, no auto updates
229  noRepo = true;
230  return;
231  }
232 
233  var repositorySettings = await repositorySettingsTask.ConfigureAwait(false);
234 
235  //the main point of auto update is to pull the remote
236  await repo.FetchOrigin(repositorySettings.AccessUser, repositorySettings.AccessToken, NextProgressReporter(), jobCancellationToken).ConfigureAwait(false);
237 
238  var startSha = repo.Head;
239 
240  //take appropriate auto update actions
241  bool shouldSyncTracked;
242  if (repositorySettings.AutoUpdatesKeepTestMerges.Value)
243  {
244  var result = await repo.MergeOrigin(repositorySettings.CommitterName, repositorySettings.CommitterEmail, NextProgressReporter(), jobCancellationToken).ConfigureAwait(false);
245  if (!result.HasValue)
246  return;
247  shouldSyncTracked = result.Value;
248  }
249  else
250  {
251  await repo.ResetToOrigin(NextProgressReporter(), jobCancellationToken).ConfigureAwait(false);
252  shouldSyncTracked = true;
253  }
254 
255  //synch if necessary
256  if (repositorySettings.AutoUpdatesSynchronize.Value && startSha != repo.Head)
257  await repo.Sychronize(repositorySettings.AccessUser, repositorySettings.AccessToken, repositorySettings.CommitterName, repositorySettings.CommitterEmail, NextProgressReporter(), shouldSyncTracked, jobCancellationToken).ConfigureAwait(false);
258 
259  progressReporter(5 * ProgressStep);
260  }
261  }, cancellationToken).ConfigureAwait(false);
262 
263  await jobManager.WaitForJobCompletion(repositoryUpdateJob, user, cancellationToken, default).ConfigureAwait(false);
264 
265  if (noRepo)
266  continue;
267 
268  //finally set up the job
269  var compileProcessJob = new Job
270  {
271  StartedBy = user,
272  Instance = repositoryUpdateJob.Instance,
273  Description = "Scheduled code deployment",
274  CancelRightsType = RightsType.DreamMaker,
275  CancelRight = (ulong)DreamMakerRights.CancelCompile
276  };
277 
278  await jobManager.RegisterOperation(compileProcessJob, CompileProcess, cancellationToken).ConfigureAwait(false);
279 
280  await jobManager.WaitForJobCompletion(compileProcessJob, user, cancellationToken, default).ConfigureAwait(false);
281  }
282  catch (OperationCanceledException)
283  {
284  logger.LogDebug("Cancelled auto update job!");
285  throw;
286  }
287  catch (Exception e)
288  {
289  logger.LogWarning("Error in auto update loop! Exception: {0}", e);
290  continue;
291  }
292  }
293  catch (OperationCanceledException)
294  {
295  break;
296  }
297  logger.LogTrace("Leaving auto update loop...");
298  }
299 
301  public void Rename(string newName)
302  {
303  if (String.IsNullOrWhiteSpace(newName))
304  throw new ArgumentNullException(nameof(newName));
305  metadata.Name = newName;
306  }
307 
309  public async Task StartAsync(CancellationToken cancellationToken)
310  {
311  await Task.WhenAll(SetAutoUpdateInterval(metadata.AutoUpdateInterval.Value), Configuration.StartAsync(cancellationToken), ByondManager.StartAsync(cancellationToken), Chat.StartAsync(cancellationToken), CompileJobConsumer.StartAsync(cancellationToken)).ConfigureAwait(false);
312 
313  //dependent on so many things, its just safer this way
314  await Watchdog.StartAsync(cancellationToken).ConfigureAwait(false);
315 
316  CompileJob latestCompileJob = null;
317  await databaseContextFactory.UseContext(async db =>
318  {
319  latestCompileJob = await db.CompileJobs.Where(x => x.Job.Instance.Id == metadata.Id).OrderByDescending(x => x.Job.StoppedAt).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
320  }).ConfigureAwait(false);
321  await dmbFactory.CleanUnusedCompileJobs(latestCompileJob, cancellationToken).ConfigureAwait(false);
322  }
323 
325  public Task StopAsync(CancellationToken cancellationToken) => Task.WhenAll(SetAutoUpdateInterval(0), Configuration.StopAsync(cancellationToken), ByondManager.StopAsync(cancellationToken), Watchdog.StopAsync(cancellationToken), Chat.StopAsync(cancellationToken), CompileJobConsumer.StopAsync(cancellationToken));
326 
328  public async Task SetAutoUpdateInterval(uint newInterval)
329  {
330  Task toWait;
331  lock (this)
332  {
333  if (timerTask != null)
334  {
335  timerCts.Cancel();
336  toWait = timerTask;
337  }
338  else
339  toWait = Task.CompletedTask;
340  }
341  await toWait.ConfigureAwait(false);
342  if (newInterval == 0)
343  return;
344  lock (this)
345  {
346  //race condition, just quit
347  if (timerTask != null)
348  return;
349  timerCts?.Dispose();
350  timerCts = new CancellationTokenSource();
351  timerTask = TimerLoop(newInterval, timerCts.Token);
352  }
353  }
354 
357  {
358  if (!dmbFactory.DmbAvailable)
359  return null;
360  return dmbFactory.LockNextDmb(0)?.CompileJob;
361  }
362  }
363 }
+Go to the documentation of this file.
1 using Microsoft.EntityFrameworkCore;
2 using Microsoft.Extensions.Logging;
3 using System;
4 using System.Linq;
5 using System.Threading;
6 using System.Threading.Tasks;
15 
16 namespace Tgstation.Server.Host.Components
17 {
19  sealed class Instance : IInstance
20  {
23 
25  public IByondManager ByondManager { get; }
26 
28  public IDreamMaker DreamMaker { get; }
29 
31  public IWatchdog Watchdog { get; }
32 
34  public IChat Chat { get; }
35 
37  public StaticFiles.IConfiguration Configuration { get; }
38 
40  public ICompileJobConsumer CompileJobConsumer { get; }
41 
46 
51 
56 
60  readonly ILogger<Instance> logger;
61 
65  readonly Api.Models.Instance metadata;
66 
70  Task timerTask;
74  CancellationTokenSource timerCts;
75 
91  public Instance(Api.Models.Instance metadata, IRepositoryManager repositoryManager, IByondManager byondManager, IDreamMaker dreamMaker, IWatchdog watchdog, IChat chat, StaticFiles.IConfiguration configuration, ICompileJobConsumer compileJobConsumer, IDatabaseContextFactory databaseContextFactory, IDmbFactory dmbFactory, IJobManager jobManager, ILogger<Instance> logger)
92  {
93  this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
94  RepositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager));
95  ByondManager = byondManager ?? throw new ArgumentNullException(nameof(byondManager));
96  DreamMaker = dreamMaker ?? throw new ArgumentNullException(nameof(dreamMaker));
97  Watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog));
98  Chat = chat ?? throw new ArgumentNullException(nameof(chat));
99  Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
100  CompileJobConsumer = compileJobConsumer ?? throw new ArgumentNullException(nameof(compileJobConsumer));
101  this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
102  this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory));
103  this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager));
104  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
105  }
106 
108  public void Dispose()
109  {
110  timerCts?.Dispose();
111  CompileJobConsumer.Dispose();
112  Configuration.Dispose();
113  Chat.Dispose();
114  Watchdog.Dispose();
116  }
117 
119  public async Task CompileProcess(Job job, IDatabaseContext databaseContext, Action<int> progressReporter, CancellationToken cancellationToken)
120  {
121  //DO NOT FOLLOW THE SUGGESTION FOR A THROW EXPRESSION HERE
122  if (job == null)
123  throw new ArgumentNullException(nameof(job));
124  if (databaseContext == null)
125  throw new ArgumentNullException(nameof(databaseContext));
126  if (progressReporter == null)
127  throw new ArgumentNullException(nameof(progressReporter));
128 
129  var ddSettingsTask = databaseContext.DreamDaemonSettings.Where(x => x.InstanceId == metadata.Id).Select(x => new DreamDaemonSettings
130  {
131  StartupTimeout = x.StartupTimeout,
132  }).FirstOrDefaultAsync(cancellationToken);
133 
134  var dreamMakerSettings = await databaseContext.DreamMakerSettings.Where(x => x.InstanceId == metadata.Id).FirstAsync(cancellationToken).ConfigureAwait(false);
135  if (dreamMakerSettings == default)
136  throw new JobException("Missing DreamMakerSettings in DB!");
137  var ddSettings = await ddSettingsTask.ConfigureAwait(false);
138  if (ddSettings == default)
139  throw new JobException("Missing DreamDaemonSettings in DB!");
140 
141  CompileJob compileJob;
142  RevisionInformation revInfo;
143  using (var repo = await RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false))
144  {
145  if (repo == null)
146  throw new JobException("Missing Repository!");
147 
148  var repoSha = repo.Head;
149  revInfo = await databaseContext.RevisionInformations.Where(x => x.CommitSha == repoSha).Include(x => x.ActiveTestMerges).ThenInclude(x => x.TestMerge).FirstOrDefaultAsync().ConfigureAwait(false);
150 
151  if (revInfo == default)
152  {
153  revInfo = new RevisionInformation
154  {
155  CommitSha = repoSha,
156  OriginCommitSha = repoSha,
157  Instance = new Models.Instance
158  {
159  Id = metadata.Id
160  }
161  };
162  logger.LogWarning(Repository.Repository.OriginTrackingErrorTemplate, repoSha);
163  databaseContext.Instances.Attach(revInfo.Instance);
164  }
165 
166  compileJob = await DreamMaker.Compile(revInfo, dreamMakerSettings, ddSettings.StartupTimeout.Value, repo, cancellationToken).ConfigureAwait(false);
167  }
168 
169  compileJob.Job = job;
170 
171  databaseContext.CompileJobs.Add(compileJob); //will be saved by job context
172 
173  job.PostComplete = ct => CompileJobConsumer.LoadCompileJob(compileJob, ct);
174  }
175 
182  async Task TimerLoop(uint minutes, CancellationToken cancellationToken)
183  {
184  while (true)
185  try
186  {
187  await Task.Delay(new TimeSpan(0, minutes > Int32.MaxValue ? Int32.MaxValue : (int)minutes, 0), cancellationToken).ConfigureAwait(false);
188 
189  try
190  {
191  Models.User user = null;
192  await databaseContextFactory.UseContext(async (db) => user = await db.Users.FirstAsync(cancellationToken).ConfigureAwait(false)).ConfigureAwait(false);
193  var repositoryUpdateJob = new Job
194  {
195  Instance = new Models.Instance
196  {
197  Id = metadata.Id
198  },
199  Description = "Scheduled repository update",
200  CancelRightsType = RightsType.Repository,
201  CancelRight = (ulong)RepositoryRights.CancelPendingChanges
202  };
203 
204  var noRepo = false;
205  await jobManager.RegisterOperation(repositoryUpdateJob, async (paramJob, databaseContext, progressReporter, jobCancellationToken) =>
206  {
207  var repositorySettingsTask = databaseContext.RepositorySettings.Where(x => x.InstanceId == metadata.Id).FirstAsync(jobCancellationToken);
208 
209  //assume 5 steps with synchronize
210  const int ProgressSections = 7;
211  const int ProgressStep = 100 / ProgressSections;
212 
213 
214  const int NumSteps = 3;
215  var doneSteps = 0;
216 
217  Action<int> NextProgressReporter()
218  {
219  var tmpDoneSteps = doneSteps;
220  ++doneSteps;
221  return progress => progressReporter((progress + 100 * tmpDoneSteps) / NumSteps);
222  };
223 
224  using (var repo = await RepositoryManager.LoadRepository(jobCancellationToken).ConfigureAwait(false))
225  {
226  if (repo == null)
227  {
228  //no repo, no auto updates
229  noRepo = true;
230  return;
231  }
232 
233  var repositorySettings = await repositorySettingsTask.ConfigureAwait(false);
234 
235  //the main point of auto update is to pull the remote
236  await repo.FetchOrigin(repositorySettings.AccessUser, repositorySettings.AccessToken, NextProgressReporter(), jobCancellationToken).ConfigureAwait(false);
237 
238  var startSha = repo.Head;
239 
240  //take appropriate auto update actions
241  bool shouldSyncTracked;
242  if (repositorySettings.AutoUpdatesKeepTestMerges.Value)
243  {
244  var result = await repo.MergeOrigin(repositorySettings.CommitterName, repositorySettings.CommitterEmail, NextProgressReporter(), jobCancellationToken).ConfigureAwait(false);
245  if (!result.HasValue)
246  return;
247  shouldSyncTracked = result.Value;
248  }
249  else
250  {
251  await repo.ResetToOrigin(NextProgressReporter(), jobCancellationToken).ConfigureAwait(false);
252  shouldSyncTracked = true;
253  }
254 
255  //synch if necessary
256  if (repositorySettings.AutoUpdatesSynchronize.Value && startSha != repo.Head)
257  await repo.Sychronize(repositorySettings.AccessUser, repositorySettings.AccessToken, repositorySettings.CommitterName, repositorySettings.CommitterEmail, NextProgressReporter(), shouldSyncTracked, jobCancellationToken).ConfigureAwait(false);
258 
259  progressReporter(5 * ProgressStep);
260  }
261  }, cancellationToken).ConfigureAwait(false);
262 
263  await jobManager.WaitForJobCompletion(repositoryUpdateJob, user, cancellationToken, default).ConfigureAwait(false);
264 
265  if (noRepo)
266  continue;
267 
268  //finally set up the job
269  var compileProcessJob = new Job
270  {
271  StartedBy = user,
272  Instance = repositoryUpdateJob.Instance,
273  Description = "Scheduled code deployment",
274  CancelRightsType = RightsType.DreamMaker,
275  CancelRight = (ulong)DreamMakerRights.CancelCompile
276  };
277 
278  await jobManager.RegisterOperation(compileProcessJob, CompileProcess, cancellationToken).ConfigureAwait(false);
279 
280  await jobManager.WaitForJobCompletion(compileProcessJob, user, cancellationToken, default).ConfigureAwait(false);
281  }
282  catch (OperationCanceledException)
283  {
284  logger.LogDebug("Cancelled auto update job!");
285  throw;
286  }
287  catch (Exception e)
288  {
289  logger.LogWarning("Error in auto update loop! Exception: {0}", e);
290  continue;
291  }
292  }
293  catch (OperationCanceledException)
294  {
295  break;
296  }
297  logger.LogTrace("Leaving auto update loop...");
298  }
299 
301  public void Rename(string newName)
302  {
303  if (String.IsNullOrWhiteSpace(newName))
304  throw new ArgumentNullException(nameof(newName));
305  metadata.Name = newName;
306  }
307 
309  public async Task StartAsync(CancellationToken cancellationToken)
310  {
311  await Task.WhenAll(SetAutoUpdateInterval(metadata.AutoUpdateInterval.Value), Configuration.StartAsync(cancellationToken), ByondManager.StartAsync(cancellationToken), Chat.StartAsync(cancellationToken), CompileJobConsumer.StartAsync(cancellationToken)).ConfigureAwait(false);
312 
313  //dependent on so many things, its just safer this way
314  await Watchdog.StartAsync(cancellationToken).ConfigureAwait(false);
315 
316  CompileJob latestCompileJob = null;
317  await databaseContextFactory.UseContext(async db =>
318  {
319  latestCompileJob = await db.CompileJobs.Where(x => x.Job.Instance.Id == metadata.Id).OrderByDescending(x => x.Job.StoppedAt).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
320  }).ConfigureAwait(false);
321  await dmbFactory.CleanUnusedCompileJobs(latestCompileJob, cancellationToken).ConfigureAwait(false);
322  }
323 
325  public Task StopAsync(CancellationToken cancellationToken) => Task.WhenAll(SetAutoUpdateInterval(0), Configuration.StopAsync(cancellationToken), ByondManager.StopAsync(cancellationToken), Watchdog.StopAsync(cancellationToken), Chat.StopAsync(cancellationToken), CompileJobConsumer.StopAsync(cancellationToken));
326 
328  public async Task SetAutoUpdateInterval(uint newInterval)
329  {
330  Task toWait;
331  lock (this)
332  {
333  if (timerTask != null)
334  {
335  timerCts.Cancel();
336  toWait = timerTask;
337  }
338  else
339  toWait = Task.CompletedTask;
340  }
341  await toWait.ConfigureAwait(false);
342  if (newInterval == 0)
343  return;
344  lock (this)
345  {
346  //race condition, just quit
347  if (timerTask != null)
348  return;
349  timerCts?.Dispose();
350  timerCts = new CancellationTokenSource();
351  timerTask = TimerLoop(newInterval, timerCts.Token);
352  }
353  }
354 
357  {
358  if (!dmbFactory.DmbAvailable)
359  return null;
360  return dmbFactory.LockNextDmb(0)?.CompileJob;
361  }
362  }
363 }
Manages the runtime of Jobs
Definition: IJobManager.cs:12
@@ -110,6 +110,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RightsType
The type of rights a model uses
Definition: RightsType.cs:6
Operation exceptions thrown from the context of a Models.Job
Definition: JobException.cs:8
+
Factory for scoping usage of IDatabaseContexts. Meant for use by Components
@@ -134,7 +135,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
For interacting with the instance services
Definition: IInstance.cs:17
DreamMakerRights
Rights for Models.DreamMaker
-
async Task< Models.CompileJob > Compile(Models.RevisionInformation revisionInformation, Api.Models.DreamMaker dreamMakerSettings, uint apiValidateTimeout, IRepository repository, CancellationToken cancellationToken)
Definition: DreamMaker.cs:269
readonly ILogger< Instance > logger
The ILogger for the Instance
Definition: Instance.cs:60
For managing the BYOND installation
diff --git a/_tgstation_8_server_8_host_2_components_2_interop_2_test_merge_8cs_source.html b/_tgstation_8_server_8_host_2_components_2_interop_2_test_merge_8cs_source.html index 5defc415be..37a48c9985 100644 --- a/_tgstation_8_server_8_host_2_components_2_interop_2_test_merge_8cs_source.html +++ b/_tgstation_8_server_8_host_2_components_2_interop_2_test_merge_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
TestMerge.cs
-Go to the documentation of this file.
1 using System;
2 using System.Globalization;
4 
5 namespace Tgstation.Server.Host.Components.Interop
6 {
10  sealed class TestMerge : TestMergeBase
11  {
15  public string TimeMerged { get; set; }
16 
20  public RevisionInformation Revision { get; set; }
21 
27  public TestMerge(Models.TestMerge testMerge, RevisionInformation revision) : base(testMerge)
28  {
29  TimeMerged = testMerge.MergedAt.Ticks.ToString(CultureInfo.InvariantCulture);
30  Revision = revision ?? throw new ArgumentNullException(nameof(revision));
31  }
32  }
33 }
+Go to the documentation of this file.
1 using System;
2 using System.Globalization;
4 
5 namespace Tgstation.Server.Host.Components.Interop
6 {
10  sealed class TestMerge : TestMergeBase
11  {
15  public string TimeMerged { get; set; }
16 
20  public RevisionInformation Revision { get; set; }
21 
27  public TestMerge(Models.TestMerge testMerge, RevisionInformation revision) : base(testMerge)
28  {
29  TimeMerged = testMerge.MergedAt.Ticks.ToString(CultureInfo.InvariantCulture);
30  Revision = revision ?? throw new ArgumentNullException(nameof(revision));
31  }
32  }
33 }
+
Layer of test merge data required internally
Definition: TestMergeBase.cs:9
- - +
This model mirrors /datum/tgs_revision_information/test_merge
Definition: TestMerge.cs:10
TestMerge(Models.TestMerge testMerge, RevisionInformation revision)
Construct a TestMerge
Definition: TestMerge.cs:27
diff --git a/_tgstation_8_server_8_host_2_components_2_repository_2_repository_8cs_source.html b/_tgstation_8_server_8_host_2_components_2_repository_2_repository_8cs_source.html index 3f1632d00b..b75e39365f 100644 --- a/_tgstation_8_server_8_host_2_components_2_repository_2_repository_8cs_source.html +++ b/_tgstation_8_server_8_host_2_components_2_repository_2_repository_8cs_source.html @@ -92,9 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Repository.cs
-Go to the documentation of this file.
1 using LibGit2Sharp;
2 using LibGit2Sharp.Handlers;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Collections.Generic;
6 using System.Globalization;
7 using System.Linq;
8 using System.Threading;
9 using System.Threading.Tasks;
11 using Tgstation.Server.Host.IO;
12 
13 namespace Tgstation.Server.Host.Components.Repository
14 {
16  sealed class Repository : IRepository
17  {
21  public const string GitHubUrl = "://github.com/";
22 
26  public const string OriginTrackingErrorTemplate = "Unable to determine most recent origin commit of {0}. Marking it as an origin commit. This may result in invalid git metadata until the next hard reset to an origin reference.";
27 
31  public const string RemoteTemporaryBranchName = "___TGSTempBranch";
32 
33  const string UnknownReference = "<UNKNOWN>";
34 
36  public bool IsGitHubRepository { get; }
37 
39  public string GitHubOwner { get; }
40 
42  public string GitHubRepoName { get; }
43 
45  public bool Tracking => Reference != null && repository.Head.IsTracking;
46 
48  public string Head => repository.Head.Tip.Sha;
49 
51  public string Reference => repository.Head.FriendlyName;
52 
54  public string Origin => repository.Network.Remotes.First().Url;
55 
59  readonly LibGit2Sharp.IRepository repository;
60 
65 
70 
75 
79  readonly ILogger<Repository> logger;
80 
84  readonly Action onDispose;
85 
86  void GetRepositoryOwnerName(string remote, out string owner, out string name)
87  {
88  //Assume standard gh format: [(git)|(https)]://github.com/owner/repo(.git)[0-1]
89  //Yes use .git twice in case it was weird
90  var toRemove = new string[] { ".git", "/", ".git" };
91  foreach (string item in toRemove)
92  if (remote.EndsWith(item, StringComparison.OrdinalIgnoreCase))
93  remote = remote.Substring(0, remote.LastIndexOf(item, StringComparison.OrdinalIgnoreCase));
94  var splits = remote.Split('/');
95  name = splits[splits.Length - 1];
96  owner = splits[splits.Length - 2].Split('.')[0];
97 
98  logger.LogTrace("GetRepositoryOwnerName({0}) => {1} / {2}", remote, owner, name);
99  }
100 
106  static CheckoutProgressHandler CheckoutProgressHandler(Action<int> progressReporter) => (a, completedSteps, totalSteps) => progressReporter((int)((((float)completedSteps) / totalSteps) * 100));
107 
117  public Repository(LibGit2Sharp.IRepository repository, IIOManager ioMananger, IEventConsumer eventConsumer, ICredentialsProvider credentialsProvider, ILogger<Repository> logger, Action onDispose)
118  {
119  this.repository = repository ?? throw new ArgumentNullException(nameof(repository));
120  this.ioMananger = ioMananger ?? throw new ArgumentNullException(nameof(ioMananger));
121  this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
122  this.credentialsProvider = credentialsProvider ?? throw new ArgumentNullException(nameof(credentialsProvider));
123  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
124  this.onDispose = onDispose ?? throw new ArgumentNullException(nameof(onDispose));
125  IsGitHubRepository = Origin.Contains(GitHubUrl, StringComparison.InvariantCultureIgnoreCase);
126  if (IsGitHubRepository)
127  {
128  GetRepositoryOwnerName(Origin, out var owner, out var name);
129  GitHubOwner = owner;
130  GitHubRepoName = name;
131  }
132  }
133 
135  public void Dispose()
136  {
137  logger.LogTrace("Disposing...");
138  repository.Dispose();
139  onDispose.Invoke();
140  }
141 
150  PushOptions GeneratePushOptions(Action<int> progressReporter, string username, string password, CancellationToken cancellationToken) => new PushOptions
151  {
152  OnPackBuilderProgress = (stage, current, total) =>
153  {
154  var baseProgress = stage == PackBuilderStage.Counting ? 0 : 25;
155  progressReporter(baseProgress + ((int)(25 * ((float)current) / total)));
156  return !cancellationToken.IsCancellationRequested;
157  },
158  OnNegotiationCompletedBeforePush = (a) => !cancellationToken.IsCancellationRequested,
159  OnPushTransferProgress = (a, sentBytes, totalBytes) =>
160  {
161  progressReporter(50 + ((int)(50 * ((float)sentBytes) / totalBytes)));
162  return !cancellationToken.IsCancellationRequested;
163  },
164  CredentialsProvider = credentialsProvider.GenerateHandler(username, password)
165  };
166 
173  void RawCheckout(string committish, Action<int> progressReporter, CancellationToken cancellationToken)
174  {
175  logger.LogTrace("Checkout: {0}", committish);
176 
177  progressReporter(0);
178  cancellationToken.ThrowIfCancellationRequested();
179 
180  Commands.Checkout(repository, committish, new CheckoutOptions
181  {
182  CheckoutModifiers = CheckoutModifiers.Force,
183  OnCheckoutProgress = CheckoutProgressHandler(progressReporter)
184  });
185 
186  cancellationToken.ThrowIfCancellationRequested();
187 
188  repository.RemoveUntrackedFiles();
189  }
190 
192  public async Task<bool?> AddTestMerge(TestMergeParameters testMergeParameters, string committerName, string committerEmail, string username, string password, Action<int> progressReporter, CancellationToken cancellationToken)
193  {
194  if (testMergeParameters == null)
195  throw new ArgumentNullException(nameof(testMergeParameters));
196  if (committerName == null)
197  throw new ArgumentNullException(nameof(committerName));
198  if (committerEmail == null)
199  throw new ArgumentNullException(nameof(committerEmail));
200  if (progressReporter == null)
201  throw new ArgumentNullException(nameof(progressReporter));
202 
203  logger.LogDebug("Begin AddTestMerge: #{0} at {1} ({4}) by <{2} ({3})>", testMergeParameters.Number, testMergeParameters.PullRequestRevision?.Substring(0, 7), committerName, committerEmail, testMergeParameters.Comment);
204 
205  if (!IsGitHubRepository)
206  throw new InvalidOperationException("Test merging is only available on GitHub hosted origin repositories!");
207 
208  var commitMessage = String.Format(CultureInfo.InvariantCulture, "Test merge of pull request #{0}{1}{2}", testMergeParameters.Number.Value, testMergeParameters.Comment != null ? Environment.NewLine : String.Empty, testMergeParameters.Comment ?? String.Empty);
209 
210  var prBranchName = String.Format(CultureInfo.InvariantCulture, "pr-{0}", testMergeParameters.Number);
211  var localBranchName = String.Format(CultureInfo.InvariantCulture, "pull/{0}/headrefs/heads/{1}", testMergeParameters.Number, prBranchName);
212 
213  var refSpec = String.Format(CultureInfo.InvariantCulture, "pull/{0}/head:{1}", testMergeParameters.Number, prBranchName);
214  var refSpecList = new List<string> { refSpec };
215  var logMessage = String.Format(CultureInfo.InvariantCulture, "Merge remote pull request #{0}", testMergeParameters.Number);
216 
217  var originalCommit = repository.Head;
218 
219  MergeResult result = null;
220 
221  var sig = new Signature(new Identity(committerName, committerEmail), DateTimeOffset.Now);
222  await Task.Factory.StartNew(() =>
223  {
224  try
225  {
226  try
227  {
228  logger.LogTrace("Fetching refspec {0}...", refSpec);
229 
230  var remote = repository.Network.Remotes.First();
231  progressReporter(0);
232  Commands.Fetch((LibGit2Sharp.Repository)repository, remote.Name, refSpecList, new FetchOptions
233  {
234  Prune = true,
235  OnProgress = (a) => !cancellationToken.IsCancellationRequested,
236  OnTransferProgress = (a) =>
237  {
238  var percentage = 50 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2));
239  progressReporter((int)percentage);
240  return !cancellationToken.IsCancellationRequested;
241  },
242  OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested,
243  CredentialsProvider = credentialsProvider.GenerateHandler(username, password)
244  }, logMessage);
245  }
246  catch (UserCancelledException) { }
247 
248  cancellationToken.ThrowIfCancellationRequested();
249 
250  repository.RemoveUntrackedFiles();
251 
252  cancellationToken.ThrowIfCancellationRequested();
253 
254  testMergeParameters.PullRequestRevision = repository.Lookup(testMergeParameters.PullRequestRevision ?? localBranchName).Sha;
255 
256  cancellationToken.ThrowIfCancellationRequested();
257 
258  logger.LogTrace("Merging {0} into {1}...", testMergeParameters.PullRequestRevision.Substring(0, 7), Reference);
259 
260  result = repository.Merge(testMergeParameters.PullRequestRevision, sig, new MergeOptions
261  {
262  CommitOnSuccess = commitMessage == null,
263  FailOnConflict = true,
264  FastForwardStrategy = FastForwardStrategy.NoFastForward,
265  SkipReuc = true,
266  OnCheckoutProgress = (a, completedSteps, totalSteps) => progressReporter(50 + ((int)((((float)completedSteps) / totalSteps) * 50)))
267  });
268  }
269  finally
270  {
271  repository.Branches.Remove(localBranchName);
272  }
273 
274  cancellationToken.ThrowIfCancellationRequested();
275 
276  if (result.Status == MergeStatus.Conflicts)
277  {
278  var revertTo = originalCommit.CanonicalName ?? originalCommit.Tip.Sha;
279  logger.LogDebug("Merge conflict, aborting and reverting to {0}", revertTo);
280  RawCheckout(revertTo, progressReporter, cancellationToken);
281  cancellationToken.ThrowIfCancellationRequested();
282  }
283 
284  repository.RemoveUntrackedFiles();
285  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
286 
287  if (result.Status == MergeStatus.Conflicts)
288  {
289  await eventConsumer.HandleEvent(EventType.RepoMergeConflict, new List<string> { originalCommit.Tip.Sha, testMergeParameters.PullRequestRevision, originalCommit.FriendlyName ?? UnknownReference, prBranchName }, cancellationToken).ConfigureAwait(false);
290  return false;
291  }
292 
293  if (commitMessage != null && result.Status != MergeStatus.UpToDate)
294  {
295  logger.LogTrace("Committing merge: \"{0}\"...", commitMessage);
296  await Task.Factory.StartNew(() => repository.Commit(commitMessage, sig, sig, new CommitOptions
297  {
298  PrettifyMessage = true
299  }), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
300  }
301 
302  return true;
303  }
304 
306  public async Task CheckoutObject(string committish, Action<int> progressReporter, CancellationToken cancellationToken)
307  {
308  if (committish == null)
309  throw new ArgumentNullException(nameof(committish));
310  if (progressReporter == null)
311  throw new ArgumentNullException(nameof(progressReporter));
312  logger.LogDebug("Checkout object: {0}...", committish);
313  await eventConsumer.HandleEvent(EventType.RepoCheckout, new List<string> { committish }, cancellationToken).ConfigureAwait(false);
314  await Task.Factory.StartNew(() =>
315  {
316  repository.RemoveUntrackedFiles();
317  RawCheckout(committish, progressReporter, cancellationToken);
318  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
319  }
320 
322  public async Task FetchOrigin(string username, string password, Action<int> progressReporter, CancellationToken cancellationToken)
323  {
324  if (progressReporter == null)
325  throw new ArgumentNullException(nameof(progressReporter));
326  logger.LogDebug("Fetch origin...");
327  await eventConsumer.HandleEvent(EventType.RepoFetch, Array.Empty<string>(), cancellationToken).ConfigureAwait(false);
328  await Task.Factory.StartNew(() =>
329  {
330  var remote = repository.Network.Remotes.First();
331  try
332  {
333  Commands.Fetch((LibGit2Sharp.Repository)repository, remote.Name, remote.FetchRefSpecs.Select(x => x.Specification), new FetchOptions
334  {
335  Prune = true,
336  OnProgress = (a) => !cancellationToken.IsCancellationRequested,
337  OnTransferProgress = (a) =>
338  {
339  var percentage = 100 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2));
340  progressReporter((int)percentage);
341  return !cancellationToken.IsCancellationRequested;
342  },
343  OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested,
344  CredentialsProvider = credentialsProvider.GenerateHandler(username, password)
345  }, "Fetch origin commits");
346  }
347  catch (UserCancelledException)
348  {
349  cancellationToken.ThrowIfCancellationRequested();
350  }
351  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
352  }
353 
362  Task PushHeadToTemporaryBranch(string username, string password, Action<int> progressReporter, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
363  {
364  logger.LogInformation("Pushing changes to temporary remote branch...");
365  var branch = repository.CreateBranch(RemoteTemporaryBranchName);
366  try
367  {
368  cancellationToken.ThrowIfCancellationRequested();
369  var remote = repository.Network.Remotes.First();
370  try
371  {
372  var forcePushString = String.Format(CultureInfo.InvariantCulture, "+{0}:{0}", branch.CanonicalName);
373  repository.Network.Push(remote,forcePushString, GeneratePushOptions(progress => progressReporter((int)(0.9f * progress)), username, password, cancellationToken));
374  var removalString = String.Format(CultureInfo.InvariantCulture, ":{0}", branch.CanonicalName);
375  repository.Network.Push(remote, removalString, GeneratePushOptions(progress => progressReporter(90 + (int)(0.1f * progress)), username, password, cancellationToken));
376  }
377  catch (UserCancelledException)
378  {
379  cancellationToken.ThrowIfCancellationRequested();
380  }
381  }
382  finally
383  {
384  repository.Branches.Remove(branch);
385  }
386  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
387 
389  public async Task ResetToOrigin(Action<int> progressReporter, CancellationToken cancellationToken)
390  {
391  if (progressReporter == null)
392  throw new ArgumentNullException(nameof(progressReporter));
393  if (!Tracking)
394  throw new JobException("Cannot reset to origin while not on a tracked reference!");
395  logger.LogTrace("Reset to origin...");
396  var trackedBranch = repository.Head.TrackedBranch;
397  await eventConsumer.HandleEvent(EventType.RepoResetOrigin, new List<string> { trackedBranch.FriendlyName, trackedBranch.Tip.Sha }, cancellationToken).ConfigureAwait(false);
398  await ResetToSha(trackedBranch.Tip.Sha, progressReporter, cancellationToken).ConfigureAwait(false);
399  }
400 
402  public Task ResetToSha(string sha, Action<int> progressReporter, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
403  {
404  if (sha == null)
405  throw new ArgumentNullException(nameof(sha));
406  if (progressReporter == null)
407  throw new ArgumentNullException(nameof(progressReporter));
408 
409  logger.LogDebug("Reset to sha: {0}", sha.Substring(0, 7));
410 
411  repository.RemoveUntrackedFiles();
412  cancellationToken.ThrowIfCancellationRequested();
413 
414  var gitObject = repository.Lookup(sha, ObjectType.Commit);
415  cancellationToken.ThrowIfCancellationRequested();
416 
417  if (gitObject == null)
418  throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot reset to non-existent SHA: {0}", sha));
419 
420  repository.Reset(ResetMode.Hard, gitObject.Peel<Commit>(), new CheckoutOptions
421  {
422  OnCheckoutProgress = CheckoutProgressHandler(progressReporter)
423  });
424  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
425 
427  public async Task CopyTo(string path, CancellationToken cancellationToken)
428  {
429  if (path == null)
430  throw new ArgumentNullException(nameof(path));
431  logger.LogTrace("Copying to {0}...", path);
432  await ioMananger.CopyDirectory(".", path, new List<string> { ".git" }, cancellationToken).ConfigureAwait(false);
433  }
434 
436  public async Task<bool?> MergeOrigin(string committerName, string committerEmail, Action<int> progressReporter, CancellationToken cancellationToken)
437  {
438  if (progressReporter == null)
439  throw new ArgumentNullException(nameof(progressReporter));
440 
441  MergeResult result = null;
442  Branch trackedBranch = null;
443 
444  var oldHead = repository.Head;
445 
446  await Task.Factory.StartNew(() =>
447  {
448  if (!Tracking)
449  throw new JobException("Cannot reset to origin while not on a tracked reference!");
450 
451  repository.RemoveUntrackedFiles();
452 
453  cancellationToken.ThrowIfCancellationRequested();
454 
455  trackedBranch = repository.Head.TrackedBranch;
456  logger.LogDebug("Merge origin/{2}: <{0} ({1})>", committerName, committerEmail, trackedBranch.FriendlyName);
457  result = repository.Merge(trackedBranch, new Signature(new Identity(committerName, committerEmail), DateTimeOffset.Now), new MergeOptions
458  {
459  CommitOnSuccess = true,
460  FailOnConflict = true,
461  FastForwardStrategy = FastForwardStrategy.Default,
462  SkipReuc = true,
463  OnCheckoutProgress = CheckoutProgressHandler(progressReporter)
464  });
465 
466  cancellationToken.ThrowIfCancellationRequested();
467 
468  if (result.Status == MergeStatus.Conflicts)
469  {
470  logger.LogDebug("Merge conflict, aborting and reverting to {0}", oldHead.FriendlyName);
471  RawCheckout(oldHead.CanonicalName, progressReporter, cancellationToken);
472  cancellationToken.ThrowIfCancellationRequested();
473  }
474 
475  repository.RemoveUntrackedFiles();
476  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
477 
478  if (result.Status == MergeStatus.Conflicts)
479  {
480  await eventConsumer.HandleEvent(EventType.RepoMergeConflict, new List<string> { oldHead.Tip.Sha, trackedBranch.Tip.Sha, oldHead.FriendlyName ?? UnknownReference, trackedBranch.FriendlyName }, cancellationToken).ConfigureAwait(false);
481  return null;
482  }
483 
484  return result.Status == MergeStatus.FastForward;
485  }
486 
488  public async Task Sychronize(string username, string password, string committerName, string committerEmail, Action<int> progressReporter, bool synchronizeTrackedBranch, CancellationToken cancellationToken)
489  {
490  if (committerName == null)
491  throw new ArgumentNullException(nameof(committerName));
492  if (committerEmail == null)
493  throw new ArgumentNullException(nameof(committerEmail));
494  if (progressReporter == null)
495  throw new ArgumentNullException(nameof(progressReporter));
496 
497  if (username == null && password == null)
498  {
499  logger.LogTrace("Not synchronizing due to lack of credentials!");
500  return;
501  }
502  logger.LogTrace("Begin Synchronize...");
503 
504  if (username == null)
505  throw new ArgumentNullException(nameof(username));
506  if (password == null)
507  throw new ArgumentNullException(nameof(password));
508 
509  var startHead = Head;
510 
511  logger.LogTrace("Configuring <{0} ({1})> as author/committer", committerName, committerEmail);
512  await Task.Factory.StartNew(() =>
513  {
514  repository.Config.Set("user.name", committerName);
515  cancellationToken.ThrowIfCancellationRequested();
516  repository.Config.Set("user.email", committerEmail);
517  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
518 
519  cancellationToken.ThrowIfCancellationRequested();
520  try
521  {
522  if (!await eventConsumer.HandleEvent(EventType.RepoPreSynchronize, new List<string> { ioMananger.ResolvePath(".") }, cancellationToken).ConfigureAwait(false))
523  {
524  logger.LogDebug("Aborted synchronize due to event handler response!");
525  return;
526  }
527  }
528  finally
529  {
530  logger.LogTrace("Resetting and cleaning untracked files...");
531  await Task.Factory.StartNew(() =>
532  {
533  repository.RemoveUntrackedFiles();
534  cancellationToken.ThrowIfCancellationRequested();
535  repository.Reset(ResetMode.Hard, repository.Head.Tip, new CheckoutOptions
536  {
537  OnCheckoutProgress = CheckoutProgressHandler(progress => progressReporter(progress / 10))
538  });
539  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
540  }
541 
542  void FinalReporter(int progress) => progressReporter((int)(((float)progress) / 100 * 90));
543 
544  if (!synchronizeTrackedBranch)
545  {
546  await PushHeadToTemporaryBranch(username, password, FinalReporter, cancellationToken).ConfigureAwait(false);
547  return;
548  }
549 
550  var sameHead = Head == startHead;
551  if (sameHead || !Tracking)
552  {
553  logger.LogTrace("Aborted synchronize due to {0}!", sameHead ? "lack of changes" : "not being on tracked reference");
554  return;
555  }
556 
557  logger.LogInformation("Synchronizing with origin...");
558 
559  await Task.Factory.StartNew(() =>
560  {
561  var remote = repository.Network.Remotes.First();
562  try
563  {
564  repository.Network.Push(repository.Head, GeneratePushOptions(FinalReporter, username, password, cancellationToken));
565  }
566  catch (NonFastForwardException)
567  {
568  logger.LogInformation("Synchronize aborted, non-fast forward!");
569  }
570  catch (UserCancelledException)
571  {
572  cancellationToken.ThrowIfCancellationRequested();
573  }
574  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
575  }
576 
578  public Task<bool> IsSha(string committish, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
579  {
580  var gitObject = repository.Lookup(committish, ObjectType.Commit);
581  return gitObject != null;
582  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
583  }
584 }
Operation exceptions thrown from the context of a Models.Job
Definition: JobException.cs:8
+Go to the documentation of this file.
1 using LibGit2Sharp;
2 using LibGit2Sharp.Handlers;
3 using Microsoft.Extensions.Logging;
4 using System;
5 using System.Collections.Generic;
6 using System.Globalization;
7 using System.Linq;
8 using System.Threading;
9 using System.Threading.Tasks;
11 using Tgstation.Server.Host.IO;
12 
13 namespace Tgstation.Server.Host.Components.Repository
14 {
16  sealed class Repository : IRepository
17  {
21  public const string GitHubUrl = "://github.com/";
22 
26  public const string OriginTrackingErrorTemplate = "Unable to determine most recent origin commit of {0}. Marking it as an origin commit. This may result in invalid git metadata until the next hard reset to an origin reference.";
27 
31  public const string RemoteTemporaryBranchName = "___TGSTempBranch";
32 
33  const string UnknownReference = "<UNKNOWN>";
34 
36  public bool IsGitHubRepository { get; }
37 
39  public string GitHubOwner { get; }
40 
42  public string GitHubRepoName { get; }
43 
45  public bool Tracking => Reference != null && repository.Head.IsTracking;
46 
48  public string Head => repository.Head.Tip.Sha;
49 
51  public string Reference => repository.Head.FriendlyName;
52 
54  public string Origin => repository.Network.Remotes.First().Url;
55 
59  readonly LibGit2Sharp.IRepository repository;
60 
65 
70 
75 
79  readonly ILogger<Repository> logger;
80 
84  readonly Action onDispose;
85 
86  void GetRepositoryOwnerName(string remote, out string owner, out string name)
87  {
88  //Assume standard gh format: [(git)|(https)]://github.com/owner/repo(.git)[0-1]
89  //Yes use .git twice in case it was weird
90  var toRemove = new string[] { ".git", "/", ".git" };
91  foreach (string item in toRemove)
92  if (remote.EndsWith(item, StringComparison.OrdinalIgnoreCase))
93  remote = remote.Substring(0, remote.LastIndexOf(item, StringComparison.OrdinalIgnoreCase));
94  var splits = remote.Split('/');
95  name = splits[splits.Length - 1];
96  owner = splits[splits.Length - 2].Split('.')[0];
97 
98  logger.LogTrace("GetRepositoryOwnerName({0}) => {1} / {2}", remote, owner, name);
99  }
100 
106  static CheckoutProgressHandler CheckoutProgressHandler(Action<int> progressReporter) => (a, completedSteps, totalSteps) => progressReporter((int)((((float)completedSteps) / totalSteps) * 100));
107 
117  public Repository(LibGit2Sharp.IRepository repository, IIOManager ioMananger, IEventConsumer eventConsumer, ICredentialsProvider credentialsProvider, ILogger<Repository> logger, Action onDispose)
118  {
119  this.repository = repository ?? throw new ArgumentNullException(nameof(repository));
120  this.ioMananger = ioMananger ?? throw new ArgumentNullException(nameof(ioMananger));
121  this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
122  this.credentialsProvider = credentialsProvider ?? throw new ArgumentNullException(nameof(credentialsProvider));
123  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
124  this.onDispose = onDispose ?? throw new ArgumentNullException(nameof(onDispose));
125  IsGitHubRepository = Origin.Contains(GitHubUrl, StringComparison.InvariantCultureIgnoreCase);
126  if (IsGitHubRepository)
127  {
128  GetRepositoryOwnerName(Origin, out var owner, out var name);
129  GitHubOwner = owner;
130  GitHubRepoName = name;
131  }
132  }
133 
135  public void Dispose()
136  {
137  logger.LogTrace("Disposing...");
138  repository.Dispose();
139  onDispose.Invoke();
140  }
141 
150  PushOptions GeneratePushOptions(Action<int> progressReporter, string username, string password, CancellationToken cancellationToken) => new PushOptions
151  {
152  OnPackBuilderProgress = (stage, current, total) =>
153  {
154  var baseProgress = stage == PackBuilderStage.Counting ? 0 : 25;
155  progressReporter(baseProgress + ((int)(25 * ((float)current) / total)));
156  return !cancellationToken.IsCancellationRequested;
157  },
158  OnNegotiationCompletedBeforePush = (a) => !cancellationToken.IsCancellationRequested,
159  OnPushTransferProgress = (a, sentBytes, totalBytes) =>
160  {
161  progressReporter(50 + ((int)(50 * ((float)sentBytes) / totalBytes)));
162  return !cancellationToken.IsCancellationRequested;
163  },
164  CredentialsProvider = credentialsProvider.GenerateHandler(username, password)
165  };
166 
173  void RawCheckout(string committish, Action<int> progressReporter, CancellationToken cancellationToken)
174  {
175  logger.LogTrace("Checkout: {0}", committish);
176 
177  progressReporter(0);
178  cancellationToken.ThrowIfCancellationRequested();
179 
180  Commands.Checkout(repository, committish, new CheckoutOptions
181  {
182  CheckoutModifiers = CheckoutModifiers.Force,
183  OnCheckoutProgress = CheckoutProgressHandler(progressReporter)
184  });
185 
186  cancellationToken.ThrowIfCancellationRequested();
187 
188  repository.RemoveUntrackedFiles();
189  }
190 
192  public async Task<bool?> AddTestMerge(TestMergeParameters testMergeParameters, string committerName, string committerEmail, string username, string password, Action<int> progressReporter, CancellationToken cancellationToken)
193  {
194  if (testMergeParameters == null)
195  throw new ArgumentNullException(nameof(testMergeParameters));
196  if (committerName == null)
197  throw new ArgumentNullException(nameof(committerName));
198  if (committerEmail == null)
199  throw new ArgumentNullException(nameof(committerEmail));
200  if (progressReporter == null)
201  throw new ArgumentNullException(nameof(progressReporter));
202 
203  logger.LogDebug("Begin AddTestMerge: #{0} at {1} ({4}) by <{2} ({3})>", testMergeParameters.Number, testMergeParameters.PullRequestRevision?.Substring(0, 7), committerName, committerEmail, testMergeParameters.Comment);
204 
205  if (!IsGitHubRepository)
206  throw new InvalidOperationException("Test merging is only available on GitHub hosted origin repositories!");
207 
208  var commitMessage = String.Format(CultureInfo.InvariantCulture, "Test merge of pull request #{0}{1}{2}", testMergeParameters.Number.Value, testMergeParameters.Comment != null ? Environment.NewLine : String.Empty, testMergeParameters.Comment ?? String.Empty);
209 
210  var prBranchName = String.Format(CultureInfo.InvariantCulture, "pr-{0}", testMergeParameters.Number);
211  var localBranchName = String.Format(CultureInfo.InvariantCulture, "pull/{0}/headrefs/heads/{1}", testMergeParameters.Number, prBranchName);
212 
213  var refSpec = String.Format(CultureInfo.InvariantCulture, "pull/{0}/head:{1}", testMergeParameters.Number, prBranchName);
214  var refSpecList = new List<string> { refSpec };
215  var logMessage = String.Format(CultureInfo.InvariantCulture, "Merge remote pull request #{0}", testMergeParameters.Number);
216 
217  var originalCommit = repository.Head;
218 
219  MergeResult result = null;
220 
221  var sig = new Signature(new Identity(committerName, committerEmail), DateTimeOffset.Now);
222  await Task.Factory.StartNew(() =>
223  {
224  try
225  {
226  try
227  {
228  logger.LogTrace("Fetching refspec {0}...", refSpec);
229 
230  var remote = repository.Network.Remotes.First();
231  progressReporter(0);
232  Commands.Fetch((LibGit2Sharp.Repository)repository, remote.Name, refSpecList, new FetchOptions
233  {
234  Prune = true,
235  OnProgress = (a) => !cancellationToken.IsCancellationRequested,
236  OnTransferProgress = (a) =>
237  {
238  var percentage = 50 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2));
239  progressReporter((int)percentage);
240  return !cancellationToken.IsCancellationRequested;
241  },
242  OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested,
243  CredentialsProvider = credentialsProvider.GenerateHandler(username, password)
244  }, logMessage);
245  }
246  catch (UserCancelledException) { }
247 
248  cancellationToken.ThrowIfCancellationRequested();
249 
250  repository.RemoveUntrackedFiles();
251 
252  cancellationToken.ThrowIfCancellationRequested();
253 
254  testMergeParameters.PullRequestRevision = repository.Lookup(testMergeParameters.PullRequestRevision ?? localBranchName).Sha;
255 
256  cancellationToken.ThrowIfCancellationRequested();
257 
258  logger.LogTrace("Merging {0} into {1}...", testMergeParameters.PullRequestRevision.Substring(0, 7), Reference);
259 
260  result = repository.Merge(testMergeParameters.PullRequestRevision, sig, new MergeOptions
261  {
262  CommitOnSuccess = commitMessage == null,
263  FailOnConflict = true,
264  FastForwardStrategy = FastForwardStrategy.NoFastForward,
265  SkipReuc = true,
266  OnCheckoutProgress = (a, completedSteps, totalSteps) => progressReporter(50 + ((int)((((float)completedSteps) / totalSteps) * 50)))
267  });
268  }
269  finally
270  {
271  repository.Branches.Remove(localBranchName);
272  }
273 
274  cancellationToken.ThrowIfCancellationRequested();
275 
276  if (result.Status == MergeStatus.Conflicts)
277  {
278  var revertTo = originalCommit.CanonicalName ?? originalCommit.Tip.Sha;
279  logger.LogDebug("Merge conflict, aborting and reverting to {0}", revertTo);
280  RawCheckout(revertTo, progressReporter, cancellationToken);
281  cancellationToken.ThrowIfCancellationRequested();
282  }
283 
284  repository.RemoveUntrackedFiles();
285  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
286 
287  if (result.Status == MergeStatus.Conflicts)
288  {
289  await eventConsumer.HandleEvent(EventType.RepoMergeConflict, new List<string> { originalCommit.Tip.Sha, testMergeParameters.PullRequestRevision, originalCommit.FriendlyName ?? UnknownReference, prBranchName }, cancellationToken).ConfigureAwait(false);
290  return false;
291  }
292 
293  if (commitMessage != null && result.Status != MergeStatus.UpToDate)
294  {
295  logger.LogTrace("Committing merge: \"{0}\"...", commitMessage);
296  await Task.Factory.StartNew(() => repository.Commit(commitMessage, sig, sig, new CommitOptions
297  {
298  PrettifyMessage = true
299  }), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
300  }
301 
302  return true;
303  }
304 
306  public async Task CheckoutObject(string committish, Action<int> progressReporter, CancellationToken cancellationToken)
307  {
308  if (committish == null)
309  throw new ArgumentNullException(nameof(committish));
310  if (progressReporter == null)
311  throw new ArgumentNullException(nameof(progressReporter));
312  logger.LogDebug("Checkout object: {0}...", committish);
313  await eventConsumer.HandleEvent(EventType.RepoCheckout, new List<string> { committish }, cancellationToken).ConfigureAwait(false);
314  await Task.Factory.StartNew(() =>
315  {
316  repository.RemoveUntrackedFiles();
317  RawCheckout(committish, progressReporter, cancellationToken);
318  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
319  }
320 
322  public async Task FetchOrigin(string username, string password, Action<int> progressReporter, CancellationToken cancellationToken)
323  {
324  if (progressReporter == null)
325  throw new ArgumentNullException(nameof(progressReporter));
326  logger.LogDebug("Fetch origin...");
327  await eventConsumer.HandleEvent(EventType.RepoFetch, Array.Empty<string>(), cancellationToken).ConfigureAwait(false);
328  await Task.Factory.StartNew(() =>
329  {
330  var remote = repository.Network.Remotes.First();
331  try
332  {
333  Commands.Fetch((LibGit2Sharp.Repository)repository, remote.Name, remote.FetchRefSpecs.Select(x => x.Specification), new FetchOptions
334  {
335  Prune = true,
336  OnProgress = (a) => !cancellationToken.IsCancellationRequested,
337  OnTransferProgress = (a) =>
338  {
339  var percentage = 100 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2));
340  progressReporter((int)percentage);
341  return !cancellationToken.IsCancellationRequested;
342  },
343  OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested,
344  CredentialsProvider = credentialsProvider.GenerateHandler(username, password)
345  }, "Fetch origin commits");
346  }
347  catch (UserCancelledException)
348  {
349  cancellationToken.ThrowIfCancellationRequested();
350  }
351  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
352  }
353 
362  Task PushHeadToTemporaryBranch(string username, string password, Action<int> progressReporter, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
363  {
364  logger.LogInformation("Pushing changes to temporary remote branch...");
365  var branch = repository.CreateBranch(RemoteTemporaryBranchName);
366  try
367  {
368  cancellationToken.ThrowIfCancellationRequested();
369  var remote = repository.Network.Remotes.First();
370  try
371  {
372  var forcePushString = String.Format(CultureInfo.InvariantCulture, "+{0}:{0}", branch.CanonicalName);
373  repository.Network.Push(remote,forcePushString, GeneratePushOptions(progress => progressReporter((int)(0.9f * progress)), username, password, cancellationToken));
374  var removalString = String.Format(CultureInfo.InvariantCulture, ":{0}", branch.CanonicalName);
375  repository.Network.Push(remote, removalString, GeneratePushOptions(progress => progressReporter(90 + (int)(0.1f * progress)), username, password, cancellationToken));
376  }
377  catch (UserCancelledException)
378  {
379  cancellationToken.ThrowIfCancellationRequested();
380  }
381  }
382  finally
383  {
384  repository.Branches.Remove(branch);
385  }
386  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
387 
389  public async Task ResetToOrigin(Action<int> progressReporter, CancellationToken cancellationToken)
390  {
391  if (progressReporter == null)
392  throw new ArgumentNullException(nameof(progressReporter));
393  if (!Tracking)
394  throw new JobException("Cannot reset to origin while not on a tracked reference!");
395  logger.LogTrace("Reset to origin...");
396  var trackedBranch = repository.Head.TrackedBranch;
397  await eventConsumer.HandleEvent(EventType.RepoResetOrigin, new List<string> { trackedBranch.FriendlyName, trackedBranch.Tip.Sha }, cancellationToken).ConfigureAwait(false);
398  await ResetToSha(trackedBranch.Tip.Sha, progressReporter, cancellationToken).ConfigureAwait(false);
399  }
400 
402  public Task ResetToSha(string sha, Action<int> progressReporter, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
403  {
404  if (sha == null)
405  throw new ArgumentNullException(nameof(sha));
406  if (progressReporter == null)
407  throw new ArgumentNullException(nameof(progressReporter));
408 
409  logger.LogDebug("Reset to sha: {0}", sha.Substring(0, 7));
410 
411  repository.RemoveUntrackedFiles();
412  cancellationToken.ThrowIfCancellationRequested();
413 
414  var gitObject = repository.Lookup(sha, ObjectType.Commit);
415  cancellationToken.ThrowIfCancellationRequested();
416 
417  if (gitObject == null)
418  throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot reset to non-existent SHA: {0}", sha));
419 
420  repository.Reset(ResetMode.Hard, gitObject.Peel<Commit>(), new CheckoutOptions
421  {
422  OnCheckoutProgress = CheckoutProgressHandler(progressReporter)
423  });
424  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
425 
427  public async Task CopyTo(string path, CancellationToken cancellationToken)
428  {
429  if (path == null)
430  throw new ArgumentNullException(nameof(path));
431  logger.LogTrace("Copying to {0}...", path);
432  await ioMananger.CopyDirectory(".", path, new List<string> { ".git" }, cancellationToken).ConfigureAwait(false);
433  }
434 
436  public async Task<bool?> MergeOrigin(string committerName, string committerEmail, Action<int> progressReporter, CancellationToken cancellationToken)
437  {
438  if (progressReporter == null)
439  throw new ArgumentNullException(nameof(progressReporter));
440 
441  MergeResult result = null;
442  Branch trackedBranch = null;
443 
444  var oldHead = repository.Head;
445 
446  await Task.Factory.StartNew(() =>
447  {
448  if (!Tracking)
449  throw new JobException("Cannot reset to origin while not on a tracked reference!");
450 
451  repository.RemoveUntrackedFiles();
452 
453  cancellationToken.ThrowIfCancellationRequested();
454 
455  trackedBranch = repository.Head.TrackedBranch;
456  logger.LogDebug("Merge origin/{2}: <{0} ({1})>", committerName, committerEmail, trackedBranch.FriendlyName);
457  result = repository.Merge(trackedBranch, new Signature(new Identity(committerName, committerEmail), DateTimeOffset.Now), new MergeOptions
458  {
459  CommitOnSuccess = true,
460  FailOnConflict = true,
461  FastForwardStrategy = FastForwardStrategy.Default,
462  SkipReuc = true,
463  OnCheckoutProgress = CheckoutProgressHandler(progressReporter)
464  });
465 
466  cancellationToken.ThrowIfCancellationRequested();
467 
468  if (result.Status == MergeStatus.Conflicts)
469  {
470  logger.LogDebug("Merge conflict, aborting and reverting to {0}", oldHead.FriendlyName);
471  RawCheckout(oldHead.CanonicalName, progressReporter, cancellationToken);
472  cancellationToken.ThrowIfCancellationRequested();
473  }
474 
475  repository.RemoveUntrackedFiles();
476  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
477 
478  if (result.Status == MergeStatus.Conflicts)
479  {
480  await eventConsumer.HandleEvent(EventType.RepoMergeConflict, new List<string> { oldHead.Tip.Sha, trackedBranch.Tip.Sha, oldHead.FriendlyName ?? UnknownReference, trackedBranch.FriendlyName }, cancellationToken).ConfigureAwait(false);
481  return null;
482  }
483 
484  return result.Status == MergeStatus.FastForward;
485  }
486 
488  public async Task Sychronize(string username, string password, string committerName, string committerEmail, Action<int> progressReporter, bool synchronizeTrackedBranch, CancellationToken cancellationToken)
489  {
490  if (committerName == null)
491  throw new ArgumentNullException(nameof(committerName));
492  if (committerEmail == null)
493  throw new ArgumentNullException(nameof(committerEmail));
494  if (progressReporter == null)
495  throw new ArgumentNullException(nameof(progressReporter));
496 
497  if (username == null && password == null)
498  {
499  logger.LogTrace("Not synchronizing due to lack of credentials!");
500  return;
501  }
502  logger.LogTrace("Begin Synchronize...");
503 
504  if (username == null)
505  throw new ArgumentNullException(nameof(username));
506  if (password == null)
507  throw new ArgumentNullException(nameof(password));
508 
509  var startHead = Head;
510 
511  logger.LogTrace("Configuring <{0} ({1})> as author/committer", committerName, committerEmail);
512  await Task.Factory.StartNew(() =>
513  {
514  repository.Config.Set("user.name", committerName);
515  cancellationToken.ThrowIfCancellationRequested();
516  repository.Config.Set("user.email", committerEmail);
517  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
518 
519  cancellationToken.ThrowIfCancellationRequested();
520  try
521  {
522  if (!await eventConsumer.HandleEvent(EventType.RepoPreSynchronize, new List<string> { ioMananger.ResolvePath(".") }, cancellationToken).ConfigureAwait(false))
523  {
524  logger.LogDebug("Aborted synchronize due to event handler response!");
525  return;
526  }
527  }
528  finally
529  {
530  logger.LogTrace("Resetting and cleaning untracked files...");
531  await Task.Factory.StartNew(() =>
532  {
533  repository.RemoveUntrackedFiles();
534  cancellationToken.ThrowIfCancellationRequested();
535  repository.Reset(ResetMode.Hard, repository.Head.Tip, new CheckoutOptions
536  {
537  OnCheckoutProgress = CheckoutProgressHandler(progress => progressReporter(progress / 10))
538  });
539  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
540  }
541 
542  void FinalReporter(int progress) => progressReporter((int)(((float)progress) / 100 * 90));
543 
544  if (!synchronizeTrackedBranch)
545  {
546  await PushHeadToTemporaryBranch(username, password, FinalReporter, cancellationToken).ConfigureAwait(false);
547  return;
548  }
549 
550  var sameHead = Head == startHead;
551  if (sameHead || !Tracking)
552  {
553  logger.LogTrace("Aborted synchronize due to {0}!", sameHead ? "lack of changes" : "not being on tracked reference");
554  return;
555  }
556 
557  logger.LogInformation("Synchronizing with origin...");
558 
559  await Task.Factory.StartNew(() =>
560  {
561  var remote = repository.Network.Remotes.First();
562  try
563  {
564  repository.Network.Push(repository.Head, GeneratePushOptions(FinalReporter, username, password, cancellationToken));
565  }
566  catch (NonFastForwardException)
567  {
568  logger.LogInformation("Synchronize aborted, non-fast forward!");
569  }
570  catch (UserCancelledException)
571  {
572  cancellationToken.ThrowIfCancellationRequested();
573  }
574  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
575  }
576 
578  public Task<bool> IsSha(string committish, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
579  {
580  var gitObject = repository.Lookup(committish, ObjectType.Commit);
581  return gitObject != null;
582  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
583  }
584 }
Operation exceptions thrown from the context of a Models.Job
Definition: JobException.cs:8
Consumes EventTypes and takes the appropriate actions
+
CredentialsHandler GenerateHandler(string username, string password)
Generate a CredentialsHandler from a given username and password
async Task CheckoutObject(string committish, Action< int > progressReporter, CancellationToken cancellationToken)
Checks out a given committish
Definition: Repository.cs:306
@@ -117,7 +118,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
void GetRepositoryOwnerName(string remote, out string owner, out string name)
Definition: Repository.cs:86
-
async Task< bool?> AddTestMerge(TestMergeParameters testMergeParameters, string committerName, string committerEmail, string username, string password, Action< int > progressReporter, CancellationToken cancellationToken)
Attempt to merge a GitHub pull request into HEAD
Definition: Repository.cs:192
Parameters for creating a TestMerge
async Task ResetToOrigin(Action< int > progressReporter, CancellationToken cancellationToken)
Requires the current HEAD to be a tracked reference. Hard resets the reference to what it tracks on t...
Definition: Repository.cs:389
diff --git a/_tgstation_8_server_8_host_2_models_2_chat_channel_8cs_source.html b/_tgstation_8_server_8_host_2_models_2_chat_channel_8cs_source.html index 91acaa52bc..336cc4deaa 100644 --- a/_tgstation_8_server_8_host_2_models_2_chat_channel_8cs_source.html +++ b/_tgstation_8_server_8_host_2_models_2_chat_channel_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
ChatChannel.cs
-Go to the documentation of this file.
2 
3 namespace Tgstation.Server.Host.Models
4 {
6  public sealed class ChatChannel : Api.Models.ChatChannel
7  {
11  public long Id { get; set; }
12 
16  public long ChatSettingsId { get; set; }
17 
21  public ChatBot ChatSettings { get; set; }
22 
27  public Api.Models.ChatChannel ToApi() => new Api.Models.ChatChannel
28  {
29  DiscordChannelId = DiscordChannelId,
30  IsAdminChannel = IsAdminChannel,
31  IsWatchdogChannel = IsWatchdogChannel,
32  IsUpdatesChannel = IsUpdatesChannel,
33  IrcChannel = IrcChannel
34  };
35  }
36 }
+Go to the documentation of this file.
2 
3 namespace Tgstation.Server.Host.Models
4 {
6  public sealed class ChatChannel : Api.Models.ChatChannel
7  {
11  public long Id { get; set; }
12 
16  public long ChatSettingsId { get; set; }
17 
21  public ChatBot ChatSettings { get; set; }
22 
27  public Api.Models.ChatChannel ToApi() => new Api.Models.ChatChannel
28  {
29  DiscordChannelId = DiscordChannelId,
30  IsAdminChannel = IsAdminChannel,
31  IsWatchdogChannel = IsWatchdogChannel,
32  IsUpdatesChannel = IsUpdatesChannel,
33  IrcChannel = IrcChannel
34  };
35  }
36 }
+ - diff --git a/_tgstation_8_server_8_host_2_models_2_compile_job_8cs_source.html b/_tgstation_8_server_8_host_2_models_2_compile_job_8cs_source.html index a8de074dab..3936a7cc0c 100644 --- a/_tgstation_8_server_8_host_2_models_2_compile_job_8cs_source.html +++ b/_tgstation_8_server_8_host_2_models_2_compile_job_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
CompileJob.cs
-Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 
4 namespace Tgstation.Server.Host.Models
5 {
7  public sealed class CompileJob : Api.Models.Internal.CompileJob
8  {
12  [Required]
13  public Job Job { get; set; }
14 
18  public long JobId { get; set; }
19 
23  [Required]
25 
29  [Required]
30  public string ByondVersion { get; set; }
31 
36  public Api.Models.CompileJob ToApi() => new Api.Models.CompileJob
37  {
38  DirectoryName = DirectoryName,
39  DmeName = DmeName,
40  Id = Id,
41  Job = Job.ToApi(),
42  Output = Output,
43  RevisionInformation = RevisionInformation.ToApi(),
44  ByondVersion = Version.Parse(ByondVersion),
45  MinimumSecurityLevel = MinimumSecurityLevel
46  };
47  }
48 }
+Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 
4 namespace Tgstation.Server.Host.Models
5 {
7  public sealed class CompileJob : Api.Models.Internal.CompileJob
8  {
12  [Required]
13  public Job Job { get; set; }
14 
18  public long JobId { get; set; }
19 
23  [Required]
25 
29  [Required]
30  public string ByondVersion { get; set; }
31 
36  public Api.Models.CompileJob ToApi() => new Api.Models.CompileJob
37  {
38  DirectoryName = DirectoryName,
39  DmeName = DmeName,
40  Id = Id,
41  Job = Job.ToApi(),
42  Output = Output,
43  RevisionInformation = RevisionInformation.ToApi(),
44  ByondVersion = Version.Parse(ByondVersion),
45  MinimumSecurityLevel = MinimumSecurityLevel
46  };
47  }
48 }
+ -
Api.Models.RevisionInformation ToApi()
Convert the RevisionInformation to it&#39;s API form
Api.Models.Job ToApi()
Convert the Job to it&#39;s API form
diff --git a/_tgstation_8_server_8_host_2_models_2_dream_daemon_settings_8cs_source.html b/_tgstation_8_server_8_host_2_models_2_dream_daemon_settings_8cs_source.html index 7662abe35f..22d4515fe0 100644 --- a/_tgstation_8_server_8_host_2_models_2_dream_daemon_settings_8cs_source.html +++ b/_tgstation_8_server_8_host_2_models_2_dream_daemon_settings_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
DreamDaemonSettings.cs
-Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Host.Models
4 {
6  public sealed class DreamDaemonSettings : Api.Models.Internal.DreamDaemonSettings
7  {
11  public long Id { get; set; }
12 
16  public int? ProcessId { get; set; }
17 
21  public string AccessToken { get; set; }
22 
26  public long InstanceId { get; set; }
27 
31  [Required]
32  public Instance Instance { get; set; }
33  }
34 }
+Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Host.Models
4 {
6  public sealed class DreamDaemonSettings : Api.Models.Internal.DreamDaemonSettings
7  {
11  public long Id { get; set; }
12 
16  public int? ProcessId { get; set; }
17 
21  public string AccessToken { get; set; }
22 
26  public long InstanceId { get; set; }
27 
31  [Required]
32  public Instance Instance { get; set; }
33  }
34 }
+
Represents an Api.Models.Instance in the database
Definition: Instance.cs:8
-
diff --git a/_tgstation_8_server_8_host_2_models_2_instance_8cs_source.html b/_tgstation_8_server_8_host_2_models_2_instance_8cs_source.html index b06e2ca3f1..6396ce3e0c 100644 --- a/_tgstation_8_server_8_host_2_models_2_instance_8cs_source.html +++ b/_tgstation_8_server_8_host_2_models_2_instance_8cs_source.html @@ -92,14 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Instance.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 
3 namespace Tgstation.Server.Host.Models
4 {
8  public sealed class Instance : Api.Models.Instance
9  {
10 
15 
20 
25 
30 
34  public List<InstanceUser> InstanceUsers { get; set; }
35 
39  public List<ChatBot> ChatSettings { get; set; }
40 
44  public List<RevisionInformation> RevisionInformations { get; set; }
45 
49  public List<Job> Jobs { get; set; }
50 
55  public Api.Models.Instance ToApi() => new Api.Models.Instance
56  {
57  AutoUpdateInterval = AutoUpdateInterval,
59  Id = Id,
60  Name = Name,
61  Path = Path,
62  Online = Online
63  };
64  }
65 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 
3 namespace Tgstation.Server.Host.Models
4 {
8  public sealed class Instance : Api.Models.Instance
9  {
10 
15 
20 
25 
30 
34  public List<InstanceUser> InstanceUsers { get; set; }
35 
39  public List<ChatBot> ChatSettings { get; set; }
40 
44  public List<RevisionInformation> RevisionInformations { get; set; }
45 
49  public List<Job> Jobs { get; set; }
50 
55  public Api.Models.Instance ToApi() => new Api.Models.Instance
56  {
57  AutoUpdateInterval = AutoUpdateInterval,
59  Id = Id,
60  Name = Name,
61  Path = Path,
62  Online = Online
63  };
64  }
65 }
Database representation of Components.Watchdog.WatchdogReattachInformation
+
ConfigurationType
The type of configuration allowed on an Instance
Represents an Api.Models.Instance in the database
Definition: Instance.cs:8
-
diff --git a/_tgstation_8_server_8_host_2_models_2_instance_user_8cs_source.html b/_tgstation_8_server_8_host_2_models_2_instance_user_8cs_source.html index bcd2a11f2d..4369eab401 100644 --- a/_tgstation_8_server_8_host_2_models_2_instance_user_8cs_source.html +++ b/_tgstation_8_server_8_host_2_models_2_instance_user_8cs_source.html @@ -92,15 +92,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceUser.cs
-Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Host.Models
4 {
6  public sealed class InstanceUser : Api.Models.InstanceUser
7  {
11  public long Id { get; set; }
12 
16  public long InstanceId { get; set; }
17 
21  [Required]
22  public Instance Instance { get; set; }
23 
27  public bool AnyRights => ByondRights != Api.Rights.ByondRights.None ||
28  ChatBotRights != Api.Rights.ChatBotRights.None ||
29  ConfigurationRights != Api.Rights.ConfigurationRights.None ||
30  DreamDaemonRights != Api.Rights.DreamDaemonRights.None ||
31  DreamMakerRights != Api.Rights.DreamMakerRights.None ||
32  InstanceUserRights != Api.Rights.InstanceUserRights.None;
33 
38  public Api.Models.InstanceUser ToApi() => new Api.Models.InstanceUser
39  {
47  UserId = UserId
48  };
49  }
50 }
ConfigurationRights
Rights for Models.ConfigurationFile
+Go to the documentation of this file.
1 using System.ComponentModel.DataAnnotations;
2 
3 namespace Tgstation.Server.Host.Models
4 {
6  public sealed class InstanceUser : Api.Models.InstanceUser
7  {
11  public long Id { get; set; }
12 
16  public long InstanceId { get; set; }
17 
21  [Required]
22  public Instance Instance { get; set; }
23 
27  public bool AnyRights => ByondRights != Api.Rights.ByondRights.None ||
28  ChatBotRights != Api.Rights.ChatBotRights.None ||
29  ConfigurationRights != Api.Rights.ConfigurationRights.None ||
30  DreamDaemonRights != Api.Rights.DreamDaemonRights.None ||
31  DreamMakerRights != Api.Rights.DreamMakerRights.None ||
32  InstanceUserRights != Api.Rights.InstanceUserRights.None;
33 
38  public Api.Models.InstanceUser ToApi() => new Api.Models.InstanceUser
39  {
47  UserId = UserId
48  };
49  }
50 }
ConfigurationRights
Rights for Models.ConfigurationFile
ByondRights
Rights for Models.Byond
Definition: ByondRights.cs:9
InstanceUserRights
Rights for an Models.Instance
+
DreamDaemonRights
Rights for Models.DreamDaemon
Represents an Api.Models.Instance in the database
Definition: Instance.cs:8
DreamMakerRights
Rights for Models.DreamMaker
-
RepositoryRights
Rights for a Models.Repository
ChatBotRights
Rights for Models.ChatBot
Definition: ChatBotRights.cs:9
diff --git a/_tgstation_8_server_8_host_2_models_2_job_8cs_source.html b/_tgstation_8_server_8_host_2_models_2_job_8cs_source.html index d860c814b5..3bc28444fb 100644 --- a/_tgstation_8_server_8_host_2_models_2_job_8cs_source.html +++ b/_tgstation_8_server_8_host_2_models_2_job_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Job.cs
-Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 using System.ComponentModel.DataAnnotations.Schema;
4 using System.Threading;
5 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Models
8 {
10  public sealed class Job : Api.Models.Internal.Job
11  {
15  [Required]
16  public User StartedBy { get; set; }
17 
21  public User CancelledBy { get; set; }
22 
26  [Required]
27  public Instance Instance { get; set; }
28 
33  [NotMapped]
34  public Func<CancellationToken, Task> PostComplete { get; set; }
35 
40  public Api.Models.Job ToApi() => new Api.Models.Job
41  {
42  Id = Id,
43  StartedAt = StartedAt,
44  StoppedAt = StoppedAt,
45  Cancelled = Cancelled,
46  CancelledBy = CancelledBy?.ToApi(false),
47  CancelRight = CancelRight,
48  CancelRightsType = CancelRightsType,
49  Description = Description,
50  ExceptionDetails = ExceptionDetails,
51  StartedBy = StartedBy.ToApi(false)
52  };
53  }
54 }
+Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
3 using System.ComponentModel.DataAnnotations.Schema;
4 using System.Threading;
5 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Models
8 {
10  public sealed class Job : Api.Models.Internal.Job
11  {
15  [Required]
16  public User StartedBy { get; set; }
17 
21  public User CancelledBy { get; set; }
22 
26  [Required]
27  public Instance Instance { get; set; }
28 
33  [NotMapped]
34  public Func<CancellationToken, Task> PostComplete { get; set; }
35 
40  public Api.Models.Job ToApi() => new Api.Models.Job
41  {
42  Id = Id,
43  StartedAt = StartedAt,
44  StoppedAt = StoppedAt,
45  Cancelled = Cancelled,
46  CancelledBy = CancelledBy?.ToApi(false),
47  CancelRight = CancelRight,
48  CancelRightsType = CancelRightsType,
49  Description = Description,
50  ExceptionDetails = ExceptionDetails,
51  StartedBy = StartedBy.ToApi(false)
52  };
53  }
54 }
+
Represents an Api.Models.Instance in the database
Definition: Instance.cs:8
-
Api.Models.Instance ToApi()
Convert the Instance to it&#39;s API form
diff --git a/_tgstation_8_server_8_host_2_models_2_repository_settings_8cs_source.html b/_tgstation_8_server_8_host_2_models_2_repository_settings_8cs_source.html index b22dfa8d5b..a9e4a5d005 100644 --- a/_tgstation_8_server_8_host_2_models_2_repository_settings_8cs_source.html +++ b/_tgstation_8_server_8_host_2_models_2_repository_settings_8cs_source.html @@ -92,13 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RepositorySettings.cs
-Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
4 
5 namespace Tgstation.Server.Host.Models
6 {
8  public sealed class RepositorySettings : Api.Models.Internal.RepositorySettings
9  {
13  public long Id { get; set; }
14 
18  public long InstanceId { get; set; }
19 
23  [Required]
24  public Instance Instance { get; set; }
25 
30  public Repository ToApi() => new Repository
31  {
32  //AccessToken = AccessToken, //never show this
33  AccessUser = AccessUser,
34  AutoUpdatesKeepTestMerges = AutoUpdatesKeepTestMerges,
35  AutoUpdatesSynchronize = AutoUpdatesSynchronize,
36  CommitterEmail = CommitterEmail,
37  CommitterName = CommitterName,
38  PushTestMergeCommits = PushTestMergeCommits,
39  ShowTestMergeCommitters = ShowTestMergeCommitters
40  //revision information and the rest retrieved by controller
41  };
42  }
43 }
+Go to the documentation of this file.
1 using System;
2 using System.ComponentModel.DataAnnotations;
4 
5 namespace Tgstation.Server.Host.Models
6 {
8  public sealed class RepositorySettings : Api.Models.Internal.RepositorySettings
9  {
13  public long Id { get; set; }
14 
18  public long InstanceId { get; set; }
19 
23  [Required]
24  public Instance Instance { get; set; }
25 
30  public Repository ToApi() => new Repository
31  {
32  //AccessToken = AccessToken, //never show this
33  AccessUser = AccessUser,
34  AutoUpdatesKeepTestMerges = AutoUpdatesKeepTestMerges,
35  AutoUpdatesSynchronize = AutoUpdatesSynchronize,
36  CommitterEmail = CommitterEmail,
37  CommitterName = CommitterName,
38  PushTestMergeCommits = PushTestMergeCommits,
39  ShowTestMergeCommitters = ShowTestMergeCommitters
40  //revision information and the rest retrieved by controller
41  };
42  }
43 }
+
Represents a git repository
Definition: Repository.cs:8
Represents an Api.Models.Instance in the database
Definition: Instance.cs:8
-
diff --git a/_tgstation_8_server_8_host_2_models_2_revision_information_8cs_source.html b/_tgstation_8_server_8_host_2_models_2_revision_information_8cs_source.html index 996c2f793e..7f69038b38 100644 --- a/_tgstation_8_server_8_host_2_models_2_revision_information_8cs_source.html +++ b/_tgstation_8_server_8_host_2_models_2_revision_information_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
RevisionInformation.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.ComponentModel.DataAnnotations;
3 using System.Linq;
4 
5 namespace Tgstation.Server.Host.Models
6 {
8  public sealed class RevisionInformation : Api.Models.Internal.RevisionInformation
9  {
13  public long Id { get; set; }
14 
18  [Required]
19  public Instance Instance { get; set; }
20 
24  public TestMerge PrimaryTestMerge { get; set; }
25 
29  public List<RevInfoTestMerge> ActiveTestMerges { get; set; }
30 
34  public List<CompileJob> CompileJobs { get; set; }
35 
40  public Api.Models.RevisionInformation ToApi() => new Api.Models.RevisionInformation
41  {
42  CommitSha = CommitSha,
43  OriginCommitSha = OriginCommitSha,
44  PrimaryTestMerge = PrimaryTestMerge?.ToApi(),
45  ActiveTestMerges = ActiveTestMerges.Select(x => x.TestMerge.ToApi()).ToList(),
46  CompileJobs = CompileJobs.Select(x => new Api.Models.CompileJob
47  {
48  Id = x.Id //anti recursion measure
49  }).ToList()
50  };
51  }
52 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.ComponentModel.DataAnnotations;
3 using System.Linq;
4 
5 namespace Tgstation.Server.Host.Models
6 {
8  public sealed class RevisionInformation : Api.Models.Internal.RevisionInformation
9  {
13  public long Id { get; set; }
14 
18  [Required]
19  public Instance Instance { get; set; }
20 
24  public TestMerge PrimaryTestMerge { get; set; }
25 
29  public List<RevInfoTestMerge> ActiveTestMerges { get; set; }
30 
34  public List<CompileJob> CompileJobs { get; set; }
35 
40  public Api.Models.RevisionInformation ToApi() => new Api.Models.RevisionInformation
41  {
42  CommitSha = CommitSha,
43  OriginCommitSha = OriginCommitSha,
44  PrimaryTestMerge = PrimaryTestMerge?.ToApi(),
45  ActiveTestMerges = ActiveTestMerges.Select(x => x.TestMerge.ToApi()).ToList(),
46  CompileJobs = CompileJobs.Select(x => new Api.Models.CompileJob
47  {
48  Id = x.Id //anti recursion measure
49  }).ToList()
50  };
51  }
52 }
+
Represents an Api.Models.Instance in the database
Definition: Instance.cs:8
-
diff --git a/_tgstation_8_server_8_host_2_models_2_test_merge_8cs_source.html b/_tgstation_8_server_8_host_2_models_2_test_merge_8cs_source.html index 0b2c61ed33..c0aeba91d7 100644 --- a/_tgstation_8_server_8_host_2_models_2_test_merge_8cs_source.html +++ b/_tgstation_8_server_8_host_2_models_2_test_merge_8cs_source.html @@ -92,10 +92,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
TestMerge.cs
-Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.ComponentModel.DataAnnotations;
3 
4 namespace Tgstation.Server.Host.Models
5 {
7  public sealed class TestMerge : Api.Models.Internal.TestMerge
8  {
12  [Required]
13  public User MergedBy { get; set; }
14 
18  [Required]
19  public RevisionInformation PrimaryRevisionInformation { get; set; }
20 
24  public long? PrimaryRevisionInformationId { get; set; }
25 
29  public List<RevInfoTestMerge> RevisonInformations { get; set; }
30 
35  public Api.Models.TestMerge ToApi() => new Api.Models.TestMerge
36  {
37  Author = Author,
38  BodyAtMerge = BodyAtMerge,
39  MergedAt = MergedAt,
40  TitleAtMerge = TitleAtMerge,
41  Comment = Comment,
42  Id = Id,
43  MergedBy = MergedBy.ToApi(false),
44  Number = Number,
45  PullRequestRevision = PullRequestRevision,
46  Url = Url
47  };
48  }
49 }
+Go to the documentation of this file.
1 using System.Collections.Generic;
2 using System.ComponentModel.DataAnnotations;
3 
4 namespace Tgstation.Server.Host.Models
5 {
7  public sealed class TestMerge : Api.Models.Internal.TestMerge
8  {
12  [Required]
13  public User MergedBy { get; set; }
14 
18  [Required]
19  public RevisionInformation PrimaryRevisionInformation { get; set; }
20 
24  public long? PrimaryRevisionInformationId { get; set; }
25 
29  public List<RevInfoTestMerge> RevisonInformations { get; set; }
30 
35  public Api.Models.TestMerge ToApi() => new Api.Models.TestMerge
36  {
37  Author = Author,
38  BodyAtMerge = BodyAtMerge,
39  MergedAt = MergedAt,
40  TitleAtMerge = TitleAtMerge,
41  Comment = Comment,
42  Id = Id,
43  MergedBy = MergedBy.ToApi(false),
44  Number = Number,
45  PullRequestRevision = PullRequestRevision,
46  Url = Url
47  };
48  }
49 }
+ -
Api.Models.RevisionInformation ToApi()
Convert the RevisionInformation to it&#39;s API form
diff --git a/_tgstation_8_server_8_host_2_models_2_user_8cs_source.html b/_tgstation_8_server_8_host_2_models_2_user_8cs_source.html index 78b2f6047e..188d85551f 100644 --- a/_tgstation_8_server_8_host_2_models_2_user_8cs_source.html +++ b/_tgstation_8_server_8_host_2_models_2_user_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
User.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel.DataAnnotations;
4 
5 namespace Tgstation.Server.Host.Models
6 {
8  public sealed class User : Api.Models.Internal.User
9  {
13  public string PasswordHash { get; set; }
14 
18  public User CreatedBy { get; set; }
19 
23  [Required]
24  public string CanonicalName { get; set; }
25 
29  public DateTimeOffset? LastPasswordUpdate { get; set; }
30 
34  public List<User> CreatedUsers { get; set; }
35 
39  public List<InstanceUser> InstanceUsers { get; set; }
40 
44  public List<TestMerge> TestMerges { get; set; }
45 
52  Api.Models.User ToApi(bool recursive, bool showDetails) => new Api.Models.User
53  {
54  AdministrationRights = showDetails ? AdministrationRights : null,
55  CreatedAt = CreatedAt,
56  CreatedBy = recursive ? CreatedBy?.ToApi(false, showDetails) : null,
57  Enabled = Enabled,
58  Id = Id,
59  InstanceManagerRights = showDetails ? InstanceManagerRights : null,
60  Name = Name,
61  SystemIdentifier = showDetails ? SystemIdentifier : null
62  };
63 
69  public Api.Models.User ToApi(bool showDetails) => ToApi(true, showDetails);
70  }
71 }
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel.DataAnnotations;
4 
5 namespace Tgstation.Server.Host.Models
6 {
8  public sealed class User : Api.Models.Internal.User
9  {
13  public string PasswordHash { get; set; }
14 
18  public User CreatedBy { get; set; }
19 
23  [Required]
24  public string CanonicalName { get; set; }
25 
29  public DateTimeOffset? LastPasswordUpdate { get; set; }
30 
34  public List<User> CreatedUsers { get; set; }
35 
39  public List<InstanceUser> InstanceUsers { get; set; }
40 
44  public List<TestMerge> TestMerges { get; set; }
45 
52  Api.Models.User ToApi(bool recursive, bool showDetails) => new Api.Models.User
53  {
54  AdministrationRights = showDetails ? AdministrationRights : null,
55  CreatedAt = CreatedAt,
56  CreatedBy = recursive ? CreatedBy?.ToApi(false, showDetails) : null,
57  Enabled = Enabled,
58  Id = Id,
59  InstanceManagerRights = showDetails ? InstanceManagerRights : null,
60  Name = Name,
61  SystemIdentifier = showDetails ? SystemIdentifier : null
62  };
63 
69  public Api.Models.User ToApi(bool showDetails) => ToApi(true, showDetails);
70  }
71 }
+
AdministrationRights
Rights for Models.Administration
InstanceManagerRights
Rights for managing Models.Instances
-
diff --git a/_tgstation_8_server_8_host_8_assembly_info_8cs.html b/_tgstation_8_server_8_host_8_assembly_info_8cs.html index df4548a1f4..43c0f7534c 100644 --- a/_tgstation_8_server_8_host_8_assembly_info_8cs.html +++ b/_tgstation_8_server_8_host_8_assembly_info_8cs.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host/obj/Release/netcoreapp2.1/Tgstation.Server.Host.AssemblyInfo.cs File Reference +tgstation-server: src/Tgstation.Server.Host/obj/Debug/netcoreapp2.1/Tgstation.Server.Host.AssemblyInfo.cs File Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/_tgstation_8_server_8_host_8_assembly_info_8cs_source.html b/_tgstation_8_server_8_host_8_assembly_info_8cs_source.html index 3c948667b4..65dcb96d57 100644 --- a/_tgstation_8_server_8_host_8_assembly_info_8cs_source.html +++ b/_tgstation_8_server_8_host_8_assembly_info_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host/obj/Release/netcoreapp2.1/Tgstation.Server.Host.AssemblyInfo.cs Source File +tgstation-server: src/Tgstation.Server.Host/obj/Debug/netcoreapp2.1/Tgstation.Server.Host.AssemblyInfo.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
@@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Tgstation.Server.Host.AssemblyInfo.cs
-Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Host")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("4.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Host")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Host")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("4.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
+Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Host")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("4.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Host")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Host")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("4.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
diff --git a/_tgstation_8_server_8_host_8_console_8_assembly_info_8cs_source.html b/_tgstation_8_server_8_host_8_console_8_assembly_info_8cs_source.html index f77a299e2b..f684eecbda 100644 --- a/_tgstation_8_server_8_host_8_console_8_assembly_info_8cs_source.html +++ b/_tgstation_8_server_8_host_8_console_8_assembly_info_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host.Console/obj/Release/netcoreapp2.1/Tgstation.Server.Host.Console.AssemblyInfo.cs Source File +tgstation-server: src/Tgstation.Server.Host.Console/obj/Debug/netcoreapp2.1/Tgstation.Server.Host.Console.AssemblyInfo.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
@@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Tgstation.Server.Host.Console.AssemblyInfo.cs
-Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Host.Console")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("4.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Host.Console")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Host.Console")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
+Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Host.Console")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("4.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Host.Console")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Host.Console")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
diff --git a/_tgstation_8_server_8_host_8_console_8_tests_8_assembly_info_8cs_source.html b/_tgstation_8_server_8_host_8_console_8_tests_8_assembly_info_8cs_source.html index 76fc39106d..5d18b1948e 100644 --- a/_tgstation_8_server_8_host_8_console_8_tests_8_assembly_info_8cs_source.html +++ b/_tgstation_8_server_8_host_8_console_8_tests_8_assembly_info_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Console.Tests/obj/Release/netcoreapp2.1/Tgstation.Server.Host.Console.Tests.AssemblyInfo.cs Source File +tgstation-server: tests/Tgstation.Server.Host.Console.Tests/obj/Debug/netcoreapp2.1/Tgstation.Server.Host.Console.Tests.AssemblyInfo.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
@@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Tgstation.Server.Host.Console.Tests.AssemblyInfo.cs
-Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Host.Console.Tests")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Host.Console.Tests")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Host.Console.Tests")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
+Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Host.Console.Tests")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Host.Console.Tests")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Host.Console.Tests")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
diff --git a/_tgstation_8_server_8_host_8_console_8_tests_8_program_8cs_source.html b/_tgstation_8_server_8_host_8_console_8_tests_8_program_8cs_source.html index 37466fca50..53fc29de6a 100644 --- a/_tgstation_8_server_8_host_8_console_8_tests_8_program_8cs_source.html +++ b/_tgstation_8_server_8_host_8_console_8_tests_8_program_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Console.Tests/obj/Release/netcoreapp2.1/Tgstation.Server.Host.Console.Tests.Program.cs Source File +tgstation-server: tests/Tgstation.Server.Host.Console.Tests/obj/Debug/netcoreapp2.1/Tgstation.Server.Host.Console.Tests.Program.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/_tgstation_8_server_8_host_8_razor_assembly_info_8cs.html b/_tgstation_8_server_8_host_8_razor_assembly_info_8cs.html index 3203ae4cf7..22f2c188b8 100644 --- a/_tgstation_8_server_8_host_8_razor_assembly_info_8cs.html +++ b/_tgstation_8_server_8_host_8_razor_assembly_info_8cs.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host/obj/Release/netcoreapp2.1/Tgstation.Server.Host.RazorAssemblyInfo.cs File Reference +tgstation-server: src/Tgstation.Server.Host/obj/Debug/netcoreapp2.1/Tgstation.Server.Host.RazorAssemblyInfo.cs File Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/_tgstation_8_server_8_host_8_razor_assembly_info_8cs_source.html b/_tgstation_8_server_8_host_8_razor_assembly_info_8cs_source.html index c53270b03d..38a995518c 100644 --- a/_tgstation_8_server_8_host_8_razor_assembly_info_8cs_source.html +++ b/_tgstation_8_server_8_host_8_razor_assembly_info_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host/obj/Release/netcoreapp2.1/Tgstation.Server.Host.RazorAssemblyInfo.cs Source File +tgstation-server: src/Tgstation.Server.Host/obj/Debug/netcoreapp2.1/Tgstation.Server.Host.RazorAssemblyInfo.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/_tgstation_8_server_8_host_8_tests_8_assembly_info_8cs.html b/_tgstation_8_server_8_host_8_tests_8_assembly_info_8cs.html index b50f09579f..13e5231737 100644 --- a/_tgstation_8_server_8_host_8_tests_8_assembly_info_8cs.html +++ b/_tgstation_8_server_8_host_8_tests_8_assembly_info_8cs.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Tests/obj/Release/netcoreapp2.1/Tgstation.Server.Host.Tests.AssemblyInfo.cs File Reference +tgstation-server: tests/Tgstation.Server.Host.Tests/obj/Debug/netcoreapp2.1/Tgstation.Server.Host.Tests.AssemblyInfo.cs File Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/_tgstation_8_server_8_host_8_tests_8_assembly_info_8cs_source.html b/_tgstation_8_server_8_host_8_tests_8_assembly_info_8cs_source.html index 4e22852711..76db250606 100644 --- a/_tgstation_8_server_8_host_8_tests_8_assembly_info_8cs_source.html +++ b/_tgstation_8_server_8_host_8_tests_8_assembly_info_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Tests/obj/Release/netcoreapp2.1/Tgstation.Server.Host.Tests.AssemblyInfo.cs Source File +tgstation-server: tests/Tgstation.Server.Host.Tests/obj/Debug/netcoreapp2.1/Tgstation.Server.Host.Tests.AssemblyInfo.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
@@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Tgstation.Server.Host.Tests.AssemblyInfo.cs
-Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Host.Tests")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Host.Tests")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Host.Tests")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
+Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Host.Tests")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Host.Tests")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Host.Tests")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
diff --git a/_tgstation_8_server_8_host_8_tests_8_program_8cs_source.html b/_tgstation_8_server_8_host_8_tests_8_program_8cs_source.html index bd9abbdcce..6f4b8b8668 100644 --- a/_tgstation_8_server_8_host_8_tests_8_program_8cs_source.html +++ b/_tgstation_8_server_8_host_8_tests_8_program_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Tests/obj/Release/netcoreapp2.1/Tgstation.Server.Host.Tests.Program.cs Source File +tgstation-server: tests/Tgstation.Server.Host.Tests/obj/Debug/netcoreapp2.1/Tgstation.Server.Host.Tests.Program.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/_tgstation_8_server_8_host_8_watchdog_8_assembly_info_8cs.html b/_tgstation_8_server_8_host_8_watchdog_8_assembly_info_8cs.html index 33f02c41de..1966731549 100644 --- a/_tgstation_8_server_8_host_8_watchdog_8_assembly_info_8cs.html +++ b/_tgstation_8_server_8_host_8_watchdog_8_assembly_info_8cs.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host.Watchdog/obj/Release/netstandard2.0/Tgstation.Server.Host.Watchdog.AssemblyInfo.cs File Reference +tgstation-server: src/Tgstation.Server.Host.Watchdog/obj/Debug/netstandard2.0/Tgstation.Server.Host.Watchdog.AssemblyInfo.cs File Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/_tgstation_8_server_8_host_8_watchdog_8_assembly_info_8cs_source.html b/_tgstation_8_server_8_host_8_watchdog_8_assembly_info_8cs_source.html index 6ffc693699..a1859dd9c4 100644 --- a/_tgstation_8_server_8_host_8_watchdog_8_assembly_info_8cs_source.html +++ b/_tgstation_8_server_8_host_8_watchdog_8_assembly_info_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host.Watchdog/obj/Release/netstandard2.0/Tgstation.Server.Host.Watchdog.AssemblyInfo.cs Source File +tgstation-server: src/Tgstation.Server.Host.Watchdog/obj/Debug/netstandard2.0/Tgstation.Server.Host.Watchdog.AssemblyInfo.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
@@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Tgstation.Server.Host.Watchdog.AssemblyInfo.cs
-Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Host.Watchdog")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("4.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Host.Watchdog")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Host.Watchdog")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("4.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
+Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Host.Watchdog")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("4.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Host.Watchdog")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Host.Watchdog")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("4.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
diff --git a/_tgstation_8_server_8_host_8_watchdog_8_tests_8_assembly_info_8cs_source.html b/_tgstation_8_server_8_host_8_watchdog_8_tests_8_assembly_info_8cs_source.html index 6b02753515..1bc613db7b 100644 --- a/_tgstation_8_server_8_host_8_watchdog_8_tests_8_assembly_info_8cs_source.html +++ b/_tgstation_8_server_8_host_8_watchdog_8_tests_8_assembly_info_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Watchdog.Tests/obj/Release/netcoreapp2.1/Tgstation.Server.Host.Watchdog.Tests.AssemblyInfo.cs Source File +tgstation-server: tests/Tgstation.Server.Host.Watchdog.Tests/obj/Debug/netcoreapp2.1/Tgstation.Server.Host.Watchdog.Tests.AssemblyInfo.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
@@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Tgstation.Server.Host.Watchdog.Tests.AssemblyInfo.cs
-Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Host.Watchdog.Tests")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Host.Watchdog.Tests")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Host.Watchdog.Tests")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
+Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Host.Watchdog.Tests")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Host.Watchdog.Tests")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Host.Watchdog.Tests")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
diff --git a/_tgstation_8_server_8_host_8_watchdog_8_tests_8_program_8cs_source.html b/_tgstation_8_server_8_host_8_watchdog_8_tests_8_program_8cs_source.html index 05521465ef..30b70e2f1d 100644 --- a/_tgstation_8_server_8_host_8_watchdog_8_tests_8_program_8cs_source.html +++ b/_tgstation_8_server_8_host_8_watchdog_8_tests_8_program_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Watchdog.Tests/obj/Release/netcoreapp2.1/Tgstation.Server.Host.Watchdog.Tests.Program.cs Source File +tgstation-server: tests/Tgstation.Server.Host.Watchdog.Tests/obj/Debug/netcoreapp2.1/Tgstation.Server.Host.Watchdog.Tests.Program.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/_tgstation_8_server_8_tests_8_assembly_info_8cs.html b/_tgstation_8_server_8_tests_8_assembly_info_8cs.html index 6ffe9f992d..26e4c9ba82 100644 --- a/_tgstation_8_server_8_tests_8_assembly_info_8cs.html +++ b/_tgstation_8_server_8_tests_8_assembly_info_8cs.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Tests/obj/Release/netcoreapp2.1/Tgstation.Server.Tests.AssemblyInfo.cs File Reference +tgstation-server: tests/Tgstation.Server.Tests/obj/Debug/netcoreapp2.1/Tgstation.Server.Tests.AssemblyInfo.cs File Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/_tgstation_8_server_8_tests_8_assembly_info_8cs_source.html b/_tgstation_8_server_8_tests_8_assembly_info_8cs_source.html index 4ac2885489..ce315e8a0d 100644 --- a/_tgstation_8_server_8_tests_8_assembly_info_8cs_source.html +++ b/_tgstation_8_server_8_tests_8_assembly_info_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Tests/obj/Release/netcoreapp2.1/Tgstation.Server.Tests.AssemblyInfo.cs Source File +tgstation-server: tests/Tgstation.Server.Tests/obj/Debug/netcoreapp2.1/Tgstation.Server.Tests.AssemblyInfo.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
@@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Tgstation.Server.Tests.AssemblyInfo.cs
-Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Tests")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Tests")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Tests")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
+Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
3 // This code was generated by a tool.
4 // Runtime Version:4.0.30319.42000
5 //
6 // Changes to this file may cause incorrect behavior and will be lost if
7 // the code is regenerated.
8 // </auto-generated>
9 //------------------------------------------------------------------------------
10 
11 using System;
12 using System.Reflection;
13 
14 [assembly: System.Reflection.AssemblyCompanyAttribute("Tgstation.Server.Tests")]
15 [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
16 [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
17 [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
18 [assembly: System.Reflection.AssemblyProductAttribute("Tgstation.Server.Tests")]
19 [assembly: System.Reflection.AssemblyTitleAttribute("Tgstation.Server.Tests")]
20 [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
21 
22 // Generated by the MSBuild WriteCodeFragment class.
23 
diff --git a/_tgstation_8_server_8_tests_8_program_8cs_source.html b/_tgstation_8_server_8_tests_8_program_8cs_source.html index ba2161b75f..fa15cfe690 100644 --- a/_tgstation_8_server_8_tests_8_program_8cs_source.html +++ b/_tgstation_8_server_8_tests_8_program_8cs_source.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Tests/obj/Release/netcoreapp2.1/Tgstation.Server.Tests.Program.cs Source File +tgstation-server: tests/Tgstation.Server.Tests/obj/Debug/netcoreapp2.1/Tgstation.Server.Tests.Program.cs Source File @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/_token_8cs_source.html b/_token_8cs_source.html index d6510b5456..fe501e99a6 100644 --- a/_token_8cs_source.html +++ b/_token_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Token.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public sealed class Token
9  {
13  public string Bearer { get; set; }
14 
18  public DateTimeOffset? ExpiresAt { get; set; }
19  }
20 }
- +Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Api.Models
4 {
8  public sealed class Token
9  {
13  public string Bearer { get; set; }
14 
18  public DateTimeOffset? ExpiresAt { get; set; }
19  }
20 }
+
Represents a JWT returned by the API
Definition: Token.cs:8
diff --git a/_token_factory_8cs_source.html b/_token_factory_8cs_source.html index 9e6e6d8966..737ab22c28 100644 --- a/_token_factory_8cs_source.html +++ b/_token_factory_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
TokenFactory.cs
-Go to the documentation of this file.
1 using Microsoft.IdentityModel.Tokens;
2 using System;
3 using System.Globalization;
4 using System.IdentityModel.Tokens.Jwt;
5 using System.Reflection;
6 using System.Security.Claims;
8 
9 namespace Tgstation.Server.Host.Security
10 {
12  sealed class TokenFactory : ITokenFactory
13  {
17  const int TokenExpiryMinutes = 15;
18 
19  public static readonly string TokenAudience = typeof(Token).Assembly.GetName().Name;
20  public static readonly string TokenIssuer = Assembly.GetExecutingAssembly().GetName().Name;
21  public static readonly byte[] TokenSigningKey = CryptographySuite.GetSecureBytes(256);
22 
24  public Token CreateToken(Models.User user)
25  {
26  if (user == null)
27  throw new ArgumentNullException(nameof(user));
28 
29  var expiry = DateTimeOffset.Now.AddMinutes(TokenExpiryMinutes);
30  var claims = new Claim[]
31  {
32  new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString(CultureInfo.InvariantCulture)),
33  new Claim(JwtRegisteredClaimNames.Exp, $"{expiry.ToUnixTimeSeconds()}"),
34  new Claim(JwtRegisteredClaimNames.Nbf, $"{DateTimeOffset.Now.ToUnixTimeSeconds()}"),
35  new Claim(JwtRegisteredClaimNames.Iss, TokenIssuer),
36  new Claim(JwtRegisteredClaimNames.Aud, TokenAudience)
37  };
38 
39  var key = new SymmetricSecurityKey(TokenSigningKey);
40 
41  var token = new JwtSecurityToken(new JwtHeader(new SigningCredentials(key, SecurityAlgorithms.HmacSha256)), new JwtPayload(claims));
42  return new Token { Bearer = new JwtSecurityTokenHandler().WriteToken(token), ExpiresAt = expiry };
43  }
44  }
45 }
+Go to the documentation of this file.
1 using Microsoft.IdentityModel.Tokens;
2 using System;
3 using System.Globalization;
4 using System.IdentityModel.Tokens.Jwt;
5 using System.Reflection;
6 using System.Security.Claims;
8 
9 namespace Tgstation.Server.Host.Security
10 {
12  sealed class TokenFactory : ITokenFactory
13  {
17  const int TokenExpiryMinutes = 15;
18 
19  public static readonly string TokenAudience = typeof(Token).Assembly.GetName().Name;
20  public static readonly string TokenIssuer = Assembly.GetExecutingAssembly().GetName().Name;
21  public static readonly byte[] TokenSigningKey = CryptographySuite.GetSecureBytes(256);
22 
24  public Token CreateToken(Models.User user)
25  {
26  if (user == null)
27  throw new ArgumentNullException(nameof(user));
28 
29  var expiry = DateTimeOffset.Now.AddMinutes(TokenExpiryMinutes);
30  var claims = new Claim[]
31  {
32  new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString(CultureInfo.InvariantCulture)),
33  new Claim(JwtRegisteredClaimNames.Exp, $"{expiry.ToUnixTimeSeconds()}"),
34  new Claim(JwtRegisteredClaimNames.Nbf, $"{DateTimeOffset.Now.ToUnixTimeSeconds()}"),
35  new Claim(JwtRegisteredClaimNames.Iss, TokenIssuer),
36  new Claim(JwtRegisteredClaimNames.Aud, TokenAudience)
37  };
38 
39  var key = new SymmetricSecurityKey(TokenSigningKey);
40 
41  var token = new JwtSecurityToken(new JwtHeader(new SigningCredentials(key, SecurityAlgorithms.HmacSha256)), new JwtPayload(claims));
42  return new Token { Bearer = new JwtSecurityTokenHandler().WriteToken(token), ExpiresAt = expiry };
43  }
44  }
45 }
+
static byte[] GetSecureBytes(int amount)
Generates a secure set of bytes
-
Represents a JWT returned by the API
Definition: Token.cs:8
Token CreateToken(Models.User user)
Create a Token for a given user
Definition: TokenFactory.cs:24
diff --git a/_unauthorized_exception_8cs_source.html b/_unauthorized_exception_8cs_source.html index 3f9bd1b492..c5a3883af0 100644 --- a/_unauthorized_exception_8cs_source.html +++ b/_unauthorized_exception_8cs_source.html @@ -92,14 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
UnauthorizedException.cs
-Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
10  public sealed class UnauthorizedException : ClientException
11  {
15  public UnauthorizedException() : base(new ErrorMessage
16  {
17  Message = "Invalid credentials!",
18  SeverApiVersion = null
19  }, HttpStatusCode.Unauthorized)
20  { }
21 
26  public UnauthorizedException(string message) : base(message) { }
27 
33  public UnauthorizedException(string message, Exception innerException) : base(message, innerException) { }
34  }
35 }
+Go to the documentation of this file.
1 using System;
2 using System.Net;
4 
5 namespace Tgstation.Server.Client
6 {
10  public sealed class UnauthorizedException : ClientException
11  {
15  public UnauthorizedException() : base(new ErrorMessage
16  {
17  Message = "Invalid credentials!",
18  SeverApiVersion = null
19  }, HttpStatusCode.Unauthorized)
20  { }
21 
26  public UnauthorizedException(string message) : base(message) { }
27 
33  public UnauthorizedException(string message, Exception innerException) : base(message, innerException) { }
34  }
35 }
+
UnauthorizedException(string message)
Construct an UnauthorizedException with a message
UnauthorizedException(string message, Exception innerException)
Construct an UnauthorizedException with a message and innerException
UnauthorizedException()
Construct an UnauthorizedException
-
Occurs when the client provides invalid credentials
Represents an error message returned by the server
Definition: ErrorMessage.cs:8
diff --git a/_unrecognized_response_exception_8cs_source.html b/_unrecognized_response_exception_8cs_source.html index 575afc7078..39d993fa97 100644 --- a/_unrecognized_response_exception_8cs_source.html +++ b/_unrecognized_response_exception_8cs_source.html @@ -92,14 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
UnrecognizedResponseException.cs
-Go to the documentation of this file.
1 using System;
2 using System.Globalization;
3 using System.Net;
5 
6 namespace Tgstation.Server.Client
7 {
9  {
15  public UnrecognizedResponseException(string data, HttpStatusCode statusCode) : base(new ErrorMessage
16  {
17  Message = String.Format(CultureInfo.InvariantCulture, "Unrecognized response body: {0}", data),
18  SeverApiVersion = null
19  }, statusCode)
20  { }
21 
26 
31  public UnrecognizedResponseException(string message) : base(message) { }
32 
38  public UnrecognizedResponseException(string message, Exception innerException) : base(message, innerException) { }
39  }
40 }
+Go to the documentation of this file.
1 using System;
2 using System.Globalization;
3 using System.Net;
5 
6 namespace Tgstation.Server.Client
7 {
9  {
15  public UnrecognizedResponseException(string data, HttpStatusCode statusCode) : base(new ErrorMessage
16  {
17  Message = String.Format(CultureInfo.InvariantCulture, "Unrecognized response body: {0}", data),
18  SeverApiVersion = null
19  }, statusCode)
20  { }
21 
26 
31  public UnrecognizedResponseException(string message) : base(message) { }
32 
38  public UnrecognizedResponseException(string message, Exception innerException) : base(message, innerException) { }
39  }
40 }
+
UnrecognizedResponseException(string message)
Construct an UnrecognizedResponseException with a message
UnrecognizedResponseException()
Construct a UnrecognizedResponseException
-
UnrecognizedResponseException(string data, HttpStatusCode statusCode)
Construct an UnrecognizedResponseException with the data of a response body and the statusCode ...
UnrecognizedResponseException(string message, Exception innerException)
Construct an UnrecognizedResponseException with a message and innerException
diff --git a/_updates_configuration_8cs_source.html b/_updates_configuration_8cs_source.html index 2365a031bd..0a566d5b15 100644 --- a/_updates_configuration_8cs_source.html +++ b/_updates_configuration_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
UpdatesConfiguration.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Configuration
2 {
6  public sealed class UpdatesConfiguration
7  {
11  public const string Section = "Updates";
12 
16  public long GitHubRepositoryId { get; set; }
17 
21  public string GitTagPrefix { get; set; }
22 
26  public string UpdatePackageAssetName { get; set; }
27  }
28 }
Configuration for the automatic update system
- +Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Configuration
2 {
6  public sealed class UpdatesConfiguration
7  {
11  public const string Section = "Updates";
12 
16  public long GitHubRepositoryId { get; set; }
17 
21  public string GitTagPrefix { get; set; }
22 
26  public string UpdatePackageAssetName { get; set; }
27  }
28 }
+
Configuration for the automatic update system
diff --git a/_user_controller_8cs_source.html b/_user_controller_8cs_source.html index a6569ad489..123b71ee1f 100644 --- a/_user_controller_8cs_source.html +++ b/_user_controller_8cs_source.html @@ -92,13 +92,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
UserController.cs
-Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using Microsoft.Extensions.Options;
5 using System;
6 using System.Collections.Generic;
7 using System.Globalization;
8 using System.Linq;
9 using System.Net;
10 using System.Threading;
11 using System.Threading.Tasks;
12 using Tgstation.Server.Api;
18 
19 namespace Tgstation.Server.Host.Controllers
20 {
24  [Route(Routes.User)]
25  public sealed class UserController : ModelController<UserUpdate>
26  {
31 
36 
40  readonly ILogger<UserController> logger;
41 
46 
56  public UserController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ISystemIdentityFactory systemIdentityFactory, ICryptographySuite cryptographySuite, ILogger<UserController> logger, IOptions<GeneralConfiguration> generalConfigurationOptions) : base(databaseContext, authenticationContextFactory, logger, false)
57  {
58  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
59  this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory));
60  this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
61  generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
62  }
63 
65  [TgsAuthorize(AdministrationRights.WriteUsers)]
66  public override async Task<IActionResult> Create([FromBody] UserUpdate model, CancellationToken cancellationToken)
67  {
68  if (model == null)
69  throw new ArgumentNullException(nameof(model));
70 
71  if (!(model.Password == null ^ model.SystemIdentifier == null))
72  return BadRequest(new ErrorMessage { Message = "User must have exactly one of either a password or system identifier!" });
73 
74  model.Name = model.Name?.Trim();
75  if (model.Name?.Length == 0)
76  model.Name = null;
77 
78  if (!(model.Name == null ^ model.SystemIdentifier == null))
79  return BadRequest(new ErrorMessage { Message = "User must have a name if and only if user has no system identifier!" });
80 
81  var dbUser = new Models.User
82  {
83  AdministrationRights = model.AdministrationRights ?? AdministrationRights.None,
84  CreatedAt = DateTimeOffset.Now,
85  CreatedBy = AuthenticationContext.User,
86  Enabled = model.Enabled ?? false,
87  InstanceManagerRights = model.InstanceManagerRights ?? InstanceManagerRights.None,
88  Name = model.Name,
89  SystemIdentifier = model.SystemIdentifier,
90  InstanceUsers = new List<Models.InstanceUser>()
91  };
92 
93  if (model.SystemIdentifier != null)
94  try
95  {
96  using (var sysIdentity = await systemIdentityFactory.CreateSystemIdentity(dbUser, cancellationToken).ConfigureAwait(false))
97  {
98  if (sysIdentity == null)
99  return StatusCode((int)HttpStatusCode.Gone);
100  dbUser.Name = sysIdentity.Username;
101  dbUser.SystemIdentifier = sysIdentity.Uid;
102  }
103  }
104  catch (NotImplementedException)
105  {
106  return StatusCode((int)HttpStatusCode.NotImplemented);
107  }
108  else
109  {
110  if (model.Password.Length < generalConfiguration.MinimumPasswordLength)
111  return BadRequest(new ErrorMessage { Message = String.Format(CultureInfo.InvariantCulture, "Password must be at least {0} characters long!", generalConfiguration.MinimumPasswordLength) });
112  cryptographySuite.SetUserPassword(dbUser, model.Password);
113  }
114 
115  dbUser.CanonicalName = dbUser.Name.ToUpperInvariant();
116 
117  DatabaseContext.Users.Add(dbUser);
118 
119  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
120 
121  return StatusCode((int)HttpStatusCode.Created, dbUser.ToApi(true));
122  }
123 
125  [TgsAuthorize(AdministrationRights.WriteUsers | AdministrationRights.EditOwnPassword)]
126  public override async Task<IActionResult> Update([FromBody] UserUpdate model, CancellationToken cancellationToken)
127  {
128  if (model == null)
129  throw new ArgumentNullException(nameof(model));
130 
131  var passwordEditOnly = !AuthenticationContext.User.AdministrationRights.Value.HasFlag(AdministrationRights.WriteUsers);
132 
133  var originalUser = passwordEditOnly ? AuthenticationContext.User : await DatabaseContext.Users.Where(x => x.Id == model.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
134  if (originalUser == default)
135  return NotFound();
136 
137  if (passwordEditOnly && (model.Id != originalUser.Id || model.InstanceManagerRights.HasValue || model.AdministrationRights.HasValue || model.Enabled.HasValue || model.SystemIdentifier != null || model.Name != null))
138  return Forbid();
139 
140  if (model.Password != null)
141  {
142  if (originalUser.PasswordHash == null)
143  return BadRequest(new ErrorMessage { Message = "Cannot convert a system user to a password user!" });
144  cryptographySuite.SetUserPassword(originalUser, model.Password);
145  }
146  else if (model.SystemIdentifier != null && model.SystemIdentifier != originalUser.SystemIdentifier)
147  return BadRequest(new ErrorMessage { Message = "Cannot change a user's system identifier!" });
148 
149  if (model.Name != null && model.Name.ToUpperInvariant() != originalUser.CanonicalName)
150  return BadRequest(new ErrorMessage { Message = "Can only change capitalization of a user's name!" });
151 
152  originalUser.InstanceManagerRights = model.InstanceManagerRights ?? originalUser.InstanceManagerRights;
153  originalUser.AdministrationRights = model.AdministrationRights ?? originalUser.AdministrationRights;
154  originalUser.Enabled = model.Enabled ?? originalUser.Enabled;
155  originalUser.Name = model.Name ?? originalUser.Name;
156 
157  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
158 
159  return Json(originalUser.ToApi(true));
160  }
161 
163  [TgsAuthorize]
164  public override Task<IActionResult> Read(CancellationToken cancellationToken) => Task.FromResult<IActionResult>(Json(AuthenticationContext.User.ToApi(true)));
165 
167  [TgsAuthorize(AdministrationRights.ReadUsers)]
168  public override async Task<IActionResult> List(CancellationToken cancellationToken)
169  {
170  var users = await DatabaseContext.Users.ToListAsync(cancellationToken).ConfigureAwait(false);
171  return Json(users.Select(x => x.ToApi(true)));
172  }
173 
175  [TgsAuthorize]
176  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
177  {
178  if (id == AuthenticationContext.User.Id)
179  return await Read(cancellationToken).ConfigureAwait(false);
180 
181  if (!((AdministrationRights)AuthenticationContext.GetRight(RightsType.Administration)).HasFlag(AdministrationRights.ReadUsers))
182  return Forbid();
183 
184  var user = await DatabaseContext.Users.Where(x => x.Id == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
185  if (user == default)
186  return NotFound();
187  return Json(user.ToApi(true));
188  }
189  }
190 }
+Go to the documentation of this file.
1 using Microsoft.AspNetCore.Mvc;
2 using Microsoft.EntityFrameworkCore;
3 using Microsoft.Extensions.Logging;
4 using Microsoft.Extensions.Options;
5 using System;
6 using System.Collections.Generic;
7 using System.Globalization;
8 using System.Linq;
9 using System.Net;
10 using System.Threading;
11 using System.Threading.Tasks;
12 using Tgstation.Server.Api;
18 
19 namespace Tgstation.Server.Host.Controllers
20 {
24  [Route(Routes.User)]
25  public sealed class UserController : ModelController<UserUpdate>
26  {
31 
36 
40  readonly ILogger<UserController> logger;
41 
46 
56  public UserController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ISystemIdentityFactory systemIdentityFactory, ICryptographySuite cryptographySuite, ILogger<UserController> logger, IOptions<GeneralConfiguration> generalConfigurationOptions) : base(databaseContext, authenticationContextFactory, logger, false)
57  {
58  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
59  this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory));
60  this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite));
61  generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
62  }
63 
65  [TgsAuthorize(AdministrationRights.WriteUsers)]
66  public override async Task<IActionResult> Create([FromBody] UserUpdate model, CancellationToken cancellationToken)
67  {
68  if (model == null)
69  throw new ArgumentNullException(nameof(model));
70 
71  if (!(model.Password == null ^ model.SystemIdentifier == null))
72  return BadRequest(new ErrorMessage { Message = "User must have exactly one of either a password or system identifier!" });
73 
74  model.Name = model.Name?.Trim();
75  if (model.Name?.Length == 0)
76  model.Name = null;
77 
78  if (!(model.Name == null ^ model.SystemIdentifier == null))
79  return BadRequest(new ErrorMessage { Message = "User must have a name if and only if user has no system identifier!" });
80 
81  var dbUser = new Models.User
82  {
83  AdministrationRights = model.AdministrationRights ?? AdministrationRights.None,
84  CreatedAt = DateTimeOffset.Now,
85  CreatedBy = AuthenticationContext.User,
86  Enabled = model.Enabled ?? false,
87  InstanceManagerRights = model.InstanceManagerRights ?? InstanceManagerRights.None,
88  Name = model.Name,
89  SystemIdentifier = model.SystemIdentifier,
90  InstanceUsers = new List<Models.InstanceUser>()
91  };
92 
93  if (model.SystemIdentifier != null)
94  try
95  {
96  using (var sysIdentity = await systemIdentityFactory.CreateSystemIdentity(dbUser, cancellationToken).ConfigureAwait(false))
97  {
98  if (sysIdentity == null)
99  return StatusCode((int)HttpStatusCode.Gone);
100  dbUser.Name = sysIdentity.Username;
101  dbUser.SystemIdentifier = sysIdentity.Uid;
102  }
103  }
104  catch (NotImplementedException)
105  {
106  return StatusCode((int)HttpStatusCode.NotImplemented);
107  }
108  else
109  {
110  if (model.Password.Length < generalConfiguration.MinimumPasswordLength)
111  return BadRequest(new ErrorMessage { Message = String.Format(CultureInfo.InvariantCulture, "Password must be at least {0} characters long!", generalConfiguration.MinimumPasswordLength) });
112  cryptographySuite.SetUserPassword(dbUser, model.Password);
113  }
114 
115  dbUser.CanonicalName = dbUser.Name.ToUpperInvariant();
116 
117  DatabaseContext.Users.Add(dbUser);
118 
119  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
120 
121  return StatusCode((int)HttpStatusCode.Created, dbUser.ToApi(true));
122  }
123 
125  [TgsAuthorize(AdministrationRights.WriteUsers | AdministrationRights.EditOwnPassword)]
126  public override async Task<IActionResult> Update([FromBody] UserUpdate model, CancellationToken cancellationToken)
127  {
128  if (model == null)
129  throw new ArgumentNullException(nameof(model));
130 
131  var passwordEditOnly = !AuthenticationContext.User.AdministrationRights.Value.HasFlag(AdministrationRights.WriteUsers);
132 
133  var originalUser = passwordEditOnly ? AuthenticationContext.User : await DatabaseContext.Users.Where(x => x.Id == model.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
134  if (originalUser == default)
135  return NotFound();
136 
137  if (passwordEditOnly && (model.Id != originalUser.Id || model.InstanceManagerRights.HasValue || model.AdministrationRights.HasValue || model.Enabled.HasValue || model.SystemIdentifier != null || model.Name != null))
138  return Forbid();
139 
140  if (model.Password != null)
141  {
142  if (originalUser.PasswordHash == null)
143  return BadRequest(new ErrorMessage { Message = "Cannot convert a system user to a password user!" });
144  cryptographySuite.SetUserPassword(originalUser, model.Password);
145  }
146  else if (model.SystemIdentifier != null && model.SystemIdentifier != originalUser.SystemIdentifier)
147  return BadRequest(new ErrorMessage { Message = "Cannot change a user's system identifier!" });
148 
149  if (model.Name != null && model.Name.ToUpperInvariant() != originalUser.CanonicalName)
150  return BadRequest(new ErrorMessage { Message = "Can only change capitalization of a user's name!" });
151 
152  originalUser.InstanceManagerRights = model.InstanceManagerRights ?? originalUser.InstanceManagerRights;
153  originalUser.AdministrationRights = model.AdministrationRights ?? originalUser.AdministrationRights;
154  originalUser.Enabled = model.Enabled ?? originalUser.Enabled;
155  originalUser.Name = model.Name ?? originalUser.Name;
156 
157  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
158 
159  return Json(originalUser.ToApi(true));
160  }
161 
163  [TgsAuthorize]
164  public override Task<IActionResult> Read(CancellationToken cancellationToken) => Task.FromResult<IActionResult>(Json(AuthenticationContext.User.ToApi(true)));
165 
167  [TgsAuthorize(AdministrationRights.ReadUsers)]
168  public override async Task<IActionResult> List(CancellationToken cancellationToken)
169  {
170  var users = await DatabaseContext.Users.ToListAsync(cancellationToken).ConfigureAwait(false);
171  return Json(users.Select(x => x.ToApi(true)));
172  }
173 
175  [TgsAuthorize]
176  public override async Task<IActionResult> GetId(long id, CancellationToken cancellationToken)
177  {
178  if (id == AuthenticationContext.User.Id)
179  return await Read(cancellationToken).ConfigureAwait(false);
180 
181  if (!((AdministrationRights)AuthenticationContext.GetRight(RightsType.Administration)).HasFlag(AdministrationRights.ReadUsers))
182  return Forbid();
183 
184  var user = await DatabaseContext.Users.Where(x => x.Id == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
185  if (user == default)
186  return NotFound();
187  return Json(user.ToApi(true));
188  }
189  }
190 }
const string User
The Models.User controller
Definition: Routes.cs:24
RightsType
The type of rights a model uses
Definition: RightsType.cs:6
+ @@ -119,7 +120,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
InstanceManagerRights
Rights for managing Models.Instances
-
override async Task< IActionResult > Update([FromBody] UserUpdate model, CancellationToken cancellationToken)
An ApiController representing a TModel
diff --git a/_user_update_8cs_source.html b/_user_update_8cs_source.html index c4d00c39f7..de77efa232 100644 --- a/_user_update_8cs_source.html +++ b/_user_update_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
UserUpdate.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
6  public sealed class UserUpdate : User
7  {
11  public string Password { get; set; }
12  }
13 }
+Go to the documentation of this file.
1 namespace Tgstation.Server.Api.Models
2 {
6  public sealed class UserUpdate : User
7  {
11  public string Password { get; set; }
12  }
13 }
For editing a given User. Will never be returned by the API
Definition: UserUpdate.cs:6
diff --git a/_users_client_8cs_source.html b/_users_client_8cs_source.html index 801a200a5a..191249b8ad 100644 --- a/_users_client_8cs_source.html +++ b/_users_client_8cs_source.html @@ -92,15 +92,15 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
UsersClient.cs
-Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client
9 {
11  sealed class UsersClient : IUsersClient
12  {
17 
22  public UsersClient(IApiClient apiClient)
23  {
24  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
25  }
26 
28  public Task<User> Create(UserUpdate user, CancellationToken cancellationToken) => apiClient.Create<UserUpdate, User>(Routes.User, user ?? throw new ArgumentNullException(nameof(user)), cancellationToken);
29 
31  public Task<User> GetId(User user, CancellationToken cancellationToken) => apiClient.Read<User>(Routes.SetID(Routes.User, user?.Id ?? throw new ArgumentNullException(nameof(user))), cancellationToken);
32 
34  public Task<IReadOnlyList<User>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<User>>(Routes.List(Routes.User), cancellationToken);
35 
37  public Task<User> Read(CancellationToken cancellationToken) => apiClient.Read<User>(Routes.User, cancellationToken);
38 
40  public Task<User> Update(UserUpdate user, CancellationToken cancellationToken) => apiClient.Update<UserUpdate, User>(Routes.User, user ?? throw new ArgumentNullException(nameof(user)), cancellationToken);
41  }
42 }
readonly IApiClient apiClient
The apiClient for the UsersClient
Definition: UsersClient.cs:16
+Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Threading;
4 using System.Threading.Tasks;
5 using Tgstation.Server.Api;
7 
8 namespace Tgstation.Server.Client
9 {
11  sealed class UsersClient : IUsersClient
12  {
17 
22  public UsersClient(IApiClient apiClient)
23  {
24  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
25  }
26 
28  public Task<User> Create(UserUpdate user, CancellationToken cancellationToken) => apiClient.Create<UserUpdate, User>(Routes.User, user ?? throw new ArgumentNullException(nameof(user)), cancellationToken);
29 
31  public Task<User> GetId(User user, CancellationToken cancellationToken) => apiClient.Read<User>(Routes.SetID(Routes.User, user?.Id ?? throw new ArgumentNullException(nameof(user))), cancellationToken);
32 
34  public Task<IReadOnlyList<User>> List(CancellationToken cancellationToken) => apiClient.Read<IReadOnlyList<User>>(Routes.List(Routes.User), cancellationToken);
35 
37  public Task<User> Read(CancellationToken cancellationToken) => apiClient.Read<User>(Routes.User, cancellationToken);
38 
40  public Task<User> Update(UserUpdate user, CancellationToken cancellationToken) => apiClient.Update<UserUpdate, User>(Routes.User, user ?? throw new ArgumentNullException(nameof(user)), cancellationToken);
41  }
42 }
readonly IApiClient apiClient
The apiClient for the UsersClient
Definition: UsersClient.cs:16
const string User
The Models.User controller
Definition: Routes.cs:24
+
Routes to a server actions
Definition: Routes.cs:9
-
long Id
The ID of the User
Definition: User.cs:15
static string SetID(string route, long id)
Apply an id postfix to a route
diff --git a/_version_command_8cs_source.html b/_version_command_8cs_source.html index 4ee65d3a29..b458d4a5d8 100644 --- a/_version_command_8cs_source.html +++ b/_version_command_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
VersionCommand.cs
-Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Components.Chat.Commands
7 {
11  sealed class VersionCommand : ICommand
12  {
14  public string Name => "version";
15 
17  public string HelpText => "Displays the tgstation server version";
18 
20  public bool AdminOnly => false;
21 
26 
31  public VersionCommand(IApplication application)
32  {
33  this.application = application ?? throw new ArgumentNullException(nameof(application));
34  }
35 
37  public Task<string> Invoke(string arguments, User user, CancellationToken cancellationToken) => Task.FromResult(application.VersionString);
38  }
39 }
Configures the ASP.NET Core web application
Definition: IApplication.cs:8
+Go to the documentation of this file.
1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
5 
6 namespace Tgstation.Server.Host.Components.Chat.Commands
7 {
11  sealed class VersionCommand : ICommand
12  {
14  public string Name => "version";
15 
17  public string HelpText => "Displays the tgstation server version";
18 
20  public bool AdminOnly => false;
21 
26 
31  public VersionCommand(IApplication application)
32  {
33  this.application = application ?? throw new ArgumentNullException(nameof(application));
34  }
35 
37  public Task<string> Invoke(string arguments, User user, CancellationToken cancellationToken) => Task.FromResult(application.VersionString);
38  }
39 }
Configures the ASP.NET Core web application
Definition: IApplication.cs:8
+
ICommand to return the IApplication.VersionString
readonly IApplication application
The IApplication for the VersionCommand
-
VersionCommand(IApplication application)
Construct a VersionCommand
Represents a tgs_chat_user datum
Definition: User.cs:10
diff --git a/_watchdog_2_i_watchdog_factory_8cs_source.html b/_watchdog_2_i_watchdog_factory_8cs_source.html index 12e4704cea..2a91771db1 100644 --- a/_watchdog_2_i_watchdog_factory_8cs_source.html +++ b/_watchdog_2_i_watchdog_factory_8cs_source.html @@ -92,7 +92,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
IWatchdogFactory.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 
3 namespace Tgstation.Server.Host.Watchdog
4 {
8  public interface IWatchdogFactory
9  {
15  IWatchdog CreateWatchdog(ILoggerFactory loggerFactory);
16  }
17 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 
3 namespace Tgstation.Server.Host.Watchdog
4 {
8  public interface IWatchdogFactory
9  {
15  IWatchdog CreateWatchdog(ILoggerFactory loggerFactory);
16  }
17 }
The watchdog for a Host
Definition: IWatchdog.cs:9
diff --git a/_watchdog_2_watchdog_8cs_source.html b/_watchdog_2_watchdog_8cs_source.html index d1f005272d..6da77ef9d4 100644 --- a/_watchdog_2_watchdog_8cs_source.html +++ b/_watchdog_2_watchdog_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
Watchdog.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Collections.Generic;
4 using System.Diagnostics;
5 using System.Globalization;
6 using System.IO;
7 using System.Linq;
8 using System.Reflection;
9 using System.Runtime.InteropServices;
10 using System.Threading;
11 using System.Threading.Tasks;
12 
13 namespace Tgstation.Server.Host.Watchdog
14 {
16  sealed class Watchdog : IWatchdog
17  {
18 
22  readonly ILogger<Watchdog> logger;
23 
28  public Watchdog(ILogger<Watchdog> logger)
29  {
30  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
31  }
32 
34  public async Task RunAsync(string[] args, CancellationToken cancellationToken)
35  {
36  logger.LogInformation("Host watchdog starting...");
37 
38  var enviromentPath = Environment.GetEnvironmentVariable("PATH");
39  var paths = enviromentPath.Split(';');
40  var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
41 
42  var exeName = "dotnet";
43  IEnumerable<string> enumerator;
44  if (isWindows)
45  {
46  exeName += ".exe";
47  enumerator = paths;
48  }
49  else
50  enumerator = paths.Select(x => x.Split(':')).SelectMany(x => x);
51 
52  enumerator = enumerator.Select(x => Path.Combine(x, exeName));
53 
54  var dotnetPath = enumerator
55  .Where(x =>
56  {
57  logger.LogTrace("Checking for dotnet at {0}", x);
58  return File.Exists(x);
59  })
60  .FirstOrDefault();
61 
62  if (dotnetPath == default)
63  {
64  logger.LogCritical("Unable to locate dotnet executable in PATH! Please ensure the .NET Core runtime is installed and is in your PATH!");
65  return;
66  }
67  logger.LogInformation("Detected dotnet executable at {0}", dotnetPath);
68 
69  var rootLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
70 
71  var assemblyStoragePath = Path.Combine(rootLocation, "lib"); //always always next to watchdog
72  var defaultAssemblyPath = Path.GetFullPath(Path.Combine(assemblyStoragePath, "Default"));
73 #if DEBUG
74  //just copy the shit where it belongs
75  Directory.Delete(assemblyStoragePath, true);
76  Directory.CreateDirectory(defaultAssemblyPath);
77 
78  var sourcePath = "../../../../Tgstation.Server.Host/bin/Debug/netcoreapp2.1";
79  foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories))
80  Directory.CreateDirectory(dirPath.Replace(sourcePath, defaultAssemblyPath));
81 
82  foreach (string newPath in Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories))
83  File.Copy(newPath, newPath.Replace(sourcePath, defaultAssemblyPath), true);
84 
85  const string AppSettingsJson = "appsettings.json";
86  var rootJson = Path.Combine(rootLocation, AppSettingsJson);
87  File.Delete(rootJson);
88  File.Move(Path.Combine(defaultAssemblyPath, AppSettingsJson), rootJson);
89 #endif
90 
91  var assemblyName = String.Join(".", nameof(Tgstation), nameof(Server), nameof(Host), "dll");
92  var assemblyPath = Path.Combine(defaultAssemblyPath, assemblyName);
93 
94  if (assemblyPath.Contains("\""))
95  {
96  logger.LogCritical("Running from paths with \"'s in the name is not supported!");
97  return;
98  }
99 
100  if (!File.Exists(assemblyPath))
101  {
102  logger.LogCritical("Unable to locate host assembly!");
103  return;
104  }
105 
106  string updateDirectory = null;
107  try
108  {
109  while (!cancellationToken.IsCancellationRequested)
110  using (logger.BeginScope("Host invocation"))
111  {
112  updateDirectory = Path.GetFullPath(Path.Combine(assemblyStoragePath, Guid.NewGuid().ToString()));
113  logger.LogInformation("Update path set to {0}", updateDirectory);
114  using (var process = new Process())
115  {
116  process.StartInfo.FileName = dotnetPath;
117  process.StartInfo.WorkingDirectory = Environment.CurrentDirectory; //for appsettings
118 
119  var arguments = new List<string>
120  {
121  '"' + assemblyPath + '"',
122  updateDirectory
123  };
124  if (Debugger.IsAttached)
125  arguments.Add("--attach-debugger");
126  arguments.AddRange(args);
127 
128  process.StartInfo.Arguments = String.Join(" ", arguments);
129 
130  process.StartInfo.UseShellExecute = false; //runs in the same console
131 
132  var tcs = new TaskCompletionSource<object>();
133  process.Exited += (a, b) =>
134  {
135  tcs.TrySetResult(null);
136  };
137  process.EnableRaisingEvents = true;
138 
139  logger.LogInformation("Launching host...");
140 
141  var iShotTheSheriff = false;
142  try
143  {
144  process.Start();
145 
146  using (var processCts = new CancellationTokenSource())
147  using (processCts.Token.Register(() => tcs.TrySetResult(null)))
148  using (cancellationToken.Register(() =>
149  {
150  if (!Directory.Exists(updateDirectory))
151  {
152  logger.LogInformation("Cancellation requested! Writing shutdown lock file...");
153  File.WriteAllBytes(updateDirectory, Array.Empty<byte>());
154  }
155  else
156  logger.LogWarning("Cancellation requested while update directory exists!");
157 
158  logger.LogInformation("Will force close host process if it doesn't exit in 10 seconds...");
159 
160  try
161  {
162  processCts.CancelAfter(TimeSpan.FromSeconds(10));
163  }
164  catch (ObjectDisposedException) { } //race conditions
165  }))
166  await tcs.Task.ConfigureAwait(false);
167  }
168  catch (InvalidOperationException) { }
169  finally
170  {
171  try
172  {
173  if (!process.HasExited)
174  {
175  iShotTheSheriff = true;
176  process.Kill();
177  process.WaitForExit();
178  }
179  }
180  catch (InvalidOperationException) { }
181  logger.LogInformation("Host exited!");
182  }
183 
184  switch (process.ExitCode)
185  {
186  case 0:
187  return;
188  case 1:
189  if (!cancellationToken.IsCancellationRequested)
190  //just a restart
191  logger.LogInformation("Watchdog will restart host...");
192  else
193  logger.LogWarning("Host requested restart but watchdog shutdown is in progress!");
194  break;
195  case 2:
196  //update path is now an exception document
197  logger.LogCritical("Host crashed, propagating exception dump...");
198  var data = File.ReadAllText(updateDirectory);
199  try
200  {
201  File.Delete(updateDirectory);
202  }
203  catch (Exception e)
204  {
205  logger.LogWarning("Unable to delete exception dump file at {0}! Exception: {1}", updateDirectory, e);
206  }
207  throw new Exception(String.Format(CultureInfo.InvariantCulture, "Host propagated exception: {0}", data));
208  default:
209  if (iShotTheSheriff)
210  {
211  logger.LogWarning("Watchdog forced to kill host process!");
212  cancellationToken.ThrowIfCancellationRequested();
213  }
214  throw new Exception(String.Format(CultureInfo.InvariantCulture, "Host crashed with exit code {0}!", process.ExitCode));
215  }
216  }
217 
218  //HEY YOU
219  //BE WARNED THAT IF YOU DEBUGGED THE HOST PROCESS THAT JUST LAUNCHED THE DEBUGGER WILL HOLD A LOCK ON THE DIRECTORY
220  //THIS MEANS THE FIRST DIRECTORY.MOVE WILL THROW
221  if (Directory.Exists(updateDirectory))
222  {
223  logger.LogInformation("Applying server update...");
224  if (isWindows)
225  {
226  //windows dick sucking resource unlocking
227  GC.Collect();
228  await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken).ConfigureAwait(false);
229  }
230  var tempPath = Path.Combine(assemblyStoragePath, Guid.NewGuid().ToString());
231  try
232  {
233  Directory.Move(defaultAssemblyPath, tempPath);
234  try
235  {
236  Directory.Move(updateDirectory, defaultAssemblyPath);
237  logger.LogInformation("Server update complete, deleting old server...");
238  try
239  {
240  Directory.Delete(tempPath, true);
241  }
242  catch (Exception e)
243  {
244  logger.LogWarning("Error deleting old server at {0}! Exception: {1}", tempPath, e);
245  }
246  }
247  catch (Exception e)
248  {
249  logger.LogError("Error moving updated server directory, attempting revert! Exception: {0}", e);
250  Directory.Delete(defaultAssemblyPath, true);
251  Directory.Move(tempPath, defaultAssemblyPath);
252  logger.LogInformation("Revert successful!");
253  }
254  }
255  catch (Exception e)
256  {
257  logger.LogWarning("Failed to move out active host assembly! Exception: {0}", e);
258  }
259  }
260  }
261  }
262  catch (OperationCanceledException)
263  {
264  logger.LogDebug("Exiting due to cancellation...");
265  if (!Directory.Exists(updateDirectory))
266  File.Delete(updateDirectory);
267  else
268  Directory.Delete(updateDirectory, true);
269  }
270  catch (Exception e)
271  {
272  logger.LogCritical("Watchdog error! Exception: {0}", e);
273  }
274  finally
275  {
276  logger.LogInformation("Host watchdog exiting...");
277  }
278  }
279  }
280 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Collections.Generic;
4 using System.Diagnostics;
5 using System.Globalization;
6 using System.IO;
7 using System.Linq;
8 using System.Reflection;
9 using System.Runtime.InteropServices;
10 using System.Threading;
11 using System.Threading.Tasks;
12 
13 namespace Tgstation.Server.Host.Watchdog
14 {
16  sealed class Watchdog : IWatchdog
17  {
18 
22  readonly ILogger<Watchdog> logger;
23 
28  public Watchdog(ILogger<Watchdog> logger)
29  {
30  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
31  }
32 
34  public async Task RunAsync(string[] args, CancellationToken cancellationToken)
35  {
36  logger.LogInformation("Host watchdog starting...");
37 
38  var enviromentPath = Environment.GetEnvironmentVariable("PATH");
39  var paths = enviromentPath.Split(';');
40  var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
41 
42  var exeName = "dotnet";
43  IEnumerable<string> enumerator;
44  if (isWindows)
45  {
46  exeName += ".exe";
47  enumerator = paths;
48  }
49  else
50  enumerator = paths.Select(x => x.Split(':')).SelectMany(x => x);
51 
52  enumerator = enumerator.Select(x => Path.Combine(x, exeName));
53 
54  var dotnetPath = enumerator
55  .Where(x =>
56  {
57  logger.LogTrace("Checking for dotnet at {0}", x);
58  return File.Exists(x);
59  })
60  .FirstOrDefault();
61 
62  if (dotnetPath == default)
63  {
64  logger.LogCritical("Unable to locate dotnet executable in PATH! Please ensure the .NET Core runtime is installed and is in your PATH!");
65  return;
66  }
67  logger.LogInformation("Detected dotnet executable at {0}", dotnetPath);
68 
69  var rootLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
70 
71  var assemblyStoragePath = Path.Combine(rootLocation, "lib"); //always always next to watchdog
72  var defaultAssemblyPath = Path.GetFullPath(Path.Combine(assemblyStoragePath, "Default"));
73 #if DEBUG
74  //just copy the shit where it belongs
75  Directory.Delete(assemblyStoragePath, true);
76  Directory.CreateDirectory(defaultAssemblyPath);
77 
78  var sourcePath = "../../../../Tgstation.Server.Host/bin/Debug/netcoreapp2.1";
79  foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories))
80  Directory.CreateDirectory(dirPath.Replace(sourcePath, defaultAssemblyPath));
81 
82  foreach (string newPath in Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories))
83  File.Copy(newPath, newPath.Replace(sourcePath, defaultAssemblyPath), true);
84 
85  const string AppSettingsJson = "appsettings.json";
86  var rootJson = Path.Combine(rootLocation, AppSettingsJson);
87  File.Delete(rootJson);
88  File.Move(Path.Combine(defaultAssemblyPath, AppSettingsJson), rootJson);
89 #endif
90 
91  var assemblyName = String.Join(".", nameof(Tgstation), nameof(Server), nameof(Host), "dll");
92  var assemblyPath = Path.Combine(defaultAssemblyPath, assemblyName);
93 
94  if (assemblyPath.Contains("\""))
95  {
96  logger.LogCritical("Running from paths with \"'s in the name is not supported!");
97  return;
98  }
99 
100  if (!File.Exists(assemblyPath))
101  {
102  logger.LogCritical("Unable to locate host assembly!");
103  return;
104  }
105 
106  string updateDirectory = null;
107  try
108  {
109  while (!cancellationToken.IsCancellationRequested)
110  using (logger.BeginScope("Host invocation"))
111  {
112  updateDirectory = Path.GetFullPath(Path.Combine(assemblyStoragePath, Guid.NewGuid().ToString()));
113  logger.LogInformation("Update path set to {0}", updateDirectory);
114  using (var process = new Process())
115  {
116  process.StartInfo.FileName = dotnetPath;
117  process.StartInfo.WorkingDirectory = Environment.CurrentDirectory; //for appsettings
118 
119  var arguments = new List<string>
120  {
121  '"' + assemblyPath + '"',
122  updateDirectory
123  };
124  if (Debugger.IsAttached)
125  arguments.Add("--attach-debugger");
126  arguments.AddRange(args);
127 
128  process.StartInfo.Arguments = String.Join(" ", arguments);
129 
130  process.StartInfo.UseShellExecute = false; //runs in the same console
131 
132  var tcs = new TaskCompletionSource<object>();
133  process.Exited += (a, b) =>
134  {
135  tcs.TrySetResult(null);
136  };
137  process.EnableRaisingEvents = true;
138 
139  logger.LogInformation("Launching host...");
140 
141  var iShotTheSheriff = false;
142  try
143  {
144  process.Start();
145 
146  using (var processCts = new CancellationTokenSource())
147  using (processCts.Token.Register(() => tcs.TrySetResult(null)))
148  using (cancellationToken.Register(() =>
149  {
150  if (!Directory.Exists(updateDirectory))
151  {
152  logger.LogInformation("Cancellation requested! Writing shutdown lock file...");
153  File.WriteAllBytes(updateDirectory, Array.Empty<byte>());
154  }
155  else
156  logger.LogWarning("Cancellation requested while update directory exists!");
157 
158  logger.LogInformation("Will force close host process if it doesn't exit in 10 seconds...");
159 
160  try
161  {
162  processCts.CancelAfter(TimeSpan.FromSeconds(10));
163  }
164  catch (ObjectDisposedException) { } //race conditions
165  }))
166  await tcs.Task.ConfigureAwait(false);
167  }
168  catch (InvalidOperationException) { }
169  finally
170  {
171  try
172  {
173  if (!process.HasExited)
174  {
175  iShotTheSheriff = true;
176  process.Kill();
177  process.WaitForExit();
178  }
179  }
180  catch (InvalidOperationException) { }
181  logger.LogInformation("Host exited!");
182  }
183 
184  switch (process.ExitCode)
185  {
186  case 0:
187  return;
188  case 1:
189  if (!cancellationToken.IsCancellationRequested)
190  //just a restart
191  logger.LogInformation("Watchdog will restart host...");
192  else
193  logger.LogWarning("Host requested restart but watchdog shutdown is in progress!");
194  break;
195  case 2:
196  //update path is now an exception document
197  logger.LogCritical("Host crashed, propagating exception dump...");
198  var data = File.ReadAllText(updateDirectory);
199  try
200  {
201  File.Delete(updateDirectory);
202  }
203  catch (Exception e)
204  {
205  logger.LogWarning("Unable to delete exception dump file at {0}! Exception: {1}", updateDirectory, e);
206  }
207  throw new Exception(String.Format(CultureInfo.InvariantCulture, "Host propagated exception: {0}", data));
208  default:
209  if (iShotTheSheriff)
210  {
211  logger.LogWarning("Watchdog forced to kill host process!");
212  cancellationToken.ThrowIfCancellationRequested();
213  }
214  throw new Exception(String.Format(CultureInfo.InvariantCulture, "Host crashed with exit code {0}!", process.ExitCode));
215  }
216  }
217 
218  //HEY YOU
219  //BE WARNED THAT IF YOU DEBUGGED THE HOST PROCESS THAT JUST LAUNCHED THE DEBUGGER WILL HOLD A LOCK ON THE DIRECTORY
220  //THIS MEANS THE FIRST DIRECTORY.MOVE WILL THROW
221  if (Directory.Exists(updateDirectory))
222  {
223  logger.LogInformation("Applying server update...");
224  if (isWindows)
225  {
226  //windows dick sucking resource unlocking
227  GC.Collect();
228  await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken).ConfigureAwait(false);
229  }
230  var tempPath = Path.Combine(assemblyStoragePath, Guid.NewGuid().ToString());
231  try
232  {
233  Directory.Move(defaultAssemblyPath, tempPath);
234  try
235  {
236  Directory.Move(updateDirectory, defaultAssemblyPath);
237  logger.LogInformation("Server update complete, deleting old server...");
238  try
239  {
240  Directory.Delete(tempPath, true);
241  }
242  catch (Exception e)
243  {
244  logger.LogWarning("Error deleting old server at {0}! Exception: {1}", tempPath, e);
245  }
246  }
247  catch (Exception e)
248  {
249  logger.LogError("Error moving updated server directory, attempting revert! Exception: {0}", e);
250  Directory.Delete(defaultAssemblyPath, true);
251  Directory.Move(tempPath, defaultAssemblyPath);
252  logger.LogInformation("Revert successful!");
253  }
254  }
255  catch (Exception e)
256  {
257  logger.LogWarning("Failed to move out active host assembly! Exception: {0}", e);
258  }
259  }
260  }
261  }
262  catch (OperationCanceledException)
263  {
264  logger.LogDebug("Exiting due to cancellation...");
265  if (!Directory.Exists(updateDirectory))
266  File.Delete(updateDirectory);
267  else
268  Directory.Delete(updateDirectory, true);
269  }
270  catch (Exception e)
271  {
272  logger.LogCritical("Watchdog error! Exception: {0}", e);
273  }
274  finally
275  {
276  logger.LogInformation("Host watchdog exiting...");
277  }
278  }
279  }
280 }
+
async Task RunAsync(string[] args, CancellationToken cancellationToken)
Run the IWatchdog
Definition: Watchdog.cs:34
readonly ILogger< Watchdog > logger
The ILogger for the Watchdog
Definition: Watchdog.cs:22
-
The watchdog for a Host
Definition: IWatchdog.cs:9
diff --git a/_watchdog_2_watchdog_factory_8cs_source.html b/_watchdog_2_watchdog_factory_8cs_source.html index 6ab0804212..a3a82a1aea 100644 --- a/_watchdog_2_watchdog_factory_8cs_source.html +++ b/_watchdog_2_watchdog_factory_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
WatchdogFactory.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Diagnostics.CodeAnalysis;
4 using System.Runtime.InteropServices;
5 
6 namespace Tgstation.Server.Host.Watchdog
7 {
9  public sealed class WatchdogFactory : IWatchdogFactory
10  {
12  [ExcludeFromCodeCoverage]
13  public IWatchdog CreateWatchdog(ILoggerFactory loggerFactory) => new Watchdog(loggerFactory?.CreateLogger<Watchdog>() ?? throw new ArgumentNullException(nameof(loggerFactory)));
14  }
15 }
+Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Diagnostics.CodeAnalysis;
4 using System.Runtime.InteropServices;
5 
6 namespace Tgstation.Server.Host.Watchdog
7 {
9  public sealed class WatchdogFactory : IWatchdogFactory
10  {
12  [ExcludeFromCodeCoverage]
13  public IWatchdog CreateWatchdog(ILoggerFactory loggerFactory) => new Watchdog(loggerFactory?.CreateLogger<Watchdog>() ?? throw new ArgumentNullException(nameof(loggerFactory)));
14  }
15 }
+ -
The watchdog for a Host
Definition: IWatchdog.cs:9
diff --git a/_watchdog_launch_result_8cs_source.html b/_watchdog_launch_result_8cs_source.html index 5b4d772a86..fe886b50fa 100644 --- a/_watchdog_launch_result_8cs_source.html +++ b/_watchdog_launch_result_8cs_source.html @@ -92,9 +92,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
WatchdogLaunchResult.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Host.Components.Watchdog
4 {
8  public sealed class WatchdogLaunchResult
9  {
13  public LaunchResult Alpha { get; set; }
14 
18  public LaunchResult Bravo { get; set; }
19  }
20 }
Represents the result of trying to start a DD process
Definition: LaunchResult.cs:9
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Host.Components.Watchdog
4 {
8  public sealed class WatchdogLaunchResult
9  {
13  public LaunchResult Alpha { get; set; }
14 
18  public LaunchResult Bravo { get; set; }
19  }
20 }
Represents the result of trying to start a DD process
Definition: LaunchResult.cs:9
+ -
diff --git a/_watchdog_reattach_information_base_8cs_source.html b/_watchdog_reattach_information_base_8cs_source.html index 8a5b36feb6..7f4da40ca0 100644 --- a/_watchdog_reattach_information_base_8cs_source.html +++ b/_watchdog_reattach_information_base_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
WatchdogReattachInformationBase.cs
-Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Host.Models
4 {
8  public abstract class WatchdogReattachInformationBase
9  {
13  public bool AlphaIsActive { get; set; }
14 
19 
25  {
26  AlphaIsActive = copy?.AlphaIsActive ?? throw new ArgumentNullException(nameof(copy));
27  }
28  }
29 }
WatchdogReattachInformationBase()
Construct a WatchdogReattachInformationBase
+Go to the documentation of this file.
1 using System;
2 
3 namespace Tgstation.Server.Host.Models
4 {
8  public abstract class WatchdogReattachInformationBase
9  {
13  public bool AlphaIsActive { get; set; }
14 
19 
25  {
26  AlphaIsActive = copy?.AlphaIsActive ?? throw new ArgumentNullException(nameof(copy));
27  }
28  }
29 }
WatchdogReattachInformationBase()
Construct a WatchdogReattachInformationBase
+
WatchdogReattachInformationBase(WatchdogReattachInformationBase copy)
Construct a WatchdogReattachInformation from a given copy
-
diff --git a/_windows_byond_installer_8cs_source.html b/_windows_byond_installer_8cs_source.html index 21231cdc33..f0688612b8 100644 --- a/_windows_byond_installer_8cs_source.html +++ b/_windows_byond_installer_8cs_source.html @@ -92,12 +92,13 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
WindowsByondInstaller.cs
-Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Globalization;
4 using System.Text;
5 using System.Threading;
6 using System.Threading.Tasks;
8 using Tgstation.Server.Host.IO;
9 
10 namespace Tgstation.Server.Host.Components.Byond
11 {
16  {
20  const string ByondRevisionsURL = "https://secure.byond.com/download/build/{0}/{0}.{1}_byond.zip";
24  const string ByondConfigDir = "byond/cfg";
28  const string ByondDDConfig = "daemon.txt";
32  const string ByondNoPromptTrustedMode = "trusted-check 0";
36  const string ByondDXDir = "byond/directx";
37 
39  public string DreamDaemonName => "dreamdaemon.exe";
40 
42  public string DreamMakerName => "dm.exe";
43 
48 
53 
57  readonly ILogger<WindowsByondInstaller> logger;
58 
62  readonly SemaphoreSlim semaphore;
63 
68 
75  public WindowsByondInstaller(IIOManager ioManager, IProcessExecutor processExecutor, ILogger<WindowsByondInstaller> logger)
76  {
77  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
78  this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor));
79  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
80 
81  semaphore = new SemaphoreSlim(1);
82  installedDirectX = false;
83  }
84 
86  public void Dispose() => semaphore.Dispose();
87 
89  public async Task CleanCache(CancellationToken cancellationToken)
90  {
91  try
92  {
93  await ioManager.DeleteDirectory(ioManager.ConcatPath(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "byond/cache"), cancellationToken).ConfigureAwait(false);
94  }
95  catch (Exception e)
96  {
97  logger.LogWarning("Error deleting BYOND cache! Exception: {0}", e);
98  }
99  }
100 
102  public Task<byte[]> DownloadVersion(Version version, CancellationToken cancellationToken)
103  {
104  var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsURL, version.Major, version.Minor);
105 
106  return ioManager.DownloadFile(new Uri(url), cancellationToken);
107  }
108 
110  public async Task InstallByond(string path, Version version, CancellationToken cancellationToken)
111  {
112  async Task SetNoPromptTrusted()
113  {
114  var configPath = ioManager.ConcatPath(path, ByondConfigDir);
115  await ioManager.CreateDirectory(configPath, cancellationToken).ConfigureAwait(false);
116  await ioManager.WriteAllBytes(ioManager.ConcatPath(configPath, ByondDDConfig), Encoding.UTF8.GetBytes(ByondNoPromptTrustedMode), cancellationToken).ConfigureAwait(false);
117  };
118 
119  var setNoPromptTrustedModeTask = SetNoPromptTrusted();
120 
121  //after this version lummox made DD depend of directx lol
122  if (version.Major >= 512 && version.Minor >= 1427 && !installedDirectX)
123  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
124  if (!installedDirectX)
125  {
126  //always install it, it's pretty fast and will do better redundancy checking than us
127  var rbdx = ioManager.ConcatPath(path, ByondDXDir);
128  //noShellExecute because we aren't doing runas shennanigans
129  using (var p = processExecutor.LaunchProcess(ioManager.ConcatPath(rbdx, "DXSETUP.exe"), rbdx, "/silent", noShellExecute: true))
130  {
131  int exitCode;
132  using (cancellationToken.Register(() => p.Terminate()))
133  exitCode = await p.Lifetime.ConfigureAwait(false);
134  cancellationToken.ThrowIfCancellationRequested();
135 
136  if (exitCode != 0)
137  throw new JobException(String.Format(CultureInfo.InvariantCulture, "Failed to install included DirectX! Exit code: {0}", exitCode));
138  installedDirectX = true;
139  }
140  }
141 
142  await setNoPromptTrustedModeTask.ConfigureAwait(false);
143  }
144  }
145 }
readonly SemaphoreSlim semaphore
The SemaphoreSlim for the WindowsByondInstaller
+Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using System;
3 using System.Globalization;
4 using System.Text;
5 using System.Threading;
6 using System.Threading.Tasks;
8 using Tgstation.Server.Host.IO;
9 
10 namespace Tgstation.Server.Host.Components.Byond
11 {
16  {
20  const string ByondRevisionsURL = "https://secure.byond.com/download/build/{0}/{0}.{1}_byond.zip";
24  const string ByondConfigDir = "byond/cfg";
28  const string ByondDDConfig = "daemon.txt";
32  const string ByondNoPromptTrustedMode = "trusted-check 0";
36  const string ByondDXDir = "byond/directx";
37 
39  public string DreamDaemonName => "dreamdaemon.exe";
40 
42  public string DreamMakerName => "dm.exe";
43 
48 
53 
57  readonly ILogger<WindowsByondInstaller> logger;
58 
62  readonly SemaphoreSlim semaphore;
63 
68 
75  public WindowsByondInstaller(IIOManager ioManager, IProcessExecutor processExecutor, ILogger<WindowsByondInstaller> logger)
76  {
77  this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
78  this.processExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor));
79  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
80 
81  semaphore = new SemaphoreSlim(1);
82  installedDirectX = false;
83  }
84 
86  public void Dispose() => semaphore.Dispose();
87 
89  public async Task CleanCache(CancellationToken cancellationToken)
90  {
91  try
92  {
93  await ioManager.DeleteDirectory(ioManager.ConcatPath(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "byond/cache"), cancellationToken).ConfigureAwait(false);
94  }
95  catch (Exception e)
96  {
97  logger.LogWarning("Error deleting BYOND cache! Exception: {0}", e);
98  }
99  }
100 
102  public Task<byte[]> DownloadVersion(Version version, CancellationToken cancellationToken)
103  {
104  var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsURL, version.Major, version.Minor);
105 
106  return ioManager.DownloadFile(new Uri(url), cancellationToken);
107  }
108 
110  public async Task InstallByond(string path, Version version, CancellationToken cancellationToken)
111  {
112  async Task SetNoPromptTrusted()
113  {
114  var configPath = ioManager.ConcatPath(path, ByondConfigDir);
115  await ioManager.CreateDirectory(configPath, cancellationToken).ConfigureAwait(false);
116  await ioManager.WriteAllBytes(ioManager.ConcatPath(configPath, ByondDDConfig), Encoding.UTF8.GetBytes(ByondNoPromptTrustedMode), cancellationToken).ConfigureAwait(false);
117  };
118 
119  var setNoPromptTrustedModeTask = SetNoPromptTrusted();
120 
121  //after this version lummox made DD depend of directx lol
122  if (version.Major >= 512 && version.Minor >= 1427 && !installedDirectX)
123  using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false))
124  if (!installedDirectX)
125  {
126  //always install it, it's pretty fast and will do better redundancy checking than us
127  var rbdx = ioManager.ConcatPath(path, ByondDXDir);
128  //noShellExecute because we aren't doing runas shennanigans
129  using (var p = processExecutor.LaunchProcess(ioManager.ConcatPath(rbdx, "DXSETUP.exe"), rbdx, "/silent", noShellExecute: true))
130  {
131  int exitCode;
132  using (cancellationToken.Register(() => p.Terminate()))
133  exitCode = await p.Lifetime.ConfigureAwait(false);
134  cancellationToken.ThrowIfCancellationRequested();
135 
136  if (exitCode != 0)
137  throw new JobException(String.Format(CultureInfo.InvariantCulture, "Failed to install included DirectX! Exit code: {0}", exitCode));
138  installedDirectX = true;
139  }
140  }
141 
142  await setNoPromptTrustedModeTask.ConfigureAwait(false);
143  }
144  }
145 }
readonly SemaphoreSlim semaphore
The SemaphoreSlim for the WindowsByondInstaller
Task< byte[]> DownloadVersion(Version version, CancellationToken cancellationToken)
Download a given BYOND version
readonly IProcessExecutor processExecutor
The IProcessExecutor for the WindowsByondInstaller
Operation exceptions thrown from the context of a Models.Job
Definition: JobException.cs:8
readonly ILogger< WindowsByondInstaller > logger
The ILogger for the WindowsByondInstaller
+ @@ -105,7 +106,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
For downloading and installing BYOND extractions for a given system
async Task CleanCache(CancellationToken cancellationToken)
Attempts to cleans the BYOND cache folder for the system
-
WindowsByondInstaller(IIOManager ioManager, IProcessExecutor processExecutor, ILogger< WindowsByondInstaller > logger)
Construct a WindowsByondInstaller
async Task InstallByond(string path, Version version, CancellationToken cancellationToken)
Does actions necessary to get an extracted BYOND installation working
diff --git a/_windows_post_write_handler_8cs_source.html b/_windows_post_write_handler_8cs_source.html index 4a8b3fbbf9..7d0caa432d 100644 --- a/_windows_post_write_handler_8cs_source.html +++ b/_windows_post_write_handler_8cs_source.html @@ -92,8 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
WindowsPostWriteHandler.cs
-Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Core
2 {
7  {
9  public void HandleWrite(string filePath) { }
10  }
11 }
- +Go to the documentation of this file.
1 namespace Tgstation.Server.Host.Core
2 {
7  {
9  public void HandleWrite(string filePath) { }
10  }
11 }
+
void HandleWrite(string filePath)
For handling system specific necessities after a write
IPostWriteHandler for Windows systems
diff --git a/_windows_symlink_factory_8cs_source.html b/_windows_symlink_factory_8cs_source.html index 4d7f9fdf12..0fad3fd843 100644 --- a/_windows_symlink_factory_8cs_source.html +++ b/_windows_symlink_factory_8cs_source.html @@ -92,11 +92,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
WindowsSymlinkFactory.cs
-Go to the documentation of this file.
1 using System.ComponentModel;
2 using System.IO;
3 using System.Runtime.InteropServices;
4 using System.Threading;
5 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.IO
8 {
13  {
15  public Task CreateSymbolicLink(string targetPath, string linkPath, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
16  {
17  //check if its not a file
18  var flags = File.Exists(targetPath) ? 0 : 1; //SYMBOLIC_LINK_FLAG_DIRECTORY
19 #if DEBUG
20  flags |= 2; //SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE on win10 1607+ developer mode
21 #endif
22 
23  cancellationToken.ThrowIfCancellationRequested();
24  if (!NativeMethods.CreateSymbolicLink(linkPath, targetPath, flags))
25  throw new Win32Exception(Marshal.GetLastWin32Error());
26  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
27  }
28 }
+Go to the documentation of this file.
1 using System.ComponentModel;
2 using System.IO;
3 using System.Runtime.InteropServices;
4 using System.Threading;
5 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.IO
8 {
13  {
15  public Task CreateSymbolicLink(string targetPath, string linkPath, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
16  {
17  //check if its not a file
18  var flags = File.Exists(targetPath) ? 0 : 1; //SYMBOLIC_LINK_FLAG_DIRECTORY
19 #if DEBUG
20  flags |= 2; //SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE on win10 1607+ developer mode
21 #endif
22 
23  cancellationToken.ThrowIfCancellationRequested();
24  if (!NativeMethods.CreateSymbolicLink(linkPath, targetPath, flags))
25  throw new Win32Exception(Marshal.GetLastWin32Error());
26  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
27  }
28 }
+
static bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags)
See https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createsymboliclinkw ...
-
Native methods used by the code
Definition: NativeMethods.cs:9
diff --git a/_windows_system_identity_8cs_source.html b/_windows_system_identity_8cs_source.html index f3979a5396..7b44bfe7e3 100644 --- a/_windows_system_identity_8cs_source.html +++ b/_windows_system_identity_8cs_source.html @@ -92,12 +92,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
WindowsSystemIdentity.cs
-Go to the documentation of this file.
1 using System;
2 using System.DirectoryServices.AccountManagement;
3 using System.Security.Principal;
4 using System.Threading;
5 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Security
8 {
13  {
17  readonly WindowsIdentity identity;
18 
22  readonly UserPrincipal userPrincipal;
23 
28  public WindowsSystemIdentity(WindowsIdentity identity)
29  {
30  this.identity = identity ?? throw new ArgumentNullException(nameof(identity));
31  }
32 
37  public WindowsSystemIdentity(UserPrincipal userPrincipal)
38  {
39  this.userPrincipal = userPrincipal ?? throw new ArgumentNullException(nameof(userPrincipal));
40  }
41 
43  public void Dispose()
44  {
45  if (identity != null)
46  identity.Dispose();
47  else
48  {
49  var context = userPrincipal.Context;
50  userPrincipal.Dispose();
51  context.Dispose();
52  }
53  }
54 
56  public string Uid => (userPrincipal?.Sid ?? identity.User).ToString();
57 
59  public string Username => userPrincipal?.Name ?? identity.Name;
60 
63  {
64  if (identity != null)
65  return new WindowsSystemIdentity((WindowsIdentity)identity.Clone());
66  //can't clone a UP, shouldn't be trying to anyway, cloning is for impersonation
67  throw new InvalidOperationException("Cannot clone a UserPrincipal based WindowsSystemIdentity!");
68  }
69 
71  public Task RunImpersonated(Action action, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
72  {
73  if (action == null)
74  throw new ArgumentNullException(nameof(action));
75  if (identity == null)
76  throw new InvalidOperationException("Impersonate using a UserPrincipal based WindowsSystemIdentity!");
77  WindowsIdentity.RunImpersonated(identity.AccessToken, action);
78  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
79  }
80 }
+Go to the documentation of this file.
1 using System;
2 using System.DirectoryServices.AccountManagement;
3 using System.Security.Principal;
4 using System.Threading;
5 using System.Threading.Tasks;
6 
7 namespace Tgstation.Server.Host.Security
8 {
13  {
17  readonly WindowsIdentity identity;
18 
22  readonly UserPrincipal userPrincipal;
23 
28  public WindowsSystemIdentity(WindowsIdentity identity)
29  {
30  this.identity = identity ?? throw new ArgumentNullException(nameof(identity));
31  }
32 
37  public WindowsSystemIdentity(UserPrincipal userPrincipal)
38  {
39  this.userPrincipal = userPrincipal ?? throw new ArgumentNullException(nameof(userPrincipal));
40  }
41 
43  public void Dispose()
44  {
45  if (identity != null)
46  identity.Dispose();
47  else
48  {
49  var context = userPrincipal.Context;
50  userPrincipal.Dispose();
51  context.Dispose();
52  }
53  }
54 
56  public string Uid => (userPrincipal?.Sid ?? identity.User).ToString();
57 
59  public string Username => userPrincipal?.Name ?? identity.Name;
60 
63  {
64  if (identity != null)
65  return new WindowsSystemIdentity((WindowsIdentity)identity.Clone());
66  //can't clone a UP, shouldn't be trying to anyway, cloning is for impersonation
67  throw new InvalidOperationException("Cannot clone a UserPrincipal based WindowsSystemIdentity!");
68  }
69 
71  public Task RunImpersonated(Action action, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
72  {
73  if (action == null)
74  throw new ArgumentNullException(nameof(action));
75  if (identity == null)
76  throw new InvalidOperationException("Impersonate using a UserPrincipal based WindowsSystemIdentity!");
77  WindowsIdentity.RunImpersonated(identity.AccessToken, action);
78  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
79  }
80 }
+
Represents a user on the current System.Runtime.InteropServices.OSPlatform
WindowsSystemIdentity(WindowsIdentity identity)
Construct a WindowsSystemIdentity using a WindowsIdentity
-
WindowsSystemIdentity(UserPrincipal userPrincipal)
Construct a WindowsSystemIdentity using a UserPrincipal
ISystemIdentity Clone()
Clone the ISystemIdentity creating another copy that must have IDisposable.Dispose called on it ...
readonly WindowsIdentity identity
The WindowsIdentity for the WindowsSystemIdentity
diff --git a/_windows_system_identity_factory_8cs_source.html b/_windows_system_identity_factory_8cs_source.html index 3bc1ea336b..488593a880 100644 --- a/_windows_system_identity_factory_8cs_source.html +++ b/_windows_system_identity_factory_8cs_source.html @@ -92,7 +92,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
WindowsSystemIdentityFactory.cs
-Go to the documentation of this file.
1 using Microsoft.Win32.SafeHandles;
2 using System;
3 using System.DirectoryServices.AccountManagement;
4 using System.Runtime.InteropServices;
5 using System.Security.Principal;
6 using System.Threading;
7 using System.Threading.Tasks;
9 
10 namespace Tgstation.Server.Host.Security
11 {
16  {
18  public Task<ISystemIdentity> CreateSystemIdentity(User user, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
19  {
20  if (user == null)
21  throw new ArgumentNullException(nameof(user));
22 
23  if (user.SystemIdentifier == null)
24  throw new InvalidOperationException("User's SystemIdentifier must not be null!");
25 
26  PrincipalContext pc = null;
27  UserPrincipal principal = null;
28  //machine logon first cause it's faster
29  pc = new PrincipalContext(ContextType.Machine);
30  principal = UserPrincipal.FindByIdentity(pc, user.SystemIdentifier);
31  if (principal == null)
32  {
33  pc.Dispose();
34  //try domain now
35  try
36  {
37  pc = new PrincipalContext(ContextType.Domain);
38  principal = UserPrincipal.FindByIdentity(pc, user.SystemIdentifier);
39  }
40  catch (PrincipalServerDownException) { }
41 
42  if (principal == null)
43  {
44  pc?.Dispose();
45  return null;
46  }
47  }
48  return (ISystemIdentity)new WindowsSystemIdentity(principal);
49  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
50 
52  public Task<ISystemIdentity> CreateSystemIdentity(string username, string password, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
53  {
54  if (username == null)
55  throw new ArgumentNullException(nameof(username));
56  if (password == null)
57  throw new ArgumentNullException(nameof(password));
58  var splits = username.Split('\\');
59 
60  var res = NativeMethods.LogonUser(splits.Length > 1 ? splits[1] : splits[0], splits.Length > 1 ? splits[0] : null, password, 3 /*LOGON32_LOGON_NETWORK*/, 0 /*LOGON32_PROVIDER_DEFAULT*/, out var token);
61  if (!res)
62  return null;
63 
64  using (var handle = new SafeAccessTokenHandle(token)) //checked internally, windows identity always duplicates the handle when constructed with a userToken
65  return (ISystemIdentity)new WindowsSystemIdentity(new WindowsIdentity(handle.DangerousGetHandle())); //https://github.com/dotnet/corefx/blob/6ed61acebe3214fcf79b4274f2bb9b55c0604a4d/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs#L271
66  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
67  }
68 }
+Go to the documentation of this file.
1 using Microsoft.Win32.SafeHandles;
2 using System;
3 using System.DirectoryServices.AccountManagement;
4 using System.Runtime.InteropServices;
5 using System.Security.Principal;
6 using System.Threading;
7 using System.Threading.Tasks;
9 
10 namespace Tgstation.Server.Host.Security
11 {
16  {
18  public Task<ISystemIdentity> CreateSystemIdentity(User user, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
19  {
20  if (user == null)
21  throw new ArgumentNullException(nameof(user));
22 
23  if (user.SystemIdentifier == null)
24  throw new InvalidOperationException("User's SystemIdentifier must not be null!");
25 
26  PrincipalContext pc = null;
27  UserPrincipal principal = null;
28  //machine logon first cause it's faster
29  pc = new PrincipalContext(ContextType.Machine);
30  principal = UserPrincipal.FindByIdentity(pc, user.SystemIdentifier);
31  if (principal == null)
32  {
33  pc.Dispose();
34  //try domain now
35  try
36  {
37  pc = new PrincipalContext(ContextType.Domain);
38  principal = UserPrincipal.FindByIdentity(pc, user.SystemIdentifier);
39  }
40  catch (PrincipalServerDownException) { }
41 
42  if (principal == null)
43  {
44  pc?.Dispose();
45  return null;
46  }
47  }
48  return (ISystemIdentity)new WindowsSystemIdentity(principal);
49  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
50 
52  public Task<ISystemIdentity> CreateSystemIdentity(string username, string password, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
53  {
54  if (username == null)
55  throw new ArgumentNullException(nameof(username));
56  if (password == null)
57  throw new ArgumentNullException(nameof(password));
58  var splits = username.Split('\\');
59 
60  var res = NativeMethods.LogonUser(splits.Length > 1 ? splits[1] : splits[0], splits.Length > 1 ? splits[0] : null, password, 3 /*LOGON32_LOGON_NETWORK*/, 0 /*LOGON32_PROVIDER_DEFAULT*/, out var token);
61  if (!res)
62  return null;
63 
64  using (var handle = new SafeAccessTokenHandle(token)) //checked internally, windows identity always duplicates the handle when constructed with a userToken
65  return (ISystemIdentity)new WindowsSystemIdentity(new WindowsIdentity(handle.DangerousGetHandle())); //https://github.com/dotnet/corefx/blob/6ed61acebe3214fcf79b4274f2bb9b55c0604a4d/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs#L271
66  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
67  }
68 }
+
Represents a user on the current System.Runtime.InteropServices.OSPlatform
@@ -100,7 +101,6 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
static bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken)
See https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184(v=vs.85).aspx ...
-
Native methods used by the code
Definition: NativeMethods.cs:9
diff --git a/annotated.html b/annotated.html index a69ea21645..43a6ed9975 100644 --- a/annotated.html +++ b/annotated.html @@ -99,35 +99,38 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  NModels  NInternal  CChatBotManage the server chat bots - CCompileJobRepresents a run of DreamMaker - CDreamDaemonLaunchParametersLaunch settings for DreamDaemon - CDreamDaemonSettingsConfigurable settings for DreamDaemon - CJobRepresents a long running job - CRepositorySettingsRepresents configurable settings for a Repository - CRevisionInformationRepresents information about a current git revison - CTestMergeRepresents a merge of a GitHub pull request - CTestMergeBaseLayer of test merge data required internally - CUserRepresents a server User + CChatConnectionStringBuilderHelper for building ChatBot.ConnectionStrings + CCompileJobRepresents a run of DreamMaker + CDreamDaemonLaunchParametersLaunch settings for DreamDaemon + CDreamDaemonSettingsConfigurable settings for DreamDaemon + CJobRepresents a long running job + CRepositorySettingsRepresents configurable settings for a Repository + CRevisionInformationRepresents information about a current git revison + CTestMergeRepresents a merge of a GitHub pull request + CTestMergeBaseLayer of test merge data required internally + CUserRepresents a server User  CAdministration  CByondRepresents a BYOND installation  CChatBot  CChatChannelIndicates a chat channel  CCompileJob  CConfigurationFileRepresents a game configuration file. Create and delete actions uncerimonuously overwrite/delete files - CDreamDaemonRepresents an instance of BYOND's DreamDaemon game server. Create action starts the server. Delete action shuts down the server - CDreamMakerRepresents the state of the DreamMaker compiler. Create action starts a new compile. Delete action cancels the current compile - CErrorMessageRepresents an error message returned by the server - CInstanceMetadata about a server instance - CInstanceUserRepresents a Users permissions in an Instance - CJobRepresents a long running job on the server. Model is read-only, updates attempt to cancel the job - CRepositoryRepresents a git repository - CRevisionInformation - CServerInformationRepresents basic server information - CTestMerge - CTestMergeParametersParameters for creating a TestMerge - CTokenRepresents a JWT returned by the API - CUser - CUserUpdateFor editing a given User. Will never be returned by the API + CDiscordConnectionStringBuilderChatConnectionStringBuilder for ChatProvider.Discord + CDreamDaemonRepresents an instance of BYOND's DreamDaemon game server. Create action starts the server. Delete action shuts down the server + CDreamMakerRepresents the state of the DreamMaker compiler. Create action starts a new compile. Delete action cancels the current compile + CErrorMessageRepresents an error message returned by the server + CInstanceMetadata about a server instance + CInstanceUserRepresents a Users permissions in an Instance + CIrcConnectionStringBuilderChatConnectionStringBuilder for ChatProvider.Irc + CJobRepresents a long running job on the server. Model is read-only, updates attempt to cancel the job + CRepositoryRepresents a git repository + CRevisionInformation + CServerInformationRepresents basic server information + CTestMerge + CTestMergeParametersParameters for creating a TestMerge + CTokenRepresents a JWT returned by the API + CUser + CUserUpdateFor editing a given User. Will never be returned by the API  NRights  NTests  CTestRightsTests for Rights diff --git a/api.html b/api.html index c28e3b00fd..77768e45f1 100644 --- a/api.html +++ b/api.html @@ -260,15 +260,15 @@ Jobs

I DELETE "/Job/{JobId}" => OK

Chat Bots

-

Each chat bot is represented by a Tgstation.Server.Api.Models.ChatSettings object

+

Each chat bot is represented by a Tgstation.Server.Api.Models.ChatBot object

Chat bots can be created/updated/deleted with the following requests respectively

-

I PUT "/Chat" Tgstation.Server.Api.Models.ChatSettings => Tgstation.Server.Api.Models.ChatSettings I POST "/Chat" Tgstation.Server.Api.Models.ChatSettings => Tgstation.Server.Api.Models.ChatSettings I DELETE "/Chat/{ChatSettingsId}" => OK

-

The Tgstation.Server.Api.Models.Internal.ChatSettings.ConnectionString must differ based on what kind of chat bot you wish to create

-

For IRC chat bots it should be in the following format: "\<Server URL or IP address\>;\<Server Port\>;\<Bot nickname\>;\<1 to use SSL, 0 otherwise\>[;\<</tt>The @ref Tgstation.Server.Api.Models.IrcPasswordType<tt>;\<The password\>]"

+

I PUT "/Chat" Tgstation.Server.Api.Models.ChatBot => Tgstation.Server.Api.Models.ChatBot I POST "/Chat" Tgstation.Server.Api.Models.ChatBot => Tgstation.Server.Api.Models.ChatBot I DELETE "/Chat/{ChatBotId}" => OK

+

The Tgstation.Server.Api.Models.Internal.ChatBot.ConnectionString must differ based on what kind of chat bot you wish to create. Each Tgstation.Server.Api.Models.ChatProvider has a Tgstation.Server.Api.Models.Internal.ChatConnectionStringBuilder that dictates how to form it

+

For IRC chat bots see Tgstation.Server.Api.Models.IrcConnectionStringBuilder For Discord chat bots see Tgstation.Server.Api.Models.DiscordConnectionStringBuilder

For Discord chat bots it should be the bot's Token

A specific bot's settings may be retrieved with:

-

I GET "/Chat/{ChatSettingsId}" => Tgstation.Server.Api.Models.ChatSettings

-

Also note that if the Tgstation.Server.Api.Models.ChatSettings.Channels is present in a POST request, the list will fully replace any active channels

+

I GET "/Chat/{ChatBotId}" => Tgstation.Server.Api.Models.ChatBot

+

Also note that if the Tgstation.Server.Api.Models.ChatBot.Channels is present in a POST request, the list will fully replace any active channels

Byond Version Management

To get the Byond version used for new compilations use the following request:

diff --git a/class_auto_generated_program.html b/class_auto_generated_program.html index 180eeab0e0..126ab2dcb0 100644 --- a/class_auto_generated_program.html +++ b/class_auto_generated_program.html @@ -295,12 +295,12 @@ Static Private Member Functions

The documentation for this class was generated from the following files:
diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_chat_bot-members.html b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_chat_bot-members.html index 6377cd05ce..dc09db9f99 100644 --- a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_chat_bot-members.html +++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_chat_bot-members.html @@ -100,11 +100,12 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - - - - - + + + + + +
ChannelsTgstation.Server.Api.Models.ChatBot
ConnectionStringTgstation.Server.Api.Models.Internal.ChatBot
EnabledTgstation.Server.Api.Models.Internal.ChatBot
IdTgstation.Server.Api.Models.Internal.ChatBot
NameTgstation.Server.Api.Models.Internal.ChatBot
ProviderTgstation.Server.Api.Models.Internal.ChatBot
ValidateProviderChannelTypes()Tgstation.Server.Api.Models.ChatBot
ConnectionStringBuilderTgstation.Server.Api.Models.Internal.ChatBot
EnabledTgstation.Server.Api.Models.Internal.ChatBot
IdTgstation.Server.Api.Models.Internal.ChatBot
NameTgstation.Server.Api.Models.Internal.ChatBot
ProviderTgstation.Server.Api.Models.Internal.ChatBot
ValidateProviderChannelTypes()Tgstation.Server.Api.Models.ChatBot
diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_bot-members.html b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_bot-members.html index 6525e26906..fe3fda1f4b 100644 --- a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_bot-members.html +++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_bot-members.html @@ -99,10 +99,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');

This is the complete list of members for Tgstation.Server.Api.Models.Internal.ChatBot, including all inherited members.

- - - - + + + + +
ConnectionStringTgstation.Server.Api.Models.Internal.ChatBot
EnabledTgstation.Server.Api.Models.Internal.ChatBot
IdTgstation.Server.Api.Models.Internal.ChatBot
NameTgstation.Server.Api.Models.Internal.ChatBot
ProviderTgstation.Server.Api.Models.Internal.ChatBot
ConnectionStringBuilderTgstation.Server.Api.Models.Internal.ChatBot
EnabledTgstation.Server.Api.Models.Internal.ChatBot
IdTgstation.Server.Api.Models.Internal.ChatBot
NameTgstation.Server.Api.Models.Internal.ChatBot
ProviderTgstation.Server.Api.Models.Internal.ChatBot
+ +
+
+ + + + + +
+ + + + +
ChatConnectionStringBuilder Tgstation.Server.Api.Models.Internal.ChatBot.ConnectionStringBuilder
+
+getset
+
+ +

The ChatConnectionStringBuilder which maps to the ConnectionString

+ +

Definition at line 44 of file ChatBot.cs.

@@ -179,7 +206,7 @@ Properties

If the connection is enabled

-

Definition at line 24 of file ChatBot.cs.

+

Definition at line 26 of file ChatBot.cs.

Referenced by Tgstation.Server.Host.Components.Chat.Chat.ChangeSettings().

@@ -205,7 +232,7 @@ Properties

The settings id

-

Definition at line 13 of file ChatBot.cs.

+

Definition at line 15 of file ChatBot.cs.

Referenced by Tgstation.Server.Host.Components.Chat.Chat.ChangeSettings(), and Tgstation.Server.Client.Components.ChatBotsClient.ChatBotsClient().

@@ -231,7 +258,7 @@ Properties

The name of the connection

-

Definition at line 19 of file ChatBot.cs.

+

Definition at line 21 of file ChatBot.cs.

@@ -255,7 +282,7 @@ Properties

The ChatProvider used for the connection

-

Definition at line 29 of file ChatBot.cs.

+

Definition at line 31 of file ChatBot.cs.

diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_connection_string_builder-members.html b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_connection_string_builder-members.html new file mode 100644 index 0000000000..32f72415e5 --- /dev/null +++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_connection_string_builder-members.html @@ -0,0 +1,111 @@ + + + + + + +tgstation-server: Member List + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+
Tgstation.Server.Api.Models.Internal.ChatConnectionStringBuilder Member List
+
+ + + + + diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_connection_string_builder.html b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_connection_string_builder.html new file mode 100644 index 0000000000..1ac01442d4 --- /dev/null +++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_connection_string_builder.html @@ -0,0 +1,197 @@ + + + + + + +tgstation-server: Tgstation.Server.Api.Models.Internal.ChatConnectionStringBuilder Class Reference + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+ +
+
Tgstation.Server.Api.Models.Internal.ChatConnectionStringBuilder Class Referenceabstract
+
+
+ +

Helper for building ChatBot.ConnectionStrings + More...

+
+Inheritance diagram for Tgstation.Server.Api.Models.Internal.ChatConnectionStringBuilder:
+
+
+ + +Tgstation.Server.Api.Models.DiscordConnectionStringBuilder +Tgstation.Server.Api.Models.IrcConnectionStringBuilder + +
+ + + + + +

+Public Member Functions

abstract override string ToString ()
 Gets the ChatBot.ConnectionString associated with the ChatConnectionStringBuilder More...
 
+ + + + +

+Properties

abstract bool Valid [get]
 If the ChatConnectionStringBuilder evaluates to a valid ChatBot.ConnectionString More...
 
+

Detailed Description

+

Helper for building ChatBot.ConnectionStrings

+ +

Definition at line 6 of file ChatConnectionStringBuilder.cs.

+

Member Function Documentation

+ +
+
+ + + + + +
+ + + + + + + +
abstract override string Tgstation.Server.Api.Models.Internal.ChatConnectionStringBuilder.ToString ()
+
+pure virtual
+
+
+

Property Documentation

+ +
+
+ + + + + +
+ + + + +
abstract bool Tgstation.Server.Api.Models.Internal.ChatConnectionStringBuilder.Valid
+
+get
+
+ +

If the ChatConnectionStringBuilder evaluates to a valid ChatBot.ConnectionString

+ +

Definition at line 11 of file ChatConnectionStringBuilder.cs.

+ +
+
+
The documentation for this class was generated from the following file: +
+ + + + diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_connection_string_builder.png b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_connection_string_builder.png new file mode 100644 index 0000000000000000000000000000000000000000..9e68f5cf7d755cea3779fd017e6996f85a128eed GIT binary patch literal 1858 zcmbVNc~H~W7QP{}lqHtdB}4;l4Ynw}M5>iNPeCL^5d%UB7$Q6=t0+jI2uRDLKZz_( zP*r7d7d7s$a+=SuuoF*>k@hup=-D0s= zJ7dfYrg~Q?9$o+zZLR)i-qrx<^pISRc_$!LMg9!Ao#8feXY5a#=Ho+&h#gZW_Xb+$ zF7hV%IMxRkt2bJ2kf(YN7laj-(f~RY!NUM7AAl125nvaD0Itp9;A*QD$o#q=OyAH3 zXzfZwx3K{r=+|I+-yj|jT~Ar(R*hVFPyEiz(?GNicB>ZWOuC}+il9MeV7pa-JA+!v)fmt5x_)MBbH@%4Ac0!wkww-uQ^yb1 zv^Lb=nmiDnEqdalTPvy4F}Ub%G8x(T*E`HjBdq zX&spSB@(I#*~750khQG|CRwp5^YQlqL;V*FJ2k$VMbfb7ms!%~hWG^3u9 zH1|UxZ&oLBWaRj{M^IsP=+1x|?-#N#p+eg}vdDbhVQ&Xqk?csQl76&mfH>H;p^0v? zLu9N-V?J(bm*1~)RfJr&Ea4>u*HF(ld#&kQN$>tPZ@QvU(x9YDo1XX7Uy}%96J{)x z>kYTzzV1DF#Oa4$i1^K$y0vhjKCCouv=QqrmrZTDZovIK`=F0%XDf!5>!+?eUf;SF z)hIQa3R2JhzXz8dR=EGu)4hEL07(ZjxfUH5ipSw@62S8qz8Or4C2vW)(IPSxv8)_I z0UdFG5#qnPx8lEGh>xdYg#T%uiZOvFvD<(Q2s#5=-yO^Ybi*IC|LJRq5TmIEsnt}i z)I+>o;5>gD2&P+she7XvHnAVL`s6*3`Bfd5W|{*uG6K=P^Y0wc{9^F?+mw_e`4sn+ zPU=mHy9ttsGa=17*QRZ`G}Q;B^L#8W%rH`yG|PlpFx}pb;tMkja`YJMVUWF>6kV$# zbefSa%n=`L`2K1zkDIZO=COP`LV3$ozF-H9Awr_Sh`^;O+HbIAD+nn zWS>RWg$mv)T zY!+{Y=rWe=)~oWW`5fYWzpzWNBZ?gzt(t01vM^6B5Pf6D5^YWEiM%i5{ZU<_VF%*$ zt`b^W&U~R{bh5Fk&e8;#$?6Owdv~@xiiRfce9kf(pGEEur0iW%5qxv^tDJKejZNKb z4Q7VRYOQw-jt9%K8^$730mJvQ@l{yWtrAot(T3*W=NLcw9p6>+Q?)@r3cTvWJNl}=;HE+OvZUFqE+EXaWQ+s z?}~n}Xd#bC>=|fkfkM^&=~nVCRJ?yo+}zMTd4%~F`}1GFPvVwh2W%E*eH)$SR@d&n z+`Y3Lg}2D8pCeRTI=SaLM0{k7H$>JxdSM z?yR_AOoxuKU}g4;;Vr+86Qtqdo-l544)L(K@`EtT`s#n5^vjD7#r@Fz+tLjBpI{Sw Y9`L=-B^!Mv{NVx82@jX* + + + + + +tgstation-server: Member List + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+
+
Tgstation.Server.Api.Models.IrcConnectionStringBuilder Member List
+
+ + + + + diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_irc_connection_string_builder.html b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_irc_connection_string_builder.html new file mode 100644 index 0000000000..8ab4f157ec --- /dev/null +++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_irc_connection_string_builder.html @@ -0,0 +1,424 @@ + + + + + + +tgstation-server: Tgstation.Server.Api.Models.IrcConnectionStringBuilder Class Reference + + + + + + + + + + +
+
+ + + + + + + +
+
tgstation-server +
+
The /tg/station 13 server suite
+
+
+ + + + + + +
+
+ + +
+ +
+ + +
+
+ +
+
Tgstation.Server.Api.Models.IrcConnectionStringBuilder Class Reference
+
+
+ +

ChatConnectionStringBuilder for ChatProvider.Irc + More...

+
+Inheritance diagram for Tgstation.Server.Api.Models.IrcConnectionStringBuilder:
+
+
+ + +Tgstation.Server.Api.Models.Internal.ChatConnectionStringBuilder + +
+ + + + + + + + + + + +

+Public Member Functions

 IrcConnectionStringBuilder ()
 Construct an IrcConnectionStringBuilder More...
 
 IrcConnectionStringBuilder (string connectionString)
 Construct a DiscordConnectionStringBuilder from a connectionString More...
 
override string ToString ()
 Gets the ChatBot.ConnectionString associated with the ChatConnectionStringBuilder More...
 
+ + + +

+Public Attributes

override bool Valid => Address != null && Port.HasValue && Port != 0 && UseSsl.HasValue && (PasswordType.HasValue ^ Password == null)
 
+ + + + + + + + + + + + + + + + + + + + + + + +

+Properties

string Address [get, set]
 The IP address or URL of the IRC server More...
 
ushort Port [get, set]
 The port the server runs on More...
 
string Nickname [get, set]
 The nickname for the bot to use More...
 
bool UseSsl [get, set]
 If the connection should be made using SSL More...
 
IrcPasswordType PasswordType [get, set]
 The optional IrcPasswordType to use More...
 
string Password [get, set]
 The optional password to use More...
 
- Properties inherited from Tgstation.Server.Api.Models.Internal.ChatConnectionStringBuilder
abstract bool Valid [get]
 If the ChatConnectionStringBuilder evaluates to a valid ChatBot.ConnectionString More...
 
+

Detailed Description

+

ChatConnectionStringBuilder for ChatProvider.Irc

+ +

Definition at line 11 of file IrcConnectionStringBuilder.cs.

+

Constructor & Destructor Documentation

+ +
+
+ + + + + + + +
Tgstation.Server.Api.Models.IrcConnectionStringBuilder.IrcConnectionStringBuilder ()
+
+ +

Construct an IrcConnectionStringBuilder

+ +

Definition at line 49 of file IrcConnectionStringBuilder.cs.

+
49 { }
+
+
+ +
+
+ + + + + + + + +
Tgstation.Server.Api.Models.IrcConnectionStringBuilder.IrcConnectionStringBuilder (string connectionString)
+
+ +

Construct a DiscordConnectionStringBuilder from a connectionString

+
Parameters
+ + +
connectionStringThe connection string
+
+
+ +

Definition at line 55 of file IrcConnectionStringBuilder.cs.

+
56  {
57  if (connectionString == null)
58  throw new ArgumentNullException(nameof(connectionString));
59  var splits = connectionString.Split(';');
60 
61  Address = splits[0];
62 
63  if (splits.Length < 2)
64  return;
65 
66  if (UInt16.TryParse(splits[1], out var port))
67  Port = port;
68 
69  if (splits.Length < 3)
70  return;
71 
72  Nickname = splits[2];
73 
74  if (splits.Length < 4)
75  return;
76 
77  if (Boolean.TryParse(splits[3], out var useSsl))
78  UseSsl = useSsl;
79 
80  if (splits.Length < 5)
81  return;
82  if (Enum.TryParse<IrcPasswordType>(splits[4], out var passwordType))
83  switch (passwordType)
84  {
85  case IrcPasswordType.NickServ:
86  case IrcPasswordType.Sasl:
87  case IrcPasswordType.Server:
88  PasswordType = passwordType;
89  break;
90  }
91 
92  if (splits.Length < 6)
93  return;
94 
95  var rest = new List<string>(splits);
96  rest.RemoveRange(0, 5);
97  Password = String.Join(";", rest);
98  }
string Address
The IP address or URL of the IRC server
+ +
bool UseSsl
If the connection should be made using SSL
+ +
IrcPasswordType
Represents the type of a password for a ChatProvider.Irc
+
IrcPasswordType PasswordType
The optional IrcPasswordType to use
+ +
+
+
+

Member Function Documentation

+ +
+
+ + + + + +
+ + + + + + + +
override string Tgstation.Server.Api.Models.IrcConnectionStringBuilder.ToString ()
+
+virtual
+
+ +

Gets the ChatBot.ConnectionString associated with the ChatConnectionStringBuilder

+ +

Implements Tgstation.Server.Api.Models.Internal.ChatConnectionStringBuilder.

+ +

Definition at line 101 of file IrcConnectionStringBuilder.cs.

+
102  {
103  var sb = new StringBuilder();
104  sb.Append(Address);
105  sb.Append(';');
106  sb.Append(Port);
107  sb.Append(';');
108  sb.Append(Nickname);
109  sb.Append(';');
110  if(UseSsl.HasValue)
111  sb.Append(Convert.ToInt32(UseSsl.Value));
112  if (PasswordType.HasValue)
113  {
114  sb.Append(';');
115  sb.Append((int)PasswordType);
116  sb.Append(';');
117  sb.Append(Password);
118  }
119  return sb.ToString();
120  }
string Address
The IP address or URL of the IRC server
+ +
bool UseSsl
If the connection should be made using SSL
+ +
IrcPasswordType PasswordType
The optional IrcPasswordType to use
+ +
+
+
+

Member Data Documentation

+ +
+
+ + + + +
override bool Tgstation.Server.Api.Models.IrcConnectionStringBuilder.Valid => Address != null && Port.HasValue && Port != 0 && UseSsl.HasValue && (PasswordType.HasValue ^ Password == null)
+
+ +

+ +

Definition at line 14 of file IrcConnectionStringBuilder.cs.

+ +
+
+

Property Documentation

+ +
+
+ + + + + +
+ + + + +
string Tgstation.Server.Api.Models.IrcConnectionStringBuilder.Address
+
+getset
+
+ +

The IP address or URL of the IRC server

+ +

Definition at line 19 of file IrcConnectionStringBuilder.cs.

+ +
+
+ +
+
+ + + + + +
+ + + + +
string Tgstation.Server.Api.Models.IrcConnectionStringBuilder.Nickname
+
+getset
+
+ +

The nickname for the bot to use

+ +

Definition at line 29 of file IrcConnectionStringBuilder.cs.

+ +
+
+ +
+
+ + + + + +
+ + + + +
string Tgstation.Server.Api.Models.IrcConnectionStringBuilder.Password
+
+getset
+
+ +

The optional password to use

+ +

Definition at line 44 of file IrcConnectionStringBuilder.cs.

+ +
+
+ +
+
+ + + + + +
+ + + + +
IrcPasswordType Tgstation.Server.Api.Models.IrcConnectionStringBuilder.PasswordType
+
+getset
+
+ +

The optional IrcPasswordType to use

+ +

Definition at line 39 of file IrcConnectionStringBuilder.cs.

+ +
+
+ +
+
+ + + + + +
+ + + + +
ushort Tgstation.Server.Api.Models.IrcConnectionStringBuilder.Port
+
+getset
+
+ +

The port the server runs on

+ +

Definition at line 24 of file IrcConnectionStringBuilder.cs.

+ +
+
+ +
+
+ + + + + +
+ + + + +
bool Tgstation.Server.Api.Models.IrcConnectionStringBuilder.UseSsl
+
+getset
+
+ +

If the connection should be made using SSL

+ +

Definition at line 34 of file IrcConnectionStringBuilder.cs.

+ +
+
+
The documentation for this class was generated from the following file: +
+ + + + diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_irc_connection_string_builder.png b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_irc_connection_string_builder.png new file mode 100644 index 0000000000000000000000000000000000000000..e379afcf8f1b5de6b5cd808d6ec0de85a74c47d8 GIT binary patch literal 1370 zcmeAS@N?(olHy`uVBq!ia0y~yU~B@i12~w0r1H`xZ6GBP;1lBd|Nnm=^TnI5rTvE{ z09jys;J^Xa&O7ozE=Ng_UoZnu5eQs86=KA|!1B`5#WAFU@$KB#c~2C0-1z&he6N2p zSB1x6IUC=@v5iL@MwB}Rms|Qk;}6#ch$aMyD?+`Z0}pATwk31et6-#@X|PA ztGIL-za*We+Q5m6uS0wfhYk2Dk?xN82b=nt7UrgsX6Kk;Svzy|p?Pa@PeAAA< z|Mc_8@a=oHU77vm>!#>p?c~j=;+F+A`)V0;9c7)OS7qs3%YS|P!t=I!zn<;fb>oWv zUGryI4hyn(cbr)MCGoS8(#9%@1+EF}qXZ|Kzuxcmq4|&R@&`7mN=ovV{?~HNdwyQq z1)Lu07r7`X39UjS#G7(&Z@U6aITIM_w(nyIVhHqNVVL;qmlqpDBZyTc=)uNN2xJ}f zVqjPZq-lzO{pIk|R1Njg3|z#pgdu4P2ZPg8BwWhwxOYe6KTztuf6Rt6VPWEvpMkgC zv=5)+W+`ER}d_4wDvJQq_wsWq{mu3r4=%hJ}H z|Mok4ICXTN;XYewV0t~iER!?4?fl(Owrk#LUp4tr=(ql$221I&9Wx#kuUq%~%crwN zTQA4wv7Bv{J)F4XY;MlE?;ox17CvT!V9pp`sk_u?f2)lHk`N1Pp#+7 z-4(TMUGR(Z3GtQr&$nsFN}pn{u47EIb-B>|y0O|ZlY`X&luUy|Wm!I-o9%0{Z2I~_ z;cV$^)t`f=AJyVEFa9mjSoGFrnK#Si^`@(ztt)<%xw3G3ZuCt{kL>~_%-Jkvv-$Qt z_1tt-AYtKrg&jIBYMi_DUiR8P^Nilxdcf!RMa_b~PgaNd1!L}fW@x@wC;o$jt?IMm zTaIU+>{zQx#UpNqi@vaG@@9FaC$ag{NvA98e^61O z#4NhZaQ14eOFH?!e6@KjYxC!=?)LqAr8fBlgZQq_0~;8AblBy{J

Definition at line 27 of file ChatBotsClient.cs.

-

References Tgstation.Server.Api.Routes.Chat, Tgstation.Server.Api.Models.Internal.ChatBot.Id, Tgstation.Server.Api.Routes.List(), and Tgstation.Server.Api.Routes.SetID().

+

References Tgstation.Server.Api.Routes.Chat, Tgstation.Server.Api.Models.Internal.ChatBot.Id, Tgstation.Server.Api.Routes.List(), and Tgstation.Server.Api.Routes.SetID().

28  {
29  this.apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
30  this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
31  }
readonly IApiClient apiClient
The IApiClient for the ChatBotsClient
diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_chat.html b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_chat.html index 2dc3c20ff4..75cc3d48de 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_chat.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_chat.html @@ -368,7 +368,7 @@ Private Attributes

Definition at line 395 of file Chat.cs.

-

References Tgstation.Server.Host.Components.Chat.Providers.IProvider.Disconnect(), Tgstation.Server.Api.Models.Internal.ChatBot.Enabled, and Tgstation.Server.Api.Models.Internal.ChatBot.Id.

+

References Tgstation.Server.Host.Components.Chat.Providers.IProvider.Disconnect(), Tgstation.Server.Api.Models.Internal.ChatBot.Enabled, and Tgstation.Server.Api.Models.Internal.ChatBot.Id.

396  {
397  if (newSettings == null)
398  throw new ArgumentNullException(nameof(newSettings));
399  IProvider provider;
400 
401  async Task DisconnectProvider(IProvider p)
402  {
403  try
404  {
405  await p.Disconnect(cancellationToken).ConfigureAwait(false);
406  }
407  finally
408  {
409  p.Dispose();
410  }
411  }
412 
413  Task disconnectTask;
414  lock (providers)
415  {
416  //raw settings changes forces a rebuild of the provider
417  if (providers.TryGetValue(newSettings.Id, out provider))
418  {
419  providers.Remove(newSettings.Id);
420  disconnectTask = DisconnectProvider(provider);
421  }
422  else
423  disconnectTask = Task.CompletedTask;
424  if (newSettings.Enabled.Value)
425  {
426  provider = providerFactory.CreateProvider(newSettings);
427  providers.Add(newSettings.Id, provider);
428  }
429  }
430 
431  lock (mappedChannels)
432  foreach (var I in mappedChannels.Where(x => x.Value.ProviderId == newSettings.Id).Select(x => x.Key).ToList())
433  mappedChannels.Remove(I);
434 
435  await disconnectTask.ConfigureAwait(false);
436 
437  if (started)
438  {
439  if (newSettings.Enabled.Value)
440  await provider.Connect(cancellationToken).ConfigureAwait(false);
441  lock (this)
442  {
443  //same thread shennanigans
444  var oldOne = connectionsUpdated;
445  connectionsUpdated = new TaskCompletionSource<object>();
446  oldOne.SetResult(null);
447  }
448  }
449  }
Task Disconnect(CancellationToken cancellationToken)
Gracefully disconnects the provider. Implies a call to IDisposable.Dispose
bool started
If StartAsync(CancellationToken) has been called
Definition: Chat.cs:94
readonly Dictionary< long, IProvider > providers
Map of IProviders in use, keyed by ChatBot.Id
Definition: Chat.cs:49
@@ -376,9 +376,9 @@ Private Attributes
IProvider CreateProvider(ChatBot settings)
Create a IProvider
readonly IProviderFactory providerFactory
The IProviderFactory for the Chat
Definition: Chat.cs:24
TaskCompletionSource< object > connectionsUpdated
The TaskCompletionSource<TResult> that completes when ChatBots change
Definition: Chat.cs:84
- +
readonly Dictionary< ulong, ChannelMapping > mappedChannels
Map of Channel.RealIds to ChannelMappings
Definition: Chat.cs:54
-
bool Enabled
If the connection is enabled
Definition: ChatBot.cs:24
+
bool Enabled
If the connection is enabled
Definition: ChatBot.cs:26
diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_provider_factory.html b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_provider_factory.html index fa4b1d517d..1e6a06e72e 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_provider_factory.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_provider_factory.html @@ -193,11 +193,12 @@ Private Attributes

Definition at line 36 of file ProviderFactory.cs.

-
37  {
38  if (settings == null)
39  throw new ArgumentNullException(nameof(settings));
40  switch (settings.Provider)
41  {
42  case ChatProvider.Irc:
43  //Connection string semicolon delimited until the password field
44  if (settings.ConnectionString == null)
45  throw new InvalidOperationException("ConnectionString cannot be null!");
46  var splits = settings.ConnectionString.Split(';');
47  if (splits.Length < 4)
48  throw new InvalidOperationException("Invalid connection string!");
49 
50  var address = splits[0];
51  if (!UInt16.TryParse(splits[1], out var port))
52  throw new InvalidOperationException("Unable to parse port!");
53  var nick = splits[2];
54  if (!Int32.TryParse(splits[3], out var intSsl))
55  throw new InvalidOperationException("Unable to parse ssl option!");
56 
57  IrcPasswordType? passwordType = null;
58  string password = null;
59  if (splits.Length > 4)
60  {
61  if (splits.Length < 6)
62  throw new InvalidOperationException("Invalid connection string!");
63  if (!Int32.TryParse(splits[4], out var intPasswordType))
64  throw new InvalidOperationException("Unable to parse password type!");
65 
66  passwordType = (IrcPasswordType)intPasswordType;
67  switch (passwordType)
68  {
69  case IrcPasswordType.NickServ:
70  case IrcPasswordType.Sasl:
71  case IrcPasswordType.Server:
72  break;
73  default:
74  throw new InvalidOperationException("Invalid password type!");
75  }
76 
77  var rest = new List<string>(splits);
78  rest.RemoveRange(0, 5);
79  password = String.Join(";", rest);
80  }
81 
82  return new IrcProvider(loggerFactory.CreateLogger<IrcProvider>(), application, address, port, nick, password, passwordType, intSsl != 0);
83  case ChatProvider.Discord:
84  //discord is just the bot token
85  return new DiscordProvider(loggerFactory.CreateLogger<DiscordProvider>(), settings.ConnectionString);
86  default:
87  throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid ChatProvider: {0}", settings.Provider));
88  }
89  }
readonly IApplication application
The IApplication for the ProviderFactory
+
37  {
38  if (settings == null)
39  throw new ArgumentNullException(nameof(settings));
40  var builder = settings.ConnectionStringBuilder;
41  if (builder == null || !builder.Valid)
42  throw new InvalidOperationException("Invalid ChatConnectionStringBuilder!");
43  switch (settings.Provider)
44  {
45  case ChatProvider.Irc:
46  var ircBuilder = (IrcConnectionStringBuilder)builder;
47  return new IrcProvider(loggerFactory.CreateLogger<IrcProvider>(), application, ircBuilder.Address, ircBuilder.Port.Value, ircBuilder.Nickname, ircBuilder.Password, ircBuilder.PasswordType, ircBuilder.UseSsl.Value);
48  case ChatProvider.Discord:
49  var discordBuilder = (DiscordConnectionStringBuilder)builder;
50  return new DiscordProvider(loggerFactory.CreateLogger<DiscordProvider>(), discordBuilder.BotToken);
51  default:
52  throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid ChatProvider: {0}", settings.Provider));
53  }
54  }
readonly IApplication application
The IApplication for the ProviderFactory
-
IrcPasswordType
Represents the type of a password passed to the constructor of IrcProvider
+
ChatConnectionStringBuilder for ChatProvider.Discord
readonly ILoggerFactory loggerFactory
The ILoggerFactory for the ProviderFactory
+
ChatConnectionStringBuilder for ChatProvider.Irc
ChatProvider
Represents a chat service provider
Definition: ChatProvider.cs:6
diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider.html b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider.html index 2941194557..e79db92612 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider.html @@ -118,7 +118,7 @@ Inheritance diagram for Tgstation.Server.Host.Components.Chat.Providers.IrcProvi - + @@ -184,8 +184,8 @@ Private Attributes - - + + @@ -273,7 +273,7 @@ Additional Inherited Members - + @@ -902,7 +902,7 @@ Additional Inherited Members @@ -912,7 +912,7 @@ Additional Inherited Members

Public Member Functions

 IrcProvider (ILogger< IrcProvider > logger, IApplication application, string address, ushort port, string nickname, string password, IrcPasswordType?passwordType, bool useSsl)
 IrcProvider (ILogger< IrcProvider > logger, IApplication application, string address, ushort port, string nickname, string password, IrcPasswordType?passwordType, bool useSsl)
 Construct an IrcProvider More...
 
override void Dispose ()
readonly string password
 Password which will used for authentication More...
 
readonly IrcPasswordType passwordType
 The IrcPasswordType of password More...
readonly IrcPasswordType passwordType
 The IrcPasswordType of password More...
 
readonly Dictionary< ulong, string > channelIdMap
 Map of Channel.RealIds to channel names More...
IrcPasswordTypeIrcPasswordType passwordType,
- +
readonly IrcPasswordType Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.passwordTypereadonly IrcPasswordType Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.passwordType
-

The IrcPasswordType of password

+

The IrcPasswordType of password

Definition at line 57 of file IrcProvider.cs.

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_chat_controller.html b/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_chat_controller.html index 52b4d3014f..3aa57eefe2 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_chat_controller.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_controllers_1_1_chat_controller.html @@ -276,14 +276,14 @@ Private Attributes

Definition at line 61 of file ChatController.cs.

References Tgstation.Server.Host.Models.DatabaseContext< TParentContext >.ChatBots, Tgstation.Server.Api.Models.Instance.Id, Tgstation.Server.Api.Rights.List, Tgstation.Server.Api.Models.ErrorMessage.Message, and Tgstation.Server.Host.Models.DatabaseContext< TParentContext >.Save().

-
62  {
63  if (model == null)
64  throw new ArgumentNullException(nameof(model));
65 
66  if (String.IsNullOrWhiteSpace(model.Name))
67  return BadRequest(new ErrorMessage { Message = "name cannot be null or whitespace!" });
68 
69  if (String.IsNullOrWhiteSpace(model.ConnectionString))
70  return BadRequest(new ErrorMessage { Message = "connection_string cannot be null or whitespace!" });
71 
72  if (!model.Provider.HasValue)
73  return BadRequest(new ErrorMessage { Message = "provider cannot be null!" });
74 
75  switch (model.Provider)
76  {
77  case ChatProvider.Discord:
78  case ChatProvider.Irc:
79  break;
80  default:
81  return BadRequest(new ErrorMessage { Message = "Invalid provider!" });
82  }
83 
84  if (!model.Enabled.HasValue)
85  return BadRequest(new ErrorMessage { Message = "enabled cannot be null!" });
86 
87  if (!model.ValidateProviderChannelTypes())
88  return BadRequest(new ErrorMessage { Message = "One or more of channels aren't formatted correctly for the given provider!" });
89 
90  //try to update das db first
91  var dbModel = new Models.ChatBot
92  {
93  Name = model.Name,
94  ConnectionString = model.ConnectionString,
95  Enabled = model.Enabled,
96  Channels = model.Channels?.Select(x => ConvertApiChatChannel(x)).ToList() ?? new List<Models.ChatChannel>(), //important that this isn't null
97  InstanceId = Instance.Id,
98  Provider = model.Provider,
99  };
100 
101  DatabaseContext.ChatBots.Add(dbModel);
102 
103  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
104 
105  try
106  {
107  try
108  {
109  //try to create it
110  var instance = instanceManager.GetInstance(Instance);
111  await instance.Chat.ChangeSettings(dbModel, cancellationToken).ConfigureAwait(false);
112 
113  if (dbModel.Channels.Count > 0)
114  await instance.Chat.ChangeChannels(dbModel.Id, dbModel.Channels, cancellationToken).ConfigureAwait(false);
115  }
116  catch
117  {
118  //undo the add
119  DatabaseContext.ChatBots.Remove(dbModel);
120  await DatabaseContext.Save(default).ConfigureAwait(false);
121  throw;
122  }
123  }
124  catch (InvalidOperationException e)
125  {
126  return BadRequest(new ErrorMessage { Message = e.Message });
127  }
128  return StatusCode((int)HttpStatusCode.Created, dbModel.ToApi());
129  }
IInstance GetInstance(Models.Instance metadata)
Get the IInstance associated with given metadata
+
62  {
63  if (model == null)
64  throw new ArgumentNullException(nameof(model));
65 
66  if (String.IsNullOrWhiteSpace(model.Name))
67  return BadRequest(new ErrorMessage { Message = "name cannot be null or whitespace!" });
68 
69  if (String.IsNullOrWhiteSpace(model.ConnectionString))
70  return BadRequest(new ErrorMessage { Message = "connection_string cannot be null or whitespace!" });
71 
72  if (!model.Provider.HasValue)
73  return BadRequest(new ErrorMessage { Message = "provider cannot be null!" });
74 
75  switch (model.Provider)
76  {
77  case ChatProvider.Discord:
78  case ChatProvider.Irc:
79  break;
80  default:
81  return BadRequest(new ErrorMessage { Message = "Invalid provider!" });
82  }
83 
84  if (!model.ValidateProviderChannelTypes())
85  return BadRequest(new ErrorMessage { Message = "One or more of channels aren't formatted correctly for the given provider!" });
86 
87  model.Enabled = model.Enabled ?? false;
88 
89  //try to update das db first
90  var dbModel = new Models.ChatBot
91  {
92  Name = model.Name,
93  ConnectionString = model.ConnectionString,
94  Enabled = model.Enabled,
95  Channels = model.Channels?.Select(x => ConvertApiChatChannel(x)).ToList() ?? new List<Models.ChatChannel>(), //important that this isn't null
96  InstanceId = Instance.Id,
97  Provider = model.Provider,
98  };
99 
100  DatabaseContext.ChatBots.Add(dbModel);
101 
102  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
103 
104  try
105  {
106  try
107  {
108  //try to create it
109  var instance = instanceManager.GetInstance(Instance);
110  await instance.Chat.ChangeSettings(dbModel, cancellationToken).ConfigureAwait(false);
111 
112  if (dbModel.Channels.Count > 0)
113  await instance.Chat.ChangeChannels(dbModel.Id, dbModel.Channels, cancellationToken).ConfigureAwait(false);
114  }
115  catch
116  {
117  //undo the add
118  DatabaseContext.ChatBots.Remove(dbModel);
119  await DatabaseContext.Save(default).ConfigureAwait(false);
120  throw;
121  }
122  }
123  catch (InvalidOperationException e)
124  {
125  return BadRequest(new ErrorMessage { Message = e.Message });
126  }
127  return StatusCode((int)HttpStatusCode.Created, dbModel.ToApi());
128  }
IInstance GetInstance(Models.Instance metadata)
Get the IInstance associated with given metadata
Task Save(CancellationToken cancellationToken)
Saves changes made to the IDatabaseContext
IChat Chat
The IChat for the IInstance
Definition: IInstance.cs:37
string Message
A human readable description of the error
Definition: ErrorMessage.cs:13
long Id
The id of the Instance. Not modifiable
Definition: Instance.cs:14
-
override async Task< IActionResult > List(CancellationToken cancellationToken)
Attempt to list entries of the TModel
+
override async Task< IActionResult > List(CancellationToken cancellationToken)
Attempt to list entries of the TModel
Metadata about a server instance
Definition: Instance.cs:9
static Models.ChatChannel ConvertApiChatChannel(Api.Models.ChatChannel api)
Converts api to a ChatChannel
readonly IInstanceManager instanceManager
The IInstanceManager for the ChatController
@@ -328,10 +328,10 @@ Private Attributes

Reimplemented from Tgstation.Server.Host.Controllers.ModelController< Api.Models.ChatBot >.

-

Definition at line 133 of file ChatController.cs.

+

Definition at line 132 of file ChatController.cs.

References Tgstation.Server.Host.Models.DatabaseContext< TParentContext >.ChatBots.

-
134  {
135  var instance = instanceManager.GetInstance(Instance);
136  await Task.WhenAll(instance.Chat.DeleteConnection(id, cancellationToken), DatabaseContext.ChatBots.Where(x => x.Id == id).DeleteAsync(cancellationToken)).ConfigureAwait(false);
137 
138  return Ok();
139  }
IInstance GetInstance(Models.Instance metadata)
Get the IInstance associated with given metadata
+
133  {
134  var instance = instanceManager.GetInstance(Instance);
135  await Task.WhenAll(instance.Chat.DeleteConnection(id, cancellationToken), DatabaseContext.ChatBots.Where(x => x.Id == id).DeleteAsync(cancellationToken)).ConfigureAwait(false);
136 
137  return Ok();
138  }
IInstance GetInstance(Models.Instance metadata)
Get the IInstance associated with given metadata
Metadata about a server instance
Definition: Instance.cs:9
@@ -375,10 +375,10 @@ Private Attributes

Reimplemented from Tgstation.Server.Host.Controllers.ModelController< Api.Models.ChatBot >.

-

Definition at line 160 of file ChatController.cs.

+

Definition at line 159 of file ChatController.cs.

References Tgstation.Server.Host.Models.DatabaseContext< TParentContext >.ChatBots, and Tgstation.Server.Host.Security.AuthenticationContext.GetRight().

-
161  {
162  var query = DatabaseContext.ChatBots.Where(x => x.Id == id).Include(x => x.Channels);
163 
164  var results = await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
165  if (results == default)
166  return StatusCode((int)HttpStatusCode.Gone);
167 
168  var connectionStrings = (AuthenticationContext.GetRight(RightsType.ChatBots) & (ulong)ChatBotRights.ReadConnectionString) != 0;
169 
170  if (!connectionStrings)
171  results.ConnectionString = null;
172 
173  return Json(results.ToApi());
174  }
+
160  {
161  var query = DatabaseContext.ChatBots.Where(x => x.Id == id).Include(x => x.Channels);
162 
163  var results = await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
164  if (results == default)
165  return StatusCode((int)HttpStatusCode.Gone);
166 
167  var connectionStrings = (AuthenticationContext.GetRight(RightsType.ChatBots) & (ulong)ChatBotRights.ReadConnectionString) != 0;
168 
169  if (!connectionStrings)
170  results.ConnectionString = null;
171 
172  return Json(results.ToApi());
173  }
RightsType
The type of rights a model uses
Definition: RightsType.cs:6
ulong GetRight(RightsType rightsType)
Get the value of a given rightsType
@@ -413,10 +413,10 @@ Private Attributes

Reimplemented from Tgstation.Server.Host.Controllers.ModelController< Api.Models.ChatBot >.

-

Definition at line 143 of file ChatController.cs.

+

Definition at line 142 of file ChatController.cs.

References Tgstation.Server.Host.Models.DatabaseContext< TParentContext >.ChatBots, Tgstation.Server.Host.Security.AuthenticationContext.GetRight(), and Tgstation.Server.Api.Models.Instance.Id.

-
144  {
145  var query = DatabaseContext.ChatBots.Where(x => x.InstanceId == Instance.Id).Include(x => x.Channels);
146 
147  var results = await query.ToListAsync(cancellationToken).ConfigureAwait(false);
148 
149  var connectionStrings = (AuthenticationContext.GetRight(RightsType.ChatBots) & (ulong)ChatBotRights.ReadConnectionString) != 0;
150 
151  if (!connectionStrings)
152  foreach (var I in results)
153  I.ConnectionString = null;
154 
155  return Json(results.Select(x => x.ToApi()));
156  }
+
143  {
144  var query = DatabaseContext.ChatBots.Where(x => x.InstanceId == Instance.Id).Include(x => x.Channels);
145 
146  var results = await query.ToListAsync(cancellationToken).ConfigureAwait(false);
147 
148  var connectionStrings = (AuthenticationContext.GetRight(RightsType.ChatBots) & (ulong)ChatBotRights.ReadConnectionString) != 0;
149 
150  if (!connectionStrings)
151  foreach (var I in results)
152  I.ConnectionString = null;
153 
154  return Json(results.Select(x => x.ToApi()));
155  }
RightsType
The type of rights a model uses
Definition: RightsType.cs:6
ulong GetRight(RightsType rightsType)
Get the value of a given rightsType
@@ -453,10 +453,10 @@ Private Attributes

-

Definition at line 178 of file ChatController.cs.

+

Definition at line 177 of file ChatController.cs.

References Tgstation.Server.Host.Models.DatabaseContext< TParentContext >.ChatBots, Tgstation.Server.Host.Models.DatabaseContext< TParentContext >.ChatChannels, Tgstation.Server.Host.Security.AuthenticationContext.GetRight(), Tgstation.Server.Api.Models.Instance.Id, and Tgstation.Server.Host.Models.DatabaseContext< TParentContext >.Save().

-
179  {
180  if (model == null)
181  throw new ArgumentNullException(nameof(model));
182 
183  if (model.Provider.HasValue && !model.ValidateProviderChannelTypes())
184  return BadRequest(new ErrorMessage { Message = "One or more of channels aren't formatted correctly for the given provider!" });
185 
186  var query = DatabaseContext.ChatBots.Where(x => x.InstanceId == Instance.Id && x.Id == model.Id).Include(x => x.Channels);
187 
188  var current = await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
189 
190  if (current == default)
191  return StatusCode((int)HttpStatusCode.Gone);
192 
193  var userRights = (ChatBotRights)AuthenticationContext.GetRight(RightsType.ChatBots);
194 
195  bool anySettingsModified = false;
196 
197  bool CheckModified<T>(Expression<Func<Api.Models.Internal.ChatBot, T>> expression, ChatBotRights requiredRight)
198  {
199  var memberSelectorExpression = (MemberExpression)expression.Body;
200  var property = (PropertyInfo)memberSelectorExpression.Member;
201 
202  var newVal = property.GetValue(model);
203  if (newVal == null)
204  return false;
205  if (!userRights.HasFlag(requiredRight) && property.GetValue(current) != newVal)
206  return true;
207 
208  property.SetValue(current, newVal);
209  anySettingsModified = true;
210  return false;
211  };
212 
213  var oldProvider = current.Provider;
214 
215  if (CheckModified(x => x.ConnectionString, ChatBotRights.WriteConnectionString)
216  || CheckModified(x => x.Enabled, ChatBotRights.WriteEnabled)
217  || CheckModified(x => x.Name, ChatBotRights.WriteName)
218  || CheckModified(x => x.Provider, ChatBotRights.WriteProvider)
219  || (model.Channels != null && !userRights.HasFlag(ChatBotRights.WriteChannels)))
220  return Forbid();
221 
222  var hasChannels = model.Channels != null;
223  if (hasChannels || (model.Provider.HasValue && model.Provider != oldProvider))
224  {
225  DatabaseContext.ChatChannels.RemoveRange(current.Channels);
226  if (hasChannels)
227  {
228  var dbChannels = model.Channels.Select(x => ConvertApiChatChannel(x)).ToList();
229  DatabaseContext.ChatChannels.AddRange(dbChannels);
230  current.Channels = dbChannels;
231  }
232  else
233  current.Channels.Clear();
234  }
235 
236  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
237 
239 
240  if (anySettingsModified)
241  //have to rebuild the thing first
242  await chat.ChangeSettings(current, cancellationToken).ConfigureAwait(false);
243 
244  if (model.Channels != null || anySettingsModified)
245  await chat.ChangeChannels(current.Id, current.Channels, cancellationToken).ConfigureAwait(false);
246 
247  if (userRights.HasFlag(ChatBotRights.Read))
248  {
249  if (!userRights.HasFlag(ChatBotRights.ReadConnectionString))
250  current.ConnectionString = null;
251  return Json(current.ToApi());
252  }
253  return Ok();
254  }
+
178  {
179  if (model == null)
180  throw new ArgumentNullException(nameof(model));
181 
182  if (model.Provider.HasValue && !model.ValidateProviderChannelTypes())
183  return BadRequest(new ErrorMessage { Message = "One or more of channels aren't formatted correctly for the given provider!" });
184 
185  var query = DatabaseContext.ChatBots.Where(x => x.InstanceId == Instance.Id && x.Id == model.Id).Include(x => x.Channels);
186 
187  var current = await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false);
188 
189  if (current == default)
190  return StatusCode((int)HttpStatusCode.Gone);
191 
192  var userRights = (ChatBotRights)AuthenticationContext.GetRight(RightsType.ChatBots);
193 
194  bool anySettingsModified = false;
195 
196  bool CheckModified<T>(Expression<Func<Api.Models.Internal.ChatBot, T>> expression, ChatBotRights requiredRight)
197  {
198  var memberSelectorExpression = (MemberExpression)expression.Body;
199  var property = (PropertyInfo)memberSelectorExpression.Member;
200 
201  var newVal = property.GetValue(model);
202  if (newVal == null)
203  return false;
204  if (!userRights.HasFlag(requiredRight) && property.GetValue(current) != newVal)
205  return true;
206 
207  property.SetValue(current, newVal);
208  anySettingsModified = true;
209  return false;
210  };
211 
212  var oldProvider = current.Provider;
213 
214  if (CheckModified(x => x.ConnectionString, ChatBotRights.WriteConnectionString)
215  || CheckModified(x => x.Enabled, ChatBotRights.WriteEnabled)
216  || CheckModified(x => x.Name, ChatBotRights.WriteName)
217  || CheckModified(x => x.Provider, ChatBotRights.WriteProvider)
218  || (model.Channels != null && !userRights.HasFlag(ChatBotRights.WriteChannels)))
219  return Forbid();
220 
221  var hasChannels = model.Channels != null;
222  if (hasChannels || (model.Provider.HasValue && model.Provider != oldProvider))
223  {
224  DatabaseContext.ChatChannels.RemoveRange(current.Channels);
225  if (hasChannels)
226  {
227  var dbChannels = model.Channels.Select(x => ConvertApiChatChannel(x)).ToList();
228  DatabaseContext.ChatChannels.AddRange(dbChannels);
229  current.Channels = dbChannels;
230  }
231  else
232  current.Channels.Clear();
233  }
234 
235  await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
236 
238 
239  if (anySettingsModified)
240  //have to rebuild the thing first
241  await chat.ChangeSettings(current, cancellationToken).ConfigureAwait(false);
242 
243  if (model.Channels != null || anySettingsModified)
244  await chat.ChangeChannels(current.Id, current.Channels, cancellationToken).ConfigureAwait(false);
245 
246  if (userRights.HasFlag(ChatBotRights.Read))
247  {
248  if (!userRights.HasFlag(ChatBotRights.ReadConnectionString))
249  current.ConnectionString = null;
250  return Json(current.ToApi());
251  }
252  return Ok();
253  }
RightsType
The type of rights a model uses
Definition: RightsType.cs:6
IInstance GetInstance(Models.Instance metadata)
Get the IInstance associated with given metadata
diff --git a/class_tgstation_1_1_server_1_1_host_1_1_models_1_1_chat_bot-members.html b/class_tgstation_1_1_server_1_1_host_1_1_models_1_1_chat_bot-members.html index 7498d53280..b43cdb25e9 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_models_1_1_chat_bot-members.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_models_1_1_chat_bot-members.html @@ -100,13 +100,14 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - - - - - - - + + + + + + + +
ChannelsTgstation.Server.Host.Models.ChatBot
ConnectionStringTgstation.Server.Api.Models.Internal.ChatBot
EnabledTgstation.Server.Api.Models.Internal.ChatBot
IdTgstation.Server.Api.Models.Internal.ChatBot
InstanceTgstation.Server.Host.Models.ChatBot
InstanceIdTgstation.Server.Host.Models.ChatBot
NameTgstation.Server.Api.Models.Internal.ChatBot
ProviderTgstation.Server.Api.Models.Internal.ChatBot
ToApi()Tgstation.Server.Host.Models.ChatBot
ConnectionStringBuilderTgstation.Server.Api.Models.Internal.ChatBot
EnabledTgstation.Server.Api.Models.Internal.ChatBot
IdTgstation.Server.Api.Models.Internal.ChatBot
InstanceTgstation.Server.Host.Models.ChatBot
InstanceIdTgstation.Server.Host.Models.ChatBot
NameTgstation.Server.Api.Models.Internal.ChatBot
ProviderTgstation.Server.Api.Models.Internal.ChatBot
ToApi()Tgstation.Server.Host.Models.ChatBot
@@ -416,7 +416,7 @@ Private Attributes

Definition at line 31 of file DatabaseContext.cs.

-

Referenced by Tgstation.Server.Host.Controllers.ChatController.Create(), Tgstation.Server.Host.Controllers.ChatController.Delete(), Tgstation.Server.Host.Controllers.ChatController.GetId(), Tgstation.Server.Host.Controllers.ChatController.List(), and Tgstation.Server.Host.Controllers.ChatController.Update().

+

Referenced by Tgstation.Server.Host.Controllers.ChatController.Create(), Tgstation.Server.Host.Controllers.ChatController.Delete(), Tgstation.Server.Host.Controllers.ChatController.GetId(), Tgstation.Server.Host.Controllers.ChatController.List(), and Tgstation.Server.Host.Controllers.ChatController.Update().

@@ -442,7 +442,7 @@ Private Attributes

Definition at line 43 of file DatabaseContext.cs.

-

Referenced by Tgstation.Server.Host.Controllers.ChatController.Update().

+

Referenced by Tgstation.Server.Host.Controllers.ChatController.Update().

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_security_1_1_authentication_context.html b/class_tgstation_1_1_server_1_1_host_1_1_security_1_1_authentication_context.html index 918aac4ffc..73416e460c 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_security_1_1_authentication_context.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_security_1_1_authentication_context.html @@ -289,7 +289,7 @@ Private Attributes

References Tgstation.Server.Api.Rights.RightsHelper.IsInstanceRight(), and Tgstation.Server.Api.Rights.RightsHelper.RightToType().

-

Referenced by Tgstation.Server.Host.Controllers.JobController.Delete(), Tgstation.Server.Host.Controllers.ChatController.GetId(), Tgstation.Server.Host.Controllers.UserController.GetId(), Tgstation.Server.Host.Controllers.ChatController.List(), Tgstation.Server.Host.Controllers.DreamDaemonController.ReadImpl(), Tgstation.Server.Host.Controllers.DreamDaemonController.Update(), Tgstation.Server.Host.Controllers.ChatController.Update(), and Tgstation.Server.Host.Controllers.RepositoryController.Update().

+

Referenced by Tgstation.Server.Host.Controllers.JobController.Delete(), Tgstation.Server.Host.Controllers.ChatController.GetId(), Tgstation.Server.Host.Controllers.UserController.GetId(), Tgstation.Server.Host.Controllers.ChatController.List(), Tgstation.Server.Host.Controllers.DreamDaemonController.ReadImpl(), Tgstation.Server.Host.Controllers.DreamDaemonController.Update(), Tgstation.Server.Host.Controllers.ChatController.Update(), and Tgstation.Server.Host.Controllers.RepositoryController.Update().

63  {
64  var isInstance = RightsHelper.IsInstanceRight(rightsType);
65 
66  //forces the null user check
67  var pullThis = User;
68 
69  if (isInstance && InstanceUser == null)
70  return 0;
71  var rightsEnum = RightsHelper.RightToType(rightsType);
72  // use the api versions because they're the ones that contain the actual properties
73  var typeToCheck = isInstance ? typeof(InstanceUser) : typeof(User);
74 
75  var nullableType = typeof(Nullable<>);
76  var nullableRightsType = nullableType.MakeGenericType(rightsEnum);
77 
78  var prop = typeToCheck.GetProperties().Where(x => x.PropertyType == nullableRightsType).First();
79 
80  var right = prop.GetMethod.Invoke(isInstance ? (object)InstanceUser : User, Array.Empty<object>());
81 
82  if (right == null)
83  throw new InvalidOperationException("A user right was null!");
84  return (ulong)right;
85  }
static bool IsInstanceRight(RightsType rightsType)
Check if a given rightsType is meant for an Models.Instance
diff --git a/classes.html b/classes.html index bb954c5d1e..b8ce96b986 100644 --- a/classes.html +++ b/classes.html @@ -94,95 +94,96 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
A | B | C | D | E | G | H | I | J | K | L | M | N | P | R | S | T | U | V | W
- - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - + - - - - - - - - - + + + + + + + + + + - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + - - - + + - - - - - - - - + + + + + + + - - - - - - - - + + + + + + + + +
  A  
-
DiscordProvider (Tgstation.Server.Host.Components.Chat.Providers)   IHttpClient (Tgstation.Server.Client)   JsonTrackingContext (Tgstation.Server.Host.Components.Chat)   ServerClientFactory (Tgstation.Server.Client)   
DmbFactory (Tgstation.Server.Host.Components.Compiler)   IIdentityCache (Tgstation.Server.Host.Security)   
  K  
-
ServerErrorException (Tgstation.Server.Client)   
Administration (Tgstation.Server.Api.Models)   DmbProvider (Tgstation.Server.Host.Components.Compiler)   IInstance (Tgstation.Server.Host.Components)   ServerFactory (Tgstation.Server.Host)   
AdministrationClient (Tgstation.Server.Client)   DreamDaemon (Tgstation.Server.Api.Models)   IInstanceClient (Tgstation.Server.Client.Components)   KekCommand (Tgstation.Server.Host.Components.Chat.Commands)   ServerInformation (Tgstation.Server.Api.Models)   
AdministrationController (Tgstation.Server.Host.Controllers)   DreamDaemonClient (Tgstation.Server.Client.Components)   IInstanceFactory (Tgstation.Server.Host.Components)   
  L  
-
ServerService (Tgstation.Server.Host.Service)   
AdministrationTest (Tgstation.Server.Tests)   DreamDaemonController (Tgstation.Server.Host.Controllers)   IInstanceManager (Tgstation.Server.Host.Components)   ServerSideModifications (Tgstation.Server.Host.Components.StaticFiles)   
ApiClient (Tgstation.Server.Client)   DreamDaemonLaunchParameters (Tgstation.Server.Api.Models.Internal)   IInstanceManagerClient (Tgstation.Server.Client)   LaunchResult (Tgstation.Server.Host.Components.Watchdog)   ServiceBase   
ApiClientFactory (Tgstation.Server.Client)   DreamDaemonSettings (Tgstation.Server.Api.Models.Internal)   IInstanceUserClient (Tgstation.Server.Client.Components)   
  M  
-
ServiceUnavailableException (Tgstation.Server.Client)   
ApiConflictException (Tgstation.Server.Client)   DreamDaemonSettings (Tgstation.Server.Host.Models)   IIOManager (Tgstation.Server.Host.IO)   SessionController (Tgstation.Server.Host.Components.Watchdog)   
ApiController (Tgstation.Server.Host.Controllers)   DreamMaker (Tgstation.Server.Api.Models)   IJobManager (Tgstation.Server.Host.Core)   Message (Tgstation.Server.Host.Components.Chat.Providers)   SessionControllerFactory (Tgstation.Server.Host.Components.Watchdog)   
ApiHeaders (Tgstation.Server.Api)   DreamMaker (Tgstation.Server.Host.Components.Compiler)   IJobsClient (Tgstation.Server.Client.Components)   MethodNotSupportedException (Tgstation.Server.Client)   SqlServerDatabaseContext (Tgstation.Server.Host.Models)   
ApiMismatchException (Tgstation.Server.Client)   DreamMakerClient (Tgstation.Server.Client.Components)   IJsonTrackingContext (Tgstation.Server.Host.Components.Chat)   Migration   SqlServerDatabaseContextModelSnapshot (Tgstation.Server.Host.Models.Migrations)   
Application (Tgstation.Server.Host.Core)   DreamMakerController (Tgstation.Server.Host.Controllers)   Instance (Tgstation.Server.Api.Models)   ModelController (Tgstation.Server.Host.Controllers)   SqlServerDesignTimeDbContextFactory (Tgstation.Server.Host.Models.Migrations)   
ApplicationBuilderExtensions (Tgstation.Server.Host.Core)   DreamMakerSettings (Tgstation.Server.Host.Models)   Instance (Tgstation.Server.Host.Models)   ModelSnapshot   SynchronousIOManager (Tgstation.Server.Host.IO)   
AuthenticationContext (Tgstation.Server.Host.Security)   
  E  
-
Instance (Tgstation.Server.Host.Components)   MonitorState (Tgstation.Server.Host.Components.Watchdog)   
  T  
+
DesignTimeDbContextFactoryHelpers (Tgstation.Server.Host.Models.Migrations)   IGitHubClientFactory (Tgstation.Server.Host.Core)   JsonConverter   Server (Tgstation.Server.Host)   
DiscordConnectionStringBuilder (Tgstation.Server.Api.Models)   IHostedService   JsonFile (Tgstation.Server.Host.Components.Interop)   ServerClient (Tgstation.Server.Client)   
Administration (Tgstation.Server.Api.Models)   DiscordProvider (Tgstation.Server.Host.Components.Chat.Providers)   IHttpClient (Tgstation.Server.Client)   JsonSubFileList (Tgstation.Server.Host.Components.Interop)   ServerClientFactory (Tgstation.Server.Client)   
AdministrationClient (Tgstation.Server.Client)   DmbFactory (Tgstation.Server.Host.Components.Compiler)   IIdentityCache (Tgstation.Server.Host.Security)   JsonTrackingContext (Tgstation.Server.Host.Components.Chat)   ServerErrorException (Tgstation.Server.Client)   
AdministrationController (Tgstation.Server.Host.Controllers)   DmbProvider (Tgstation.Server.Host.Components.Compiler)   IInstance (Tgstation.Server.Host.Components)   
  K  
+
ServerFactory (Tgstation.Server.Host)   
AdministrationTest (Tgstation.Server.Tests)   DreamDaemon (Tgstation.Server.Api.Models)   IInstanceClient (Tgstation.Server.Client.Components)   ServerInformation (Tgstation.Server.Api.Models)   
ApiClient (Tgstation.Server.Client)   DreamDaemonClient (Tgstation.Server.Client.Components)   IInstanceFactory (Tgstation.Server.Host.Components)   KekCommand (Tgstation.Server.Host.Components.Chat.Commands)   ServerService (Tgstation.Server.Host.Service)   
ApiClientFactory (Tgstation.Server.Client)   DreamDaemonController (Tgstation.Server.Host.Controllers)   IInstanceManager (Tgstation.Server.Host.Components)   
  L  
+
ServerSideModifications (Tgstation.Server.Host.Components.StaticFiles)   
ApiConflictException (Tgstation.Server.Client)   DreamDaemonLaunchParameters (Tgstation.Server.Api.Models.Internal)   IInstanceManagerClient (Tgstation.Server.Client)   ServiceBase   
ApiController (Tgstation.Server.Host.Controllers)   DreamDaemonSettings (Tgstation.Server.Api.Models.Internal)   IInstanceUserClient (Tgstation.Server.Client.Components)   LaunchResult (Tgstation.Server.Host.Components.Watchdog)   ServiceUnavailableException (Tgstation.Server.Client)   
ApiHeaders (Tgstation.Server.Api)   DreamDaemonSettings (Tgstation.Server.Host.Models)   IIOManager (Tgstation.Server.Host.IO)   
  M  
+
SessionController (Tgstation.Server.Host.Components.Watchdog)   
ApiMismatchException (Tgstation.Server.Client)   DreamMaker (Tgstation.Server.Api.Models)   IJobManager (Tgstation.Server.Host.Core)   SessionControllerFactory (Tgstation.Server.Host.Components.Watchdog)   
Application (Tgstation.Server.Host.Core)   DreamMaker (Tgstation.Server.Host.Components.Compiler)   IJobsClient (Tgstation.Server.Client.Components)   Message (Tgstation.Server.Host.Components.Chat.Providers)   SqlServerDatabaseContext (Tgstation.Server.Host.Models)   
ApplicationBuilderExtensions (Tgstation.Server.Host.Core)   DreamMakerClient (Tgstation.Server.Client.Components)   IJsonTrackingContext (Tgstation.Server.Host.Components.Chat)   MethodNotSupportedException (Tgstation.Server.Client)   SqlServerDatabaseContextModelSnapshot (Tgstation.Server.Host.Models.Migrations)   
AuthenticationContext (Tgstation.Server.Host.Security)   DreamMakerController (Tgstation.Server.Host.Controllers)   Instance (Tgstation.Server.Host.Components)   Migration   SqlServerDesignTimeDbContextFactory (Tgstation.Server.Host.Models.Migrations)   
AuthenticationContextFactory (Tgstation.Server.Host.Security)   DreamMakerSettings (Tgstation.Server.Host.Models)   Instance (Tgstation.Server.Api.Models)   ModelController (Tgstation.Server.Host.Controllers)   SynchronousIOManager (Tgstation.Server.Host.IO)   
AuthorizeAttribute   
  E  
+
Instance (Tgstation.Server.Host.Models)   ModelSnapshot   
  T  
AuthenticationContextFactory (Tgstation.Server.Host.Security)   InstanceClient (Tgstation.Server.Client.Components)   MSAddMinimumSecurity (Tgstation.Server.Host.Models.Migrations)   
AuthorizeAttribute   ErrorMessage (Tgstation.Server.Api.Models)   InstanceController (Tgstation.Server.Host.Controllers)   MSInitialCreate (Tgstation.Server.Host.Models.Migrations)   TemporaryDmbProvider (Tgstation.Server.Host.Components.Compiler)   
AutoGeneratedProgram   EventConsumer (Tgstation.Server.Host.Components)   InstanceFactory (Tgstation.Server.Host.Components)   MSNullableAndForeignKeyCleanup (Tgstation.Server.Host.Models.Migrations)   TestApiClient (Tgstation.Server.Client.Tests)   
AutoGeneratedProgram   InstanceClient (Tgstation.Server.Client.Components)   MonitorState (Tgstation.Server.Host.Components.Watchdog)   
  B  
-
EventNotification (Tgstation.Server.Host.Components.Interop)   InstanceManager (Tgstation.Server.Host.Components)   MYAddMinimumSecurity (Tgstation.Server.Host.Models.Migrations)   TestApiHeaders (Tgstation.Server.Api.Tests)   
Exception   InstanceManagerClient (Tgstation.Server.Client)   MYInitialCreate (Tgstation.Server.Host.Models.Migrations)   TestAuthenticationContext (Tgstation.Server.Host.Security.Tests)   
BoolConverter (Tgstation.Server.Host.Components.Chat.Commands)   
  G  
-
InstanceManagerTest (Tgstation.Server.Tests)   MYNullableAndForeignKeyCleanup (Tgstation.Server.Host.Models.Migrations)   TestDreamDaemonClient (Tgstation.Server.Client.Components.Tests)   
Byond (Tgstation.Server.Api.Models)   InstanceTest (Tgstation.Server.Tests.Instance)   MySqlDatabaseContext (Tgstation.Server.Host.Models)   TestingServer (Tgstation.Server.Tests)   
ByondClient (Tgstation.Server.Client.Components)   GeneralConfiguration (Tgstation.Server.Host.Configuration)   InstanceUser (Tgstation.Server.Api.Models)   MySqlDatabaseContextModelSnapshot (Tgstation.Server.Host.Models.Migrations)   TestMerge (Tgstation.Server.Api.Models.Internal)   
ByondCommand (Tgstation.Server.Host.Components.Chat.Commands)   GitHubClientFactory (Tgstation.Server.Host.Core)   InstanceUser (Tgstation.Server.Host.Models)   MySqlDesignTimeDbContextFactory (Tgstation.Server.Host.Models.Migrations)   TestMerge (Tgstation.Server.Host.Components.Interop)   
ByondController (Tgstation.Server.Host.Controllers)   
  H  
-
InstanceUserClient (Tgstation.Server.Client.Components)   
  N  
-
TestMerge (Tgstation.Server.Host.Models)   
ByondExecutableLock (Tgstation.Server.Host.Components.Byond)   InstanceUserController (Tgstation.Server.Host.Controllers)   TestMerge (Tgstation.Server.Api.Models)   
ByondManager (Tgstation.Server.Host.Components.Byond)   HomeController (Tgstation.Server.Host.Controllers)   InsufficientPermissionsException (Tgstation.Server.Client)   NativeMethods (Tgstation.Server.Host)   TestMergeBase (Tgstation.Server.Api.Models.Internal)   
ByondTest (Tgstation.Server.Tests.Instance)   HttpClient (Tgstation.Server.Client)   IntegrationTest (Tgstation.Server.Tests)   
  P  
-
TestMergeParameters (Tgstation.Server.Api.Models)   
ErrorMessage (Tgstation.Server.Api.Models)   InstanceController (Tgstation.Server.Host.Controllers)   MSAddMinimumSecurity (Tgstation.Server.Host.Models.Migrations)   TemporaryDmbProvider (Tgstation.Server.Host.Components.Compiler)   
EventConsumer (Tgstation.Server.Host.Components)   InstanceFactory (Tgstation.Server.Host.Components)   MSInitialCreate (Tgstation.Server.Host.Models.Migrations)   TestApiClient (Tgstation.Server.Client.Tests)   
BoolConverter (Tgstation.Server.Host.Components.Chat.Commands)   EventNotification (Tgstation.Server.Host.Components.Interop)   InstanceManager (Tgstation.Server.Host.Components)   MSNullableAndForeignKeyCleanup (Tgstation.Server.Host.Models.Migrations)   TestApiHeaders (Tgstation.Server.Api.Tests)   
Byond (Tgstation.Server.Api.Models)   Exception   InstanceManagerClient (Tgstation.Server.Client)   MYAddMinimumSecurity (Tgstation.Server.Host.Models.Migrations)   TestAuthenticationContext (Tgstation.Server.Host.Security.Tests)   
ByondClient (Tgstation.Server.Client.Components)   
  G  
+
InstanceManagerTest (Tgstation.Server.Tests)   MYInitialCreate (Tgstation.Server.Host.Models.Migrations)   TestDreamDaemonClient (Tgstation.Server.Client.Components.Tests)   
ByondCommand (Tgstation.Server.Host.Components.Chat.Commands)   InstanceTest (Tgstation.Server.Tests.Instance)   MYNullableAndForeignKeyCleanup (Tgstation.Server.Host.Models.Migrations)   TestingServer (Tgstation.Server.Tests)   
ByondController (Tgstation.Server.Host.Controllers)   GeneralConfiguration (Tgstation.Server.Host.Configuration)   InstanceUser (Tgstation.Server.Api.Models)   MySqlDatabaseContext (Tgstation.Server.Host.Models)   TestMerge (Tgstation.Server.Api.Models)   
ByondExecutableLock (Tgstation.Server.Host.Components.Byond)   GitHubClientFactory (Tgstation.Server.Host.Core)   InstanceUser (Tgstation.Server.Host.Models)   MySqlDatabaseContextModelSnapshot (Tgstation.Server.Host.Models.Migrations)   TestMerge (Tgstation.Server.Host.Models)   
ByondManager (Tgstation.Server.Host.Components.Byond)   
  H  
+
InstanceUserClient (Tgstation.Server.Client.Components)   MySqlDesignTimeDbContextFactory (Tgstation.Server.Host.Models.Migrations)   TestMerge (Tgstation.Server.Api.Models.Internal)   
ByondTest (Tgstation.Server.Tests.Instance)   InstanceUserController (Tgstation.Server.Host.Controllers)   
  N  
+
TestMerge (Tgstation.Server.Host.Components.Interop)   
  C  
-
  I  
-
IPostWriteHandler (Tgstation.Server.Host.Core)   TestProgram (Tgstation.Server.Host.Console.Tests)   
IProcess (Tgstation.Server.Host.Core)   PosixByondInstaller (Tgstation.Server.Host.Components.Byond)   TestRights (Tgstation.Server.Api.Rights.Tests)   
Channel (Tgstation.Server.Host.Components.Chat)   IAdministrationClient (Tgstation.Server.Client)   IProcessBase (Tgstation.Server.Host.Core)   PosixPostWriteHandler (Tgstation.Server.Host.Core)   TestServerClientFactory (Tgstation.Server.Client.Tests)   
ChannelMapping (Tgstation.Server.Host.Components.Chat)   IApiClient (Tgstation.Server.Client)   IProcessExecutor (Tgstation.Server.Host.Core)   PosixSymlinkFactory (Tgstation.Server.Host.IO)   TestServerService (Tgstation.Server.Host.Service.Tests)   
Chat (Tgstation.Server.Host.Components.Chat)   IApiClientFactory (Tgstation.Server.Client)   IProvider (Tgstation.Server.Host.Components.Chat.Providers)   PosixSystemIdentityFactory (Tgstation.Server.Host.Security)   TestWatchdog (Tgstation.Server.Host.Watchdog.Tests)   
ChatBot (Tgstation.Server.Api.Models)   IApplication (Tgstation.Server.Host.Core)   IProviderFactory (Tgstation.Server.Host.Components.Chat)   Process (Tgstation.Server.Host.Core)   TestWatchdogFactory (Tgstation.Server.Host.Watchdog.Tests)   
ChatBot (Tgstation.Server.Api.Models.Internal)   IAuthenticationContext (Tgstation.Server.Host.Security)   IrcProvider (Tgstation.Server.Host.Components.Chat.Providers)   ProcessExecutor (Tgstation.Server.Host.Core)   TgsAuthorizeAttribute (Tgstation.Server.Host.Controllers)   
ChatBot (Tgstation.Server.Host.Models)   IAuthenticationContextFactory (Tgstation.Server.Host.Security)   IReattachInfoHandler (Tgstation.Server.Host.Components.Watchdog)   Program (Tgstation.Server.Host.Console)   Token (Tgstation.Server.Api.Models)   
ChatBotsClient (Tgstation.Server.Client.Components)   IByondClient (Tgstation.Server.Client.Components)   IRepository (Tgstation.Server.Host.Components.Repository)   Program (Tgstation.Server.Host.Service)   TokenFactory (Tgstation.Server.Host.Security)   
ChatChannel (Tgstation.Server.Api.Models)   IByondExecutableLock (Tgstation.Server.Host.Components.Byond)   IRepositoryClient (Tgstation.Server.Client.Components)   Program (Tgstation.Server.Host)   
  U  
+
HomeController (Tgstation.Server.Host.Controllers)   InsufficientPermissionsException (Tgstation.Server.Client)   TestMergeBase (Tgstation.Server.Api.Models.Internal)   
HttpClient (Tgstation.Server.Client)   IntegrationTest (Tgstation.Server.Tests)   NativeMethods (Tgstation.Server.Host)   TestMergeParameters (Tgstation.Server.Api.Models)   
Channel (Tgstation.Server.Host.Components.Chat)   
  I  
+
IPostWriteHandler (Tgstation.Server.Host.Core)   
  P  
+
TestProgram (Tgstation.Server.Host.Console.Tests)   
ChannelMapping (Tgstation.Server.Host.Components.Chat)   IProcess (Tgstation.Server.Host.Core)   TestRights (Tgstation.Server.Api.Rights.Tests)   
Chat (Tgstation.Server.Host.Components.Chat)   IAdministrationClient (Tgstation.Server.Client)   IProcessBase (Tgstation.Server.Host.Core)   PosixByondInstaller (Tgstation.Server.Host.Components.Byond)   TestServerClientFactory (Tgstation.Server.Client.Tests)   
ChatBot (Tgstation.Server.Api.Models)   IApiClient (Tgstation.Server.Client)   IProcessExecutor (Tgstation.Server.Host.Core)   PosixPostWriteHandler (Tgstation.Server.Host.Core)   TestServerService (Tgstation.Server.Host.Service.Tests)   
ChatBot (Tgstation.Server.Host.Models)   IApiClientFactory (Tgstation.Server.Client)   IProvider (Tgstation.Server.Host.Components.Chat.Providers)   PosixSymlinkFactory (Tgstation.Server.Host.IO)   TestWatchdog (Tgstation.Server.Host.Watchdog.Tests)   
ChatBot (Tgstation.Server.Api.Models.Internal)   IApplication (Tgstation.Server.Host.Core)   IProviderFactory (Tgstation.Server.Host.Components.Chat)   PosixSystemIdentityFactory (Tgstation.Server.Host.Security)   TestWatchdogFactory (Tgstation.Server.Host.Watchdog.Tests)   
ChatBotsClient (Tgstation.Server.Client.Components)   IAuthenticationContext (Tgstation.Server.Host.Security)   IrcConnectionStringBuilder (Tgstation.Server.Api.Models)   Process (Tgstation.Server.Host.Core)   TgsAuthorizeAttribute (Tgstation.Server.Host.Controllers)   
ChatChannel (Tgstation.Server.Api.Models)   IAuthenticationContextFactory (Tgstation.Server.Host.Security)   IrcProvider (Tgstation.Server.Host.Components.Chat.Providers)   ProcessExecutor (Tgstation.Server.Host.Core)   Token (Tgstation.Server.Api.Models)   
ChatChannel (Tgstation.Server.Host.Models)   IByondClient (Tgstation.Server.Client.Components)   IReattachInfoHandler (Tgstation.Server.Host.Components.Watchdog)   Program (Tgstation.Server.Host)   TokenFactory (Tgstation.Server.Host.Security)   
ChatCommand (Tgstation.Server.Host.Components.Interop)   IByondExecutableLock (Tgstation.Server.Host.Components.Byond)   IRepository (Tgstation.Server.Host.Components.Repository)   Program (Tgstation.Server.Host.Console)   
  U  
ChatChannel (Tgstation.Server.Host.Models)   IByondInstaller (Tgstation.Server.Host.Components.Byond)   IRepositoryManager (Tgstation.Server.Host.Components.Repository)   Provider (Tgstation.Server.Host.Components.Chat.Providers)   
ChatCommand (Tgstation.Server.Host.Components.Interop)   IByondManager (Tgstation.Server.Host.Components.Byond)   IRequestLogger (Tgstation.Server.Client)   ProviderFactory (Tgstation.Server.Host.Components.Chat)   UnauthorizedException (Tgstation.Server.Client)   
ChatController (Tgstation.Server.Host.Controllers)   IChat (Tgstation.Server.Host.Components.Chat)   IServer (Tgstation.Server.Host)   PullRequestsCommand (Tgstation.Server.Host.Components.Chat.Commands)   UnrecognizedResponseException (Tgstation.Server.Client)   
ChatFactory (Tgstation.Server.Host.Components.Chat)   IChatBotsClient (Tgstation.Server.Client.Components)   IServerClient (Tgstation.Server.Client)   
  R  
-
UpdatesConfiguration (Tgstation.Server.Host.Configuration)   
ClaimsInjector (Tgstation.Server.Host.Security)   IChatFactory (Tgstation.Server.Host.Components.Chat)   IServerClientFactory (Tgstation.Server.Client)   User (Tgstation.Server.Api.Models)   
ClientException (Tgstation.Server.Client)   IClaimsInjector (Tgstation.Server.Host.Security)   IServerControl (Tgstation.Server.Host.Core)   RateLimitException (Tgstation.Server.Client)   User (Tgstation.Server.Host.Models)   
CommandFactory (Tgstation.Server.Host.Components.Chat.Commands)   ICommand (Tgstation.Server.Host.Components.Chat.Commands)   IServerFactory (Tgstation.Server.Host)   ReattachInfoHandler (Tgstation.Server.Host.Components)   User (Tgstation.Server.Api.Models.Internal)   
CommCommand (Tgstation.Server.Host.Components.Interop)   ICommandFactory (Tgstation.Server.Host.Components.Chat.Commands)   ISessionController (Tgstation.Server.Host.Components.Watchdog)   ReattachInformation (Tgstation.Server.Host.Components.Watchdog)   User (Tgstation.Server.Host.Components.Chat)   
CommContext (Tgstation.Server.Host.Components.Interop)   ICommContext (Tgstation.Server.Host.Components.Interop)   ISessionControllerFactory (Tgstation.Server.Host.Components.Watchdog)   ReattachInformation (Tgstation.Server.Host.Models)   UserController (Tgstation.Server.Host.Controllers)   
CompileJob (Tgstation.Server.Api.Models)   ICommHandler (Tgstation.Server.Host.Components.Interop)   ISymlinkFactory (Tgstation.Server.Host.IO)   ReattachInformationBase (Tgstation.Server.Host.Models)   UsersClient (Tgstation.Server.Client)   
CompileJob (Tgstation.Server.Host.Models)   ICompileJobConsumer (Tgstation.Server.Host.Components.Compiler)   ISynchronousIOManager (Tgstation.Server.Host.IO)   Repository (Tgstation.Server.Host.Components.Repository)   UserUpdate (Tgstation.Server.Api.Models)   
CompileJob (Tgstation.Server.Api.Models.Internal)   IConfiguration (Tgstation.Server.Host.Components.StaticFiles)   ISystemIdentity (Tgstation.Server.Host.Security)   Repository (Tgstation.Server.Api.Models)   
  V  
+
ChatConnectionStringBuilder (Tgstation.Server.Api.Models.Internal)   IByondInstaller (Tgstation.Server.Host.Components.Byond)   IRepositoryClient (Tgstation.Server.Client.Components)   Program (Tgstation.Server.Host.Service)   
ChatController (Tgstation.Server.Host.Controllers)   IByondManager (Tgstation.Server.Host.Components.Byond)   IRepositoryManager (Tgstation.Server.Host.Components.Repository)   Provider (Tgstation.Server.Host.Components.Chat.Providers)   UnauthorizedException (Tgstation.Server.Client)   
ChatFactory (Tgstation.Server.Host.Components.Chat)   IChat (Tgstation.Server.Host.Components.Chat)   IRequestLogger (Tgstation.Server.Client)   ProviderFactory (Tgstation.Server.Host.Components.Chat)   UnrecognizedResponseException (Tgstation.Server.Client)   
ClaimsInjector (Tgstation.Server.Host.Security)   IChatBotsClient (Tgstation.Server.Client.Components)   IServer (Tgstation.Server.Host)   PullRequestsCommand (Tgstation.Server.Host.Components.Chat.Commands)   UpdatesConfiguration (Tgstation.Server.Host.Configuration)   
ClientException (Tgstation.Server.Client)   IChatFactory (Tgstation.Server.Host.Components.Chat)   IServerClient (Tgstation.Server.Client)   
  R  
+
User (Tgstation.Server.Host.Models)   
CommandFactory (Tgstation.Server.Host.Components.Chat.Commands)   IClaimsInjector (Tgstation.Server.Host.Security)   IServerClientFactory (Tgstation.Server.Client)   User (Tgstation.Server.Host.Components.Chat)   
CommCommand (Tgstation.Server.Host.Components.Interop)   ICommand (Tgstation.Server.Host.Components.Chat.Commands)   IServerControl (Tgstation.Server.Host.Core)   RateLimitException (Tgstation.Server.Client)   User (Tgstation.Server.Api.Models)   
CommContext (Tgstation.Server.Host.Components.Interop)   ICommandFactory (Tgstation.Server.Host.Components.Chat.Commands)   IServerFactory (Tgstation.Server.Host)   ReattachInfoHandler (Tgstation.Server.Host.Components)   User (Tgstation.Server.Api.Models.Internal)   
CompileJob (Tgstation.Server.Api.Models.Internal)   ICommContext (Tgstation.Server.Host.Components.Interop)   ISessionController (Tgstation.Server.Host.Components.Watchdog)   ReattachInformation (Tgstation.Server.Host.Components.Watchdog)   UserController (Tgstation.Server.Host.Controllers)   
CompileJob (Tgstation.Server.Api.Models)   ICommHandler (Tgstation.Server.Host.Components.Interop)   ISessionControllerFactory (Tgstation.Server.Host.Components.Watchdog)   ReattachInformation (Tgstation.Server.Host.Models)   UsersClient (Tgstation.Server.Client)   
CompileJob (Tgstation.Server.Host.Models)   ICompileJobConsumer (Tgstation.Server.Host.Components.Compiler)   ISymlinkFactory (Tgstation.Server.Host.IO)   ReattachInformationBase (Tgstation.Server.Host.Models)   UserUpdate (Tgstation.Server.Api.Models)   
Configuration (Tgstation.Server.Host.Components.StaticFiles)   IConfiguration (Tgstation.Server.Host.Components.StaticFiles)   ISynchronousIOManager (Tgstation.Server.Host.IO)   Repository (Tgstation.Server.Api.Models)   
  V  
Configuration (Tgstation.Server.Host.Components.StaticFiles)   IConfigurationClient (Tgstation.Server.Client.Components)   ISystemIdentityFactory (Tgstation.Server.Host.Security)   RepositoryClient (Tgstation.Server.Client.Components)   
ConfigurationClient (Tgstation.Server.Client.Components)   ICredentialsProvider (Tgstation.Server.Host.Components.Repository)   ITokenFactory (Tgstation.Server.Host.Security)   RepositoryController (Tgstation.Server.Host.Controllers)   VersionCommand (Tgstation.Server.Host.Components.Chat.Commands)   
ConfigurationController (Tgstation.Server.Host.Controllers)   ICryptographySuite (Tgstation.Server.Host.Security)   IUsersClient (Tgstation.Server.Client)   RepositoryManager (Tgstation.Server.Host.Components.Repository)   
  W  
+
ConfigurationClient (Tgstation.Server.Client.Components)   IConfigurationClient (Tgstation.Server.Client.Components)   ISystemIdentity (Tgstation.Server.Host.Security)   Repository (Tgstation.Server.Host.Components.Repository)   
ConfigurationController (Tgstation.Server.Host.Controllers)   ICredentialsProvider (Tgstation.Server.Host.Components.Repository)   ISystemIdentityFactory (Tgstation.Server.Host.Security)   RepositoryClient (Tgstation.Server.Client.Components)   VersionCommand (Tgstation.Server.Host.Components.Chat.Commands)   
ConfigurationFile (Tgstation.Server.Api.Models)   ICryptographySuite (Tgstation.Server.Host.Security)   ITokenFactory (Tgstation.Server.Host.Security)   RepositoryController (Tgstation.Server.Host.Controllers)   
  W  
ConfigurationFile (Tgstation.Server.Api.Models)   ICustomCommandHandler (Tgstation.Server.Host.Components.Chat)   IWatchdog (Tgstation.Server.Host.Watchdog)   RepositorySettings (Tgstation.Server.Api.Models.Internal)   
ConfigurationTest (Tgstation.Server.Tests.Instance)   IDatabaseContext (Tgstation.Server.Host.Models)   IWatchdog (Tgstation.Server.Host.Components.Watchdog)   RepositorySettings (Tgstation.Server.Host.Models)   Watchdog (Tgstation.Server.Host.Watchdog)   
ConflictException (Tgstation.Server.Client)   IDatabaseContextFactory (Tgstation.Server.Host.Core)   IWatchdogFactory (Tgstation.Server.Host.Watchdog)   RequestTimeoutException (Tgstation.Server.Client)   Watchdog (Tgstation.Server.Host.Components.Watchdog)   
Constants (Tgstation.Server.Host.Components.Interop)   IDatabaseSeeder (Tgstation.Server.Host.Models)   IWatchdogFactory (Tgstation.Server.Host.Components.Watchdog)   ResolvingIOManager (Tgstation.Server.Host.IO)   WatchdogFactory (Tgstation.Server.Host.Components.Watchdog)   
Controller   IdentityCache (Tgstation.Server.Host.Security)   
  J  
-
Response (Tgstation.Server.Host.Components.Chat)   WatchdogFactory (Tgstation.Server.Host.Watchdog)   
CredentialsProvider (Tgstation.Server.Host.Components.Repository)   IdentityCacheObject (Tgstation.Server.Host.Security)   RevInfoTestMerge (Tgstation.Server.Host.Models)   WatchdogLaunchResult (Tgstation.Server.Host.Components.Watchdog)   
CryptographySuite (Tgstation.Server.Host.Security)   IDesignTimeDbContextFactory   Job (Tgstation.Server.Api.Models.Internal)   RevisionCommand (Tgstation.Server.Host.Components.Chat.Commands)   WatchdogReattachInformation (Tgstation.Server.Host.Models)   
CustomCommand (Tgstation.Server.Host.Components.Chat.Commands)   IDisposable   Job (Tgstation.Server.Api.Models)   RevisionInformation (Tgstation.Server.Host.Models)   WatchdogReattachInformation (Tgstation.Server.Host.Components.Watchdog)   
ConfigurationTest (Tgstation.Server.Tests.Instance)   ICustomCommandHandler (Tgstation.Server.Host.Components.Chat)   IUsersClient (Tgstation.Server.Client)   RepositoryManager (Tgstation.Server.Host.Components.Repository)   
ConflictException (Tgstation.Server.Client)   IDatabaseContext (Tgstation.Server.Host.Models)   IWatchdog (Tgstation.Server.Host.Watchdog)   RepositorySettings (Tgstation.Server.Host.Models)   Watchdog (Tgstation.Server.Host.Watchdog)   
Constants (Tgstation.Server.Host.Components.Interop)   IDatabaseContextFactory (Tgstation.Server.Host.Core)   IWatchdog (Tgstation.Server.Host.Components.Watchdog)   RepositorySettings (Tgstation.Server.Api.Models.Internal)   Watchdog (Tgstation.Server.Host.Components.Watchdog)   
Controller   IDatabaseSeeder (Tgstation.Server.Host.Models)   IWatchdogFactory (Tgstation.Server.Host.Watchdog)   RequestTimeoutException (Tgstation.Server.Client)   WatchdogFactory (Tgstation.Server.Host.Components.Watchdog)   
CredentialsProvider (Tgstation.Server.Host.Components.Repository)   IdentityCache (Tgstation.Server.Host.Security)   IWatchdogFactory (Tgstation.Server.Host.Components.Watchdog)   ResolvingIOManager (Tgstation.Server.Host.IO)   WatchdogFactory (Tgstation.Server.Host.Watchdog)   
CryptographySuite (Tgstation.Server.Host.Security)   IdentityCacheObject (Tgstation.Server.Host.Security)   
  J  
+
Response (Tgstation.Server.Host.Components.Chat)   WatchdogLaunchResult (Tgstation.Server.Host.Components.Watchdog)   
CustomCommand (Tgstation.Server.Host.Components.Chat.Commands)   IDesignTimeDbContextFactory   RevInfoTestMerge (Tgstation.Server.Host.Models)   WatchdogReattachInformation (Tgstation.Server.Host.Components.Watchdog)   
  D  
-
IDmbFactory (Tgstation.Server.Host.Components.Compiler)   Job (Tgstation.Server.Host.Models)   RevisionInformation (Tgstation.Server.Api.Models.Internal)   WatchdogReattachInformationBase (Tgstation.Server.Host.Models)   
IDmbProvider (Tgstation.Server.Host.Components)   JobController (Tgstation.Server.Host.Controllers)   RevisionInformation (Tgstation.Server.Api.Models)   WindowsByondInstaller (Tgstation.Server.Host.Components.Byond)   
DatabaseConfiguration (Tgstation.Server.Host.Configuration)   IDreamDaemonClient (Tgstation.Server.Client.Components)   JobException (Tgstation.Server.Host)   RightsHelper (Tgstation.Server.Api.Rights)   WindowsPostWriteHandler (Tgstation.Server.Host.Core)   
DatabaseContext (Tgstation.Server.Host.Models)   IDreamMaker (Tgstation.Server.Host.Components.Compiler)   JobHandler (Tgstation.Server.Host.Core)   Routes (Tgstation.Server.Api)   WindowsSymlinkFactory (Tgstation.Server.Host.IO)   
DatabaseContextFactory (Tgstation.Server.Host.Core)   IDreamMakerClient (Tgstation.Server.Client.Components)   JobManager (Tgstation.Server.Host.Core)   
  S  
-
WindowsSystemIdentity (Tgstation.Server.Host.Security)   
DatabaseSeeder (Tgstation.Server.Host.Models)   IEventConsumer (Tgstation.Server.Host.Components)   JobsClient (Tgstation.Server.Client.Components)   WindowsSystemIdentityFactory (Tgstation.Server.Host.Security)   
DbContext   IEventLog   JsonConverter   SemaphoreSlimContext (Tgstation.Server.Host.Core)   
DefaultIOManager (Tgstation.Server.Host.IO)   IGitHubClientFactory (Tgstation.Server.Host.Core)   JsonFile (Tgstation.Server.Host.Components.Interop)   Server (Tgstation.Server.Host)   
DesignTimeDbContextFactoryHelpers (Tgstation.Server.Host.Models.Migrations)   IHostedService   JsonSubFileList (Tgstation.Server.Host.Components.Interop)   ServerClient (Tgstation.Server.Client)   
IDisposable   Job (Tgstation.Server.Api.Models.Internal)   RevisionCommand (Tgstation.Server.Host.Components.Chat.Commands)   WatchdogReattachInformation (Tgstation.Server.Host.Models)   
IDmbFactory (Tgstation.Server.Host.Components.Compiler)   Job (Tgstation.Server.Api.Models)   RevisionInformation (Tgstation.Server.Api.Models)   WatchdogReattachInformationBase (Tgstation.Server.Host.Models)   
DatabaseConfiguration (Tgstation.Server.Host.Configuration)   IDmbProvider (Tgstation.Server.Host.Components)   Job (Tgstation.Server.Host.Models)   RevisionInformation (Tgstation.Server.Host.Models)   WindowsByondInstaller (Tgstation.Server.Host.Components.Byond)   
DatabaseContext (Tgstation.Server.Host.Models)   IDreamDaemonClient (Tgstation.Server.Client.Components)   JobController (Tgstation.Server.Host.Controllers)   RevisionInformation (Tgstation.Server.Api.Models.Internal)   WindowsPostWriteHandler (Tgstation.Server.Host.Core)   
DatabaseContextFactory (Tgstation.Server.Host.Core)   IDreamMaker (Tgstation.Server.Host.Components.Compiler)   JobException (Tgstation.Server.Host)   RightsHelper (Tgstation.Server.Api.Rights)   WindowsSymlinkFactory (Tgstation.Server.Host.IO)   
DatabaseSeeder (Tgstation.Server.Host.Models)   IDreamMakerClient (Tgstation.Server.Client.Components)   JobHandler (Tgstation.Server.Host.Core)   Routes (Tgstation.Server.Api)   WindowsSystemIdentity (Tgstation.Server.Host.Security)   
DbContext   IEventConsumer (Tgstation.Server.Host.Components)   JobManager (Tgstation.Server.Host.Core)   
  S  
+
WindowsSystemIdentityFactory (Tgstation.Server.Host.Security)   
DefaultIOManager (Tgstation.Server.Host.IO)   IEventLog   JobsClient (Tgstation.Server.Client.Components)   
SemaphoreSlimContext (Tgstation.Server.Host.Core)   
A | B | C | D | E | G | H | I | J | K | L | M | N | P | R | S | T | U | V | W
diff --git a/dir_91e2bbee1291fcb0db7e0417b617d01e.html b/dir_019c6d3af924151b681494cc466be1ac.html similarity index 93% rename from dir_91e2bbee1291fcb0db7e0417b617d01e.html rename to dir_019c6d3af924151b681494cc466be1ac.html index b47601cd5d..f266cfd73a 100644 --- a/dir_91e2bbee1291fcb0db7e0417b617d01e.html +++ b/dir_019c6d3af924151b681494cc466be1ac.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Client.Tests/obj/Release Directory Reference +tgstation-server: tests/Tgstation.Server.Client.Tests/obj/Debug Directory Reference @@ -84,18 +84,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netcoreapp2.1
directory  netcoreapp2.1
 
diff --git a/dir_53c056aa6a96f034cca75f793964be6e.html b/dir_0b6a380fe7fc1e1d640d1ac854fc3fb6.html similarity index 92% rename from dir_53c056aa6a96f034cca75f793964be6e.html rename to dir_0b6a380fe7fc1e1d640d1ac854fc3fb6.html index 221965305a..f668df272f 100644 --- a/dir_53c056aa6a96f034cca75f793964be6e.html +++ b/dir_0b6a380fe7fc1e1d640d1ac854fc3fb6.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Tests/obj/Release Directory Reference +tgstation-server: tests/Tgstation.Server.Host.Tests/obj/Debug Directory Reference @@ -84,18 +84,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netcoreapp2.1
directory  netcoreapp2.1
 
diff --git a/dir_103b33cf1ab7043de6f87dfa61afa23f.html b/dir_103b33cf1ab7043de6f87dfa61afa23f.html index aa10758ae0..7501f6c633 100644 --- a/dir_103b33cf1ab7043de6f87dfa61afa23f.html +++ b/dir_103b33cf1ab7043de6f87dfa61afa23f.html @@ -95,7 +95,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_b2c89c850c962495793d09ea3a2ee385.html b/dir_1083fcf52a7d94a2efffb6ca88246acf.html similarity index 95% rename from dir_b2c89c850c962495793d09ea3a2ee385.html rename to dir_1083fcf52a7d94a2efffb6ca88246acf.html index 9e631c4d68..e8b31798d9 100644 --- a/dir_b2c89c850c962495793d09ea3a2ee385.html +++ b/dir_1083fcf52a7d94a2efffb6ca88246acf.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Console.Tests/obj/Release/netcoreapp2.1 Directory Reference +tgstation-server: tests/Tgstation.Server.Host.Console.Tests/obj/Debug/netcoreapp2.1 Directory Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_177c182c6d1f29bec543c3fdad3072d5.html b/dir_177c182c6d1f29bec543c3fdad3072d5.html index c5f1f80c35..de1657aed2 100644 --- a/dir_177c182c6d1f29bec543c3fdad3072d5.html +++ b/dir_177c182c6d1f29bec543c3fdad3072d5.html @@ -95,7 +95,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_53aae571c66a052729f8e64ccc397130.html b/dir_2553b13cbb871373b0c30fb90d66217c.html similarity index 94% rename from dir_53aae571c66a052729f8e64ccc397130.html rename to dir_2553b13cbb871373b0c30fb90d66217c.html index 41068033a6..fae236a9e2 100644 --- a/dir_53aae571c66a052729f8e64ccc397130.html +++ b/dir_2553b13cbb871373b0c30fb90d66217c.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Tests/obj/Release/netcoreapp2.1 Directory Reference +tgstation-server: tests/Tgstation.Server.Host.Tests/obj/Debug/netcoreapp2.1 Directory Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_90400719279795a0c5ea912516a4c874.html b/dir_2b9d136d9ffdab429a63a74fca28b300.html similarity index 92% rename from dir_90400719279795a0c5ea912516a4c874.html rename to dir_2b9d136d9ffdab429a63a74fca28b300.html index 80c2dd4c01..5f8a13ad47 100644 --- a/dir_90400719279795a0c5ea912516a4c874.html +++ b/dir_2b9d136d9ffdab429a63a74fca28b300.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host/obj/Release Directory Reference +tgstation-server: src/Tgstation.Server.Host/obj/Debug Directory Reference @@ -84,18 +84,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netcoreapp2.1
directory  netcoreapp2.1
 
diff --git a/dir_7ae0efc0613c36c848fa310214e0aee3.html b/dir_2e67631cb23401d696bcfdbe81f86f0f.html similarity index 92% rename from dir_7ae0efc0613c36c848fa310214e0aee3.html rename to dir_2e67631cb23401d696bcfdbe81f86f0f.html index 6f771886e7..dd30a6f223 100644 --- a/dir_7ae0efc0613c36c848fa310214e0aee3.html +++ b/dir_2e67631cb23401d696bcfdbe81f86f0f.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Api/obj/Release Directory Reference +tgstation-server: src/Tgstation.Server.Api/obj/Debug Directory Reference @@ -84,18 +84,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netstandard2.0
directory  netstandard2.0
 
diff --git a/dir_446bdfc1cd19b69c11765b5665ebfd45.html b/dir_446bdfc1cd19b69c11765b5665ebfd45.html index e9184ffb05..00898166ed 100644 --- a/dir_446bdfc1cd19b69c11765b5665ebfd45.html +++ b/dir_446bdfc1cd19b69c11765b5665ebfd45.html @@ -95,7 +95,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_4122b363a9d577bed19bef337e757ff3.html b/dir_48968f849f7ddb061f7130e079fe66a6.html similarity index 92% rename from dir_4122b363a9d577bed19bef337e757ff3.html rename to dir_48968f849f7ddb061f7130e079fe66a6.html index b3a9b1e4ea..196118d239 100644 --- a/dir_4122b363a9d577bed19bef337e757ff3.html +++ b/dir_48968f849f7ddb061f7130e079fe66a6.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Client/obj/Release Directory Reference +tgstation-server: src/Tgstation.Server.Client/obj/Debug Directory Reference @@ -84,18 +84,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netstandard2.0
directory  netstandard2.0
 
diff --git a/dir_d45f494620bc0048ee4a31fa4f7391e1.html b/dir_4c0c7e2f4fc60f51e2347a2d8dda94ce.html similarity index 94% rename from dir_d45f494620bc0048ee4a31fa4f7391e1.html rename to dir_4c0c7e2f4fc60f51e2347a2d8dda94ce.html index f7d7774036..2f3126d81e 100644 --- a/dir_d45f494620bc0048ee4a31fa4f7391e1.html +++ b/dir_4c0c7e2f4fc60f51e2347a2d8dda94ce.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Api.Tests/obj/Release/netcoreapp2.1 Directory Reference +tgstation-server: tests/Tgstation.Server.Api.Tests/obj/Debug/netcoreapp2.1 Directory Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_5e0b464d3b4be546c63c5188ffeb96e4.html b/dir_5e0b464d3b4be546c63c5188ffeb96e4.html index f2a57b69f7..6a9180c00b 100644 --- a/dir_5e0b464d3b4be546c63c5188ffeb96e4.html +++ b/dir_5e0b464d3b4be546c63c5188ffeb96e4.html @@ -99,8 +99,6 @@ Files   file  IProvider.cs [code]   -file  IrcPasswordType.cs [code] -  file  IrcProvider.cs [code]   file  Provider.cs [code] diff --git a/dir_61d7974a657c30011f457b376d274ee7.html b/dir_61d7974a657c30011f457b376d274ee7.html index 9bb71e5fa1..a47941bde3 100644 --- a/dir_61d7974a657c30011f457b376d274ee7.html +++ b/dir_61d7974a657c30011f457b376d274ee7.html @@ -116,6 +116,8 @@ Files   file  ConfigurationType.cs [code]   +file  DiscordConnectionStringBuilder.cs [code] +  file  DreamDaemon.cs [code]   file  DreamDaemonSecurity.cs [code] @@ -130,6 +132,10 @@ Files   file  InstanceUser.cs [code]   +file  IrcConnectionStringBuilder.cs [code] +  +file  IrcPasswordType.cs [code] +  file  Job.cs [code]   file  Repository.cs [code] diff --git a/dir_685b5aa2d8a661ab5bc3f68b331887b0.html b/dir_685b5aa2d8a661ab5bc3f68b331887b0.html index 218f75929a..c1123aed64 100644 --- a/dir_685b5aa2d8a661ab5bc3f68b331887b0.html +++ b/dir_685b5aa2d8a661ab5bc3f68b331887b0.html @@ -95,7 +95,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_689f0584ee97ae9ca308532531d9c5d2.html b/dir_689f0584ee97ae9ca308532531d9c5d2.html index 6c9954610a..fd4c537aa3 100644 --- a/dir_689f0584ee97ae9ca308532531d9c5d2.html +++ b/dir_689f0584ee97ae9ca308532531d9c5d2.html @@ -95,7 +95,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_74017a19412e2f90cc6ad11fcac82495.html b/dir_74017a19412e2f90cc6ad11fcac82495.html index 7f8fa7d13d..6be6b1c3a2 100644 --- a/dir_74017a19412e2f90cc6ad11fcac82495.html +++ b/dir_74017a19412e2f90cc6ad11fcac82495.html @@ -95,7 +95,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_0e792d76844bf2d7c79795a56a50ebec.html b/dir_87039fa7b841d848ca75981d64237477.html similarity index 92% rename from dir_0e792d76844bf2d7c79795a56a50ebec.html rename to dir_87039fa7b841d848ca75981d64237477.html index 23a89b0749..adbdcbf855 100644 --- a/dir_0e792d76844bf2d7c79795a56a50ebec.html +++ b/dir_87039fa7b841d848ca75981d64237477.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Tests/obj/Release Directory Reference +tgstation-server: tests/Tgstation.Server.Tests/obj/Debug Directory Reference @@ -84,18 +84,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netcoreapp2.1
directory  netcoreapp2.1
 
diff --git a/dir_87a4f2035ed18e30422f4b60aebc4381.html b/dir_87a4f2035ed18e30422f4b60aebc4381.html index 758e9f5842..bf16019436 100644 --- a/dir_87a4f2035ed18e30422f4b60aebc4381.html +++ b/dir_87a4f2035ed18e30422f4b60aebc4381.html @@ -95,7 +95,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_6db5af7fcfa17a34c7abf23e195af4db.html b/dir_9257d56e926145e494a5aa5279887044.html similarity index 94% rename from dir_6db5af7fcfa17a34c7abf23e195af4db.html rename to dir_9257d56e926145e494a5aa5279887044.html index c6f101d612..f1c9e65896 100644 --- a/dir_6db5af7fcfa17a34c7abf23e195af4db.html +++ b/dir_9257d56e926145e494a5aa5279887044.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Tests/obj/Release/netcoreapp2.1 Directory Reference +tgstation-server: tests/Tgstation.Server.Tests/obj/Debug/netcoreapp2.1 Directory Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_13bee0ed20ecc83b9bcd4b869b726d42.html b/dir_a3714ab037275c284988fc90b4d045a5.html similarity index 95% rename from dir_13bee0ed20ecc83b9bcd4b869b726d42.html rename to dir_a3714ab037275c284988fc90b4d045a5.html index 22a0a8b491..8da0204063 100644 --- a/dir_13bee0ed20ecc83b9bcd4b869b726d42.html +++ b/dir_a3714ab037275c284988fc90b4d045a5.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host.Watchdog/obj/Release/netstandard2.0 Directory Reference +tgstation-server: src/Tgstation.Server.Host.Watchdog/obj/Debug/netstandard2.0 Directory Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_fcc41a8faa41492aba200a49fc7d0aed.html b/dir_a8807eef6592489c6a360d587f5b1a66.html similarity index 93% rename from dir_fcc41a8faa41492aba200a49fc7d0aed.html rename to dir_a8807eef6592489c6a360d587f5b1a66.html index 31f97c168e..884a51bfcb 100644 --- a/dir_fcc41a8faa41492aba200a49fc7d0aed.html +++ b/dir_a8807eef6592489c6a360d587f5b1a66.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host.Console/obj/Release/netcoreapp2.1 Directory Reference +tgstation-server: src/Tgstation.Server.Host.Console/obj/Debug/netcoreapp2.1 Directory Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_b524b00ec2259c3c83b44909fd47f6e3.html b/dir_b524b00ec2259c3c83b44909fd47f6e3.html index 4404253378..77ef1a1ebc 100644 --- a/dir_b524b00ec2259c3c83b44909fd47f6e3.html +++ b/dir_b524b00ec2259c3c83b44909fd47f6e3.html @@ -95,7 +95,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_900257602d294c4a4799e88420919068.html b/dir_b551a02288ef39e099594710382363a0.html similarity index 95% rename from dir_900257602d294c4a4799e88420919068.html rename to dir_b551a02288ef39e099594710382363a0.html index bc123d8ffe..e45deffdd4 100644 --- a/dir_900257602d294c4a4799e88420919068.html +++ b/dir_b551a02288ef39e099594710382363a0.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Watchdog.Tests/obj/Release/netcoreapp2.1 Directory Reference +tgstation-server: tests/Tgstation.Server.Host.Watchdog.Tests/obj/Debug/netcoreapp2.1 Directory Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_e928c7e494884ad322b5f6005c2954e1.html b/dir_b87cbb2c60cf682b61fbfcceb9bced43.html similarity index 94% rename from dir_e928c7e494884ad322b5f6005c2954e1.html rename to dir_b87cbb2c60cf682b61fbfcceb9bced43.html index f848936f1a..1a266fb6a8 100644 --- a/dir_e928c7e494884ad322b5f6005c2954e1.html +++ b/dir_b87cbb2c60cf682b61fbfcceb9bced43.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host/obj/Release/netcoreapp2.1 Directory Reference +tgstation-server: src/Tgstation.Server.Host/obj/Debug/netcoreapp2.1 Directory Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_40138fd4849573e743490d583f6d06d6.html b/dir_b92573f47b933d7a3661c26d1af2098b.html similarity index 93% rename from dir_40138fd4849573e743490d583f6d06d6.html rename to dir_b92573f47b933d7a3661c26d1af2098b.html index bcd2178ae4..8845501d3a 100644 --- a/dir_40138fd4849573e743490d583f6d06d6.html +++ b/dir_b92573f47b933d7a3661c26d1af2098b.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Console.Tests/obj/Release Directory Reference +tgstation-server: tests/Tgstation.Server.Host.Console.Tests/obj/Debug Directory Reference @@ -84,18 +84,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netcoreapp2.1
directory  netcoreapp2.1
 
diff --git a/dir_18a5e30c14613227f95d12bd9fddc614.html b/dir_be9f3ed3485a880e3213506a311823d1.html similarity index 92% rename from dir_18a5e30c14613227f95d12bd9fddc614.html rename to dir_be9f3ed3485a880e3213506a311823d1.html index 2d245f3bd5..b2ae8d72ef 100644 --- a/dir_18a5e30c14613227f95d12bd9fddc614.html +++ b/dir_be9f3ed3485a880e3213506a311823d1.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Api.Tests/obj/Release Directory Reference +tgstation-server: tests/Tgstation.Server.Api.Tests/obj/Debug Directory Reference @@ -84,18 +84,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netcoreapp2.1
directory  netcoreapp2.1
 
diff --git a/dir_298f2a364723f4f4cea115538f944a94.html b/dir_c5f98c05147bcbba78e71856485f5049.html similarity index 95% rename from dir_298f2a364723f4f4cea115538f944a94.html rename to dir_c5f98c05147bcbba78e71856485f5049.html index 38023d24fb..aa66f45877 100644 --- a/dir_298f2a364723f4f4cea115538f944a94.html +++ b/dir_c5f98c05147bcbba78e71856485f5049.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Client.Tests/obj/Release/netcoreapp2.1 Directory Reference +tgstation-server: tests/Tgstation.Server.Client.Tests/obj/Debug/netcoreapp2.1 Directory Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_83a6f600c846cd444f33e44bb9b4f9cb.html b/dir_d8a279f7113e0073f6cd65dd050c1475.html similarity index 94% rename from dir_83a6f600c846cd444f33e44bb9b4f9cb.html rename to dir_d8a279f7113e0073f6cd65dd050c1475.html index a8019a6a86..67523f1be3 100644 --- a/dir_83a6f600c846cd444f33e44bb9b4f9cb.html +++ b/dir_d8a279f7113e0073f6cd65dd050c1475.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Api/obj/Release/netstandard2.0 Directory Reference +tgstation-server: src/Tgstation.Server.Api/obj/Debug/netstandard2.0 Directory Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_e0c1bccb03249d93bc42ac0968ce43fd.html b/dir_e0c1bccb03249d93bc42ac0968ce43fd.html index 3c01320942..97505085b3 100644 --- a/dir_e0c1bccb03249d93bc42ac0968ce43fd.html +++ b/dir_e0c1bccb03249d93bc42ac0968ce43fd.html @@ -95,7 +95,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_c65d377d3c73fb4a07b3637f9c0caf4d.html b/dir_e97d380c3eebaf0856a4ec180d749620.html similarity index 93% rename from dir_c65d377d3c73fb4a07b3637f9c0caf4d.html rename to dir_e97d380c3eebaf0856a4ec180d749620.html index 8850253690..06713a10d6 100644 --- a/dir_c65d377d3c73fb4a07b3637f9c0caf4d.html +++ b/dir_e97d380c3eebaf0856a4ec180d749620.html @@ -4,7 +4,7 @@ -tgstation-server: tests/Tgstation.Server.Host.Watchdog.Tests/obj/Release Directory Reference +tgstation-server: tests/Tgstation.Server.Host.Watchdog.Tests/obj/Debug Directory Reference @@ -84,18 +84,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netcoreapp2.1
directory  netcoreapp2.1
 
diff --git a/dir_6c8d8f20501e8e412458f542d0998195.html b/dir_ea36b5ab01ad6e8b607438a02ae91964.html similarity index 93% rename from dir_6c8d8f20501e8e412458f542d0998195.html rename to dir_ea36b5ab01ad6e8b607438a02ae91964.html index 0a0cbe414e..a76bac9127 100644 --- a/dir_6c8d8f20501e8e412458f542d0998195.html +++ b/dir_ea36b5ab01ad6e8b607438a02ae91964.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host.Watchdog/obj/Release Directory Reference +tgstation-server: src/Tgstation.Server.Host.Watchdog/obj/Debug Directory Reference @@ -84,18 +84,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netstandard2.0
directory  netstandard2.0
 
diff --git a/dir_f0581cdd528a911f791df57d0f438bcb.html b/dir_f0581cdd528a911f791df57d0f438bcb.html index 92fb7d8e33..56a5aee82e 100644 --- a/dir_f0581cdd528a911f791df57d0f438bcb.html +++ b/dir_f0581cdd528a911f791df57d0f438bcb.html @@ -95,7 +95,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/dir_f2a82faa4c4beef364292694cefca86d.html b/dir_f2a82faa4c4beef364292694cefca86d.html index 04f101e4a7..79081b08a0 100644 --- a/dir_f2a82faa4c4beef364292694cefca86d.html +++ b/dir_f2a82faa4c4beef364292694cefca86d.html @@ -97,6 +97,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); Files file  ChatBot.cs [code]   +file  ChatConnectionStringBuilder.cs [code] +  file  CompileJob.cs [code]   file  DreamDaemonLaunchParameters.cs [code] diff --git a/dir_383ca23c40357ede4cca827b4a365c76.html b/dir_f88948f9bde0555374e8a59a663bdfc9.html similarity index 94% rename from dir_383ca23c40357ede4cca827b4a365c76.html rename to dir_f88948f9bde0555374e8a59a663bdfc9.html index f0da92e2cc..f4794e143b 100644 --- a/dir_383ca23c40357ede4cca827b4a365c76.html +++ b/dir_f88948f9bde0555374e8a59a663bdfc9.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Client/obj/Release/netstandard2.0 Directory Reference +tgstation-server: src/Tgstation.Server.Client/obj/Debug/netstandard2.0 Directory Reference @@ -84,7 +84,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
diff --git a/dir_7a9d035dcfcd6d9c7aa1efef4b6b58a4.html b/dir_fab1362e86740a2c573ef875dd474a67.html similarity index 92% rename from dir_7a9d035dcfcd6d9c7aa1efef4b6b58a4.html rename to dir_fab1362e86740a2c573ef875dd474a67.html index 445fc56436..1dedab1f09 100644 --- a/dir_7a9d035dcfcd6d9c7aa1efef4b6b58a4.html +++ b/dir_fab1362e86740a2c573ef875dd474a67.html @@ -4,7 +4,7 @@ -tgstation-server: src/Tgstation.Server.Host.Console/obj/Release Directory Reference +tgstation-server: src/Tgstation.Server.Host.Console/obj/Debug Directory Reference @@ -84,18 +84,18 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
-
Release Directory Reference
+
Debug Directory Reference
- +

Directories

directory  netcoreapp2.1
directory  netcoreapp2.1
 
diff --git a/dir_fb12273b841438c2f2c9b5bc5398c636.html b/dir_fb12273b841438c2f2c9b5bc5398c636.html index a687d3898e..aaa3255164 100644 --- a/dir_fb12273b841438c2f2c9b5bc5398c636.html +++ b/dir_fb12273b841438c2f2c9b5bc5398c636.html @@ -95,7 +95,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); - +

Directories

directory  Release
directory  Debug
 
diff --git a/files.html b/files.html index c939884c9d..9a1c41bdf9 100644 --- a/files.html +++ b/files.html @@ -95,15 +95,16 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');   Models   Internal  ChatBot.cs - CompileJob.cs - DreamDaemonLaunchParameters.cs - DreamDaemonSettings.cs - Job.cs - RepositorySettings.cs - RevisionInformation.cs - TestMerge.cs - TestMergeBase.cs - User.cs + ChatConnectionStringBuilder.cs + CompileJob.cs + DreamDaemonLaunchParameters.cs + DreamDaemonSettings.cs + Job.cs + RepositorySettings.cs + RevisionInformation.cs + TestMerge.cs + TestMergeBase.cs + User.cs  Administration.cs  Byond.cs  ChatBot.cs @@ -112,25 +113,28 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  CompileJob.cs  ConfigurationFile.cs  ConfigurationType.cs - DreamDaemon.cs - DreamDaemonSecurity.cs - DreamDaemonStatus.cs - DreamMaker.cs - ErrorMessage.cs - Instance.cs - InstanceUser.cs - Job.cs - Repository.cs - RevisionInformation.cs - ServerInformation.cs - TestMerge.cs - TestMergeParameters.cs - Token.cs - User.cs - UserUpdate.cs + DiscordConnectionStringBuilder.cs + DreamDaemon.cs + DreamDaemonSecurity.cs + DreamDaemonStatus.cs + DreamMaker.cs + ErrorMessage.cs + Instance.cs + InstanceUser.cs + IrcConnectionStringBuilder.cs + IrcPasswordType.cs + Job.cs + Repository.cs + RevisionInformation.cs + ServerInformation.cs + TestMerge.cs + TestMergeParameters.cs + Token.cs + User.cs + UserUpdate.cs   obj -  Release -  netstandard2.0 +  Debug +  netstandard2.0  Tgstation.Server.Api.AssemblyInfo.cs   Rights  AdministrationRights.cs @@ -167,8 +171,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  JobsClient.cs  RepositoryClient.cs   obj -  Release -  netstandard2.0 +  Debug +  netstandard2.0  Tgstation.Server.Client.AssemblyInfo.cs  AdministrationClient.cs  ApiClient.cs @@ -225,9 +229,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');   Providers  DiscordProvider.cs  IProvider.cs - IrcPasswordType.cs - IrcProvider.cs - Provider.cs + IrcProvider.cs + Provider.cs  Channel.cs  ChannelMapping.cs  Chat.cs @@ -397,8 +400,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  WatchdogReattachInformation.cs  WatchdogReattachInformationBase.cs   obj -  Release -  netcoreapp2.1 +  Debug +  netcoreapp2.1  Tgstation.Server.Host.AssemblyInfo.cs  Tgstation.Server.Host.RazorAssemblyInfo.cs   Security @@ -429,8 +432,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  ServerFactory.cs   Tgstation.Server.Host.Console   obj -  Release -  netcoreapp2.1 +  Debug +  netcoreapp2.1  Tgstation.Server.Host.Console.AssemblyInfo.cs  AssemblyInfo.cs  Program.cs @@ -442,8 +445,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  ServerService.cs   Tgstation.Server.Host.Watchdog   obj -  Release -  netstandard2.0 +  Debug +  netstandard2.0  Tgstation.Server.Host.Watchdog.AssemblyInfo.cs  AssemblyInfo.cs  IWatchdog.cs @@ -453,8 +456,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');   tests   Tgstation.Server.Api.Tests   obj -  Release -  netcoreapp2.1 +  Debug +  netcoreapp2.1  Tgstation.Server.Api.Tests.AssemblyInfo.cs  Tgstation.Server.Api.Tests.Program.cs   Rights @@ -464,16 +467,16 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');   Components  TestDreamDaemonClient.cs   obj -  Release -  netcoreapp2.1 +  Debug +  netcoreapp2.1  Tgstation.Server.Client.Tests.AssemblyInfo.cs  Tgstation.Server.Client.Tests.Program.cs  TestApiClient.cs  TestServerClientFactory.cs   Tgstation.Server.Host.Console.Tests   obj -  Release -  netcoreapp2.1 +  Debug +  netcoreapp2.1  Tgstation.Server.Host.Console.Tests.AssemblyInfo.cs  Tgstation.Server.Host.Console.Tests.Program.cs  TestProgram.cs @@ -483,16 +486,16 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  TestServerService.cs   Tgstation.Server.Host.Tests   obj -  Release -  netcoreapp2.1 +  Debug +  netcoreapp2.1  Tgstation.Server.Host.Tests.AssemblyInfo.cs  Tgstation.Server.Host.Tests.Program.cs   Security  TestAuthenticationContext.cs   Tgstation.Server.Host.Watchdog.Tests   obj -  Release -  netcoreapp2.1 +  Debug +  netcoreapp2.1  Tgstation.Server.Host.Watchdog.Tests.AssemblyInfo.cs  Tgstation.Server.Host.Watchdog.Tests.Program.cs  TestWatchdog.cs @@ -503,8 +506,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  ConfigurationTest.cs  InstanceTest.cs   obj -  Release -  netcoreapp2.1 +  Debug +  netcoreapp2.1  Tgstation.Server.Tests.AssemblyInfo.cs  Tgstation.Server.Tests.Program.cs  AdministrationTest.cs diff --git a/functions.html b/functions.html index 4577e7db80..9ff09ce7d8 100644 --- a/functions.html +++ b/functions.html @@ -178,6 +178,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Client.IServerClient , Tgstation.Server.Client.ServerClient +
  • Address +: Tgstation.Server.Api.Models.IrcConnectionStringBuilder +
  • address : Tgstation.Server.Host.Components.Chat.Providers.IrcProvider
  • @@ -263,7 +266,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); : Tgstation.Server.Client.ServerClientFactory
  • ApiConflictException() -: Tgstation.Server.Client.ApiConflictException +: Tgstation.Server.Client.ApiConflictException
  • ApiController() : Tgstation.Server.Host.Controllers.ApiController @@ -273,7 +276,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Host.Controllers.ApiController
  • ApiMismatchException() -: Tgstation.Server.Client.ApiMismatchException +: Tgstation.Server.Client.ApiMismatchException
  • ApiValidateOnly : Tgstation.Server.Host.Components.Interop.JsonFile @@ -334,7 +337,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • AuthenticationContext : Tgstation.Server.Host.Controllers.ApiController -, Tgstation.Server.Host.Security.AuthenticationContext +, Tgstation.Server.Host.Security.AuthenticationContext
  • AuthenticationContextFactory() : Tgstation.Server.Host.Security.AuthenticationContextFactory diff --git a/functions_b.html b/functions_b.html index edd398bde9..854a1a9ff0 100644 --- a/functions_b.html +++ b/functions_b.html @@ -145,6 +145,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Host.Components.Chat.Providers.IrcProvider , Tgstation.Server.Host.Components.Chat.Providers.Provider
  • +
  • BotToken +: Tgstation.Server.Api.Models.DiscordConnectionStringBuilder +
  • botToken : Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider
  • diff --git a/functions_c.html b/functions_c.html index 8206225dc9..c3e15074df 100644 --- a/functions_c.html +++ b/functions_c.html @@ -488,7 +488,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); : Tgstation.Server.Host.Core.Application
  • ConflictException() -: Tgstation.Server.Client.ConflictException +: Tgstation.Server.Client.ConflictException
  • Connect() : Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider @@ -511,6 +511,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); : Tgstation.Server.Api.Models.Internal.ChatBot , Tgstation.Server.Host.Configuration.DatabaseConfiguration
  • +
  • ConnectionStringBuilder +: Tgstation.Server.Api.Models.Internal.ChatBot +
  • connectionsUpdated : Tgstation.Server.Host.Components.Chat.Chat
  • @@ -563,11 +566,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • Create< TBody, TResult >() : Tgstation.Server.Client.ApiClient -, Tgstation.Server.Client.IApiClient +, Tgstation.Server.Client.IApiClient
  • Create< TResult >() -: Tgstation.Server.Client.ApiClient -, Tgstation.Server.Client.IApiClient +: Tgstation.Server.Client.ApiClient +, Tgstation.Server.Client.IApiClient
  • CreateApiClient() : Tgstation.Server.Client.ApiClientFactory @@ -587,8 +590,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • CreateClient() : Tgstation.Server.Client.IInstanceManagerClient , Tgstation.Server.Client.InstanceManagerClient -, Tgstation.Server.Host.Core.GitHubClientFactory -, Tgstation.Server.Host.Core.IGitHubClientFactory +, Tgstation.Server.Host.Core.GitHubClientFactory +, Tgstation.Server.Host.Core.IGitHubClientFactory
  • CreatedAt : Tgstation.Server.Api.Models.Internal.User @@ -629,8 +632,8 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Host.ServerFactory
  • CreateServerClient() -: Tgstation.Server.Client.IServerClientFactory -, Tgstation.Server.Client.ServerClientFactory +: Tgstation.Server.Client.IServerClientFactory +, Tgstation.Server.Client.ServerClientFactory
  • CreateSymbolicLink() : Tgstation.Server.Host.IO.ISymlinkFactory @@ -642,7 +645,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); : Tgstation.Server.Host.IO.DefaultIOManager
  • CreateSystemIdentity() -: Tgstation.Server.Host.Security.ISystemIdentityFactory +: Tgstation.Server.Host.Security.ISystemIdentityFactory , Tgstation.Server.Host.Security.PosixSystemIdentityFactory , Tgstation.Server.Host.Security.WindowsSystemIdentityFactory
  • diff --git a/functions_d.html b/functions_d.html index 6f1153f467..ef4bf4131a 100644 --- a/functions_d.html +++ b/functions_d.html @@ -257,6 +257,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • DiscordChannelId : Tgstation.Server.Api.Models.ChatChannel
  • +
  • DiscordConnectionStringBuilder() +: Tgstation.Server.Api.Models.DiscordConnectionStringBuilder +
  • DiscordProvider() : Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider
  • diff --git a/functions_func_d.html b/functions_func_d.html index bab6078a64..55d96bfc1d 100644 --- a/functions_func_d.html +++ b/functions_func_d.html @@ -203,6 +203,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Host.Components.Chat.Providers.IrcProvider , Tgstation.Server.Host.Components.Chat.Providers.Provider +
  • DiscordConnectionStringBuilder() +: Tgstation.Server.Api.Models.DiscordConnectionStringBuilder +
  • DiscordProvider() : Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider
  • @@ -227,7 +230,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Host.Components.Repository.Repository , Tgstation.Server.Host.Components.Repository.RepositoryManager , Tgstation.Server.Host.Components.StaticFiles.Configuration -, Tgstation.Server.Host.Components.Watchdog.SessionController +, Tgstation.Server.Host.Components.Watchdog.SessionController , Tgstation.Server.Host.Components.Watchdog.Watchdog , Tgstation.Server.Host.Core.JobHandler , Tgstation.Server.Host.Core.JobManager diff --git a/functions_func_i.html b/functions_func_i.html index 889f4e0c5a..128208913d 100644 --- a/functions_func_i.html +++ b/functions_func_i.html @@ -201,6 +201,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Host.Components.Chat.Commands.RevisionCommand , Tgstation.Server.Host.Components.Chat.Commands.VersionCommand +
  • IrcConnectionStringBuilder() +: Tgstation.Server.Api.Models.IrcConnectionStringBuilder +
  • IrcProvider() : Tgstation.Server.Host.Components.Chat.Providers.IrcProvider
  • diff --git a/functions_func_t.html b/functions_func_t.html index 55337682b9..fb4dd4cacd 100644 --- a/functions_func_t.html +++ b/functions_func_t.html @@ -212,7 +212,10 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Host.Models.User
  • ToString() -: Tgstation.Server.Host.Components.Watchdog.LaunchResult +: Tgstation.Server.Api.Models.DiscordConnectionStringBuilder +, Tgstation.Server.Api.Models.Internal.ChatConnectionStringBuilder +, Tgstation.Server.Api.Models.IrcConnectionStringBuilder +, Tgstation.Server.Host.Components.Watchdog.LaunchResult , Tgstation.Server.Host.Components.Watchdog.WatchdogReattachInformation , Tgstation.Server.Host.Models.ReattachInformationBase
  • diff --git a/functions_i.html b/functions_i.html index af5122ae3e..bca5871c9a 100644 --- a/functions_i.html +++ b/functions_i.html @@ -389,6 +389,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • IrcChannel : Tgstation.Server.Api.Models.ChatChannel
  • +
  • IrcConnectionStringBuilder() +: Tgstation.Server.Api.Models.IrcConnectionStringBuilder +
  • IrcProvider() : Tgstation.Server.Host.Components.Chat.Providers.IrcProvider
  • diff --git a/functions_n.html b/functions_n.html index b73bdb510d..57df8fc99a 100644 --- a/functions_n.html +++ b/functions_n.html @@ -165,6 +165,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • nextPort : Tgstation.Server.Host.Components.Watchdog.SessionController
  • +
  • Nickname +: Tgstation.Server.Api.Models.IrcConnectionStringBuilder +
  • nickname : Tgstation.Server.Host.Components.Chat.Providers.IrcProvider
  • diff --git a/functions_p.html b/functions_p.html index 864d4cd184..97a9865132 100644 --- a/functions_p.html +++ b/functions_p.html @@ -134,6 +134,7 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • Password : Tgstation.Server.Api.ApiHeaders +, Tgstation.Server.Api.Models.IrcConnectionStringBuilder , Tgstation.Server.Api.Models.UserUpdate
  • password @@ -148,6 +149,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • passwordHasher : Tgstation.Server.Host.Security.CryptographySuite
  • +
  • PasswordType +: Tgstation.Server.Api.Models.IrcConnectionStringBuilder +
  • passwordType : Tgstation.Server.Host.Components.Chat.Providers.IrcProvider
  • @@ -166,6 +170,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • PopulateApi() : Tgstation.Server.Host.Controllers.RepositoryController
  • +
  • Port +: Tgstation.Server.Api.Models.IrcConnectionStringBuilder +
  • port : Tgstation.Server.Host.Components.Chat.Providers.IrcProvider
  • diff --git a/functions_prop.html b/functions_prop.html index c6cd1e71b5..42f6a06657 100644 --- a/functions_prop.html +++ b/functions_prop.html @@ -159,6 +159,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); : Tgstation.Server.Host.Components.Byond.ByondManager , Tgstation.Server.Host.Components.Byond.IByondManager +
  • Address +: Tgstation.Server.Api.Models.IrcConnectionStringBuilder +
  • Administration : Tgstation.Server.Client.IServerClient , Tgstation.Server.Client.ServerClient diff --git a/functions_prop_b.html b/functions_prop_b.html index d084d5ab27..9bd88db961 100644 --- a/functions_prop_b.html +++ b/functions_prop_b.html @@ -134,6 +134,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Host.Components.Chat.Providers.IProvider , Tgstation.Server.Host.Components.Chat.Providers.Provider
  • +
  • BotToken +: Tgstation.Server.Api.Models.DiscordConnectionStringBuilder +
  • Bravo : Tgstation.Server.Host.Components.Watchdog.WatchdogLaunchResult , Tgstation.Server.Host.Components.Watchdog.WatchdogReattachInformation diff --git a/functions_prop_c.html b/functions_prop_c.html index 59a444da0a..8ca8a3370e 100644 --- a/functions_prop_c.html +++ b/functions_prop_c.html @@ -246,6 +246,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); : Tgstation.Server.Api.Models.Internal.ChatBot , Tgstation.Server.Host.Configuration.DatabaseConfiguration
  • +
  • ConnectionStringBuilder +: Tgstation.Server.Api.Models.Internal.ChatBot +
  • Content : Tgstation.Server.Api.Models.ConfigurationFile , Tgstation.Server.Host.Components.Chat.Providers.Message diff --git a/functions_prop_n.html b/functions_prop_n.html index a1ffd882ff..0f742439bf 100644 --- a/functions_prop_n.html +++ b/functions_prop_n.html @@ -139,6 +139,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • NextAction : Tgstation.Server.Host.Components.Watchdog.MonitorState
  • +
  • Nickname +: Tgstation.Server.Api.Models.IrcConnectionStringBuilder +
  • Number : Tgstation.Server.Api.Models.TestMergeParameters
  • diff --git a/functions_prop_p.html b/functions_prop_p.html index 41d00bbac0..94915a0348 100644 --- a/functions_prop_p.html +++ b/functions_prop_p.html @@ -130,17 +130,22 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
  • Password : Tgstation.Server.Api.ApiHeaders +, Tgstation.Server.Api.Models.IrcConnectionStringBuilder , Tgstation.Server.Api.Models.UserUpdate
  • PasswordHash : Tgstation.Server.Host.Models.User
  • +
  • PasswordType +: Tgstation.Server.Api.Models.IrcConnectionStringBuilder +
  • Path : Tgstation.Server.Api.Models.ConfigurationFile , Tgstation.Server.Api.Models.Instance
  • Port -: Tgstation.Server.Host.Components.Watchdog.ISessionController +: Tgstation.Server.Api.Models.IrcConnectionStringBuilder +, Tgstation.Server.Host.Components.Watchdog.ISessionController , Tgstation.Server.Host.Components.Watchdog.SessionController , Tgstation.Server.Host.Models.ReattachInformationBase
  • diff --git a/functions_prop_u.html b/functions_prop_u.html index 122d7d1ad9..4a1a7e0b44 100644 --- a/functions_prop_u.html +++ b/functions_prop_u.html @@ -166,6 +166,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search'); , Tgstation.Server.Host.Models.DatabaseContext< TParentContext > , Tgstation.Server.Host.Models.IDatabaseContext +
  • UseSsl +: Tgstation.Server.Api.Models.IrcConnectionStringBuilder +
  • diff --git a/functions_prop_v.html b/functions_prop_v.html index d43314d032..476576a01e 100644 --- a/functions_prop_v.html +++ b/functions_prop_v.html @@ -123,6 +123,9 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');  

    - v -

    diff --git a/functions_v.html b/functions_v.html index c728c8f753..4275067eb6 100644 --- a/functions_v.html +++ b/functions_v.html @@ -127,6 +127,11 @@ var searchBox = new SearchBox("searchBox", "search",false,'Search');
    Here is a list of all class members with links to the classes they belong to:

    - v -

    diff --git a/namespace_tgstation_1_1_server_1_1_api_1_1_models.html b/namespace_tgstation_1_1_server_1_1_api_1_1_models.html index d1d65d1e2c..31670f839d 100644 --- a/namespace_tgstation_1_1_server_1_1_api_1_1_models.html +++ b/namespace_tgstation_1_1_server_1_1_api_1_1_models.html @@ -120,6 +120,9 @@ Classes class  ConfigurationFile  Represents a game configuration file. Create and delete actions uncerimonuously overwrite/delete files More...
      +class  DiscordConnectionStringBuilder + ChatConnectionStringBuilder for ChatProvider.Discord More...
    +  class  DreamDaemon  Represents an instance of BYOND's DreamDaemon game server. Create action starts the server. Delete action shuts down the server More...
      @@ -135,6 +138,9 @@ Classes class  InstanceUser  Represents a Users permissions in an Instance More...
      +class  IrcConnectionStringBuilder + ChatConnectionStringBuilder for ChatProvider.Irc More...
    +  class  Job  Represents a long running job on the server. Model is read-only, updates attempt to cancel the job More...
      @@ -185,6 +191,12 @@ Enumerations } The status of DreamDaemon More...
      +enum  IrcPasswordType { IrcPasswordType.Server, +IrcPasswordType.Sasl, +IrcPasswordType.NickServ + } Represents the type of a password for a ChatProvider.Irc More...
    + + 

    Enumeration Type Documentation

    @@ -335,6 +347,44 @@ Enumerations
    + +
    +
    + + + + + +
    + + + + +
    enum Tgstation.Server.Api.Models.IrcPasswordType
    +
    +strong
    +
    + +

    Represents the type of a password for a ChatProvider.Irc

    + + + + +
    Enumerator
    Server  +

    Use server authentication

    +
    Sasl  +

    Use PLAIN sasl authentication

    +
    NickServ  +

    Use NickServ authentication

    +
    + +

    Definition at line 6 of file IrcPasswordType.cs.

    +
    7  {
    11  Server,
    15  Sasl,
    19  NickServ
    20  }
    + + +
    +
    +
    Tgstation.Server.Host.Components.Chat.Providers Namespace Reference
    @@ -113,55 +112,7 @@ Classes   class  Provider   - - - - -

    -Enumerations

    enum  IrcPasswordType { IrcPasswordType.Server, -IrcPasswordType.Sasl, -IrcPasswordType.NickServ - }
     Represents the type of a password passed to the constructor of IrcProvider More...
     
    -

    Enumeration Type Documentation

    - -
    -
    - -

    Represents the type of a password passed to the constructor of IrcProvider

    - - - - -
    Enumerator
    Server  -

    Use server authentication

    -
    Sasl  -

    Use PLAIN sasl authentication

    -
    NickServ  -

    Use NickServ authentication

    -
    - -

    Definition at line 6 of file IrcPasswordType.cs.

    - -
    -