commit 54fb39d15c6cdc131cb0d5a914609d914fab8f7e Author: anian Date: Sun Mar 15 23:33:02 2026 +0800 1.4.3原版 diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..03538a4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,520 @@ +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = false +max_line_length = 120 +tab_width = 4 +ij_continuation_indent_size = 8 +ij_formatter_off_tag = @formatter:off +ij_formatter_on_tag = @formatter:on +ij_formatter_tags_enabled = false +ij_smart_tabs = false +ij_wrap_on_typing = false + +[*.java] +max_line_length = 100 +ij_continuation_indent_size = 4 +ij_java_align_consecutive_assignments = false +ij_java_align_consecutive_variable_declarations = false +ij_java_align_group_field_declarations = false +ij_java_align_multiline_annotation_parameters = false +ij_java_align_multiline_array_initializer_expression = false +ij_java_align_multiline_assignment = false +ij_java_align_multiline_binary_operation = false +ij_java_align_multiline_chained_methods = false +ij_java_align_multiline_extends_list = false +ij_java_align_multiline_for = true +ij_java_align_multiline_method_parentheses = false +ij_java_align_multiline_parameters = false +ij_java_align_multiline_parameters_in_calls = false +ij_java_align_multiline_parenthesized_expression = false +ij_java_align_multiline_records = true +ij_java_align_multiline_resources = true +ij_java_align_multiline_ternary_operation = false +ij_java_align_multiline_text_blocks = false +ij_java_align_multiline_throws_list = false +ij_java_align_subsequent_simple_methods = false +ij_java_align_throws_keyword = false +ij_java_annotation_parameter_wrap = off +ij_java_array_initializer_new_line_after_left_brace = false +ij_java_array_initializer_right_brace_on_new_line = false +ij_java_array_initializer_wrap = normal +ij_java_assert_statement_colon_on_next_line = false +ij_java_assert_statement_wrap = normal +ij_java_assignment_wrap = normal +ij_java_binary_operation_sign_on_next_line = true +ij_java_binary_operation_wrap = normal +ij_java_blank_lines_after_anonymous_class_header = 0 +ij_java_blank_lines_after_class_header = 0 +ij_java_blank_lines_after_imports = 1 +ij_java_blank_lines_after_package = 1 +ij_java_blank_lines_around_class = 1 +ij_java_blank_lines_around_field = 0 +ij_java_blank_lines_around_field_in_interface = 0 +ij_java_blank_lines_around_initializer = 1 +ij_java_blank_lines_around_method = 1 +ij_java_blank_lines_around_method_in_interface = 1 +ij_java_blank_lines_before_class_end = 0 +ij_java_blank_lines_before_imports = 0 +ij_java_blank_lines_before_method_body = 0 +ij_java_blank_lines_before_package = 1 +ij_java_block_brace_style = end_of_line +ij_java_block_comment_at_first_column = false +ij_java_call_parameters_new_line_after_left_paren = false +ij_java_call_parameters_right_paren_on_new_line = false +ij_java_call_parameters_wrap = normal +ij_java_case_statement_on_separate_line = true +ij_java_catch_on_new_line = false +ij_java_class_annotation_wrap = split_into_lines +ij_java_class_brace_style = end_of_line +ij_java_class_count_to_use_import_on_demand = 999 +ij_java_class_names_in_javadoc = 1 +ij_java_do_not_indent_top_level_class_members = false +ij_java_do_not_wrap_after_single_annotation = false +ij_java_do_while_brace_force = always +ij_java_doc_add_blank_line_after_description = true +ij_java_doc_add_blank_line_after_param_comments = false +ij_java_doc_add_blank_line_after_return = false +ij_java_doc_add_p_tag_on_empty_lines = true +ij_java_doc_align_exception_comments = true +ij_java_doc_align_param_comments = false +ij_java_doc_do_not_wrap_if_one_line = false +ij_java_doc_enable_formatting = true +ij_java_doc_enable_leading_asterisks = true +ij_java_doc_indent_on_continuation = false +ij_java_doc_keep_empty_lines = true +ij_java_doc_keep_empty_parameter_tag = true +ij_java_doc_keep_empty_return_tag = true +ij_java_doc_keep_empty_throws_tag = true +ij_java_doc_keep_invalid_tags = true +ij_java_doc_param_description_on_new_line = false +ij_java_doc_preserve_line_breaks = false +ij_java_doc_use_throws_not_exception_tag = true +ij_java_else_on_new_line = false +ij_java_enum_constants_wrap = normal +ij_java_extends_keyword_wrap = normal +ij_java_extends_list_wrap = normal +ij_java_field_annotation_wrap = split_into_lines +ij_java_finally_on_new_line = false +ij_java_for_brace_force = always +ij_java_for_statement_new_line_after_left_paren = false +ij_java_for_statement_right_paren_on_new_line = false +ij_java_for_statement_wrap = normal +ij_java_generate_final_locals = false +ij_java_generate_final_parameters = false +ij_java_if_brace_force = always +ij_java_imports_layout = $*, |, *, |, * +ij_java_indent_case_from_switch = true +ij_java_insert_inner_class_imports = false +ij_java_insert_override_annotation = true +ij_java_keep_blank_lines_before_right_brace = 2 +ij_java_keep_blank_lines_between_package_declaration_and_header = 2 +ij_java_keep_blank_lines_in_code = 2 +ij_java_keep_blank_lines_in_declarations = 2 +ij_java_keep_control_statement_in_one_line = true +ij_java_keep_first_column_comment = true +ij_java_keep_indents_on_empty_lines = false +ij_java_keep_line_breaks = true +ij_java_keep_multiple_expressions_in_one_line = false +ij_java_keep_simple_blocks_in_one_line = false +ij_java_keep_simple_classes_in_one_line = false +ij_java_keep_simple_lambdas_in_one_line = false +ij_java_keep_simple_methods_in_one_line = false +ij_java_label_indent_absolute = false +ij_java_label_indent_size = 0 +ij_java_lambda_brace_style = end_of_line +ij_java_layout_static_imports_separately = true +ij_java_line_comment_add_space = true +ij_java_line_comment_at_first_column = false +ij_java_method_annotation_wrap = split_into_lines +ij_java_method_brace_style = end_of_line +ij_java_method_call_chain_wrap = normal +ij_java_method_parameters_new_line_after_left_paren = false +ij_java_method_parameters_right_paren_on_new_line = false +ij_java_method_parameters_wrap = normal +ij_java_modifier_list_wrap = false +ij_java_names_count_to_use_import_on_demand = 999 +ij_java_new_line_after_lparen_in_record_header = false +ij_java_parameter_annotation_wrap = normal +ij_java_parentheses_expression_new_line_after_left_paren = false +ij_java_parentheses_expression_right_paren_on_new_line = false +ij_java_place_assignment_sign_on_next_line = false +ij_java_prefer_longer_names = true +ij_java_prefer_parameters_wrap = false +ij_java_record_components_wrap = normal +ij_java_repeat_synchronized = true +ij_java_replace_instanceof_and_cast = false +ij_java_replace_null_check = true +ij_java_replace_sum_lambda_with_method_ref = true +ij_java_resource_list_new_line_after_left_paren = false +ij_java_resource_list_right_paren_on_new_line = false +ij_java_resource_list_wrap = normal +ij_java_rparen_on_new_line_in_record_header = false +ij_java_space_after_closing_angle_bracket_in_type_argument = false +ij_java_space_after_colon = true +ij_java_space_after_comma = true +ij_java_space_after_comma_in_type_arguments = true +ij_java_space_after_for_semicolon = true +ij_java_space_after_quest = true +ij_java_space_after_type_cast = true +ij_java_space_before_annotation_array_initializer_left_brace = false +ij_java_space_before_annotation_parameter_list = false +ij_java_space_before_array_initializer_left_brace = true +ij_java_space_before_catch_keyword = true +ij_java_space_before_catch_left_brace = true +ij_java_space_before_catch_parentheses = true +ij_java_space_before_class_left_brace = true +ij_java_space_before_colon = true +ij_java_space_before_colon_in_foreach = true +ij_java_space_before_comma = false +ij_java_space_before_do_left_brace = true +ij_java_space_before_else_keyword = true +ij_java_space_before_else_left_brace = true +ij_java_space_before_finally_keyword = true +ij_java_space_before_finally_left_brace = true +ij_java_space_before_for_left_brace = true +ij_java_space_before_for_parentheses = true +ij_java_space_before_for_semicolon = false +ij_java_space_before_if_left_brace = true +ij_java_space_before_if_parentheses = true +ij_java_space_before_method_call_parentheses = false +ij_java_space_before_method_left_brace = true +ij_java_space_before_method_parentheses = false +ij_java_space_before_opening_angle_bracket_in_type_parameter = false +ij_java_space_before_quest = true +ij_java_space_before_switch_left_brace = true +ij_java_space_before_switch_parentheses = true +ij_java_space_before_synchronized_left_brace = true +ij_java_space_before_synchronized_parentheses = true +ij_java_space_before_try_left_brace = true +ij_java_space_before_try_parentheses = true +ij_java_space_before_type_parameter_list = false +ij_java_space_before_while_keyword = true +ij_java_space_before_while_left_brace = true +ij_java_space_before_while_parentheses = true +ij_java_space_inside_one_line_enum_braces = false +ij_java_space_within_empty_array_initializer_braces = false +ij_java_space_within_empty_method_call_parentheses = false +ij_java_space_within_empty_method_parentheses = false +ij_java_spaces_around_additive_operators = true +ij_java_spaces_around_assignment_operators = true +ij_java_spaces_around_bitwise_operators = true +ij_java_spaces_around_equality_operators = true +ij_java_spaces_around_lambda_arrow = true +ij_java_spaces_around_logical_operators = true +ij_java_spaces_around_method_ref_dbl_colon = false +ij_java_spaces_around_multiplicative_operators = true +ij_java_spaces_around_relational_operators = true +ij_java_spaces_around_shift_operators = true +ij_java_spaces_around_type_bounds_in_type_parameters = true +ij_java_spaces_around_unary_operator = false +ij_java_spaces_within_angle_brackets = false +ij_java_spaces_within_annotation_parentheses = false +ij_java_spaces_within_array_initializer_braces = false +ij_java_spaces_within_braces = false +ij_java_spaces_within_brackets = false +ij_java_spaces_within_cast_parentheses = false +ij_java_spaces_within_catch_parentheses = false +ij_java_spaces_within_for_parentheses = false +ij_java_spaces_within_if_parentheses = false +ij_java_spaces_within_method_call_parentheses = false +ij_java_spaces_within_method_parentheses = false +ij_java_spaces_within_parentheses = false +ij_java_spaces_within_switch_parentheses = false +ij_java_spaces_within_synchronized_parentheses = false +ij_java_spaces_within_try_parentheses = false +ij_java_spaces_within_while_parentheses = false +ij_java_special_else_if_treatment = true +ij_java_subclass_name_suffix = Impl +ij_java_ternary_operation_signs_on_next_line = true +ij_java_ternary_operation_wrap = normal +ij_java_test_name_suffix = Test +ij_java_throws_keyword_wrap = normal +ij_java_throws_list_wrap = normal +ij_java_use_external_annotations = false +ij_java_use_fq_class_names = false +ij_java_use_relative_indents = false +ij_java_use_single_class_imports = true +ij_java_variable_annotation_wrap = normal +ij_java_visibility = public +ij_java_while_brace_force = always +ij_java_while_on_new_line = false +ij_java_wrap_comments = false +ij_java_wrap_first_method_in_call_chain = false +ij_java_wrap_long_lines = true + +[*.properties] +ij_properties_align_group_field_declarations = false +ij_properties_keep_blank_lines = false +ij_properties_key_value_delimiter = equals +ij_properties_spaces_around_key_value_delimiter = false + +[.editorconfig] +ij_editorconfig_align_group_field_declarations = false +ij_editorconfig_space_after_colon = false +ij_editorconfig_space_after_comma = true +ij_editorconfig_space_before_colon = false +ij_editorconfig_space_before_comma = false +ij_editorconfig_spaces_around_assignment_operators = true + +[{*.ant, *.fxml, *.jhm, *.jnlp, *.jrxml, *.jspx, *.pom, *.rng, *.tagx, *.tld, *.wsdl, *.xml, *.xsd, *.xsl, *.xslt, *.xul}] +ij_xml_align_attributes = true +ij_xml_align_text = false +ij_xml_attribute_wrap = normal +ij_xml_block_comment_at_first_column = true +ij_xml_keep_blank_lines = 2 +ij_xml_keep_indents_on_empty_lines = false +ij_xml_keep_line_breaks = true +ij_xml_keep_line_breaks_in_text = true +ij_xml_keep_whitespaces = false +ij_xml_keep_whitespaces_around_cdata = preserve +ij_xml_keep_whitespaces_inside_cdata = false +ij_xml_line_comment_at_first_column = true +ij_xml_space_after_tag_name = false +ij_xml_space_around_equals_in_attribute = false +ij_xml_space_inside_empty_tag = false +ij_xml_text_wrap = normal + +[{*.bash, *.sh, *.zsh}] +indent_size = 2 +tab_width = 2 +ij_shell_binary_ops_start_line = false +ij_shell_keep_column_alignment_padding = false +ij_shell_minify_program = false +ij_shell_redirect_followed_by_space = false +ij_shell_switch_cases_indented = false + +[{*.gant, *.gradle, *.groovy, *.gy}] +ij_groovy_align_group_field_declarations = false +ij_groovy_align_multiline_array_initializer_expression = false +ij_groovy_align_multiline_assignment = false +ij_groovy_align_multiline_binary_operation = false +ij_groovy_align_multiline_chained_methods = false +ij_groovy_align_multiline_extends_list = false +ij_groovy_align_multiline_for = true +ij_groovy_align_multiline_list_or_map = true +ij_groovy_align_multiline_method_parentheses = false +ij_groovy_align_multiline_parameters = true +ij_groovy_align_multiline_parameters_in_calls = false +ij_groovy_align_multiline_resources = true +ij_groovy_align_multiline_ternary_operation = false +ij_groovy_align_multiline_throws_list = false +ij_groovy_align_named_args_in_map = true +ij_groovy_align_throws_keyword = false +ij_groovy_array_initializer_new_line_after_left_brace = false +ij_groovy_array_initializer_right_brace_on_new_line = false +ij_groovy_array_initializer_wrap = off +ij_groovy_assert_statement_wrap = off +ij_groovy_assignment_wrap = off +ij_groovy_binary_operation_wrap = off +ij_groovy_blank_lines_after_class_header = 0 +ij_groovy_blank_lines_after_imports = 1 +ij_groovy_blank_lines_after_package = 1 +ij_groovy_blank_lines_around_class = 1 +ij_groovy_blank_lines_around_field = 0 +ij_groovy_blank_lines_around_field_in_interface = 0 +ij_groovy_blank_lines_around_method = 1 +ij_groovy_blank_lines_around_method_in_interface = 1 +ij_groovy_blank_lines_before_imports = 1 +ij_groovy_blank_lines_before_method_body = 0 +ij_groovy_blank_lines_before_package = 0 +ij_groovy_block_brace_style = end_of_line +ij_groovy_block_comment_at_first_column = true +ij_groovy_call_parameters_new_line_after_left_paren = false +ij_groovy_call_parameters_right_paren_on_new_line = false +ij_groovy_call_parameters_wrap = off +ij_groovy_catch_on_new_line = false +ij_groovy_class_annotation_wrap = split_into_lines +ij_groovy_class_brace_style = end_of_line +ij_groovy_class_count_to_use_import_on_demand = 5 +ij_groovy_do_while_brace_force = never +ij_groovy_else_on_new_line = false +ij_groovy_enum_constants_wrap = off +ij_groovy_extends_keyword_wrap = off +ij_groovy_extends_list_wrap = off +ij_groovy_field_annotation_wrap = split_into_lines +ij_groovy_finally_on_new_line = false +ij_groovy_for_brace_force = never +ij_groovy_for_statement_new_line_after_left_paren = false +ij_groovy_for_statement_right_paren_on_new_line = false +ij_groovy_for_statement_wrap = off +ij_groovy_if_brace_force = never +ij_groovy_import_annotation_wrap = 2 +ij_groovy_indent_case_from_switch = true +ij_groovy_indent_label_blocks = true +ij_groovy_insert_inner_class_imports = false +ij_groovy_keep_blank_lines_before_right_brace = 2 +ij_groovy_keep_blank_lines_in_code = 2 +ij_groovy_keep_blank_lines_in_declarations = 2 +ij_groovy_keep_control_statement_in_one_line = true +ij_groovy_keep_first_column_comment = true +ij_groovy_keep_indents_on_empty_lines = false +ij_groovy_keep_line_breaks = true +ij_groovy_keep_multiple_expressions_in_one_line = false +ij_groovy_keep_simple_blocks_in_one_line = false +ij_groovy_keep_simple_classes_in_one_line = true +ij_groovy_keep_simple_lambdas_in_one_line = true +ij_groovy_keep_simple_methods_in_one_line = true +ij_groovy_label_indent_absolute = false +ij_groovy_label_indent_size = 0 +ij_groovy_lambda_brace_style = end_of_line +ij_groovy_layout_static_imports_separately = true +ij_groovy_line_comment_add_space = false +ij_groovy_line_comment_at_first_column = true +ij_groovy_method_annotation_wrap = split_into_lines +ij_groovy_method_brace_style = end_of_line +ij_groovy_method_call_chain_wrap = off +ij_groovy_method_parameters_new_line_after_left_paren = false +ij_groovy_method_parameters_right_paren_on_new_line = false +ij_groovy_method_parameters_wrap = off +ij_groovy_modifier_list_wrap = false +ij_groovy_names_count_to_use_import_on_demand = 3 +ij_groovy_parameter_annotation_wrap = off +ij_groovy_parentheses_expression_new_line_after_left_paren = false +ij_groovy_parentheses_expression_right_paren_on_new_line = false +ij_groovy_prefer_parameters_wrap = false +ij_groovy_resource_list_new_line_after_left_paren = false +ij_groovy_resource_list_right_paren_on_new_line = false +ij_groovy_resource_list_wrap = off +ij_groovy_space_after_assert_separator = true +ij_groovy_space_after_colon = true +ij_groovy_space_after_comma = true +ij_groovy_space_after_comma_in_type_arguments = true +ij_groovy_space_after_for_semicolon = true +ij_groovy_space_after_quest = true +ij_groovy_space_after_type_cast = true +ij_groovy_space_before_annotation_parameter_list = false +ij_groovy_space_before_array_initializer_left_brace = false +ij_groovy_space_before_assert_separator = false +ij_groovy_space_before_catch_keyword = true +ij_groovy_space_before_catch_left_brace = true +ij_groovy_space_before_catch_parentheses = true +ij_groovy_space_before_class_left_brace = true +ij_groovy_space_before_closure_left_brace = true +ij_groovy_space_before_colon = true +ij_groovy_space_before_comma = false +ij_groovy_space_before_do_left_brace = true +ij_groovy_space_before_else_keyword = true +ij_groovy_space_before_else_left_brace = true +ij_groovy_space_before_finally_keyword = true +ij_groovy_space_before_finally_left_brace = true +ij_groovy_space_before_for_left_brace = true +ij_groovy_space_before_for_parentheses = true +ij_groovy_space_before_for_semicolon = false +ij_groovy_space_before_if_left_brace = true +ij_groovy_space_before_if_parentheses = true +ij_groovy_space_before_method_call_parentheses = false +ij_groovy_space_before_method_left_brace = true +ij_groovy_space_before_method_parentheses = false +ij_groovy_space_before_quest = true +ij_groovy_space_before_switch_left_brace = true +ij_groovy_space_before_switch_parentheses = true +ij_groovy_space_before_synchronized_left_brace = true +ij_groovy_space_before_synchronized_parentheses = true +ij_groovy_space_before_try_left_brace = true +ij_groovy_space_before_try_parentheses = true +ij_groovy_space_before_while_keyword = true +ij_groovy_space_before_while_left_brace = true +ij_groovy_space_before_while_parentheses = true +ij_groovy_space_in_named_argument = true +ij_groovy_space_in_named_argument_before_colon = false +ij_groovy_space_within_empty_array_initializer_braces = false +ij_groovy_space_within_empty_method_call_parentheses = false +ij_groovy_spaces_around_additive_operators = true +ij_groovy_spaces_around_assignment_operators = true +ij_groovy_spaces_around_bitwise_operators = true +ij_groovy_spaces_around_equality_operators = true +ij_groovy_spaces_around_lambda_arrow = true +ij_groovy_spaces_around_logical_operators = true +ij_groovy_spaces_around_multiplicative_operators = true +ij_groovy_spaces_around_regex_operators = true +ij_groovy_spaces_around_relational_operators = true +ij_groovy_spaces_around_shift_operators = true +ij_groovy_spaces_within_annotation_parentheses = false +ij_groovy_spaces_within_array_initializer_braces = false +ij_groovy_spaces_within_braces = true +ij_groovy_spaces_within_brackets = false +ij_groovy_spaces_within_cast_parentheses = false +ij_groovy_spaces_within_catch_parentheses = false +ij_groovy_spaces_within_for_parentheses = false +ij_groovy_spaces_within_gstring_injection_braces = false +ij_groovy_spaces_within_if_parentheses = false +ij_groovy_spaces_within_list_or_map = false +ij_groovy_spaces_within_method_call_parentheses = false +ij_groovy_spaces_within_method_parentheses = false +ij_groovy_spaces_within_parentheses = false +ij_groovy_spaces_within_switch_parentheses = false +ij_groovy_spaces_within_synchronized_parentheses = false +ij_groovy_spaces_within_try_parentheses = false +ij_groovy_spaces_within_tuple_expression = false +ij_groovy_spaces_within_while_parentheses = false +ij_groovy_special_else_if_treatment = true +ij_groovy_ternary_operation_wrap = off +ij_groovy_throws_keyword_wrap = off +ij_groovy_throws_list_wrap = off +ij_groovy_use_flying_geese_braces = false +ij_groovy_use_fq_class_names = false +ij_groovy_use_fq_class_names_in_javadoc = true +ij_groovy_use_relative_indents = false +ij_groovy_use_single_class_imports = true +ij_groovy_variable_annotation_wrap = off +ij_groovy_while_brace_force = never +ij_groovy_while_on_new_line = false +ij_groovy_wrap_long_lines = false + +[{*.har, *.json}] +indent_size = 2 +ij_json_keep_blank_lines_in_code = 0 +ij_json_keep_indents_on_empty_lines = false +ij_json_keep_line_breaks = true +ij_json_space_after_colon = true +ij_json_space_after_comma = true +ij_json_space_before_colon = true +ij_json_space_before_comma = false +ij_json_spaces_within_braces = false +ij_json_spaces_within_brackets = false +ij_json_wrap_long_lines = false + +[{*.htm, *.html, *.sht, *.shtm, *.shtml}] +ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3 +ij_html_align_attributes = true +ij_html_align_text = false +ij_html_attribute_wrap = normal +ij_html_block_comment_at_first_column = true +ij_html_do_not_align_children_of_min_lines = 0 +ij_html_do_not_break_if_inline_tags = title, h1, h2, h3, h4, h5, h6, p +ij_html_do_not_indent_children_of_tags = html, body, thead, tbody, tfoot +ij_html_enforce_quotes = false +ij_html_inline_tags = a, abbr, acronym, b, basefont, bdo, big, br, cite, cite, code, dfn, em, font, i, img, input, kbd, label, q, s, samp, select, small, span, strike, strong, sub, sup, textarea, tt, u, var +ij_html_keep_blank_lines = 2 +ij_html_keep_indents_on_empty_lines = false +ij_html_keep_line_breaks = true +ij_html_keep_line_breaks_in_text = true +ij_html_keep_whitespaces = false +ij_html_keep_whitespaces_inside = span, pre, textarea +ij_html_line_comment_at_first_column = true +ij_html_new_line_after_last_attribute = never +ij_html_new_line_before_first_attribute = never +ij_html_quote_style = double +ij_html_remove_new_line_before_tags = br +ij_html_space_after_tag_name = false +ij_html_space_around_equality_in_attribute = false +ij_html_space_inside_empty_tag = false +ij_html_text_wrap = normal + +[{*.yaml, *.yml}] +indent_size = 2 +ij_yaml_keep_indents_on_empty_lines = false +ij_yaml_keep_line_breaks = true + +[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}] +charset = utf-8 +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +end_of_line = lf +max_line_length = 100 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..85f12f4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,80 @@ +### Maven +target/ +logs/ +!.mvn/wrapper/maven-wrapper.jar + +### Gradle +.gradle +/build/ +/out/ +!gradle/wrapper/gradle-wrapper.jar +bin/ + +### Node +node_modules/ + +### STS +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA +.idea +*.iws +*.iml +*.ipr +log/ + +### NetBeans +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +### Mac +.DS_Store +*/.DS_Store + +### VSCode +*.project +*.factorypath +.vscode +!.vscode/extensions.json + +### Compiled class file +*.class + +### Log file +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +### BlueJ files +*.ctxt + +### Package Files +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +### Local file +application-local.yml +application-local.yaml +application-local.properties + +/ui/build/ +/workplace/ +/src/main/resources/console/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..20d40b6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/OWNERS b/OWNERS new file mode 100644 index 0000000..eeb425e --- /dev/null +++ b/OWNERS @@ -0,0 +1,10 @@ +reviewers: +- ruibaby +- guqing +- JohnNiang +- wangzhen-fit2cloud + +approvers: +- ruibaby +- guqing +- JohnNiang diff --git a/README.md b/README.md new file mode 100644 index 0000000..165dcc8 --- /dev/null +++ b/README.md @@ -0,0 +1,235 @@ +# plugin-friends +> 1.3.4版本重构朋友圈插件 +> 如果使用了之前版本,请清掉之前的所有数据,不然会有问题 +> `订阅` `订阅文章` 数据全部清除 + +![Snipaste_2024-09-08_23-38-00.png](https://api.minio.yyds.pink/halo-docs/2024/09/Snipaste_2024-09-08_23-38-00.png) + +* 朋友圈管理插件, 支持在 Console 进行管理以及为主题端提供 `/friends` 页面路由。 +* 提供对 RSS 链接的订阅功能,支持获取其订阅内容 + +## 朋友圈页面 +![Snipaste_2024-09-08_22-48-49.png](https://api.minio.yyds.pink/halo-docs/2024/09/Snipaste_2024-09-08_22-48-49.png) + +## 后台页面 +![Snipaste_2024-09-08_22-47-55.png](https://api.minio.yyds.pink/halo-docs/2024/09/Snipaste_2024-09-08_22-47-55.png) + +## 编辑器插入 RSS展示 + +### 编辑器 +* 列表视图 + ![Snipaste_2024-09-08_22-51-10.png](https://api.minio.yyds.pink/halo-docs/2024/09/Snipaste_2024-09-08_22-51-10.png) +* 网格视图 + ![Snipaste_2024-09-08_22-51-17.png](https://api.minio.yyds.pink/halo-docs/2024/09/Snipaste_2024-09-08_22-51-17.png) + +### 页面 +* 列表视图 + ![Snipaste_2024-09-08_22-51-57.png](https://api.minio.yyds.pink/halo-docs/2024/09/Snipaste_2024-09-08_22-51-57.png) +* 网格视图 + ![Snipaste_2024-09-08_22-51-42.png](https://api.minio.yyds.pink/halo-docs/2024/09/Snipaste_2024-09-08_22-51-42.png) + +## 使用方式 +* 在应用市场下载并启用。 +* 启用插件之后会在 Console 的左侧添加一个`朋友圈`的菜单项,点击即可进入`朋友圈`管理页面。 + +## 特性 +* 内置模板,无需主题支持,但也可以通过主题自定义模板。 + +## 📃文档 +https://docs.kunkunyu.com/docs/plugin-friends + +## 主题适配 +目前此插件为主题端提供了 `/friends` 路由,模板为 `friends.html`,也提供了 Finder API,可以将瞬间列表渲染到任何地方。 + +## 模板变量 +路由信息 +* 模板路径:/templates/friends.html +* 访问路径:/friends + +### 变量 +* friends + +### 变量类型 +* UrlContextListResult<[FriendPostVo](#FriendPostVo)> + +#### 示例 +```bash + +``` + +## Finder API + +### listAll() + +#### 描述 +获取全部订阅文章内容。 + +#### 参数 +无 + +#### 返回值 +List<[FriendPostVo](#FriendPostVo)> + +#### 示例 + +```bash +
+
+ +
+ avatar + + + +
+
+
+``` + +### list(page, size) + +#### 描述 +根据分页参数获取订阅文章内容。 + +#### 参数 +* page: int - 分页页码,从 1 开始 +* size: int - 分页条数 + +#### 返回值 +[ListResult](#ListResult) + +#### 示例 + +```bash + +
+
+ +
+ avatar + + + +
+
+
+
+ +
+
+``` + +## 类型定义 +### FriendPostVo +```bash +{ + "metadata": { + "name": "string", // 唯一标识 + "generateName": "string", + "version": 0, + "creationTimestamp": "2024-01-16T16:13:17.925131783Z", // 创建时间 + }, + "apiVersion": "friend.moony.la/v1alpha1", + "kind": "FriendPost", + "spec": { + "authorUrl": "string", // 作者链接 + "author": "string", // 作者名称 + "logo": "string", // 作者logo + "title": "string", // 标题 + "postLink": "string", // 链接 + "description": "string", // 内容 + "pubDate": "date", // 文章发布时间 + } +} +``` + +### ListResult + +```bash +{ + "page": 0, // 当前页码 + "size": 0, // 每页条数 + "total": 0, // 总条数 + "items": "List<#FriendPostVo>", // 订阅文章列表数据 + "first": true, // 是否为第一页 + "last": true, // 是否为最后一页 + "hasNext": true, // 是否有下一页 + "hasPrevious": true, // 是否有上一页 + "totalPages": 0 // 总页数 +} +``` + +### UrlContextListResult + +```bash +{ + "page": 0, // 当前页码 + "size": 0, // 每页条数 + "total": 0, // 总条数 + "items": "List<#FriendPostVo>", // 订阅文章列表数据 + "first": true, // 是否为第一页 + "last": true, // 是否为最后一页 + "hasNext": true, // 是否有下一页 + "hasPrevious": true, // 是否有上一页 + "totalPages": 0, // 总页数 + "prevUrl": "string", // 上一页链接 + "nextUrl": "string" // 下一页链接 +} +``` + +## 开发环境 + +```bash +git clone git@github.com:chengzhongxue/plugin-friends.git + +# 或者当你 fork 之后 + +git clone git@github.com:{your_github_id}/plugin-friends.git +``` + +```bash +cd path/to/plugin-friends +``` + +```bash +# macOS / Linux +./gradlew pnpmInstall + +# Windows +./gradlew.bat pnpmInstall +``` + +```bash +# macOS / Linux +./gradlew haloServer + +# Windows +./gradlew.bat haloServer +``` + +```bash +# macOS / Linux +./gradlew build + +# Windows +./gradlew.bat build +``` \ No newline at end of file diff --git a/api-docs/openapi/v3_0/pluginFriendsApi.json b/api-docs/openapi/v3_0/pluginFriendsApi.json new file mode 100644 index 0000000..b0ca3cb --- /dev/null +++ b/api-docs/openapi/v3_0/pluginFriendsApi.json @@ -0,0 +1,1520 @@ +{ + "openapi" : "3.0.1", + "info" : { + "title" : "Halo", + "version" : "2.20.11" + }, + "servers" : [ { + "url" : "http://localhost:65429", + "description" : "Generated server url" + } ], + "security" : [ { + "basicAuth" : [ ], + "bearerAuth" : [ ] + } ], + "paths" : { + "/apis/api.friend.moony.la/v1alpha1/friendposts" : { + "get" : { + "description" : "List friendPost.", + "operationId" : "ListFriendPosts", + "parameters" : [ { + "description" : "Page number. Default is 0.", + "in" : "query", + "name" : "page", + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "description" : "Size number. Default is 0.", + "in" : "query", + "name" : "size", + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "description" : "Label selector. e.g.: hidden!=true", + "in" : "query", + "name" : "labelSelector", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "description" : "Field selector. e.g.: metadata.name==halo", + "in" : "query", + "name" : "fieldSelector", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "description" : "Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.", + "in" : "query", + "name" : "sort", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "description" : "CronFriendPost filtered by linkName.", + "in" : "query", + "name" : "linkName", + "schema" : { + "type" : "string" + } + }, { + "description" : "CronFriendPost filtered by keyword.", + "in" : "query", + "name" : "keyword", + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "default" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/FriendPostList" + } + } + }, + "description" : "default response" + } + }, + "tags" : [ "api.friend.moony.la/v1alpha1/FriendPost" ] + } + }, + "/apis/api.friend.moony.la/v1alpha1/friendposts/-/delete" : { + "delete" : { + "description" : "Delete All RssFeedSyncLog.", + "operationId" : "DeleteAllRssFeedSyncLog", + "responses" : { + "default" : { + "content" : { }, + "description" : "default response" + } + }, + "tags" : [ "api.friend.moony.la/v1alpha1/FriendPost" ] + } + }, + "/apis/api.friend.moony.la/v1alpha1/parsingrss" : { + "get" : { + "operationId" : "ParsingRss", + "parameters" : [ { + "in" : "query", + "name" : "rssUrl", + "required" : true, + "schema" : { + "type" : "string" + } + }, { + "in" : "query", + "name" : "fetchLimitNumber", + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "default" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/RssDetail" + } + } + }, + "description" : "default response" + } + }, + "tags" : [ "api.friend.moony.la/v1alpha1/FriendPost" ] + } + }, + "/apis/api.friend.moony.la/v1alpha1/rsssynclogs" : { + "get" : { + "description" : "List RssSyncLog.", + "operationId" : "ListRssSyncLogs", + "parameters" : [ { + "description" : "Page number. Default is 0.", + "in" : "query", + "name" : "page", + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "description" : "Size number. Default is 0.", + "in" : "query", + "name" : "size", + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "description" : "Label selector. e.g.: hidden!=true", + "in" : "query", + "name" : "labelSelector", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "description" : "Field selector. e.g.: metadata.name==halo", + "in" : "query", + "name" : "fieldSelector", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "description" : "Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.", + "in" : "query", + "name" : "sort", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "in" : "query", + "name" : "keyword", + "schema" : { + "type" : "string" + } + }, { + "in" : "query", + "name" : "state", + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "default" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/ListedRssSyncLogList" + } + } + }, + "description" : "default response" + } + }, + "tags" : [ "api.friend.moony.la/v1alpha1/FriendPost" ] + } + }, + "/apis/api.friend.moony.la/v1alpha1/syncrssfeed/{name}" : { + "post" : { + "operationId" : "SyncRssFeed", + "parameters" : [ { + "in" : "path", + "name" : "name", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "default" : { + "content" : { }, + "description" : "default response" + } + }, + "tags" : [ "api.friend.moony.la/v1alpha1/FriendPost" ] + } + }, + "/apis/friend.moony.la/v1alpha1/cronfriendposts" : { + "get" : { + "description" : "List CronFriendPost", + "operationId" : "listCronFriendPost", + "parameters" : [ { + "description" : "Page number. Default is 0.", + "in" : "query", + "name" : "page", + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "description" : "Size number. Default is 0.", + "in" : "query", + "name" : "size", + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "description" : "Label selector. e.g.: hidden!=true", + "in" : "query", + "name" : "labelSelector", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "description" : "Field selector. e.g.: metadata.name==halo", + "in" : "query", + "name" : "fieldSelector", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "description" : "Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.", + "in" : "query", + "name" : "sort", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } ], + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/CronFriendPostList" + } + } + }, + "description" : "Response cronfriendposts" + } + }, + "tags" : [ "CronFriendPostV1alpha1" ] + }, + "post" : { + "description" : "Create CronFriendPost", + "operationId" : "createCronFriendPost", + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/CronFriendPost" + } + } + }, + "description" : "Fresh cronfriendpost" + }, + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/CronFriendPost" + } + } + }, + "description" : "Response cronfriendposts created just now" + } + }, + "tags" : [ "CronFriendPostV1alpha1" ] + } + }, + "/apis/friend.moony.la/v1alpha1/cronfriendposts/{name}" : { + "delete" : { + "description" : "Delete CronFriendPost", + "operationId" : "deleteCronFriendPost", + "parameters" : [ { + "description" : "Name of cronfriendpost", + "in" : "path", + "name" : "name", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "Response cronfriendpost deleted just now" + } + }, + "tags" : [ "CronFriendPostV1alpha1" ] + }, + "get" : { + "description" : "Get CronFriendPost", + "operationId" : "getCronFriendPost", + "parameters" : [ { + "description" : "Name of cronfriendpost", + "in" : "path", + "name" : "name", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/CronFriendPost" + } + } + }, + "description" : "Response single cronfriendpost" + } + }, + "tags" : [ "CronFriendPostV1alpha1" ] + }, + "patch" : { + "description" : "Patch CronFriendPost", + "operationId" : "patchCronFriendPost", + "parameters" : [ { + "description" : "Name of cronfriendpost", + "in" : "path", + "name" : "name", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json-patch+json" : { + "schema" : { + "$ref" : "#/components/schemas/JsonPatch" + } + } + } + }, + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/CronFriendPost" + } + } + }, + "description" : "Response cronfriendpost patched just now" + } + }, + "tags" : [ "CronFriendPostV1alpha1" ] + }, + "put" : { + "description" : "Update CronFriendPost", + "operationId" : "updateCronFriendPost", + "parameters" : [ { + "description" : "Name of cronfriendpost", + "in" : "path", + "name" : "name", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/CronFriendPost" + } + } + }, + "description" : "Updated cronfriendpost" + }, + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/CronFriendPost" + } + } + }, + "description" : "Response cronfriendposts updated just now" + } + }, + "tags" : [ "CronFriendPostV1alpha1" ] + } + }, + "/apis/friend.moony.la/v1alpha1/friendposts" : { + "get" : { + "description" : "List FriendPost", + "operationId" : "listFriendPost", + "parameters" : [ { + "description" : "Page number. Default is 0.", + "in" : "query", + "name" : "page", + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "description" : "Size number. Default is 0.", + "in" : "query", + "name" : "size", + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "description" : "Label selector. e.g.: hidden!=true", + "in" : "query", + "name" : "labelSelector", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "description" : "Field selector. e.g.: metadata.name==halo", + "in" : "query", + "name" : "fieldSelector", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "description" : "Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.", + "in" : "query", + "name" : "sort", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } ], + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/FriendPostList" + } + } + }, + "description" : "Response friendposts" + } + }, + "tags" : [ "FriendPostV1alpha1" ] + }, + "post" : { + "description" : "Create FriendPost", + "operationId" : "createFriendPost", + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/FriendPost" + } + } + }, + "description" : "Fresh friendpost" + }, + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/FriendPost" + } + } + }, + "description" : "Response friendposts created just now" + } + }, + "tags" : [ "FriendPostV1alpha1" ] + } + }, + "/apis/friend.moony.la/v1alpha1/friendposts/{name}" : { + "delete" : { + "description" : "Delete FriendPost", + "operationId" : "deleteFriendPost", + "parameters" : [ { + "description" : "Name of friendpost", + "in" : "path", + "name" : "name", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "Response friendpost deleted just now" + } + }, + "tags" : [ "FriendPostV1alpha1" ] + }, + "get" : { + "description" : "Get FriendPost", + "operationId" : "getFriendPost", + "parameters" : [ { + "description" : "Name of friendpost", + "in" : "path", + "name" : "name", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/FriendPost" + } + } + }, + "description" : "Response single friendpost" + } + }, + "tags" : [ "FriendPostV1alpha1" ] + }, + "patch" : { + "description" : "Patch FriendPost", + "operationId" : "patchFriendPost", + "parameters" : [ { + "description" : "Name of friendpost", + "in" : "path", + "name" : "name", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json-patch+json" : { + "schema" : { + "$ref" : "#/components/schemas/JsonPatch" + } + } + } + }, + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/FriendPost" + } + } + }, + "description" : "Response friendpost patched just now" + } + }, + "tags" : [ "FriendPostV1alpha1" ] + }, + "put" : { + "description" : "Update FriendPost", + "operationId" : "updateFriendPost", + "parameters" : [ { + "description" : "Name of friendpost", + "in" : "path", + "name" : "name", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/FriendPost" + } + } + }, + "description" : "Updated friendpost" + }, + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/FriendPost" + } + } + }, + "description" : "Response friendposts updated just now" + } + }, + "tags" : [ "FriendPostV1alpha1" ] + } + }, + "/apis/friend.moony.la/v1alpha1/rssfeedsynclogs" : { + "get" : { + "description" : "List RssFeedSyncLog", + "operationId" : "listRssFeedSyncLog", + "parameters" : [ { + "description" : "Page number. Default is 0.", + "in" : "query", + "name" : "page", + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "description" : "Size number. Default is 0.", + "in" : "query", + "name" : "size", + "schema" : { + "type" : "integer", + "format" : "int32" + } + }, { + "description" : "Label selector. e.g.: hidden!=true", + "in" : "query", + "name" : "labelSelector", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "description" : "Field selector. e.g.: metadata.name==halo", + "in" : "query", + "name" : "fieldSelector", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + }, { + "description" : "Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.", + "in" : "query", + "name" : "sort", + "schema" : { + "type" : "array", + "items" : { + "type" : "string" + } + } + } ], + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/RssFeedSyncLogList" + } + } + }, + "description" : "Response rssfeedsynclogs" + } + }, + "tags" : [ "RssFeedSyncLogV1alpha1" ] + }, + "post" : { + "description" : "Create RssFeedSyncLog", + "operationId" : "createRssFeedSyncLog", + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/RssFeedSyncLog" + } + } + }, + "description" : "Fresh rssfeedsynclog" + }, + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/RssFeedSyncLog" + } + } + }, + "description" : "Response rssfeedsynclogs created just now" + } + }, + "tags" : [ "RssFeedSyncLogV1alpha1" ] + } + }, + "/apis/friend.moony.la/v1alpha1/rssfeedsynclogs/{name}" : { + "delete" : { + "description" : "Delete RssFeedSyncLog", + "operationId" : "deleteRssFeedSyncLog", + "parameters" : [ { + "description" : "Name of rssfeedsynclog", + "in" : "path", + "name" : "name", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "description" : "Response rssfeedsynclog deleted just now" + } + }, + "tags" : [ "RssFeedSyncLogV1alpha1" ] + }, + "get" : { + "description" : "Get RssFeedSyncLog", + "operationId" : "getRssFeedSyncLog", + "parameters" : [ { + "description" : "Name of rssfeedsynclog", + "in" : "path", + "name" : "name", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/RssFeedSyncLog" + } + } + }, + "description" : "Response single rssfeedsynclog" + } + }, + "tags" : [ "RssFeedSyncLogV1alpha1" ] + }, + "patch" : { + "description" : "Patch RssFeedSyncLog", + "operationId" : "patchRssFeedSyncLog", + "parameters" : [ { + "description" : "Name of rssfeedsynclog", + "in" : "path", + "name" : "name", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "application/json-patch+json" : { + "schema" : { + "$ref" : "#/components/schemas/JsonPatch" + } + } + } + }, + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/RssFeedSyncLog" + } + } + }, + "description" : "Response rssfeedsynclog patched just now" + } + }, + "tags" : [ "RssFeedSyncLogV1alpha1" ] + }, + "put" : { + "description" : "Update RssFeedSyncLog", + "operationId" : "updateRssFeedSyncLog", + "parameters" : [ { + "description" : "Name of rssfeedsynclog", + "in" : "path", + "name" : "name", + "required" : true, + "schema" : { + "type" : "string" + } + } ], + "requestBody" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/RssFeedSyncLog" + } + } + }, + "description" : "Updated rssfeedsynclog" + }, + "responses" : { + "200" : { + "content" : { + "*/*" : { + "schema" : { + "$ref" : "#/components/schemas/RssFeedSyncLog" + } + } + }, + "description" : "Response rssfeedsynclogs updated just now" + } + }, + "tags" : [ "RssFeedSyncLogV1alpha1" ] + } + } + }, + "components" : { + "schemas" : { + "AddOperation" : { + "required" : [ "op", "path", "value" ], + "type" : "object", + "properties" : { + "op" : { + "type" : "string", + "enum" : [ "add" ] + }, + "path" : { + "pattern" : "^(/[^/~]*(~[01][^/~]*)*)*$", + "type" : "string", + "description" : "A JSON Pointer path pointing to the location to move/copy from.", + "example" : "/a/b/c" + }, + "value" : { + "description" : "Value can be any JSON value" + } + } + }, + "Channel" : { + "type" : "object", + "properties" : { + "description" : { + "type" : "string" + }, + "items" : { + "type" : "array", + "items" : { + "$ref" : "#/components/schemas/Item" + } + }, + "link" : { + "type" : "string" + }, + "title" : { + "type" : "string" + } + } + }, + "CopyOperation" : { + "required" : [ "op", "from", "path" ], + "type" : "object", + "properties" : { + "from" : { + "pattern" : "^(/[^/~]*(~[01][^/~]*)*)*$", + "type" : "string", + "description" : "A JSON Pointer path pointing to the location to move/copy from.", + "example" : "/a/b/c" + }, + "op" : { + "type" : "string", + "enum" : [ "copy" ] + }, + "path" : { + "pattern" : "^(/[^/~]*(~[01][^/~]*)*)*$", + "type" : "string", + "description" : "A JSON Pointer path pointing to the location to move/copy from.", + "example" : "/a/b/c" + } + } + }, + "CronFriendPost" : { + "required" : [ "apiVersion", "kind", "metadata" ], + "type" : "object", + "properties" : { + "apiVersion" : { + "type" : "string" + }, + "kind" : { + "type" : "string" + }, + "metadata" : { + "$ref" : "#/components/schemas/Metadata" + }, + "spec" : { + "$ref" : "#/components/schemas/CronSpec" + }, + "status" : { + "$ref" : "#/components/schemas/CronStatus" + } + } + }, + "CronFriendPostList" : { + "required" : [ "first", "hasNext", "hasPrevious", "items", "last", "page", "size", "total", "totalPages" ], + "type" : "object", + "properties" : { + "first" : { + "type" : "boolean", + "description" : "Indicates whether current page is the first page." + }, + "hasNext" : { + "type" : "boolean", + "description" : "Indicates whether current page has previous page." + }, + "hasPrevious" : { + "type" : "boolean", + "description" : "Indicates whether current page has previous page." + }, + "items" : { + "type" : "array", + "description" : "A chunk of items.", + "items" : { + "$ref" : "#/components/schemas/CronFriendPost" + } + }, + "last" : { + "type" : "boolean", + "description" : "Indicates whether current page is the last page." + }, + "page" : { + "type" : "integer", + "description" : "Page number, starts from 1. If not set or equal to 0, it means no pagination.", + "format" : "int32" + }, + "size" : { + "type" : "integer", + "description" : "Size of each page. If not set or equal to 0, it means no pagination.", + "format" : "int32" + }, + "total" : { + "type" : "integer", + "description" : "Total elements.", + "format" : "int64" + }, + "totalPages" : { + "type" : "integer", + "description" : "Indicates total pages.", + "format" : "int64" + } + } + }, + "CronSpec" : { + "type" : "object", + "properties" : { + "cron" : { + "type" : "string" + }, + "disableSyncList" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "successfulRetainLimit" : { + "minimum" : 0, + "type" : "integer", + "format" : "int32" + }, + "suspend" : { + "type" : "boolean" + }, + "timezone" : { + "type" : "string" + } + } + }, + "CronStatus" : { + "type" : "object", + "properties" : { + "lastScheduledTimestamp" : { + "type" : "string", + "format" : "date-time" + }, + "nextSchedulingTimestamp" : { + "type" : "string", + "format" : "date-time" + } + } + }, + "FriendPost" : { + "required" : [ "apiVersion", "kind", "metadata", "spec" ], + "type" : "object", + "properties" : { + "apiVersion" : { + "type" : "string" + }, + "kind" : { + "type" : "string" + }, + "metadata" : { + "$ref" : "#/components/schemas/Metadata" + }, + "spec" : { + "$ref" : "#/components/schemas/FriendPostSpec" + } + } + }, + "FriendPostList" : { + "required" : [ "first", "hasNext", "hasPrevious", "items", "last", "page", "size", "total", "totalPages" ], + "type" : "object", + "properties" : { + "first" : { + "type" : "boolean", + "description" : "Indicates whether current page is the first page." + }, + "hasNext" : { + "type" : "boolean", + "description" : "Indicates whether current page has previous page." + }, + "hasPrevious" : { + "type" : "boolean", + "description" : "Indicates whether current page has previous page." + }, + "items" : { + "type" : "array", + "description" : "A chunk of items.", + "items" : { + "$ref" : "#/components/schemas/FriendPost" + } + }, + "last" : { + "type" : "boolean", + "description" : "Indicates whether current page is the last page." + }, + "page" : { + "type" : "integer", + "description" : "Page number, starts from 1. If not set or equal to 0, it means no pagination.", + "format" : "int32" + }, + "size" : { + "type" : "integer", + "description" : "Size of each page. If not set or equal to 0, it means no pagination.", + "format" : "int32" + }, + "total" : { + "type" : "integer", + "description" : "Total elements.", + "format" : "int64" + }, + "totalPages" : { + "type" : "integer", + "description" : "Indicates total pages.", + "format" : "int64" + } + } + }, + "FriendPostSpec" : { + "type" : "object", + "properties" : { + "author" : { + "type" : "string" + }, + "authorUrl" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "linkName" : { + "type" : "string" + }, + "logo" : { + "type" : "string" + }, + "postLink" : { + "type" : "string" + }, + "pubDate" : { + "type" : "string", + "format" : "date-time" + }, + "title" : { + "type" : "string" + } + } + }, + "Item" : { + "type" : "object", + "properties" : { + "author" : { + "type" : "string" + }, + "description" : { + "type" : "string" + }, + "link" : { + "type" : "string" + }, + "pubDate" : { + "type" : "string", + "format" : "date-time" + }, + "title" : { + "type" : "string" + }, + "uri" : { + "type" : "string" + } + } + }, + "JsonPatch" : { + "minItems" : 1, + "uniqueItems" : true, + "type" : "array", + "description" : "JSON schema for JSONPatch operations", + "items" : { + "oneOf" : [ { + "$ref" : "#/components/schemas/AddOperation" + }, { + "$ref" : "#/components/schemas/ReplaceOperation" + }, { + "$ref" : "#/components/schemas/TestOperation" + }, { + "$ref" : "#/components/schemas/RemoveOperation" + }, { + "$ref" : "#/components/schemas/MoveOperation" + }, { + "$ref" : "#/components/schemas/CopyOperation" + } ] + } + }, + "Link" : { + "required" : [ "apiVersion", "kind", "metadata" ], + "type" : "object", + "properties" : { + "apiVersion" : { + "type" : "string" + }, + "kind" : { + "type" : "string" + }, + "metadata" : { + "$ref" : "#/components/schemas/Metadata" + }, + "spec" : { + "$ref" : "#/components/schemas/LinkSpec" + } + } + }, + "LinkSpec" : { + "required" : [ "displayName", "url" ], + "type" : "object", + "properties" : { + "description" : { + "type" : "string" + }, + "displayName" : { + "type" : "string" + }, + "groupName" : { + "type" : "string" + }, + "logo" : { + "type" : "string" + }, + "priority" : { + "type" : "integer", + "format" : "int32" + }, + "url" : { + "type" : "string" + } + } + }, + "ListedRssSyncLog" : { + "required" : [ "link" ], + "type" : "object", + "properties" : { + "link" : { + "$ref" : "#/components/schemas/Link" + }, + "log" : { + "$ref" : "#/components/schemas/RssFeedSyncLog" + } + }, + "description" : "A chunk of items." + }, + "ListedRssSyncLogList" : { + "required" : [ "first", "hasNext", "hasPrevious", "items", "last", "page", "size", "total", "totalPages" ], + "type" : "object", + "properties" : { + "first" : { + "type" : "boolean", + "description" : "Indicates whether current page is the first page." + }, + "hasNext" : { + "type" : "boolean", + "description" : "Indicates whether current page has previous page." + }, + "hasPrevious" : { + "type" : "boolean", + "description" : "Indicates whether current page has previous page." + }, + "items" : { + "type" : "array", + "description" : "A chunk of items.", + "items" : { + "$ref" : "#/components/schemas/ListedRssSyncLog" + } + }, + "last" : { + "type" : "boolean", + "description" : "Indicates whether current page is the last page." + }, + "page" : { + "type" : "integer", + "description" : "Page number, starts from 1. If not set or equal to 0, it means no pagination.", + "format" : "int32" + }, + "size" : { + "type" : "integer", + "description" : "Size of each page. If not set or equal to 0, it means no pagination.", + "format" : "int32" + }, + "total" : { + "type" : "integer", + "description" : "Total elements.", + "format" : "int64" + }, + "totalPages" : { + "type" : "integer", + "description" : "Indicates total pages.", + "format" : "int64" + } + } + }, + "Metadata" : { + "required" : [ "name" ], + "type" : "object", + "properties" : { + "annotations" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + }, + "creationTimestamp" : { + "type" : "string", + "format" : "date-time", + "nullable" : true + }, + "deletionTimestamp" : { + "type" : "string", + "format" : "date-time", + "nullable" : true + }, + "finalizers" : { + "uniqueItems" : true, + "type" : "array", + "nullable" : true, + "items" : { + "type" : "string", + "nullable" : true + } + }, + "generateName" : { + "type" : "string", + "description" : "The name field will be generated automatically according to the given generateName field" + }, + "labels" : { + "type" : "object", + "additionalProperties" : { + "type" : "string" + } + }, + "name" : { + "type" : "string", + "description" : "Metadata name" + }, + "version" : { + "type" : "integer", + "format" : "int64", + "nullable" : true + } + } + }, + "MoveOperation" : { + "required" : [ "op", "from", "path" ], + "type" : "object", + "properties" : { + "from" : { + "pattern" : "^(/[^/~]*(~[01][^/~]*)*)*$", + "type" : "string", + "description" : "A JSON Pointer path pointing to the location to move/copy from.", + "example" : "/a/b/c" + }, + "op" : { + "type" : "string", + "enum" : [ "move" ] + }, + "path" : { + "pattern" : "^(/[^/~]*(~[01][^/~]*)*)*$", + "type" : "string", + "description" : "A JSON Pointer path pointing to the location to move/copy from.", + "example" : "/a/b/c" + } + } + }, + "RemoveOperation" : { + "required" : [ "op", "path" ], + "type" : "object", + "properties" : { + "op" : { + "type" : "string", + "enum" : [ "remove" ] + }, + "path" : { + "pattern" : "^(/[^/~]*(~[01][^/~]*)*)*$", + "type" : "string", + "description" : "A JSON Pointer path pointing to the location to move/copy from.", + "example" : "/a/b/c" + } + } + }, + "ReplaceOperation" : { + "required" : [ "op", "path", "value" ], + "type" : "object", + "properties" : { + "op" : { + "type" : "string", + "enum" : [ "replace" ] + }, + "path" : { + "pattern" : "^(/[^/~]*(~[01][^/~]*)*)*$", + "type" : "string", + "description" : "A JSON Pointer path pointing to the location to move/copy from.", + "example" : "/a/b/c" + }, + "value" : { + "description" : "Value can be any JSON value" + } + } + }, + "RssDetail" : { + "type" : "object", + "properties" : { + "channel" : { + "$ref" : "#/components/schemas/Channel" + } + } + }, + "RssFeedSyncLog" : { + "required" : [ "apiVersion", "kind", "linkName", "metadata", "state", "syncTime" ], + "type" : "object", + "properties" : { + "apiVersion" : { + "type" : "string" + }, + "failureMessage" : { + "type" : "string" + }, + "failureReason" : { + "type" : "string" + }, + "kind" : { + "type" : "string" + }, + "linkName" : { + "type" : "string" + }, + "metadata" : { + "$ref" : "#/components/schemas/Metadata" + }, + "state" : { + "type" : "string", + "default" : "nolink", + "enum" : [ "success", "failed", "onwait", "nolink" ] + }, + "syncTime" : { + "type" : "string", + "format" : "date-time" + } + } + }, + "RssFeedSyncLogList" : { + "required" : [ "first", "hasNext", "hasPrevious", "items", "last", "page", "size", "total", "totalPages" ], + "type" : "object", + "properties" : { + "first" : { + "type" : "boolean", + "description" : "Indicates whether current page is the first page." + }, + "hasNext" : { + "type" : "boolean", + "description" : "Indicates whether current page has previous page." + }, + "hasPrevious" : { + "type" : "boolean", + "description" : "Indicates whether current page has previous page." + }, + "items" : { + "type" : "array", + "description" : "A chunk of items.", + "items" : { + "$ref" : "#/components/schemas/RssFeedSyncLog" + } + }, + "last" : { + "type" : "boolean", + "description" : "Indicates whether current page is the last page." + }, + "page" : { + "type" : "integer", + "description" : "Page number, starts from 1. If not set or equal to 0, it means no pagination.", + "format" : "int32" + }, + "size" : { + "type" : "integer", + "description" : "Size of each page. If not set or equal to 0, it means no pagination.", + "format" : "int32" + }, + "total" : { + "type" : "integer", + "description" : "Total elements.", + "format" : "int64" + }, + "totalPages" : { + "type" : "integer", + "description" : "Indicates total pages.", + "format" : "int64" + } + } + }, + "TestOperation" : { + "required" : [ "op", "path", "value" ], + "type" : "object", + "properties" : { + "op" : { + "type" : "string", + "enum" : [ "test" ] + }, + "path" : { + "pattern" : "^(/[^/~]*(~[01][^/~]*)*)*$", + "type" : "string", + "description" : "A JSON Pointer path pointing to the location to move/copy from.", + "example" : "/a/b/c" + }, + "value" : { + "description" : "Value can be any JSON value" + } + } + } + }, + "securitySchemes" : { + "basicAuth" : { + "scheme" : "basic", + "type" : "http" + }, + "bearerAuth" : { + "bearerFormat" : "JWT", + "scheme" : "bearer", + "type" : "http" + } + } + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..6bbb5a3 --- /dev/null +++ b/build.gradle @@ -0,0 +1,102 @@ +plugins { + id 'java' + id "com.github.node-gradle.node" version "7.0.2" + id "io.freefair.lombok" version "8.0.1" + id "run.halo.plugin.devtools" version "0.2.0" +} + +group 'la.moony.friends' +sourceCompatibility = JavaVersion.VERSION_17 + +repositories { + mavenCentral() + maven { url 'https://s01.oss.sonatype.org/content/repositories/releases' } + maven { url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' } + maven { url 'https://repo.spring.io/milestone' } +} + +dependencies { + implementation platform('run.halo.tools.platform:plugin:2.20.0-SNAPSHOT') + compileOnly 'run.halo.app:api' + + testImplementation 'run.halo.app:api' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + implementation 'com.rometools:rome:2.1.0' +} + +test { + useJUnitPlatform() +} + +tasks.withType(JavaCompile).configureEach { + options.encoding = "UTF-8" +} + +node { + nodeProjectDir = file("${project.projectDir}") +} + +tasks.register('buildFrontend', PnpmTask) { + args = ['build'] + dependsOn('installDepsForUI') +} + +tasks.register('installDepsForUI', PnpmTask) { + args = ['install'] +} + +build { + // build frontend before build + tasks.named('compileJava').configure { + dependsOn('buildFrontend') + } +} + +halo { + version = '2.21.0' + superAdminUsername = 'admin' + superAdminPassword = 'admin' + externalUrl = 'http://localhost:8081' + docker { + // windows 需要改为 npipe:////./pipe/docker_engine + // linux、macos 需要改为 unix:///var/run/docker.sock + url = 'unix:///var/run/docker.sock' + apiVersion = '1.48' + } + port = 8081 + + // 开启 debug 断点模式 + debug = true + debugPort = 5011 + + containerName = 'plugin-friends' + // 控制台会挂起,等待点击 Attach debugger 按钮 + suspend = true +} + +haloPlugin { + openApi { + outputDir = file("$rootDir/api-docs/openapi/v3_0") + // 用于定义 API 分组规则 + groupingRules { + // 此名称为 group name,定义后 groupedApiMappings 中的 /v3/api-docs/ 后的名称需要与之相同,要避免与 halo 中已经存在的 group 相同避免生成后出现与插件无关的 API + pluginFriendsApi { + // 分组显示名称 + displayName = 'Extension API for Plugin Friends' + // 分组的 API 规则 + pathsToMatch = ["/apis/friend.moony.la/v1alpha1/**", + "/apis/api.friend.moony.la/v1alpha1/**" + ] + } + } + groupedApiMappings = [ + '/v3/api-docs/pluginFriendsApi': 'pluginFriendsApi.json' + ] + generator { + // 默认配置可缺省 + outputDir = file("${projectDir}/ui/src/api/generated") + } + } + +} + diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..0060beb --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +version=1.3.7 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..ccebba7 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..171d876 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..79a61d4 --- /dev/null +++ b/gradlew @@ -0,0 +1,244 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..6689b85 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/package.json b/package.json new file mode 100644 index 0000000..1743676 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "scripts": { + "build": "pnpm --filter \"./packages/**\" build && pnpm --filter ./ui build" + } +} diff --git a/packages/fridends-rss/.eslintrc.json b/packages/fridends-rss/.eslintrc.json new file mode 100644 index 0000000..789afa3 --- /dev/null +++ b/packages/fridends-rss/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "root": true, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 2020, + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"], + "env": { + "browser": true + }, + "rules": { + "no-prototype-builtins": "off", + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-unused-vars": [ + "warn", + { + "argsIgnorePattern": "^_" + } + ] + } +} diff --git a/packages/fridends-rss/.gitignore b/packages/fridends-rss/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/packages/fridends-rss/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/packages/fridends-rss/.prettierrc.json b/packages/fridends-rss/.prettierrc.json new file mode 100644 index 0000000..dc7872f --- /dev/null +++ b/packages/fridends-rss/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "trailingComma": "es5", + "tabWidth": 2, + "singleQuote": true, + "bracketSpacing": true, + "arrowParens": "always", + "printWidth": 100 +} diff --git a/packages/fridends-rss/package.json b/packages/fridends-rss/package.json new file mode 100644 index 0000000..7aa4ae1 --- /dev/null +++ b/packages/fridends-rss/package.json @@ -0,0 +1,59 @@ +{ + "name": "@kunkunyu/fridends-rss", + "author": { + "name": "困困鱼", + "url": "https://github.com/chengzhongxue" + }, + "maintainers": [ + { + "name": "困困鱼", + "url": "https://github.com/chengzhongxue" + } + ], + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/friends-rss.js" + }, + "./var.css": "./var.css" + }, + "main": "./dist/friends-rss.js", + "module": "./dist/friends-rss.js", + "types": "./dist/index.d.ts", + "files": [ + "dist", + "var.css" + ], + "scripts": { + "dev": "vite --config vite.config.dev.ts", + "build": "vite build --config vite.config.lib.ts", + "lint": "lit-analyzer && eslint 'src/**/*.ts'", + "prettier": "prettier \"**/*.{cjs,html,js,json,md,ts}\" --ignore-path ./.gitignore --write", + "test": "vitest", + "test:ci": "vitest --run" + }, + "dependencies": { + "lit": "^3.1.3" + }, + "devDependencies": { + "@iconify/json": "^2.2.216", + "@rushstack/eslint-patch": "^1.7.2", + "@types/node": "20", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "@unocss/preset-icons": "^0.60.4", + "@unocss/reset": "^0.60.4", + "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "lit-analyzer": "^2.0.3", + "prettier": "^3.3.0", + "rollup-plugin-copy": "^3.5.0", + "typescript": "^5.4.5", + "unocss": "^0.60.4", + "vite": "^5.2.12", + "vite-plugin-dts": "^3.9.1", + "vitest": "^1.6.0" + } +} diff --git a/packages/fridends-rss/src/friends-rss.ts b/packages/fridends-rss/src/friends-rss.ts new file mode 100644 index 0000000..d315a22 --- /dev/null +++ b/packages/fridends-rss/src/friends-rss.ts @@ -0,0 +1,93 @@ +import resetStyles from '@unocss/reset/tailwind.css?inline'; +import { LitElement, css, html, unsafeCSS } from 'lit'; +import { property, state } from 'lit/decorators.js'; +import { RssDetail } from './types'; +import { repeat } from 'lit/directives/repeat.js'; +import './loading-bar'; + +export class FriendsRss extends LitElement { + @property({ type: String }) + src = ''; + + @property({ type: String }) + layout: 'list' | 'grid' = 'list'; + + @state() + rssDetail?: RssDetail; + + @state() + loading = false; + + override connectedCallback() { + super.connectedCallback(); + this.fetchRssDetail(); + } + + async fetchRssDetail() { + // Mock + try { + this.loading = true; + const response = await fetch( + `/apis/api.friend.moony.la/v1alpha1/parsingrss?rssUrl=${this.src}` + ); + + if (!response.ok) { + throw new Error('Failed to fetch site data'); + } + + this.rssDetail = (await response.json()) as RssDetail; + } catch (error) { + console.error(error); + } finally { + this.loading = false; + } + } + + override render() { + if (this.loading) { + return html``; + } + + return html` +
    + ${repeat( + // @ts-ignore + this.rssDetail?.channel.items, + (item) => item.link, + (item) => html` +
  • + +

    + ${item?.title} +

    +
    +

    ${item?.description}

    +
  • + ` + )} +
+ `; + } + + static override styles = [ + unsafeCSS(resetStyles), + css` + :host { + display: inline-block; + width: 100%; + } + @unocss-placeholder; + `, + ]; +} + +customElements.get('friends-rss') || customElements.define('friends-rss', FriendsRss); + +declare global { + interface HTMLElementTagNameMap { + 'friends-rss': FriendsRss; + } +} diff --git a/packages/fridends-rss/src/index.ts b/packages/fridends-rss/src/index.ts new file mode 100644 index 0000000..2b2b81b --- /dev/null +++ b/packages/fridends-rss/src/index.ts @@ -0,0 +1,2 @@ +export * from './friends-rss'; + diff --git a/packages/fridends-rss/src/loading-bar.ts b/packages/fridends-rss/src/loading-bar.ts new file mode 100644 index 0000000..2425f40 --- /dev/null +++ b/packages/fridends-rss/src/loading-bar.ts @@ -0,0 +1,51 @@ +import { LitElement, css, html } from 'lit'; + +export class LoadingBar extends LitElement { + override render() { + return html` + + + + + `; + } + + static override styles = css` + :host { + display: flex; + align-items: center; + justify-content: center; + padding: 1em; + } + svg { + height: 1.25em; + width: 1.25em; + animation: spin 1s linear infinite; + } + + @keyframes spin { + to { + transform: rotate(360deg); + } + } + `; +} + +customElements.get('loading-bar') || customElements.define('loading-bar', LoadingBar); + +declare global { + interface HTMLElementTagNameMap { + 'loading-block': LoadingBar; + } +} diff --git a/packages/fridends-rss/src/types/index.ts b/packages/fridends-rss/src/types/index.ts new file mode 100644 index 0000000..ec20c2b --- /dev/null +++ b/packages/fridends-rss/src/types/index.ts @@ -0,0 +1,28 @@ +export interface RssDetail { + 'channel': Channel; +} + +export interface Channel { + + 'description'?: string; + + 'items': Array; + + 'link': string; + + 'title': string; +} +export interface Item { + + 'author'?: string; + + 'description'?: string; + + 'link': string; + + 'pubDate': string; + + 'title': string; + + 'uri'?: string; +} \ No newline at end of file diff --git a/packages/fridends-rss/src/vite-env.d.ts b/packages/fridends-rss/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/packages/fridends-rss/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/fridends-rss/src/vite/shared-plugin-config.ts b/packages/fridends-rss/src/vite/shared-plugin-config.ts new file mode 100644 index 0000000..4604e17 --- /dev/null +++ b/packages/fridends-rss/src/vite/shared-plugin-config.ts @@ -0,0 +1,14 @@ +import presetIcons from '@unocss/preset-icons'; +import { presetUno } from 'unocss'; +import UnoCSS from 'unocss/vite'; + +export const sharedPluginsConfig = [ + UnoCSS({ + mode: 'shadow-dom', + presets: [presetUno(), presetIcons()], + shortcuts: { + 'text-title': 'text-[var(--friends-rss-title-color,#18181b)]', + 'text-description': 'text-[var(--friends-rss-description-color,#71717a)]', + }, + }), +]; diff --git a/packages/fridends-rss/tsconfig.json b/packages/fridends-rss/tsconfig.json new file mode 100644 index 0000000..6078f67 --- /dev/null +++ b/packages/fridends-rss/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "target": "es2021", + "module": "es2020", + "lib": ["es2021", "DOM", "DOM.Iterable"], + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "inlineSources": true, + "outDir": "./types", + "rootDir": "./src", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitThis": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "forceConsistentCasingInFileNames": true, + "noImplicitOverride": true, + "skipLibCheck": true, + "plugins": [ + { + "name": "ts-lit-plugin", + "strict": true + } + ] + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/fridends-rss/var.css b/packages/fridends-rss/var.css new file mode 100644 index 0000000..e09bac7 --- /dev/null +++ b/packages/fridends-rss/var.css @@ -0,0 +1,16 @@ +@media (prefers-color-scheme: dark) { + .color-scheme-auto, + [data-color-scheme='auto'] friends-rss { + color-scheme: dark; + --friends-rss-title-color: #f4f4f5; + --friends-rss-description-color: #a1a1aa; + } +} + +.color-scheme-dark, +.dark, +[data-color-scheme='dark'] friends-rss { + color-scheme: dark; + --friends-rss-title-color: #f4f4f5; + --friends-rss-description-color: #a1a1aa; +} diff --git a/packages/fridends-rss/vite.config.dev.ts b/packages/fridends-rss/vite.config.dev.ts new file mode 100644 index 0000000..446415a --- /dev/null +++ b/packages/fridends-rss/vite.config.dev.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite'; +import { sharedPluginsConfig } from './src/vite/shared-plugin-config'; + +export default defineConfig({ + plugins: [...sharedPluginsConfig], + server: { + proxy: { + '/apis': { + target: 'http://localhost:8090', + changeOrigin: true, + }, + }, + }, +}); diff --git a/packages/fridends-rss/vite.config.lib.ts b/packages/fridends-rss/vite.config.lib.ts new file mode 100644 index 0000000..00b09be --- /dev/null +++ b/packages/fridends-rss/vite.config.lib.ts @@ -0,0 +1,34 @@ +import copy from 'rollup-plugin-copy'; +import { fileURLToPath } from 'url'; +import { defineConfig } from 'vite'; +import dts from 'vite-plugin-dts'; +import { sharedPluginsConfig } from './src/vite/shared-plugin-config'; + +export default defineConfig({ + build: { + lib: { + entry: 'src/index.ts', + name: 'friends-rss', + fileName: 'friends-rss', + formats: ['iife', 'es'], + }, + emptyOutDir: true, + rollupOptions: { + output: { + extend: true, + }, + }, + }, + plugins: [ + ...sharedPluginsConfig, + dts(), + copy({ + targets: [ + { + src: ['./dist/friends-rss.iife.js', './var.css'], + dest: fileURLToPath(new URL('../../src/main/resources/static', import.meta.url)), + }, + ], + }), + ], +}); diff --git a/packages/fridends-rss/vitest.config.ts b/packages/fridends-rss/vitest.config.ts new file mode 100644 index 0000000..6ec74ee --- /dev/null +++ b/packages/fridends-rss/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['src/**/*.test.ts'], + }, +}); diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..9453fe0 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - 'ui' + - 'packages/**' diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..a41e22d --- /dev/null +++ b/settings.gradle @@ -0,0 +1,7 @@ +pluginManagement { + repositories { + gradlePluginPortal() + } +} +rootProject.name = 'plugin-friends' + diff --git a/src/main/java/la/moony/friends/FriendsHeadProcessor.java b/src/main/java/la/moony/friends/FriendsHeadProcessor.java new file mode 100644 index 0000000..ec38943 --- /dev/null +++ b/src/main/java/la/moony/friends/FriendsHeadProcessor.java @@ -0,0 +1,45 @@ +package la.moony.friends; + +import lombok.RequiredArgsConstructor; +import org.pf4j.PluginWrapper; +import org.springframework.stereotype.Component; +import org.springframework.util.PropertyPlaceholderHelper; +import org.thymeleaf.context.ITemplateContext; +import org.thymeleaf.model.IModel; +import org.thymeleaf.model.IModelFactory; +import org.thymeleaf.processor.element.IElementModelStructureHandler; +import reactor.core.publisher.Mono; +import run.halo.app.theme.dialect.TemplateHeadProcessor; + +import java.util.Properties; + +@Component +@RequiredArgsConstructor +public class FriendsHeadProcessor implements TemplateHeadProcessor { + + static final PropertyPlaceholderHelper PROPERTY_PLACEHOLDER_HELPER = + new PropertyPlaceholderHelper("${", "}"); + + private final PluginWrapper pluginWrapper; + + @Override + public Mono process(ITemplateContext context, IModel model, + IElementModelStructureHandler structureHandler) { + final IModelFactory modelFactory = context.getModelFactory(); + model.add(modelFactory.createText(friendsRssComponentScript())); + return Mono.empty(); + } + + private String friendsRssComponentScript() { + + final Properties properties = new Properties(); + properties.setProperty("version", pluginWrapper.getDescriptor().getVersion()); + + return PROPERTY_PLACEHOLDER_HELPER.replacePlaceholders(""" + + + + + """, properties); + } +} diff --git a/src/main/java/la/moony/friends/FriendsPlugin.java b/src/main/java/la/moony/friends/FriendsPlugin.java new file mode 100644 index 0000000..98c19ae --- /dev/null +++ b/src/main/java/la/moony/friends/FriendsPlugin.java @@ -0,0 +1,79 @@ +package la.moony.friends; + +import la.moony.friends.extension.CronFriendPost; +import la.moony.friends.extension.FriendPost; +import la.moony.friends.extension.RssFeedSyncLog; +import org.springframework.stereotype.Component; +import run.halo.app.extension.Scheme; +import run.halo.app.extension.SchemeManager; +import run.halo.app.extension.index.IndexSpec; +import run.halo.app.plugin.BasePlugin; +import run.halo.app.plugin.PluginContext; + +import static run.halo.app.extension.index.IndexAttributeFactory.simpleAttribute; + +@Component +public class FriendsPlugin extends BasePlugin { + + private final SchemeManager schemeManager; + + public FriendsPlugin(PluginContext pluginContext, SchemeManager schemeManager) { + super(pluginContext); + this.schemeManager = schemeManager; + } + + @Override + public void start() { + schemeManager.register(FriendPost.class, indexSpecs -> { + indexSpecs.add(new IndexSpec() + .setName("spec.author") + .setIndexFunc( + simpleAttribute(FriendPost.class, friendPost -> friendPost.getSpec().getAuthor()))); + indexSpecs.add(new IndexSpec() + .setName("spec.title") + .setIndexFunc( + simpleAttribute(FriendPost.class, friendPost -> friendPost.getSpec().getTitle()))); + indexSpecs.add(new IndexSpec() + .setName("spec.description") + .setIndexFunc( + simpleAttribute(FriendPost.class, friendPost -> friendPost.getSpec().getDescription()))); + indexSpecs.add(new IndexSpec() + .setName("spec.postLink") + .setIndexFunc( + simpleAttribute(FriendPost.class, friendPost -> friendPost.getSpec().getPostLink()))); + indexSpecs.add(new IndexSpec() + .setName("spec.authorUrl") + .setIndexFunc( + simpleAttribute(FriendPost.class, friendPost -> friendPost.getSpec().getAuthorUrl()))); + indexSpecs.add(new IndexSpec() + .setName("spec.linkName") + .setIndexFunc( + simpleAttribute(FriendPost.class, friendPost -> friendPost.getSpec().getLinkName()))); + indexSpecs.add(new IndexSpec() + .setName("spec.pubDate") + .setIndexFunc(simpleAttribute(FriendPost.class, friendPost -> { + var pubDate = friendPost.getSpec().getPubDate(); + return pubDate == null ? null : pubDate.toString(); + })) + ); + }); + schemeManager.register(CronFriendPost.class); + schemeManager.register(RssFeedSyncLog.class, indexSpecs -> { + indexSpecs.add(new IndexSpec() + .setName("state") + .setIndexFunc(simpleAttribute(RssFeedSyncLog.class, rssFeedSyncLog -> { + var state = rssFeedSyncLog.getState(); + return state == null ? null : state.name(); + }))); + }); + + } + + @Override + public void stop() { + schemeManager.unregister(Scheme.buildFromType(FriendPost.class)); + schemeManager.unregister(Scheme.buildFromType(CronFriendPost.class)); + schemeManager.unregister(Scheme.buildFromType(RssFeedSyncLog.class)); + + } +} diff --git a/src/main/java/la/moony/friends/ListedRssSyncLog.java b/src/main/java/la/moony/friends/ListedRssSyncLog.java new file mode 100644 index 0000000..571e3fa --- /dev/null +++ b/src/main/java/la/moony/friends/ListedRssSyncLog.java @@ -0,0 +1,19 @@ +package la.moony.friends; + +import io.swagger.v3.oas.annotations.media.Schema; +import la.moony.friends.extension.Link; +import la.moony.friends.extension.RssFeedSyncLog; +import lombok.Data; +import lombok.experimental.Accessors; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +@Data +@Accessors(chain = true) +public class ListedRssSyncLog { + + @Schema(requiredMode = REQUIRED) + private Link link; + + private RssFeedSyncLog log; +} diff --git a/src/main/java/la/moony/friends/RssFeedSyncEvent.java b/src/main/java/la/moony/friends/RssFeedSyncEvent.java new file mode 100644 index 0000000..fb7d716 --- /dev/null +++ b/src/main/java/la/moony/friends/RssFeedSyncEvent.java @@ -0,0 +1,20 @@ +package la.moony.friends; + +import la.moony.friends.extension.Link; +import lombok.Getter; +import org.springframework.context.ApplicationEvent; +import java.util.List; + +@Getter +public class RssFeedSyncEvent extends ApplicationEvent { + private final Link link; + private final int sum; + private final List disableSyncList; + + public RssFeedSyncEvent(Object source, Link link, int sum, List disableSyncList) { + super(source); + this.link = link; + this.sum = sum; + this.disableSyncList = disableSyncList; + } +} diff --git a/src/main/java/la/moony/friends/endpoint/FriendEndpoint.java b/src/main/java/la/moony/friends/endpoint/FriendEndpoint.java new file mode 100644 index 0000000..f37f3d5 --- /dev/null +++ b/src/main/java/la/moony/friends/endpoint/FriendEndpoint.java @@ -0,0 +1,170 @@ +package la.moony.friends.endpoint; + +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import la.moony.friends.ListedRssSyncLog; +import la.moony.friends.extension.FriendPost; +import la.moony.friends.extension.RssFeedSyncLog; +import la.moony.friends.query.FriendPostQuery; +import la.moony.friends.query.RssSyncLogQuery; +import la.moony.friends.service.RssDetailService; +import la.moony.friends.service.RssFeedSyncLogService; +import la.moony.friends.vo.RssDetail; +import org.springdoc.webflux.core.fn.SpringdocRouteBuilder; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.endpoint.CustomEndpoint; +import run.halo.app.extension.GroupVersion; +import run.halo.app.extension.ListOptions; +import run.halo.app.extension.ListResult; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.extension.router.selector.FieldSelector; + +import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder; +import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder; +import static run.halo.app.extension.index.query.QueryFactory.isNull; + +@Component +public class FriendEndpoint implements CustomEndpoint { + + private final String tag = "api.friend.moony.la/v1alpha1/FriendPost"; + + + private final ReactiveExtensionClient client; + + private final RssDetailService rssDetailService; + + private final RssFeedSyncLogService rssFeedSyncLogService; + + + public FriendEndpoint(ReactiveExtensionClient client, RssDetailService rssDetailService, + RssFeedSyncLogService rssFeedSyncLogService) { + this.client = client; + this.rssDetailService = rssDetailService; + this.rssFeedSyncLogService = rssFeedSyncLogService; + } + + @Override + public RouterFunction endpoint() { + return SpringdocRouteBuilder.route() + .GET("friendposts", this::listFriendPost, builder -> { + builder.operationId("ListFriendPosts") + .description("List friendPost.") + .tag(tag) + .response( + responseBuilder() + .implementation(ListResult.generateGenericClass(FriendPost.class)) + ); + FriendPostQuery.buildParameters(builder); + }) + .DELETE("friendposts/-/delete", this::deleteAllRssFeedSyncLog, builder -> { + builder.operationId("DeleteAllRssFeedSyncLog") + .description("Delete All RssFeedSyncLog.") + .tag(tag) + .response(responseBuilder() + .implementation(Void.class)); + }) + .GET("rsssynclogs", this::listRssSyncLog, builder -> { + builder.operationId("ListRssSyncLogs") + .description("List RssSyncLog.") + .tag(tag) + .response( + responseBuilder() + .implementation(ListResult.generateGenericClass(ListedRssSyncLog.class)) + ); + RssSyncLogQuery.buildParameters(builder); + }) + .GET("parsingrss", this::parsingRss, builder -> { + builder.operationId("ParsingRss") + .tag(tag) + .parameter(parameterBuilder() + .name("rssUrl") + .in(ParameterIn.QUERY) + .required(true) + .implementation(String.class) + ) + .parameter(parameterBuilder() + .name("fetchLimitNumber") + .in(ParameterIn.QUERY) + .required(false) + .implementation(String.class) + ) + .response( + responseBuilder() + .implementation(RssDetail.class) + ); + }) + .POST("syncrssfeed/{name}", this::syncRssFeed, + builder -> builder.operationId("SyncRssFeed") + .tag(tag) + .parameter(parameterBuilder().name("name") + .in(ParameterIn.PATH) + .required(true) + .implementation(String.class)) + .response(responseBuilder() + .implementation(Void.class)) + ) + .build(); + } + + + Mono listFriendPost(ServerRequest request) { + FriendPostQuery friendPostQuery = new FriendPostQuery(request); + return client.listBy(FriendPost.class, friendPostQuery.toListOptions(),friendPostQuery.toPageRequest()) + .flatMap(friendPosts -> ServerResponse.ok().bodyValue(friendPosts)); + } + + Mono deleteAllRssFeedSyncLog(ServerRequest request) { + return deleteAllRssFeedSyncLog() + .then(deleteAllFriendPost()) + .then(ServerResponse.ok().build()); + } + + private Mono deleteAllRssFeedSyncLog() { + ListOptions listOptions = new ListOptions().setFieldSelector( + FieldSelector.of(isNull("metadata.deletionTimestamp"))); + + return client.listAll(RssFeedSyncLog.class, listOptions, Sort.unsorted()) + .flatMap(rssFeedSyncLog -> client.delete(rssFeedSyncLog)) + .then(); + } + + private Mono deleteAllFriendPost() { + ListOptions listOptions = new ListOptions().setFieldSelector( + FieldSelector.of(isNull("metadata.deletionTimestamp"))); + + return client.listAll(FriendPost.class, listOptions, Sort.unsorted()) + .flatMap(friendPost -> client.delete(friendPost)) + .then(); + } + + Mono listRssSyncLog(ServerRequest request) { + RssSyncLogQuery rssSyncLogQuery = new RssSyncLogQuery(request); + return rssFeedSyncLogService.listRssSyncLog(rssSyncLogQuery) + .flatMap(listRssSyncLogs -> ServerResponse.ok().bodyValue(listRssSyncLogs)); + } + + Mono parsingRss(ServerRequest request) { + final var rssUrl = request.queryParam("rssUrl").orElseThrow(); + final var fetchLimitNumber = request.queryParam("fetchLimitNumber") + .map(Integer::parseInt) + .orElse(5); + return rssDetailService.fetchRssDetail(rssUrl,fetchLimitNumber) + .flatMap(rssDetail -> ServerResponse.ok().bodyValue(rssDetail)); + } + + private Mono syncRssFeed(ServerRequest request) { + var name = request.pathVariable("name"); + + return rssFeedSyncLogService.publishRssFeedSyncEvent(name) + .flatMap(rssFeedSyncEvent -> ServerResponse.ok().build()); + } + + @Override + public GroupVersion groupVersion() { + return GroupVersion.parseAPIVersion("api.friend.moony.la/v1alpha1"); + } +} diff --git a/src/main/java/la/moony/friends/extension/CronFriendPost.java b/src/main/java/la/moony/friends/extension/CronFriendPost.java new file mode 100644 index 0000000..621f745 --- /dev/null +++ b/src/main/java/la/moony/friends/extension/CronFriendPost.java @@ -0,0 +1,56 @@ +package la.moony.friends.extension; + + +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.Instant; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import run.halo.app.extension.AbstractExtension; +import run.halo.app.extension.GVK; + + +@Data +@ToString +@EqualsAndHashCode(callSuper = true) +@GVK( + group = "friend.moony.la", + version = "v1alpha1", + kind = "CronFriendPost", + singular = "cronfriendpost", + plural = "cronfriendposts" +) +public class CronFriendPost extends AbstractExtension { + + + private CronSpec spec = new CronSpec(); + + private CronStatus status = new CronStatus(); + + @Data + public static class CronSpec { + private String cron; + private String timezone; + private boolean suspend; + + @Schema( + minimum = "0" + ) + private int successfulRetainLimit; + + private List disableSyncList; + + } + + @Data + public static class CronStatus { + private Instant lastScheduledTimestamp; + private Instant nextSchedulingTimestamp; + + } + + + + +} diff --git a/src/main/java/la/moony/friends/extension/FriendPost.java b/src/main/java/la/moony/friends/extension/FriendPost.java new file mode 100644 index 0000000..a8f4800 --- /dev/null +++ b/src/main/java/la/moony/friends/extension/FriendPost.java @@ -0,0 +1,43 @@ +package la.moony.friends.extension; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import run.halo.app.extension.AbstractExtension; +import run.halo.app.extension.GVK; + +import java.time.Instant; + +@Data +@ToString +@EqualsAndHashCode(callSuper = true) +@GVK(kind = "FriendPost", group = "friend.moony.la", + version = "v1alpha1", singular = "friendpost", plural = "friendposts") +public class FriendPost extends AbstractExtension { + + + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + private FriendPostSpec spec; + + @Data + public static class FriendPostSpec { + + private String author; + + private String authorUrl; + + private String logo; + + private String title; + + private String postLink; + + private String description; + + private Instant pubDate; + + private String linkName; + } + +} diff --git a/src/main/java/la/moony/friends/extension/Link.java b/src/main/java/la/moony/friends/extension/Link.java new file mode 100644 index 0000000..d1b4c06 --- /dev/null +++ b/src/main/java/la/moony/friends/extension/Link.java @@ -0,0 +1,32 @@ +package la.moony.friends.extension; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import run.halo.app.extension.AbstractExtension; +import run.halo.app.extension.GVK; +@Data +@EqualsAndHashCode(callSuper = true) +@GVK(group = "core.halo.run", version = "v1alpha1", + kind = "Link", plural = "links", singular = "link") +public class Link extends AbstractExtension { + + private LinkSpec spec; + + @Data + public static class LinkSpec { + @Schema(required = true) + private String url; + + @Schema(required = true) + private String displayName; + + private String logo; + + private String description; + + private Integer priority; + + private String groupName; + } +} \ No newline at end of file diff --git a/src/main/java/la/moony/friends/extension/LinkGroup.java b/src/main/java/la/moony/friends/extension/LinkGroup.java new file mode 100644 index 0000000..9de5f1d --- /dev/null +++ b/src/main/java/la/moony/friends/extension/LinkGroup.java @@ -0,0 +1,31 @@ +package la.moony.friends.extension; + +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.LinkedHashSet; +import lombok.Data; +import lombok.EqualsAndHashCode; +import run.halo.app.extension.AbstractExtension; +import run.halo.app.extension.GVK; + + +@Data +@EqualsAndHashCode(callSuper = true) +@GVK(group = "core.halo.run", version = "v1alpha1", kind = "LinkGroup", plural = "linkgroups", singular = "linkgroup") +public class LinkGroup extends AbstractExtension { + + private LinkGroupSpec spec; + + @Data + public static class LinkGroupSpec { + @Schema(required = true) + private String displayName; + + private Integer priority; + + @Deprecated(since = "1.2.0", forRemoval = true) + @Schema(description = "Names of links below this group.") + @ArraySchema(arraySchema = @Schema(description = "Links of this group."), schema = @Schema(description = "Name of link.")) + private LinkedHashSet links; + } +} diff --git a/src/main/java/la/moony/friends/extension/RssFeedSyncLog.java b/src/main/java/la/moony/friends/extension/RssFeedSyncLog.java new file mode 100644 index 0000000..1d954bb --- /dev/null +++ b/src/main/java/la/moony/friends/extension/RssFeedSyncLog.java @@ -0,0 +1,41 @@ +package la.moony.friends.extension; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import run.halo.app.extension.AbstractExtension; +import run.halo.app.extension.GVK; +import java.time.Instant; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +@Data +@EqualsAndHashCode(callSuper = true) +@GVK(kind = "RssFeedSyncLog", group = "friend.moony.la", + version = "v1alpha1", singular = "rssfeedsynclog", plural = "rssfeedsynclogs") +public class RssFeedSyncLog extends AbstractExtension { + + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + private String linkName; + + @Schema(requiredMode = REQUIRED,defaultValue = "nolink") + private RssFeedSyncLogState state; + + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + private Instant syncTime; + + private String failureReason; + + private String failureMessage; + + public enum RssFeedSyncLogState { + success, + failed, + onwait, + nolink; + + private RssFeedSyncLogState() { + } + + } +} \ No newline at end of file diff --git a/src/main/java/la/moony/friends/finders/FriendFinder.java b/src/main/java/la/moony/friends/finders/FriendFinder.java new file mode 100644 index 0000000..ce77557 --- /dev/null +++ b/src/main/java/la/moony/friends/finders/FriendFinder.java @@ -0,0 +1,28 @@ +package la.moony.friends.finders; + +import la.moony.friends.finders.impl.FriendFinderImpl; +import la.moony.friends.vo.FriendPostVo; +import la.moony.friends.vo.LinkGroupVo; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import run.halo.app.extension.ListResult; +import java.util.Map; +import org.springframework.lang.Nullable; + +public interface FriendFinder { + + Flux listAll(); + + Mono> list(@Nullable Integer page, @Nullable Integer size); + + /** + * Lists friendPosts by query params. + * + * @param params query params see {@link FriendFinderImpl.FriendPostQuery} + */ + Mono> list(Map params); + + Mono getByName(String friendPostName); + + Flux linkGroupBy(); +} diff --git a/src/main/java/la/moony/friends/finders/impl/FriendFinderImpl.java b/src/main/java/la/moony/friends/finders/impl/FriendFinderImpl.java new file mode 100644 index 0000000..096a26a --- /dev/null +++ b/src/main/java/la/moony/friends/finders/impl/FriendFinderImpl.java @@ -0,0 +1,240 @@ +package la.moony.friends.finders.impl; + +import jakarta.annotation.Nonnull; +import la.moony.friends.extension.FriendPost; +import la.moony.friends.extension.Link; +import la.moony.friends.extension.LinkGroup; +import la.moony.friends.extension.RssFeedSyncLog; +import la.moony.friends.finders.FriendFinder; +import la.moony.friends.util.SortUtils; +import la.moony.friends.vo.FriendPostVo; +import la.moony.friends.vo.LinkGroupVo; +import la.moony.friends.vo.LinkVo; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Sort; +import org.springframework.util.Assert; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import org.springframework.lang.NonNull; +import run.halo.app.extension.*; +import run.halo.app.extension.router.selector.FieldSelector; +import run.halo.app.infra.utils.JsonUtils; +import run.halo.app.theme.finders.Finder; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.springframework.data.domain.Sort.Order.asc; +import static run.halo.app.extension.index.query.QueryFactory.*; + + +@Finder("friendFinder") +@RequiredArgsConstructor +public class FriendFinderImpl implements FriendFinder { + + static final String UNGROUPED_NAME = "ungrouped"; + + private final ReactiveExtensionClient client; + + + + @Override + public Flux listAll() { + var listOptions = new ListOptions(); + var query = all(); + listOptions.setFieldSelector(FieldSelector.of(query)); + return client.listAll(FriendPost.class, listOptions, defaultSort()) + .flatMap(this::getFriendPostVo); + } + + @Override + public Mono> list(Integer page, Integer size) { + return pageFriendPost(new ListOptions(), getPageRequest(page, size)); + } + + private PageRequestImpl getPageRequest(Integer page, Integer size) { + return PageRequestImpl.of(pageNullSafe(page), sizeNullSafe(size), defaultSort()); + } + + @Override + public Mono> list(Map params) { + var query = Optional.ofNullable(params) + .map(map -> JsonUtils.mapToObject(map, FriendPostQuery.class)) + .orElseGet(FriendPostQuery::new); + return pageFriendPost(query.toListOptions(), query.toPageRequest()); + } + + + @Override + public Mono getByName(String friendPostName) { + return client.fetch(FriendPost.class, friendPostName) + .map(FriendPostVo::from); + } + + @Override + public Flux linkGroupBy() { + return client.listAll(LinkGroup.class, new ListOptions(), defaultGroupSort()) + .map(LinkGroupVo::from) + .concatMap(group -> linkListBy(group.getMetadata().getName()) + .collectList() + .map(group::withLinks) + .defaultIfEmpty(group) + ) + .mergeWith(Mono.defer(() -> linkListBy(UNGROUPED_NAME) + .collectList() + // do not return ungrouped group if no links + .filter(links -> !links.isEmpty()) + .flatMap(links -> ungrouped() + .map(LinkGroupVo::from) + .map(group -> group.withLinks(links)) + ) + )); + } + + private Mono> pageFriendPost(ListOptions queryOptions, PageRequest page){ + var listOptions = new ListOptions(); + var query = all(); + listOptions.setFieldSelector(FieldSelector.of(query)); + var fieldSelector = queryOptions.getFieldSelector(); + if (fieldSelector != null) { + listOptions.setFieldSelector(listOptions.getFieldSelector() + .andQuery(fieldSelector.query())); + } + + return client.listBy(FriendPost.class, listOptions, page) + .flatMap(list -> Flux.fromStream(list.get()) + .concatMap(this::getFriendPostVo) + .collectList() + .map(friendPostVos -> new ListResult<>(list.getPage(), list.getSize(), + list.getTotal(), friendPostVos) + ) + ) + .defaultIfEmpty(ListResult.emptyResult()); + + } + + public Flux linkListBy(String groupName) { + var listOptions = new ListOptions(); + var query = isNull("metadata.deletionTimestamp"); + if (UNGROUPED_NAME.equals(groupName)) { + query = and(query, isNull("spec.groupName")); + } else { + query = and(query, equal("spec.groupName", groupName)); + } + listOptions.setFieldSelector(FieldSelector.of(query)); + return client.listAll(Link.class, listOptions, defaultLinkSort()) + .flatMapSequential(this::convertToListedVo) + .collectList() + .map(links -> { + // 排序逻辑 + return links.stream() + .sorted(Comparator.comparing(link -> { + List friendPosts = link.getFriendPosts(); + if (friendPosts != null && !friendPosts.isEmpty()) { + return friendPosts.get(0).getSpec().getPubDate(); + } + return null; + }, Comparator.nullsLast(Comparator.reverseOrder()) + )).collect(Collectors.toList()); + }) + .flatMapMany(Flux::fromIterable); + } + + + public Mono convertToListedVo(@NonNull Link link) { + Assert.notNull(link, "Link must not be null"); + LinkVo linkVo = LinkVo.from(link); + linkVo.setFriendPosts(List.of()); + return Mono.just(linkVo) + .flatMap(lp -> friendPostListBy(lp.getMetadata().getName()) + .doOnNext(lp::setFriendPosts) + .thenReturn(lp) + ).flatMap(p -> { + String linkName = p.getMetadata().getName(); + return client.fetch(RssFeedSyncLog.class,"sync-log-" + linkName) + .doOnNext(p::setRssFeedSyncLog) + .thenReturn(p); + }) + .defaultIfEmpty(linkVo); + } + + public Mono> friendPostListBy(String linkName) { + var listOptions = new ListOptions(); + var query = isNull("metadata.deletionTimestamp"); + query = and(query, equal("spec.linkName", linkName)); + listOptions.setFieldSelector(FieldSelector.of(query)); + var pageRequest = PageRequestImpl.of(pageNullSafe(1), sizeNullSafe(2), defaultSort()); + return client.listBy(FriendPost.class, listOptions, pageRequest) + .flatMap(friendPosts -> Flux.fromStream(friendPosts.get()) + .map(FriendPostVo::from).collectList()); + } + + Mono ungrouped() { + LinkGroup linkGroup = new LinkGroup(); + linkGroup.setMetadata(new Metadata()); + linkGroup.getMetadata().setName(UNGROUPED_NAME); + linkGroup.setSpec(new LinkGroup.LinkGroupSpec()); + linkGroup.getSpec().setDisplayName(""); + linkGroup.getSpec().setPriority(0); + return Mono.just(linkGroup); + } + + static Sort defaultGroupSort() { + return Sort.by(asc("spec.priority"), + asc("metadata.creationTimestamp"), + asc("metadata.name") + ); + } + + static Sort defaultLinkSort() { + return Sort.by(asc("spec.priority"), + asc("metadata.creationTimestamp"), + asc("metadata.name") + ); + } + + static Sort defaultSort() { + return Sort.by("spec.pubDate").descending(); + } + + private Mono getFriendPostVo(@Nonnull FriendPost friendPost) { + FriendPostVo friendPostVo = FriendPostVo.from(friendPost); + return Mono.just(friendPostVo); + } + + static int pageNullSafe(Integer page) { + return ObjectUtils.defaultIfNull(page, 1); + } + + static int sizeNullSafe(Integer size) { + return ObjectUtils.defaultIfNull(size, 10); + } + + @Data + public static class FriendPostQuery { + private Integer page; + private Integer size; + private String linkName; + private List sort; + + public ListOptions toListOptions() { + var builder = ListOptions.builder(); + if (StringUtils.isNotBlank(linkName)) { + builder.andQuery(equal("spec.linkName", linkName)); + } + return builder.build(); + } + + public PageRequest toPageRequest() { + return PageRequestImpl.of(pageNullSafe(getPage()), + sizeNullSafe(getSize()), SortUtils.resolve(sort).and(defaultSort())); + } + } + + +} diff --git a/src/main/java/la/moony/friends/query/FriendPostQuery.java b/src/main/java/la/moony/friends/query/FriendPostQuery.java new file mode 100644 index 0000000..288d0e1 --- /dev/null +++ b/src/main/java/la/moony/friends/query/FriendPostQuery.java @@ -0,0 +1,107 @@ +package la.moony.friends.query; + +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import la.moony.friends.extension.FriendPost; +import org.apache.commons.lang3.StringUtils; +import org.springdoc.core.fn.builders.operation.Builder; +import org.springframework.data.domain.Sort; +import org.springframework.lang.Nullable; +import org.springframework.web.reactive.function.server.ServerRequest; +import run.halo.app.core.extension.endpoint.SortResolver; +import run.halo.app.extension.ListOptions; +import run.halo.app.extension.PageRequest; +import run.halo.app.extension.PageRequestImpl; +import run.halo.app.extension.router.IListRequest; +import run.halo.app.extension.router.SortableRequest; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +import static java.util.Comparator.comparing; +import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder; +import static org.springframework.data.domain.Sort.Order.desc; +import static run.halo.app.extension.index.query.QueryFactory.*; +import static run.halo.app.extension.router.QueryParamBuildUtil.sortParameter; + +public class FriendPostQuery extends SortableRequest { + + public FriendPostQuery(ServerRequest request) { + super(request.exchange()); + } + @Nullable + public String getKeyword() { + return StringUtils.defaultIfBlank(queryParams.getFirst("keyword"), null); + } + + @Nullable + public String getLinkName() { + return queryParams.getFirst("linkName"); + } + + + public ListOptions toListOptions() { + var builder = ListOptions.builder(super.toListOptions()); + + Optional.ofNullable(getKeyword()) + .filter(StringUtils::isNotBlank) + .ifPresent(keyword -> builder.andQuery(or( + contains("spec.author", keyword), + contains("spec.authorUrl", keyword), + contains("spec.title", keyword) + ))); + + Optional.ofNullable(getLinkName()) + .filter(StringUtils::isNotBlank) + .ifPresent(linkName -> builder.andQuery(equal("spec.linkName", linkName))); + + return builder.build(); + } + + public Comparator toComparator() { + List> comparators = new ArrayList<>(); + var sort = getSort(); + var ctOrder = sort.getOrderFor("pubDate"); + if (ctOrder != null) { + Comparator comparator = + comparing(friendPost -> friendPost.getSpec().getPubDate()); + if (ctOrder.isDescending()) { + comparator = comparator.reversed(); + } + comparators.add(comparator); + } + Comparator comparator = + comparing(friendPost -> friendPost.getSpec().getPubDate()); + comparators.add(comparator.reversed()); + return comparators.stream() + .reduce(Comparator::thenComparing) + .orElse(null); + + } + + public PageRequest toPageRequest() { + var sort = SortResolver.defaultInstance.resolve(exchange).and(defaultSort()); + return PageRequestImpl.of(getPage(), getSize(), sort); + } + + public static Sort defaultSort() { + return Sort.by(desc("spec.pubDate")); + } + + public static void buildParameters(Builder builder) { + IListRequest.buildParameters(builder); + builder.parameter(sortParameter()) + .parameter(parameterBuilder() + .in(ParameterIn.QUERY) + .name("linkName") + .description("CronFriendPost filtered by linkName.") + .implementation(String.class) + .required(false)) + .parameter(parameterBuilder() + .in(ParameterIn.QUERY) + .name("keyword") + .description("CronFriendPost filtered by keyword.") + .implementation(String.class) + .required(false)); + } +} \ No newline at end of file diff --git a/src/main/java/la/moony/friends/query/RssSyncLogQuery.java b/src/main/java/la/moony/friends/query/RssSyncLogQuery.java new file mode 100644 index 0000000..2cfcf87 --- /dev/null +++ b/src/main/java/la/moony/friends/query/RssSyncLogQuery.java @@ -0,0 +1,60 @@ +package la.moony.friends.query; + +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import org.apache.commons.lang3.StringUtils; +import org.springdoc.core.fn.builders.operation.Builder; +import org.springframework.lang.Nullable; +import org.springframework.web.reactive.function.server.ServerRequest; +import run.halo.app.extension.ListOptions; +import run.halo.app.extension.router.IListRequest; +import run.halo.app.extension.router.SortableRequest; +import java.util.Optional; + +import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder; +import static run.halo.app.extension.index.query.QueryFactory.*; +import static run.halo.app.extension.router.QueryParamBuildUtil.sortParameter; + +public class RssSyncLogQuery extends SortableRequest { + + public RssSyncLogQuery(ServerRequest request) { + super(request.exchange()); + } + @Nullable + public String getKeyword() { + return StringUtils.defaultIfBlank(queryParams.getFirst("keyword"), null); + } + + @Nullable + public String getState() { + return queryParams.getFirst("state"); + } + + + public ListOptions toListOptions() { + var builder = ListOptions.builder(super.toListOptions()); + + Optional.ofNullable(getKeyword()) + .filter(StringUtils::isNotBlank) + .ifPresent(keyword -> builder.andQuery(or( + contains("metadata.name", keyword), + contains("spec.displayName", keyword) + ))); + + return builder.build(); + } + + public static void buildParameters(Builder builder) { + IListRequest.buildParameters(builder); + builder.parameter(sortParameter()) + .parameter(parameterBuilder() + .in(ParameterIn.QUERY) + .name("keyword") + .implementation(String.class) + .required(false)) + .parameter(parameterBuilder() + .in(ParameterIn.QUERY) + .name("state") + .implementation(String.class) + .required(false)); + } +} \ No newline at end of file diff --git a/src/main/java/la/moony/friends/reconciler/CronCronFriendPostReconciler.java b/src/main/java/la/moony/friends/reconciler/CronCronFriendPostReconciler.java new file mode 100644 index 0000000..a71ee2d --- /dev/null +++ b/src/main/java/la/moony/friends/reconciler/CronCronFriendPostReconciler.java @@ -0,0 +1,121 @@ +package la.moony.friends.reconciler; + +import la.moony.friends.extension.CronFriendPost; +import la.moony.friends.service.RssFeedSyncLogService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.convert.ApplicationConversionService; +import org.springframework.scheduling.support.CronExpression; +import org.springframework.stereotype.Component; +import run.halo.app.extension.ExtensionClient; +import run.halo.app.extension.ExtensionUtil; +import run.halo.app.extension.controller.Controller; +import run.halo.app.extension.controller.ControllerBuilder; +import run.halo.app.extension.controller.Reconciler; +import java.time.*; +import java.util.Objects; + +@Component +public class CronCronFriendPostReconciler implements Reconciler{ + + private static final Logger log = LoggerFactory.getLogger(CronCronFriendPostReconciler.class); + + private final ExtensionClient client; + private Clock clock; + private final RssFeedSyncLogService rssFeedSyncLogService; + + + public CronCronFriendPostReconciler(ExtensionClient client,RssFeedSyncLogService rssFeedSyncLogService) { + this.client = client; + this.rssFeedSyncLogService = rssFeedSyncLogService; + this.clock = Clock.systemDefaultZone(); + } + + + void setClock(Clock clock) { + this.clock = clock; + } + + + public Result reconcile(Request request) { + return (Result)this.client.fetch(CronFriendPost.class, request.name()).map((cronFriendPost) -> { + if (ExtensionUtil.isDeleted(cronFriendPost)) { + return Result.doNotRetry(); + } else { + CronFriendPost.CronSpec spec = cronFriendPost.getSpec(); + if (!spec.isSuspend()) { + return Result.doNotRetry(); + } else { + String cron = spec.getCron(); + String timezone = spec.getTimezone(); + ZoneId zoneId = ZoneId.systemDefault(); + if (timezone != null) { + try { + zoneId = ApplicationConversionService.getSharedInstance().convert(timezone, ZoneId.class); + } catch (DateTimeException dateTimeException) { + log.error("Invalid zone ID {}", timezone, dateTimeException); + return Result.doNotRetry(); + } + } + + Instant now = Instant.now(this.clock); + if (!CronExpression.isValidExpression(cron)) { + log.error("Cron expression {} is invalid.", cron); + return Result.doNotRetry(); + } else { + CronExpression cronExp = CronExpression.parse(cron); + CronFriendPost.CronStatus status = cronFriendPost.getStatus(); + Instant lastScheduledTimestamp = status.getLastScheduledTimestamp(); + if (lastScheduledTimestamp == null) { + lastScheduledTimestamp = cronFriendPost.getMetadata().getCreationTimestamp(); + } + + ZonedDateTime nextFromNow = cronExp.next(now.atZone(zoneId)); + ZonedDateTime nextFromLast = cronExp.next(lastScheduledTimestamp.atZone(zoneId)); + + if (nextFromNow != null && nextFromLast != null) { + if (Objects.equals(nextFromNow, nextFromLast)) { + log.info("Skip scheduling and next scheduled at {}", nextFromNow); + status.setNextSchedulingTimestamp(nextFromNow.toInstant()); + this.client.update(cronFriendPost); + return new Result(true, Duration.between(now, nextFromNow)); + } else { + + + rssFeedSyncLogService.publishRssFeedSyncEvent("all").block(); + + ZonedDateTime zonedNow = now.atZone(zoneId); + ZonedDateTime scheduleTimestamp = now.atZone(zoneId); + + ZonedDateTime next; + for(next = lastScheduledTimestamp.atZone(zoneId); next != null && next.isBefore(zonedNow); next = cronExp.next(next)) { + scheduleTimestamp = next; + } + + status.setLastScheduledTimestamp(scheduleTimestamp.toInstant()); + if (next != null) { + status.setNextSchedulingTimestamp(next.toInstant()); + } + + this.client.update(cronFriendPost); + log.info("Scheduled at {} and next scheduled at {}", scheduleTimestamp, next); + return new Result(true, Duration.between(now, next)); + } + + } else { + return Result.doNotRetry(); + } + + } + + } + } + + }).orElseGet(Result::doNotRetry); + } + + + public Controller setupWith(ControllerBuilder builder) { + return builder.extension(new CronFriendPost()).workerCount(1).build(); + } +} diff --git a/src/main/java/la/moony/friends/reconciler/LinkReconciler.java b/src/main/java/la/moony/friends/reconciler/LinkReconciler.java new file mode 100644 index 0000000..f3f8f4f --- /dev/null +++ b/src/main/java/la/moony/friends/reconciler/LinkReconciler.java @@ -0,0 +1,47 @@ +package la.moony.friends.reconciler; + +import la.moony.friends.extension.FriendPost; +import la.moony.friends.extension.Link; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Component; +import run.halo.app.extension.ExtensionClient; +import run.halo.app.extension.ExtensionUtil; +import run.halo.app.extension.ListOptions; +import run.halo.app.extension.controller.Controller; +import run.halo.app.extension.controller.ControllerBuilder; +import run.halo.app.extension.controller.Reconciler; +import run.halo.app.extension.router.selector.FieldSelector; + +import static run.halo.app.extension.index.query.QueryFactory.equal; + +@Component +@RequiredArgsConstructor +public class LinkReconciler implements Reconciler { + + private final ExtensionClient client; + + @Override + public Result reconcile(Request request) { + client.fetch(Link.class, request.name()) + .ifPresent(link -> { + if (ExtensionUtil.isDeleted(link)) { + var friendPostListOptions = new ListOptions(); + friendPostListOptions.setFieldSelector(FieldSelector.of( + equal("spec.linkName",link.getMetadata().getName()) + )); + client.listAll(FriendPost.class,friendPostListOptions, Sort.by("metadata.creationTimestamp").descending()) + .forEach(friendPost -> client.delete(friendPost)); + } + }); + return Result.doNotRetry(); + } + + @Override + public Controller setupWith(ControllerBuilder builder) { + return builder + .extension(new Link()) + .build(); + } + +} diff --git a/src/main/java/la/moony/friends/reconciler/RssSyncReconciler.java b/src/main/java/la/moony/friends/reconciler/RssSyncReconciler.java new file mode 100644 index 0000000..bde516f --- /dev/null +++ b/src/main/java/la/moony/friends/reconciler/RssSyncReconciler.java @@ -0,0 +1,300 @@ +package la.moony.friends.reconciler; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.rometools.rome.feed.synd.SyndContent; +import com.rometools.rome.feed.synd.SyndEntry; +import com.rometools.rome.feed.synd.SyndFeed; +import com.rometools.rome.io.SyndFeedInput; +import com.rometools.rome.io.XmlReader; +import la.moony.friends.RssFeedSyncEvent; +import la.moony.friends.extension.CronFriendPost; +import la.moony.friends.extension.FriendPost; +import la.moony.friends.extension.Link; +import la.moony.friends.extension.RssFeedSyncLog; +import la.moony.friends.util.CommonUtils; +import la.moony.friends.util.LinkRequest; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.SmartLifecycle; +import org.springframework.context.event.EventListener; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Component; +import run.halo.app.extension.ExtensionClient; +import run.halo.app.extension.ListOptions; +import run.halo.app.extension.ListResult; +import run.halo.app.extension.Metadata; +import run.halo.app.extension.PageRequestImpl; +import run.halo.app.extension.Unstructured; +import run.halo.app.extension.controller.*; +import run.halo.app.extension.router.selector.FieldSelector; +import java.net.URL; +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static org.springframework.data.domain.Sort.Order.asc; +import static run.halo.app.extension.MetadataUtil.nullSafeAnnotations; +import static run.halo.app.extension.index.query.QueryFactory.and; +import static run.halo.app.extension.index.query.QueryFactory.equal; +import static run.halo.app.extension.index.query.QueryFactory.isNull; + +@Component +public class RssSyncReconciler implements Reconciler,SmartLifecycle { + + private static final Logger log = LoggerFactory.getLogger(RssSyncReconciler.class); + + private volatile boolean running = false; + + private final ExtensionClient client; + private final RequestQueue queue; + private final Controller controller; + private final ObjectMapper objectMapper = Unstructured.OBJECT_MAPPER; + + + public RssSyncReconciler(ExtensionClient client) { + this.client = client; + queue = new DefaultQueue<>(Instant::now); + controller = this.setupWith(null); + } + + @Override + public Result reconcile(Request request) { + Link link = request.link(); + int sum = request.sum(); + List disableSyncList = request.disableSyncList(); + var linkName = link.getMetadata().getName(); + String rssUrl = getRss(link); + boolean isContains = false; + if (ObjectUtils.isNotEmpty(disableSyncList)) { + isContains = disableSyncList.contains(linkName); + } + String syncLogName = "sync-log-" + linkName; + RssFeedSyncLog newRssFeedSyncLog = new RssFeedSyncLog(); + Metadata metadata = new Metadata(); + metadata.setName(syncLogName); + newRssFeedSyncLog.setMetadata(metadata); + newRssFeedSyncLog.setLinkName(linkName); + if (StringUtils.isNotEmpty(rssUrl) && !isContains) { + tryToSynchronizeFriendPost(newRssFeedSyncLog, link, sum); + }else if (StringUtils.isEmpty(rssUrl)) { + newRssFeedSyncLog.setSyncTime(Instant.now()); + newRssFeedSyncLog.setState(RssFeedSyncLog.RssFeedSyncLogState.nolink); + newRssFeedSyncLog.setFailureReason("No RSS link"); + newRssFeedSyncLog.setFailureMessage("无 RSS 链接."); + }else if (isContains) { + newRssFeedSyncLog.setSyncTime(Instant.now()); + newRssFeedSyncLog.setState(RssFeedSyncLog.RssFeedSyncLogState.failed); + newRssFeedSyncLog.setFailureReason("admin disables sync"); + newRssFeedSyncLog.setFailureMessage("管理员禁用同步."); + } + Optional fetch = client.fetch(RssFeedSyncLog.class, syncLogName); + if (fetch.isPresent()) { + RssFeedSyncLog rssFeedSyncLog = fetch.get(); + rssFeedSyncLog.setState(newRssFeedSyncLog.getState()); + rssFeedSyncLog.setSyncTime(newRssFeedSyncLog.getSyncTime()); + rssFeedSyncLog.setFailureReason(newRssFeedSyncLog.getFailureReason()); + rssFeedSyncLog.setFailureMessage(newRssFeedSyncLog.getFailureMessage()); + client.update(rssFeedSyncLog); + }else { + client.create(newRssFeedSyncLog); + } + return Result.doNotRetry(); + } + + public String getRss(Link link) { + var annotations = nullSafeAnnotations(link); + + //处理旧数据 rss_uri + String rssUri = annotations.get("rss_uri"); + + String rssUrl = annotations.get("rss_url"); + String isRequest = annotations.get("is_request"); + if ((StringUtils.isEmpty(rssUrl) && StringUtils.isEmpty(isRequest)) || StringUtils.isNotEmpty(rssUri)) { + String linkRss = LinkRequest.getLinkRss(link.getSpec().getUrl()); + if (StringUtils.isNotEmpty(rssUri)) { + linkRss = rssUri; + annotations.remove("rss_uri"); + annotations.put("rss_url", rssUri); + }else { + annotations.put("is_request","true"); + if (StringUtils.isNotEmpty(linkRss)) { + annotations.put("rss_url",linkRss); + } + } + updateLink(link); + return linkRss; + } + return rssUrl; + } + + + public void updateLink(Link link) { + Map extensionMap = objectMapper.convertValue(link, Map.class); + var extension = new Unstructured(extensionMap); + client.update(extension); + } + + private void tryToSynchronizeFriendPost(RssFeedSyncLog syncLog, Link link, int sum) { + var linkName = link.getMetadata().getName(); + var annotations = nullSafeAnnotations(link); + var rssUrl = annotations.get("rss_url"); + + CronFriendPost cron = new CronFriendPost(); + var spec = new CronFriendPost.CronSpec(); + spec.setSuccessfulRetainLimit(5); + cron.setSpec(spec); + syncLog.setSyncTime(Instant.now()); + List friendPostList = fetchFriendPost(rssUrl, sum, syncLog); + + // 即使获取失败也继续处理 + if (friendPostList != null && !friendPostList.isEmpty()) { + for (FriendPost rssPost : friendPostList) { + var friendPostListOptions = new ListOptions(); + friendPostListOptions.setFieldSelector(FieldSelector.of( + and(isNull("metadata.deletionTimestamp"), + equal("spec.postLink",rssPost.getSpec().getPostLink()) + ) + )); + long total = client.listBy(FriendPost.class, friendPostListOptions, + PageRequestImpl.ofSize(1)).getTotal(); + if (total==0) { + FriendPost friendPost = new FriendPost(); + // 设置元数据才能保存 + friendPost.setMetadata(new Metadata()); + friendPost.getMetadata().setGenerateName("friend-post-"); + friendPost.setSpec(rssPost.getSpec()); + var friendPostSpec = friendPost.getSpec(); + friendPostSpec.setLogo(link.getSpec().getLogo()); + friendPostSpec.setAuthor(link.getSpec().getDisplayName()); + friendPostSpec.setAuthorUrl(link.getSpec().getUrl()); + friendPostSpec.setLinkName(linkName); + String description = friendPost.getSpec().getDescription(); + //解析html内容转换成文本 + if (StringUtils.isNotEmpty(description)){ + description = CommonUtils.parseAndTruncateHtml2Text(description, 200); + String regexp = "[ *|\\s*]*"; + description = description.replaceFirst(regexp, "").trim(); + friendPostSpec.setDescription(description); + } + client.create(friendPost); + } + } + delFriendPost(linkName,sum); + } + } + + public List fetchFriendPost(String rssAddress,int postsLimit, RssFeedSyncLog sync) { + List friendPostList = new ArrayList<>(); + try { + SyndFeed feed = new SyndFeedInput().build(new XmlReader(new URL(rssAddress))); + int postCount = 0; + for (SyndEntry entry : feed.getEntries()) { + Date date = entry.getPublishedDate(); + if (date == null) { + date = entry.getUpdatedDate(); + } + String description = ""; + SyndContent content = entry.getDescription(); + if (content != null) { + description = content.getValue(); + }else { + List contents = entry.getContents(); + if (!contents.isEmpty()) { + description = contents.get(0).getValue(); + } + } + + FriendPost friendPost = new FriendPost(); + friendPost.setSpec(new FriendPost.FriendPostSpec()); + friendPost.getSpec().setTitle(entry.getTitle()); + friendPost.getSpec().setPostLink(entry.getLink()); + friendPost.getSpec().setPubDate(date.toInstant()); + friendPost.getSpec().setDescription(description); + friendPostList.add(friendPost); + postCount++; + if (postCount >= postsLimit) { + break; + } + } + sync.setState(RssFeedSyncLog.RssFeedSyncLogState.success); + } catch (Exception e) { + sync.setState(RssFeedSyncLog.RssFeedSyncLogState.failed); + sync.setFailureReason("CrawlingRSSError"); + sync.setFailureMessage(e.getMessage()); + log.error("error in crawling rss", e); + return null; + } + return friendPostList; + } + + public void delFriendPost(String linkName,Integer sum) { + var friendPostListOptions = new ListOptions(); + friendPostListOptions.setFieldSelector(FieldSelector.of( + equal("spec.linkName",linkName) + )); + long total = client.listBy(FriendPost.class, friendPostListOptions, PageRequestImpl.ofSize(1)).getTotal(); + if (total > sum) { + int l = (int) (total - sum); + PageRequestImpl pageRequestImpl = PageRequestImpl.of(pageNullSafe(1), sizeNullSafe(l), + Sort.by(asc("spec.pubDate"))); + ListResult friendPosts = + client.listBy(FriendPost.class, friendPostListOptions, pageRequestImpl); + for (FriendPost friendPost : friendPosts) { + client.delete(friendPost); + } + } + } + + static int pageNullSafe(Integer page) { + return ObjectUtils.defaultIfNull(page, 1); + } + + static int sizeNullSafe(Integer size) { + return ObjectUtils.defaultIfNull(size, 10); + } + + + @Override + public Controller setupWith(ControllerBuilder builder) { + return new DefaultController<>( + this.getClass().getName(), + this, + queue, + null, + Duration.ofMillis(300), + Duration.ofMinutes(5), + 5); + } + + @Override + public void start() { + this.controller.start(); + this.running = true; + } + + @Override + public void stop() { + this.running = false; + this.controller.dispose(); + } + + @Override + public boolean isRunning() { + return this.running; + } + + @EventListener(RssFeedSyncEvent.class) + public void onReplyEvent(RssFeedSyncEvent event) { + var request = new Request(event.getLink(),event.getSum(),event.getDisableSyncList()); + queue.addImmediately(request); + } + + public record Request(Link link,int sum,List disableSyncList) { + } +} diff --git a/src/main/java/la/moony/friends/rest/FriendRouter.java b/src/main/java/la/moony/friends/rest/FriendRouter.java new file mode 100644 index 0000000..e6be1d7 --- /dev/null +++ b/src/main/java/la/moony/friends/rest/FriendRouter.java @@ -0,0 +1,94 @@ +package la.moony.friends.rest; + +import la.moony.friends.finders.FriendFinder; +import la.moony.friends.vo.FriendPostVo; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.RouterFunctions; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; +import run.halo.app.plugin.ReactiveSettingFetcher; +import run.halo.app.theme.TemplateNameResolver; +import run.halo.app.theme.router.ModelConst; +import run.halo.app.theme.router.PageUrlUtils; +import run.halo.app.theme.router.UrlContextListResult; + +import java.util.HashMap; +import java.util.Map; + +import static run.halo.app.theme.router.PageUrlUtils.totalPage; + +@Configuration +@RequiredArgsConstructor +@Slf4j +public class FriendRouter { + + + private final FriendFinder friendFinder; + + private final TemplateNameResolver templateNameResolver; + + private final ReactiveSettingFetcher settingFetcher; + + @Bean + RouterFunction friendTemplateRoute() { + return RouterFunctions.route().GET("/friends",this::handlerFunction) + .GET("/friends/page/{page:\\d+}",this::handlerFunction) + .build(); + } + + + private Mono handlerFunction(ServerRequest request) { + return templateNameResolver.resolveTemplateNameOrDefault(request.exchange(), "friends") + .flatMap( templateName -> ServerResponse.ok().render(templateName, + java.util.Map.of(ModelConst.TEMPLATE_ID, templateName, + "friends", friendPostList(request), + "title",getFriendTitle()) + )); + } + + Mono getFriendTitle() { + return this.settingFetcher.get("base").map( + setting -> setting.get("title").asText("友链朋友圈")).defaultIfEmpty( + "友链朋友圈"); + } + + private Mono> friendPostList(ServerRequest request) { + String path = request.path(); + int pageNum = pageNumInPathVariable(request); + String linkName = request.queryParam("linkName") + .filter(StringUtils::isNotBlank) + .orElse(null); + return this.settingFetcher.get("base") + .map(item -> item.get("pageSize").asInt(10)) + .defaultIfEmpty(10) + .flatMap(pageSize -> { + Map params = new HashMap<>(); + params.put("page",pageNum); + params.put("size",pageSize); + if(StringUtils.isNotBlank(linkName)){ + params.put("linkName",linkName); + } + return friendFinder.list(params) + .map(list -> new UrlContextListResult.Builder() + .listResult(list) + .nextUrl(PageUrlUtils.nextPageUrl(path, totalPage(list))) + .prevUrl(PageUrlUtils.prevPageUrl(path)) + .build() + ); + }); + } + + + private int pageNumInPathVariable(ServerRequest request) { + String page = request.pathVariables().get("page"); + return NumberUtils.toInt(page, 1); + } + +} diff --git a/src/main/java/la/moony/friends/service/RssDetailService.java b/src/main/java/la/moony/friends/service/RssDetailService.java new file mode 100644 index 0000000..6c792b7 --- /dev/null +++ b/src/main/java/la/moony/friends/service/RssDetailService.java @@ -0,0 +1,9 @@ +package la.moony.friends.service; + +import la.moony.friends.vo.RssDetail; +import reactor.core.publisher.Mono; + +public interface RssDetailService { + + Mono fetchRssDetail(String rssUrl, int fetchLimitNumber); +} diff --git a/src/main/java/la/moony/friends/service/RssFeedSyncLogService.java b/src/main/java/la/moony/friends/service/RssFeedSyncLogService.java new file mode 100644 index 0000000..0c187a4 --- /dev/null +++ b/src/main/java/la/moony/friends/service/RssFeedSyncLogService.java @@ -0,0 +1,13 @@ +package la.moony.friends.service; + +import la.moony.friends.ListedRssSyncLog; +import la.moony.friends.query.RssSyncLogQuery; +import reactor.core.publisher.Mono; +import run.halo.app.extension.ListResult; + +public interface RssFeedSyncLogService { + + Mono> listRssSyncLog(RssSyncLogQuery query); + + Mono publishRssFeedSyncEvent(String name); +} diff --git a/src/main/java/la/moony/friends/service/impl/RssDetailServiceImpl.java b/src/main/java/la/moony/friends/service/impl/RssDetailServiceImpl.java new file mode 100644 index 0000000..c3ca9ea --- /dev/null +++ b/src/main/java/la/moony/friends/service/impl/RssDetailServiceImpl.java @@ -0,0 +1,53 @@ +package la.moony.friends.service.impl; + +import com.rometools.rome.feed.synd.SyndEntry; +import com.rometools.rome.feed.synd.SyndFeed; +import com.rometools.rome.io.SyndFeedInput; +import com.rometools.rome.io.XmlReader; +import la.moony.friends.service.RssDetailService; +import la.moony.friends.vo.RssDetail; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +@Component +public class RssDetailServiceImpl implements RssDetailService { + + + @Override + public Mono fetchRssDetail(String rssUrl, int fetchLimitNumber) { + RssDetail rssDetail = new RssDetail(); + try { + // 创建 SyndFeedInput 对象并从 URL 获取 RSS 提要 + SyndFeedInput input = new SyndFeedInput(); + SyndFeed feed = input.build(new XmlReader(new URL(rssUrl))); + RssDetail.Channel channel = new RssDetail.Channel(); + channel.setTitle(feed.getTitle()); + channel.setLink(feed.getLink()); + channel.setDescription(feed.getDescription()); + rssDetail.setChannel(channel); + List items = new ArrayList<>(); + int count = 0; + for (SyndEntry syndEntry : feed.getEntries()) { + RssDetail.Item item = new RssDetail.Item(); + item.setTitle(syndEntry.getTitle()); + item.setLink(syndEntry.getLink()); + item.setAuthor(syndEntry.getAuthor()); + item.setDescription(syndEntry.getDescription().getValue()); + item.setPubDate(syndEntry.getPublishedDate().toInstant()); + items.add(item); + count++; + if (count >= fetchLimitNumber) { + break; + } + } + channel.setItems(items); + }catch (Exception e) { + e.printStackTrace(); + } + return Mono.just(rssDetail); + } + +} diff --git a/src/main/java/la/moony/friends/service/impl/RssFeedSyncLogServiceImpl.java b/src/main/java/la/moony/friends/service/impl/RssFeedSyncLogServiceImpl.java new file mode 100644 index 0000000..07a7dc5 --- /dev/null +++ b/src/main/java/la/moony/friends/service/impl/RssFeedSyncLogServiceImpl.java @@ -0,0 +1,97 @@ +package la.moony.friends.service.impl; + +import la.moony.friends.ListedRssSyncLog; +import la.moony.friends.RssFeedSyncEvent; +import la.moony.friends.extension.CronFriendPost; +import la.moony.friends.extension.Link; +import la.moony.friends.extension.RssFeedSyncLog; +import la.moony.friends.query.RssSyncLogQuery; +import la.moony.friends.service.RssFeedSyncLogService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import run.halo.app.extension.ListOptions; +import run.halo.app.extension.ListResult; +import run.halo.app.extension.PageRequestImpl; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.extension.router.selector.FieldSelector; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +import static run.halo.app.extension.index.query.QueryFactory.equal; +import static run.halo.app.extension.index.query.QueryFactory.isNull; + +@Component +public class RssFeedSyncLogServiceImpl implements RssFeedSyncLogService { + + private final ReactiveExtensionClient client; + + private final ApplicationEventPublisher eventPublisher; + + public RssFeedSyncLogServiceImpl(ReactiveExtensionClient client, + ApplicationEventPublisher eventPublisher) { + this.client = client; + this.eventPublisher = eventPublisher; + } + + @Override + public Mono> listRssSyncLog(RssSyncLogQuery query) { + return buildListOptions(query) + .flatMap(listOptions -> client.listBy(Link.class, listOptions, + PageRequestImpl.of(query.getPage(), query.getSize(), query.getSort()) + )) + .flatMap(listResult -> Flux.fromStream(listResult.get()) + .map(this::getListedRssSyncLog) + .flatMapSequential(Function.identity()) + .collectList() + .map(listedApplications -> new ListResult<>(listResult.getPage(), listResult.getSize(), + listResult.getTotal(), listedApplications) + ) + .defaultIfEmpty(ListResult.emptyResult()) + ); + } + + private Mono getListedRssSyncLog(Link link) { + Assert.notNull(link, "The link must not be null."); + var listedRssSyncLog = new ListedRssSyncLog().setLink(link); + var logMono = client.fetch(RssFeedSyncLog.class,"sync-log-"+link.getMetadata().getName()) + .doOnNext(listedRssSyncLog::setLog); + + return Mono.when(logMono) + .thenReturn(listedRssSyncLog); + } + + Mono buildListOptions(RssSyncLogQuery query) { + return Mono.just(query.toListOptions()); + } + + public Mono publishRssFeedSyncEvent(String name) { + CronFriendPost cron = new CronFriendPost(); + CronFriendPost.CronSpec cronSpec = new CronFriendPost.CronSpec(); + cronSpec.setSuccessfulRetainLimit(5); + cronSpec.setDisableSyncList(new ArrayList<>()); + cron.setSpec(cronSpec); + return client.fetch(CronFriendPost.class, "cron-friends-default") + .defaultIfEmpty(cron) + .flatMap(cronFriendPost -> { + var spec = cronFriendPost.getSpec(); + int successfulRetainLimit = spec.getSuccessfulRetainLimit(); + List disableSyncList = spec.getDisableSyncList(); + int sum = successfulRetainLimit == 0 ? 5 : successfulRetainLimit; + var listOptions = new ListOptions(); + FieldSelector fieldSelector = FieldSelector.of(isNull("metadata.deletionTimestamp")); + if (!StringUtils.equals(name,"all")) { + fieldSelector = fieldSelector.andQuery(equal("metadata.name",name)); + } + listOptions.setFieldSelector(fieldSelector); + return client.listAll(Link.class, listOptions, Sort.by("metadata.creationTimestamp")) + .doOnNext(link -> eventPublisher.publishEvent(new RssFeedSyncEvent(this, link,sum,disableSyncList))) + .then(); + }); + } +} diff --git a/src/main/java/la/moony/friends/util/CommonUtils.java b/src/main/java/la/moony/friends/util/CommonUtils.java new file mode 100644 index 0000000..8f987e2 --- /dev/null +++ b/src/main/java/la/moony/friends/util/CommonUtils.java @@ -0,0 +1,24 @@ +package la.moony.friends.util; + +import org.apache.commons.lang3.StringUtils; +import org.jsoup.Jsoup; + +public class CommonUtils { + + public static String parseAndTruncateHtml2Text(String html, int length) { + if (StringUtils.isBlank(html)) { + return ""; + } + + String text = Jsoup.parse(html).text(); + if (text.length() <= length) { + return text; + } else { + text = text.substring(0, length); + text += "..."; + } + return text; + } + + +} diff --git a/src/main/java/la/moony/friends/util/LinkRequest.java b/src/main/java/la/moony/friends/util/LinkRequest.java new file mode 100644 index 0000000..e3fa74d --- /dev/null +++ b/src/main/java/la/moony/friends/util/LinkRequest.java @@ -0,0 +1,53 @@ +package la.moony.friends.util; + +import java.io.IOException; +import java.util.Map; +import java.util.Objects; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LinkRequest { + + private static final Logger log = LoggerFactory.getLogger(LinkRequest.class); + + private final static String USER_AGENT = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " + + "Chrome/58.0.3029.110 Safari/537.3"; + + public static String getLinkRss(String linkUrl) { + Document document = null; + try { + document = Jsoup.connect(linkUrl) + .followRedirects(true) + .maxBodySize(1024 * 1024 * 20) + .timeout(10000) + .headers( + Map.of( + "User-Agent", USER_AGENT, + "Referer", linkUrl, + "Accept", "text/html,application/xhtml+xml,application/xml" + ) + ) + .get(); + } catch (IOException e) { + log.error("Failed to get link rss ",e); + } + + if(document != null) { + Element rssElement = document.selectFirst("link[type=application/rss+xml]"); + if (Objects.nonNull(rssElement)) { + return rssElement.absUrl("href"); + } + + Element atomElement = document.selectFirst("link[type=application/atom+xml]"); + if (Objects.nonNull(atomElement)) { + return atomElement.absUrl("href"); + } + } + + return ""; + } +} diff --git a/src/main/java/la/moony/friends/util/SortUtils.java b/src/main/java/la/moony/friends/util/SortUtils.java new file mode 100644 index 0000000..5030fec --- /dev/null +++ b/src/main/java/la/moony/friends/util/SortUtils.java @@ -0,0 +1,43 @@ +package la.moony.friends.util; + +import java.util.List; +import lombok.experimental.UtilityClass; +import org.springframework.data.domain.Sort; +import org.springframework.lang.NonNull; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; + +@UtilityClass +public class SortUtils { + static final String delimiter = ","; + + /** + *

Resolve from direction params, e.g. "name,asc" or "name"

+ * + * @param directionParams direction params + * @return sort object + */ + public static Sort resolve(List directionParams) { + if (CollectionUtils.isEmpty(directionParams)) { + return Sort.unsorted(); + } + Sort.Order[] orders = new Sort.Order[directionParams.size()]; + for (int i = 0; i < directionParams.size(); i++) { + String[] parts = directionParams.get(i).split(delimiter); + if (parts.length == 1) { + orders[i] = new Sort.Order(Sort.Direction.ASC, parts[0]); + } else { + orders[i] = new Sort.Order(toDirection(parts[1]), parts[0]); + } + } + return Sort.by(orders); + } + + private static Sort.Direction toDirection(@NonNull String direction) { + Assert.notNull(direction, "Direction must not be null"); + if (direction.contains(" ")) { + throw new IllegalArgumentException("Direction must not contain whitespace"); + } + return Sort.Direction.fromString(direction); + } +} diff --git a/src/main/java/la/moony/friends/vo/FriendPostVo.java b/src/main/java/la/moony/friends/vo/FriendPostVo.java new file mode 100644 index 0000000..5b190b8 --- /dev/null +++ b/src/main/java/la/moony/friends/vo/FriendPostVo.java @@ -0,0 +1,27 @@ +package la.moony.friends.vo; + +import la.moony.friends.extension.FriendPost; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import lombok.experimental.SuperBuilder; +import run.halo.app.extension.MetadataOperator; + + +@Data +@SuperBuilder +@ToString +@EqualsAndHashCode +public class FriendPostVo { + + private MetadataOperator metadata; + + private FriendPost.FriendPostSpec spec; + + public static FriendPostVo from(FriendPost friendPost) { + return FriendPostVo.builder() + .metadata(friendPost.getMetadata()) + .spec(friendPost.getSpec()) + .build(); + } +} diff --git a/src/main/java/la/moony/friends/vo/LinkGroupVo.java b/src/main/java/la/moony/friends/vo/LinkGroupVo.java new file mode 100644 index 0000000..0dfd8c6 --- /dev/null +++ b/src/main/java/la/moony/friends/vo/LinkGroupVo.java @@ -0,0 +1,31 @@ +package la.moony.friends.vo; + +import la.moony.friends.extension.LinkGroup; +import lombok.Builder; +import lombok.Value; +import lombok.With; +import run.halo.app.extension.MetadataOperator; +import run.halo.app.theme.finders.vo.ExtensionVoOperator; + +import java.util.List; + + +@Value +@Builder +public class LinkGroupVo implements ExtensionVoOperator { + + MetadataOperator metadata; + + LinkGroup.LinkGroupSpec spec; + + @With + List links; + + public static LinkGroupVo from(LinkGroup linkGroup) { + return LinkGroupVo.builder() + .metadata(linkGroup.getMetadata()) + .spec(linkGroup.getSpec()) + .links(List.of()) + .build(); + } +} diff --git a/src/main/java/la/moony/friends/vo/LinkVo.java b/src/main/java/la/moony/friends/vo/LinkVo.java new file mode 100644 index 0000000..c364662 --- /dev/null +++ b/src/main/java/la/moony/friends/vo/LinkVo.java @@ -0,0 +1,38 @@ +package la.moony.friends.vo; + +import la.moony.friends.extension.Link; +import la.moony.friends.extension.RssFeedSyncLog; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import lombok.Value; +import lombok.With; +import lombok.experimental.SuperBuilder; +import run.halo.app.extension.MetadataOperator; +import run.halo.app.theme.finders.vo.ExtensionVoOperator; +import java.util.List; + +@Data +@SuperBuilder +@ToString +@EqualsAndHashCode +public class LinkVo implements ExtensionVoOperator { + + MetadataOperator metadata; + + Link.LinkSpec spec; + + + RssFeedSyncLog rssFeedSyncLog; + + List friendPosts; + + public static LinkVo from(Link link) { + return LinkVo.builder() + .metadata(link.getMetadata()) + .spec(link.getSpec()) + .friendPosts(List.of()) + .build(); + } +} diff --git a/src/main/java/la/moony/friends/vo/RssDetail.java b/src/main/java/la/moony/friends/vo/RssDetail.java new file mode 100644 index 0000000..793c008 --- /dev/null +++ b/src/main/java/la/moony/friends/vo/RssDetail.java @@ -0,0 +1,41 @@ +package la.moony.friends.vo; + +import lombok.Data; +import java.time.Instant; +import java.util.List; + + +@Data +public class RssDetail { + + private Channel channel; + + @Data + public static class Channel { + + private String title; + + private String link; + + private String description; + + private List items; + } + + @Data + public static class Item { + private String title; + + private String link; + + private String author; + + private String description; + + private String uri; + + private Instant pubDate; + + } + +} diff --git a/src/main/resources/extensions/annotation-setting.yaml b/src/main/resources/extensions/annotation-setting.yaml new file mode 100644 index 0000000..b33239d --- /dev/null +++ b/src/main/resources/extensions/annotation-setting.yaml @@ -0,0 +1,13 @@ +apiVersion: v1alpha1 +kind: AnnotationSetting +metadata: + name: annotation-setting-link-rss +spec: + targetRef: + group: core.halo.run + kind: Link + formSchema: + - $formkit: "url" + name: "rss_url" + label: "RSS 地址" + diff --git a/src/main/resources/extensions/reverseProxy.yaml b/src/main/resources/extensions/reverseProxy.yaml new file mode 100644 index 0000000..3602910 --- /dev/null +++ b/src/main/resources/extensions/reverseProxy.yaml @@ -0,0 +1,8 @@ +apiVersion: plugin.halo.run/v1alpha1 +kind: ReverseProxy +metadata: + name: friends-static-assets +rules: + - path: /static/** + file: + directory: static diff --git a/src/main/resources/extensions/roleTemplate.yaml b/src/main/resources/extensions/roleTemplate.yaml new file mode 100644 index 0000000..97a4094 --- /dev/null +++ b/src/main/resources/extensions/roleTemplate.yaml @@ -0,0 +1,59 @@ +apiVersion: v1alpha1 +kind: Role +metadata: + name: role-template-fridends-anonymous + labels: + halo.run/role-template: "true" + halo.run/hidden: "true" + rbac.authorization.halo.run/aggregate-to-anonymous: "true" + annotations: + rbac.authorization.halo.run/module: "朋友圈" + rbac.authorization.halo.run/display-name: "朋友圈匿名接口" +rules: + - apiGroups: ["api.friend.moony.la"] + resources: ["parsingrss","friendposts"] + verbs: [ "get", "list" ] + +--- + +apiVersion: v1alpha1 +kind: Role +metadata: + name: role-template-friends-view + labels: + halo.run/role-template: "true" + annotations: + rbac.authorization.halo.run/module: "朋友圈" + rbac.authorization.halo.run/display-name: "朋友圈查看" + rbac.authorization.halo.run/ui-permissions: | + ["plugin:friends:view"] +rules: + - apiGroups: [ "friend.moony.la" ] + resources: [ "friendposts","cronfriendposts" ] + verbs: [ "get", "list" ] + - apiGroups: [ "api.friend.moony.la" ] + resources: [ "friendposts","parsingrss"] + verbs: [ "get", "list" ] + +--- + +apiVersion: v1alpha1 +kind: Role +metadata: + name: role-template-friends-manage + labels: + halo.run/role-template: "true" + annotations: + rbac.authorization.halo.run/module: "朋友圈" + rbac.authorization.halo.run/display-name: "朋友圈管理" + rbac.authorization.halo.run/ui-permissions: | + ["plugin:friends:manage"] + rbac.authorization.halo.run/dependencies: | + ["role-template-friends-view"] +rules: + - apiGroups: [ "friend.moony.la" ] + resources: [ "friendposts","cronfriendposts"] + verbs: [ "create", "patch", "update", "delete", "deletecollection" ] + - apiGroups: [ "api.friend.moony.la" ] + resources: [ "friendposts","syncrssfeed","friendposts/delete"] + verbs: [ "create", "patch", "update", "delete", "deletecollection" ] \ No newline at end of file diff --git a/src/main/resources/extensions/settings.yaml b/src/main/resources/extensions/settings.yaml new file mode 100644 index 0000000..dc002b7 --- /dev/null +++ b/src/main/resources/extensions/settings.yaml @@ -0,0 +1,19 @@ +apiVersion: v1alpha1 +kind: Setting +metadata: + name: plugin-friends-settings +spec: + forms: + - group: base + label: 基本设置 + formSchema: + - $formkit: text + label: 页面标题 + name: title + validation: required + value: '友链朋友圈' + - $formkit: text + label: 朋友圈列表显示条数 + name: pageSize + validation: required|Number + value: 10 diff --git a/src/main/resources/logo.png b/src/main/resources/logo.png new file mode 100644 index 0000000..61e377b Binary files /dev/null and b/src/main/resources/logo.png differ diff --git a/src/main/resources/plugin.yaml b/src/main/resources/plugin.yaml new file mode 100644 index 0000000..d8aff85 --- /dev/null +++ b/src/main/resources/plugin.yaml @@ -0,0 +1,25 @@ +apiVersion: plugin.halo.run/v1alpha1 +kind: Plugin +metadata: + name: plugin-friends + annotations: + "store.halo.run/app-id": "app-yISsV" +spec: + enabled: false + requires: ">=2.21.0" + pluginDependencies: + "PluginLinks": ">=1.5.0" + author: + name: 困困鱼 + website: https://github.com/chengzhongxue + logo: logo.png + homepage: https://www.halo.run/store/apps/app-yISsV + repo: https://github.com/chengzhongxue/plugin-friends-new + issues: https://github.com/chengzhongxue/plugin-friends-new/issues + configMapName: plugin-friends-configMap + settingName: plugin-friends-settings + displayName: "朋友圈" + description: "提供对 RSS 链接的订阅功能,支持获取其订阅内容" + license: + - name: "GPL-3.0" + url: "https://github.com/chengzhongxue/plugin-friends-new/blob/main/LICENSE" \ No newline at end of file diff --git a/src/main/resources/static/friends-rss.iife.js b/src/main/resources/static/friends-rss.iife.js new file mode 100644 index 0000000..037eeff --- /dev/null +++ b/src/main/resources/static/friends-rss.iife.js @@ -0,0 +1,118 @@ +(function(it){"use strict";var xt;const St='*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:var(--un-default-border-color, #e5e7eb)}:before,:after{--un-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}';/** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */const R=globalThis,q=R.ShadowRoot&&(R.ShadyCSS===void 0||R.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,V=Symbol(),rt=new WeakMap;let nt=class{constructor(t,e,i){if(this._$cssResult$=!0,i!==V)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const e=this.t;if(q&&t===void 0){const i=e!==void 0&&e.length===1;i&&(t=rt.get(e)),t===void 0&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),i&&rt.set(e,t))}return t}toString(){return this.cssText}};const ot=r=>new nt(typeof r=="string"?r:r+"",void 0,V),at=(r,...t)=>{const e=r.length===1?r[0]:t.reduce((i,s,o)=>i+(n=>{if(n._$cssResult$===!0)return n.cssText;if(typeof n=="number")return n;throw Error("Value passed to 'css' function must be a 'css' function result: "+n+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(s)+r[o+1],r[0]);return new nt(e,r,V)},kt=(r,t)=>{if(q)r.adoptedStyleSheets=t.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(const e of t){const i=document.createElement("style"),s=R.litNonce;s!==void 0&&i.setAttribute("nonce",s),i.textContent=e.cssText,r.appendChild(i)}},lt=q?r=>r:r=>r instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return ot(e)})(r):r;/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */const{is:Ct,defineProperty:Pt,getOwnPropertyDescriptor:Ut,getOwnPropertyNames:Mt,getOwnPropertySymbols:zt,getPrototypeOf:Ot}=Object,m=globalThis,ht=m.trustedTypes,Ht=ht?ht.emptyScript:"",W=m.reactiveElementPolyfillSupport,U=(r,t)=>r,j={toAttribute(r,t){switch(t){case Boolean:r=r?Ht:null;break;case Object:case Array:r=r==null?r:JSON.stringify(r)}return r},fromAttribute(r,t){let e=r;switch(t){case Boolean:e=r!==null;break;case Number:e=r===null?null:Number(r);break;case Object:case Array:try{e=JSON.parse(r)}catch{e=null}}return e}},Z=(r,t)=>!Ct(r,t),ct={attribute:!0,type:String,converter:j,reflect:!1,useDefault:!1,hasChanged:Z};Symbol.metadata??(Symbol.metadata=Symbol("metadata")),m.litPropertyMetadata??(m.litPropertyMetadata=new WeakMap);let S=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??(this.l=[])).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=ct){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);s!==void 0&&Pt(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:o}=Ut(this.prototype,t)??{get(){return this[e]},set(n){this[e]=n}};return{get:s,set(n){const l=s==null?void 0:s.call(this);o==null||o.call(this,n),this.requestUpdate(t,l,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??ct}static _$Ei(){if(this.hasOwnProperty(U("elementProperties")))return;const t=Ot(this);t.finalize(),t.l!==void 0&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(U("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(U("properties"))){const e=this.properties,i=[...Mt(e),...zt(e)];for(const s of i)this.createProperty(s,e[s])}const t=this[Symbol.metadata];if(t!==null){const e=litPropertyMetadata.get(t);if(e!==void 0)for(const[i,s]of e)this.elementProperties.set(i,s)}this._$Eh=new Map;for(const[e,i]of this.elementProperties){const s=this._$Eu(e,i);s!==void 0&&this._$Eh.set(s,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const s of i)e.unshift(lt(s))}else t!==void 0&&e.push(lt(t));return e}static _$Eu(t,e){const i=e.attribute;return i===!1?void 0:typeof i=="string"?i:typeof t=="string"?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){var t;this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),(t=this.constructor.l)==null||t.forEach(e=>e(this))}addController(t){var e;(this._$EO??(this._$EO=new Set)).add(t),this.renderRoot!==void 0&&this.isConnected&&((e=t.hostConnected)==null||e.call(t))}removeController(t){var e;(e=this._$EO)==null||e.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return kt(t,this.constructor.elementStyles),t}connectedCallback(){var t;this.renderRoot??(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),(t=this._$EO)==null||t.forEach(e=>{var i;return(i=e.hostConnected)==null?void 0:i.call(e)})}enableUpdating(t){}disconnectedCallback(){var t;(t=this._$EO)==null||t.forEach(e=>{var i;return(i=e.hostDisconnected)==null?void 0:i.call(e)})}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){var o;const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(s!==void 0&&i.reflect===!0){const n=(((o=i.converter)==null?void 0:o.toAttribute)!==void 0?i.converter:j).toAttribute(e,i.type);this._$Em=t,n==null?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){var o,n;const i=this.constructor,s=i._$Eh.get(t);if(s!==void 0&&this._$Em!==s){const l=i.getPropertyOptions(s),a=typeof l.converter=="function"?{fromAttribute:l.converter}:((o=l.converter)==null?void 0:o.fromAttribute)!==void 0?l.converter:j;this._$Em=s;const c=a.fromAttribute(e,l.type);this[s]=c??((n=this._$Ej)==null?void 0:n.get(s))??c,this._$Em=null}}requestUpdate(t,e,i){var s;if(t!==void 0){const o=this.constructor,n=this[t];if(i??(i=o.getPropertyOptions(t)),!((i.hasChanged??Z)(n,e)||i.useDefault&&i.reflect&&n===((s=this._$Ej)==null?void 0:s.get(t))&&!this.hasAttribute(o._$Eu(t,i))))return;this.C(t,e,i)}this.isUpdatePending===!1&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:o},n){i&&!(this._$Ej??(this._$Ej=new Map)).has(t)&&(this._$Ej.set(t,n??e??this[t]),o!==!0||n!==void 0)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),s===!0&&this._$Em!==t&&(this._$Eq??(this._$Eq=new Set)).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}const t=this.scheduleUpdate();return t!=null&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var i;if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??(this.renderRoot=this.createRenderRoot()),this._$Ep){for(const[o,n]of this._$Ep)this[o]=n;this._$Ep=void 0}const s=this.constructor.elementProperties;if(s.size>0)for(const[o,n]of s){const{wrapped:l}=n,a=this[o];l!==!0||this._$AL.has(o)||a===void 0||this.C(o,void 0,n,a)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),(i=this._$EO)==null||i.forEach(s=>{var o;return(o=s.hostUpdate)==null?void 0:o.call(s)}),this.update(e)):this._$EM()}catch(s){throw t=!1,this._$EM(),s}t&&this._$AE(e)}willUpdate(t){}_$AE(t){var e;(e=this._$EO)==null||e.forEach(i=>{var s;return(s=i.hostUpdated)==null?void 0:s.call(i)}),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&(this._$Eq=this._$Eq.forEach(e=>this._$ET(e,this[e]))),this._$EM()}updated(t){}firstUpdated(t){}};S.elementStyles=[],S.shadowRootOptions={mode:"open"},S[U("elementProperties")]=new Map,S[U("finalized")]=new Map,W==null||W({ReactiveElement:S}),(m.reactiveElementVersions??(m.reactiveElementVersions=[])).push("2.1.1");/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */const M=globalThis,D=M.trustedTypes,ut=D?D.createPolicy("lit-html",{createHTML:r=>r}):void 0,dt="$lit$",b=`lit$${Math.random().toFixed(9).slice(2)}$`,pt="?"+b,Tt=`<${pt}>`,_=document,z=()=>_.createComment(""),O=r=>r===null||typeof r!="object"&&typeof r!="function",F=Array.isArray,Nt=r=>F(r)||typeof(r==null?void 0:r[Symbol.iterator])=="function",J=`[ +\f\r]`,H=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,ft=/-->/g,$t=/>/g,A=RegExp(`>|${J}(?:([^\\s"'>=/]+)(${J}*=${J}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`,"g"),gt=/'/g,mt=/"/g,bt=/^(?:script|style|textarea|title)$/i,Rt=r=>(t,...e)=>({_$litType$:r,strings:t,values:e}),I=Rt(1),v=Symbol.for("lit-noChange"),f=Symbol.for("lit-nothing"),yt=new WeakMap,w=_.createTreeWalker(_,129);function _t(r,t){if(!F(r)||!r.hasOwnProperty("raw"))throw Error("invalid template strings array");return ut!==void 0?ut.createHTML(t):t}const jt=(r,t)=>{const e=r.length-1,i=[];let s,o=t===2?"":t===3?"":"",n=H;for(let l=0;l"?(n=s??H,h=-1):p[1]===void 0?h=-2:(h=n.lastIndex-p[2].length,c=p[1],n=p[3]===void 0?A:p[3]==='"'?mt:gt):n===mt||n===gt?n=A:n===ft||n===$t?n=H:(n=A,s=void 0);const u=n===A&&r[l+1].startsWith("/>")?" ":"";o+=n===H?a+Tt:h>=0?(i.push(c),a.slice(0,h)+dt+a.slice(h)+b+u):a+b+(h===-2?l:u)}return[_t(r,o+(r[e]||"")+(t===2?"":t===3?"":"")),i]};class T{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let o=0,n=0;const l=t.length-1,a=this.parts,[c,p]=jt(t,e);if(this.el=T.createElement(c,i),w.currentNode=this.el.content,e===2||e===3){const h=this.el.content.firstChild;h.replaceWith(...h.childNodes)}for(;(s=w.nextNode())!==null&&a.length0){s.textContent=D?D.emptyScript:"";for(let u=0;u2||i[0]!==""||i[1]!==""?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=f}_$AI(t,e=this,i,s){const o=this.strings;let n=!1;if(o===void 0)t=k(this,t,e,0),n=!O(t)||t!==this._$AH&&t!==v,n&&(this._$AH=t);else{const l=t;let a,c;for(t=o[0],a=0;a{const i=(e==null?void 0:e.renderBefore)??t;let s=i._$litPart$;if(s===void 0){const o=(e==null?void 0:e.renderBefore)??null;i._$litPart$=s=new C(t.insertBefore(z(),o),o,void 0,e??{})}return s._$AI(r),s};/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */const x=globalThis;let P=class extends S{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){var e;const t=super.createRenderRoot();return(e=this.renderOptions).renderBefore??(e.renderBefore=t.firstChild),t}update(t){const e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=Wt(e,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),(t=this._$Do)==null||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),(t=this._$Do)==null||t.setConnected(!1)}render(){return v}};P._$litElement$=!0,P.finalized=!0,(xt=x.litElementHydrateSupport)==null||xt.call(x,{LitElement:P});const Q=x.litElementPolyfillSupport;Q==null||Q({LitElement:P}),(x.litElementVersions??(x.litElementVersions=[])).push("4.2.1");/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */const Zt={attribute:!0,type:String,converter:j,reflect:!1,hasChanged:Z},Ft=(r=Zt,t,e)=>{const{kind:i,metadata:s}=e;let o=globalThis.litPropertyMetadata.get(s);if(o===void 0&&globalThis.litPropertyMetadata.set(s,o=new Map),i==="setter"&&((r=Object.create(r)).wrapped=!0),o.set(e.name,r),i==="accessor"){const{name:n}=e;return{set(l){const a=t.get.call(this);t.set.call(this,l),this.requestUpdate(n,a,r)},init(l){return l!==void 0&&this.C(n,void 0,r,l),l}}}if(i==="setter"){const{name:n}=e;return function(l){const a=this[n];t.call(this,l),this.requestUpdate(n,a,r)}}throw Error("Unsupported decorator location: "+i)};function G(r){return(t,e)=>typeof e=="object"?Ft(r,t,e):((i,s,o)=>{const n=s.hasOwnProperty(o);return s.constructor.createProperty(o,i),n?Object.getOwnPropertyDescriptor(s,o):void 0})(r,t,e)}/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */function At(r){return G({...r,state:!0,attribute:!1})}/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */const Jt={CHILD:2},Kt=r=>(...t)=>({_$litDirective$:r,values:t});class Qt{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}}/** + * @license + * Copyright 2020 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */const{I:Gt}=Vt,vt=()=>document.createComment(""),N=(r,t,e)=>{var o;const i=r._$AA.parentNode,s=t===void 0?r._$AB:t._$AA;if(e===void 0){const n=i.insertBefore(vt(),s),l=i.insertBefore(vt(),s);e=new Gt(n,l,r,r.options)}else{const n=e._$AB.nextSibling,l=e._$AM,a=l!==r;if(a){let c;(o=e._$AQ)==null||o.call(e,r),e._$AM=r,e._$AP!==void 0&&(c=r._$AU)!==l._$AU&&e._$AP(c)}if(n!==s||a){let c=e._$AA;for(;c!==n;){const p=c.nextSibling;i.insertBefore(c,s),c=p}}}return e},E=(r,t,e=r)=>(r._$AI(t,e),r),Xt={},Yt=(r,t=Xt)=>r._$AH=t,te=r=>r._$AH,X=r=>{r._$AR(),r._$AA.remove()};/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */const wt=(r,t,e)=>{const i=new Map;for(let s=t;s<=e;s++)i.set(r[s],s);return i},ee=Kt(class extends Qt{constructor(r){if(super(r),r.type!==Jt.CHILD)throw Error("repeat() can only be used in text expressions")}dt(r,t,e){let i;e===void 0?e=t:t!==void 0&&(i=t);const s=[],o=[];let n=0;for(const l of r)s[n]=i?i(l,n):n,o[n]=e(l,n),n++;return{values:o,keys:s}}render(r,t,e){return this.dt(r,t,e).values}update(r,[t,e,i]){const s=te(r),{values:o,keys:n}=this.dt(t,e,i);if(!Array.isArray(s))return this.ut=n,o;const l=this.ut??(this.ut=[]),a=[];let c,p,h=0,d=s.length-1,u=0,$=o.length-1;for(;h<=d&&u<=$;)if(s[h]===null)h++;else if(s[d]===null)d--;else if(l[h]===n[u])a[u]=E(s[h],o[u]),h++,u++;else if(l[d]===n[$])a[$]=E(s[d],o[$]),d--,$--;else if(l[h]===n[$])a[$]=E(s[h],o[$]),N(r,a[$+1],s[h]),h++,$--;else if(l[d]===n[u])a[u]=E(s[d],o[u]),N(r,s[h],s[d]),d--,u++;else if(c===void 0&&(c=wt(n,u,$),p=wt(l,h,d)),c.has(l[h]))if(c.has(l[d])){const g=p.get(n[u]),st=g!==void 0?s[g]:null;if(st===null){const Et=N(r,s[h]);E(Et,o[u]),a[u]=Et}else a[u]=E(st,o[u]),N(r,s[h],st),s[g]=null;u++}else X(s[d]),d--;else X(s[h]),h++;for(;u<=$;){const g=N(r,a[$+1]);E(g,o[u]),a[u++]=g}for(;h<=d;){const g=s[h++];g!==null&&X(g)}return this.ut=n,Yt(r,a),v}}),tt=class tt extends P{render(){return I` + + + + + `}};tt.styles=at` + :host { + display: flex; + align-items: center; + justify-content: center; + padding: 1em; + } + svg { + height: 1.25em; + width: 1.25em; + animation: spin 1s linear infinite; + } + + @keyframes spin { + to { + transform: rotate(360deg); + } + } + `;let Y=tt;customElements.get("loading-bar")||customElements.define("loading-bar",Y);var se=Object.defineProperty,L=(r,t,e,i)=>{for(var s=void 0,o=r.length-1,n;o>=0;o--)(n=r[o])&&(s=n(t,e,s)||s);return s&&se(t,e,s),s};const et=class et extends P{constructor(){super(...arguments),this.src="",this.layout="list",this.loading=!1}connectedCallback(){super.connectedCallback(),this.fetchRssDetail()}async fetchRssDetail(){try{this.loading=!0;const t=await fetch(`/apis/api.friend.moony.la/v1alpha1/parsingrss?rssUrl=${this.src}`);if(!t.ok)throw new Error("Failed to fetch site data");this.rssDetail=await t.json()}catch(t){console.error(t)}finally{this.loading=!1}}render(){var t;return this.loading?I``:I` + + `}};et.styles=[ot(St),at` + :host { + display: inline-block; + width: 100%; + } + /* layer: preflights */ +*,::before,::after{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgb(0 0 0 / 0);--un-ring-shadow:0 0 rgb(0 0 0 / 0);--un-shadow-inset: ;--un-shadow:0 0 rgb(0 0 0 / 0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgb(147 197 253 / 0.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: ;}::backdrop{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgb(0 0 0 / 0);--un-ring-shadow:0 0 rgb(0 0 0 / 0);--un-shadow-inset: ;--un-shadow:0 0 rgb(0 0 0 / 0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgb(147 197 253 / 0.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: ;} +/* layer: shortcuts */ +.text-description{color:var(--friends-rss-description-color,#71717a);} +.text-title{color:var(--friends-rss-title-color,#18181b);} +/* layer: default */ +.relative{position:relative;} +.static{position:static;} +.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;} +.z-\\[1\\]{z-index:1;} +.grid{display:grid;} +.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr));} +.inline-block{display:inline-block;} +.space-y-1>:not([hidden])~:not([hidden]){--un-space-y-reverse:0;margin-top:calc(0.25rem * calc(1 - var(--un-space-y-reverse)));margin-bottom:calc(0.25rem * var(--un-space-y-reverse));} +.p-2{padding:0.5rem;} +.text-base{font-size:1rem;line-height:1.5rem;} +.text-sm{font-size:0.875rem;line-height:1.25rem;} +.font-semibold{font-weight:600;} +@media (min-width: 640px){ +.sm\\:flex-row{flex-direction:row;} +} +@media (min-width: 1024px){ +.lg\\:line-clamp-1{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;line-clamp:1;} +}; + `];let y=et;L([G({type:String})],y.prototype,"src"),L([G({type:String})],y.prototype,"layout"),L([At()],y.prototype,"rssDetail"),L([At()],y.prototype,"loading"),customElements.get("friends-rss")||customElements.define("friends-rss",y),it.FriendsRss=y,Object.defineProperty(it,Symbol.toStringTag,{value:"Module"})})(this["friends-rss"]=this["friends-rss"]||{}); diff --git a/src/main/resources/static/var.css b/src/main/resources/static/var.css new file mode 100644 index 0000000..e09bac7 --- /dev/null +++ b/src/main/resources/static/var.css @@ -0,0 +1,16 @@ +@media (prefers-color-scheme: dark) { + .color-scheme-auto, + [data-color-scheme='auto'] friends-rss { + color-scheme: dark; + --friends-rss-title-color: #f4f4f5; + --friends-rss-description-color: #a1a1aa; + } +} + +.color-scheme-dark, +.dark, +[data-color-scheme='dark'] friends-rss { + color-scheme: dark; + --friends-rss-title-color: #f4f4f5; + --friends-rss-description-color: #a1a1aa; +} diff --git a/src/main/resources/templates/friends.html b/src/main/resources/templates/friends.html new file mode 100644 index 0000000..cdd805c --- /dev/null +++ b/src/main/resources/templates/friends.html @@ -0,0 +1,1559 @@ + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+

+

这里只是我而已。

+ +
+
+
+
+
+

+
+

+
+
+ + + + + + + +
+
+
+ +
+ + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/ui/.eslintrc.cjs b/ui/.eslintrc.cjs new file mode 100644 index 0000000..16a4d81 --- /dev/null +++ b/ui/.eslintrc.cjs @@ -0,0 +1,22 @@ +/* eslint-env node */ +require("@rushstack/eslint-patch/modern-module-resolution"); + +module.exports = { + root: true, + extends: [ + "plugin:vue/vue3-recommended", + "eslint:recommended", + "@vue/eslint-config-typescript/recommended", + "@vue/eslint-config-prettier", + '@unocss', + ], + parserOptions: { + ecmaVersion: "latest", + }, + env: { + "vue/setup-compiler-macros": true, + }, + rules: { + "@unocss/enforce-class-compile": 1, + }, +}; diff --git a/ui/env.d.ts b/ui/env.d.ts new file mode 100644 index 0000000..3301ad5 --- /dev/null +++ b/ui/env.d.ts @@ -0,0 +1,16 @@ +/// +/// + +export {}; + +declare module "axios" { + export interface AxiosRequestConfig { + mute?: boolean; + } +} + +declare module "vue" { + interface ComponentCustomProperties { + $formkit: any; + } +} diff --git a/ui/package.json b/ui/package.json new file mode 100644 index 0000000..67f60cb --- /dev/null +++ b/ui/package.json @@ -0,0 +1,71 @@ +{ + "type": "module", + "scripts": { + "build": "rsbuild build", + "dev": "rsbuild build --env-mode development --watch", + "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path ../.gitignore", + "prettier": "prettier --write src/", + "type-check": "vue-tsc --build --force" + }, + "prettier": { + "arrowParens": "always", + "bracketSpacing": true, + "endOfLine": "lf", + "plugins": [ + "prettier-plugin-organize-imports" + ], + "printWidth": 100, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "useTabs": false + }, + "dependencies": { + "@halo-dev/api-client": "^2.21.1", + "@halo-dev/components": "2.21.0", + "@halo-dev/console-shared": "^2.21.0", + "@halo-dev/richtext-editor": "^2.21.0", + "axios": "^1.7.2", + "canvas-confetti": "^1.9.3", + "vue": "^3.4.31", + "@types/lodash.clonedeep": "^4.5.7", + "lodash.clonedeep": "^4.5.0", + "@vueuse/router": "^10.6.0", + "@tanstack/vue-query": "^4.26.1", + "dayjs": "^1.11.9", + "javascript-time-ago": "^2.5.9", + "@vueuse/shared": "^10.9.0", + "@formkit/core": "^1.4.0", + "@kunkunyu/fridends-rss": "workspace:*" + }, + "devDependencies": { + "@halo-dev/ui-plugin-bundler-kit": "^2.21.2", + "@iconify/json": "^2.2.224", + "@rspack/cli": "1.4.0-beta.0", + "@rspack/core": "1.4.0-beta.0", + "@rushstack/eslint-patch": "^1.10.3", + "@tsconfig/node20": "^20.1.4", + "@types/canvas-confetti": "^1.6.4", + "@types/jsdom": "^21.1.7", + "@types/node": "^20.14.9", + "@unocss/eslint-config": "^66.2.0", + "@unocss/webpack": "^66.2.0", + "@vue/eslint-config-prettier": "^9.0.0", + "@vue/eslint-config-typescript": "^13.0.0", + "@vue/test-utils": "^2.4.6", + "@vue/tsconfig": "^0.5.1", + "css-loader": "^7.1.2", + "eslint": "^8.57.0", + "eslint-plugin-vue": "^9.26.0", + "jsdom": "^24.1.0", + "npm-run-all2": "^6.2.0", + "prettier": "^3.3.2", + "prettier-plugin-organize-imports": "^4.2.0", + "style-loader": "^4.0.0", + "typescript": "~5.5.3", + "unocss": "^66.2.0", + "unplugin-icons": "^22.1.0", + "vue-loader": "^17.4.2", + "vue-tsc": "^2.0.24" + } +} diff --git a/ui/pnpm-workspace.yaml b/ui/pnpm-workspace.yaml new file mode 100644 index 0000000..045dc7f --- /dev/null +++ b/ui/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +onlyBuiltDependencies: + - core-js + - esbuild + - vue-demi diff --git a/ui/rsbuild.config.mjs b/ui/rsbuild.config.mjs new file mode 100644 index 0000000..73c9a58 --- /dev/null +++ b/ui/rsbuild.config.mjs @@ -0,0 +1,36 @@ +import { rsbuildConfig } from '@halo-dev/ui-plugin-bundler-kit'; +import { UnoCSSRspackPlugin } from '@unocss/webpack/rspack'; +import Icons from 'unplugin-icons/rspack'; + +const OUT_DIR_PROD = '../src/main/resources/console'; +const OUT_DIR_DEV = '../build/resources/main/console'; + +export default rsbuildConfig({ + rsbuild: ({ envMode }) => { + const isProduction = envMode === 'production'; + const outDir = isProduction ? OUT_DIR_PROD : OUT_DIR_DEV; + + return { + resolve: { + alias: { + '@': './src', + }, + }, + output: { + distPath: { + root: outDir, + }, + }, + tools: { + rspack: { + plugins: [ + Icons({ + compiler: 'vue3', + }), + UnoCSSRspackPlugin(), + ], + }, + }, + }; + }, +}); diff --git a/ui/src/api/generated/.gitignore b/ui/src/api/generated/.gitignore new file mode 100644 index 0000000..149b576 --- /dev/null +++ b/ui/src/api/generated/.gitignore @@ -0,0 +1,4 @@ +wwwroot/*.js +node_modules +typings +dist diff --git a/ui/src/api/generated/.npmignore b/ui/src/api/generated/.npmignore new file mode 100644 index 0000000..999d88d --- /dev/null +++ b/ui/src/api/generated/.npmignore @@ -0,0 +1 @@ +# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm \ No newline at end of file diff --git a/ui/src/api/generated/.openapi-generator-ignore b/ui/src/api/generated/.openapi-generator-ignore new file mode 100644 index 0000000..7484ee5 --- /dev/null +++ b/ui/src/api/generated/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/ui/src/api/generated/.openapi-generator/FILES b/ui/src/api/generated/.openapi-generator/FILES new file mode 100644 index 0000000..6ee88a2 --- /dev/null +++ b/ui/src/api/generated/.openapi-generator/FILES @@ -0,0 +1,38 @@ +.gitignore +.npmignore +.openapi-generator-ignore +api.ts +api/api-friend-moony-la-v1alpha1-friend-post-api.ts +api/cron-friend-post-v1alpha1-api.ts +api/friend-post-v1alpha1-api.ts +api/rss-feed-sync-log-v1alpha1-api.ts +base.ts +common.ts +configuration.ts +git_push.sh +index.ts +models/add-operation.ts +models/channel.ts +models/copy-operation.ts +models/cron-friend-post-list.ts +models/cron-friend-post.ts +models/cron-spec.ts +models/cron-status.ts +models/friend-post-list.ts +models/friend-post-spec.ts +models/friend-post.ts +models/index.ts +models/item.ts +models/json-patch-inner.ts +models/link-spec.ts +models/link.ts +models/listed-rss-sync-log-list.ts +models/listed-rss-sync-log.ts +models/metadata.ts +models/move-operation.ts +models/remove-operation.ts +models/replace-operation.ts +models/rss-detail.ts +models/rss-feed-sync-log-list.ts +models/rss-feed-sync-log.ts +models/test-operation.ts diff --git a/ui/src/api/generated/.openapi-generator/VERSION b/ui/src/api/generated/.openapi-generator/VERSION new file mode 100644 index 0000000..1985849 --- /dev/null +++ b/ui/src/api/generated/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.7.0 diff --git a/ui/src/api/generated/api.ts b/ui/src/api/generated/api.ts new file mode 100644 index 0000000..4c5a0b6 --- /dev/null +++ b/ui/src/api/generated/api.ts @@ -0,0 +1,21 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +export * from './api/api-friend-moony-la-v1alpha1-friend-post-api'; +export * from './api/cron-friend-post-v1alpha1-api'; +export * from './api/friend-post-v1alpha1-api'; +export * from './api/rss-feed-sync-log-v1alpha1-api'; + diff --git a/ui/src/api/generated/api/api-friend-moony-la-v1alpha1-friend-post-api.ts b/ui/src/api/generated/api/api-friend-moony-la-v1alpha1-friend-post-api.ts new file mode 100644 index 0000000..f6c4667 --- /dev/null +++ b/ui/src/api/generated/api/api-friend-moony-la-v1alpha1-friend-post-api.ts @@ -0,0 +1,654 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from '../configuration'; +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; +// Some imports not used depending on template conditions +// @ts-ignore +import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common'; +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base'; +// @ts-ignore +import type { FriendPostList } from '../models'; +// @ts-ignore +import type { ListedRssSyncLogList } from '../models'; +// @ts-ignore +import type { RssDetail } from '../models'; +/** + * ApiFriendMoonyLaV1alpha1FriendPostApi - axios parameter creator + * @export + */ +export const ApiFriendMoonyLaV1alpha1FriendPostApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * Delete All RssFeedSyncLog. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteAllRssFeedSyncLog: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/apis/api.friend.moony.la/v1alpha1/friendposts/-/delete`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * List friendPost. + * @param {number} [page] Page number. Default is 0. + * @param {number} [size] Size number. Default is 0. + * @param {Array} [labelSelector] Label selector. e.g.: hidden!=true + * @param {Array} [fieldSelector] Field selector. e.g.: metadata.name==halo + * @param {Array} [sort] Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. + * @param {string} [linkName] CronFriendPost filtered by linkName. + * @param {string} [keyword] CronFriendPost filtered by keyword. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listFriendPosts: async (page?: number, size?: number, labelSelector?: Array, fieldSelector?: Array, sort?: Array, linkName?: string, keyword?: string, options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/apis/api.friend.moony.la/v1alpha1/friendposts`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + if (page !== undefined) { + localVarQueryParameter['page'] = page; + } + + if (size !== undefined) { + localVarQueryParameter['size'] = size; + } + + if (labelSelector) { + localVarQueryParameter['labelSelector'] = labelSelector; + } + + if (fieldSelector) { + localVarQueryParameter['fieldSelector'] = fieldSelector; + } + + if (sort) { + localVarQueryParameter['sort'] = sort; + } + + if (linkName !== undefined) { + localVarQueryParameter['linkName'] = linkName; + } + + if (keyword !== undefined) { + localVarQueryParameter['keyword'] = keyword; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * List RssSyncLog. + * @param {number} [page] Page number. Default is 0. + * @param {number} [size] Size number. Default is 0. + * @param {Array} [labelSelector] Label selector. e.g.: hidden!=true + * @param {Array} [fieldSelector] Field selector. e.g.: metadata.name==halo + * @param {Array} [sort] Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. + * @param {string} [keyword] + * @param {string} [state] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listRssSyncLogs: async (page?: number, size?: number, labelSelector?: Array, fieldSelector?: Array, sort?: Array, keyword?: string, state?: string, options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/apis/api.friend.moony.la/v1alpha1/rsssynclogs`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + if (page !== undefined) { + localVarQueryParameter['page'] = page; + } + + if (size !== undefined) { + localVarQueryParameter['size'] = size; + } + + if (labelSelector) { + localVarQueryParameter['labelSelector'] = labelSelector; + } + + if (fieldSelector) { + localVarQueryParameter['fieldSelector'] = fieldSelector; + } + + if (sort) { + localVarQueryParameter['sort'] = sort; + } + + if (keyword !== undefined) { + localVarQueryParameter['keyword'] = keyword; + } + + if (state !== undefined) { + localVarQueryParameter['state'] = state; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @param {string} rssUrl + * @param {string} [fetchLimitNumber] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + parsingRss: async (rssUrl: string, fetchLimitNumber?: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'rssUrl' is not null or undefined + assertParamExists('parsingRss', 'rssUrl', rssUrl) + const localVarPath = `/apis/api.friend.moony.la/v1alpha1/parsingrss`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + if (rssUrl !== undefined) { + localVarQueryParameter['rssUrl'] = rssUrl; + } + + if (fetchLimitNumber !== undefined) { + localVarQueryParameter['fetchLimitNumber'] = fetchLimitNumber; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @param {string} name + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + syncRssFeed: async (name: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists('syncRssFeed', 'name', name) + const localVarPath = `/apis/api.friend.moony.la/v1alpha1/syncrssfeed/{name}` + .replace(`{${"name"}}`, encodeURIComponent(String(name))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * ApiFriendMoonyLaV1alpha1FriendPostApi - functional programming interface + * @export + */ +export const ApiFriendMoonyLaV1alpha1FriendPostApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = ApiFriendMoonyLaV1alpha1FriendPostApiAxiosParamCreator(configuration) + return { + /** + * Delete All RssFeedSyncLog. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deleteAllRssFeedSyncLog(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.deleteAllRssFeedSyncLog(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['ApiFriendMoonyLaV1alpha1FriendPostApi.deleteAllRssFeedSyncLog']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * List friendPost. + * @param {number} [page] Page number. Default is 0. + * @param {number} [size] Size number. Default is 0. + * @param {Array} [labelSelector] Label selector. e.g.: hidden!=true + * @param {Array} [fieldSelector] Field selector. e.g.: metadata.name==halo + * @param {Array} [sort] Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. + * @param {string} [linkName] CronFriendPost filtered by linkName. + * @param {string} [keyword] CronFriendPost filtered by keyword. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async listFriendPosts(page?: number, size?: number, labelSelector?: Array, fieldSelector?: Array, sort?: Array, linkName?: string, keyword?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.listFriendPosts(page, size, labelSelector, fieldSelector, sort, linkName, keyword, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['ApiFriendMoonyLaV1alpha1FriendPostApi.listFriendPosts']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * List RssSyncLog. + * @param {number} [page] Page number. Default is 0. + * @param {number} [size] Size number. Default is 0. + * @param {Array} [labelSelector] Label selector. e.g.: hidden!=true + * @param {Array} [fieldSelector] Field selector. e.g.: metadata.name==halo + * @param {Array} [sort] Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. + * @param {string} [keyword] + * @param {string} [state] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async listRssSyncLogs(page?: number, size?: number, labelSelector?: Array, fieldSelector?: Array, sort?: Array, keyword?: string, state?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.listRssSyncLogs(page, size, labelSelector, fieldSelector, sort, keyword, state, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['ApiFriendMoonyLaV1alpha1FriendPostApi.listRssSyncLogs']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @param {string} rssUrl + * @param {string} [fetchLimitNumber] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async parsingRss(rssUrl: string, fetchLimitNumber?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.parsingRss(rssUrl, fetchLimitNumber, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['ApiFriendMoonyLaV1alpha1FriendPostApi.parsingRss']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @param {string} name + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async syncRssFeed(name: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.syncRssFeed(name, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['ApiFriendMoonyLaV1alpha1FriendPostApi.syncRssFeed']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + } +}; + +/** + * ApiFriendMoonyLaV1alpha1FriendPostApi - factory interface + * @export + */ +export const ApiFriendMoonyLaV1alpha1FriendPostApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = ApiFriendMoonyLaV1alpha1FriendPostApiFp(configuration) + return { + /** + * Delete All RssFeedSyncLog. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteAllRssFeedSyncLog(options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.deleteAllRssFeedSyncLog(options).then((request) => request(axios, basePath)); + }, + /** + * List friendPost. + * @param {ApiFriendMoonyLaV1alpha1FriendPostApiListFriendPostsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listFriendPosts(requestParameters: ApiFriendMoonyLaV1alpha1FriendPostApiListFriendPostsRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.listFriendPosts(requestParameters.page, requestParameters.size, requestParameters.labelSelector, requestParameters.fieldSelector, requestParameters.sort, requestParameters.linkName, requestParameters.keyword, options).then((request) => request(axios, basePath)); + }, + /** + * List RssSyncLog. + * @param {ApiFriendMoonyLaV1alpha1FriendPostApiListRssSyncLogsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listRssSyncLogs(requestParameters: ApiFriendMoonyLaV1alpha1FriendPostApiListRssSyncLogsRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.listRssSyncLogs(requestParameters.page, requestParameters.size, requestParameters.labelSelector, requestParameters.fieldSelector, requestParameters.sort, requestParameters.keyword, requestParameters.state, options).then((request) => request(axios, basePath)); + }, + /** + * + * @param {ApiFriendMoonyLaV1alpha1FriendPostApiParsingRssRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + parsingRss(requestParameters: ApiFriendMoonyLaV1alpha1FriendPostApiParsingRssRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.parsingRss(requestParameters.rssUrl, requestParameters.fetchLimitNumber, options).then((request) => request(axios, basePath)); + }, + /** + * + * @param {ApiFriendMoonyLaV1alpha1FriendPostApiSyncRssFeedRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + syncRssFeed(requestParameters: ApiFriendMoonyLaV1alpha1FriendPostApiSyncRssFeedRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.syncRssFeed(requestParameters.name, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * Request parameters for listFriendPosts operation in ApiFriendMoonyLaV1alpha1FriendPostApi. + * @export + * @interface ApiFriendMoonyLaV1alpha1FriendPostApiListFriendPostsRequest + */ +export interface ApiFriendMoonyLaV1alpha1FriendPostApiListFriendPostsRequest { + /** + * Page number. Default is 0. + * @type {number} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiListFriendPosts + */ + readonly page?: number + + /** + * Size number. Default is 0. + * @type {number} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiListFriendPosts + */ + readonly size?: number + + /** + * Label selector. e.g.: hidden!=true + * @type {Array} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiListFriendPosts + */ + readonly labelSelector?: Array + + /** + * Field selector. e.g.: metadata.name==halo + * @type {Array} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiListFriendPosts + */ + readonly fieldSelector?: Array + + /** + * Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. + * @type {Array} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiListFriendPosts + */ + readonly sort?: Array + + /** + * CronFriendPost filtered by linkName. + * @type {string} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiListFriendPosts + */ + readonly linkName?: string + + /** + * CronFriendPost filtered by keyword. + * @type {string} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiListFriendPosts + */ + readonly keyword?: string +} + +/** + * Request parameters for listRssSyncLogs operation in ApiFriendMoonyLaV1alpha1FriendPostApi. + * @export + * @interface ApiFriendMoonyLaV1alpha1FriendPostApiListRssSyncLogsRequest + */ +export interface ApiFriendMoonyLaV1alpha1FriendPostApiListRssSyncLogsRequest { + /** + * Page number. Default is 0. + * @type {number} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiListRssSyncLogs + */ + readonly page?: number + + /** + * Size number. Default is 0. + * @type {number} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiListRssSyncLogs + */ + readonly size?: number + + /** + * Label selector. e.g.: hidden!=true + * @type {Array} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiListRssSyncLogs + */ + readonly labelSelector?: Array + + /** + * Field selector. e.g.: metadata.name==halo + * @type {Array} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiListRssSyncLogs + */ + readonly fieldSelector?: Array + + /** + * Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. + * @type {Array} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiListRssSyncLogs + */ + readonly sort?: Array + + /** + * + * @type {string} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiListRssSyncLogs + */ + readonly keyword?: string + + /** + * + * @type {string} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiListRssSyncLogs + */ + readonly state?: string +} + +/** + * Request parameters for parsingRss operation in ApiFriendMoonyLaV1alpha1FriendPostApi. + * @export + * @interface ApiFriendMoonyLaV1alpha1FriendPostApiParsingRssRequest + */ +export interface ApiFriendMoonyLaV1alpha1FriendPostApiParsingRssRequest { + /** + * + * @type {string} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiParsingRss + */ + readonly rssUrl: string + + /** + * + * @type {string} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiParsingRss + */ + readonly fetchLimitNumber?: string +} + +/** + * Request parameters for syncRssFeed operation in ApiFriendMoonyLaV1alpha1FriendPostApi. + * @export + * @interface ApiFriendMoonyLaV1alpha1FriendPostApiSyncRssFeedRequest + */ +export interface ApiFriendMoonyLaV1alpha1FriendPostApiSyncRssFeedRequest { + /** + * + * @type {string} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApiSyncRssFeed + */ + readonly name: string +} + +/** + * ApiFriendMoonyLaV1alpha1FriendPostApi - object-oriented interface + * @export + * @class ApiFriendMoonyLaV1alpha1FriendPostApi + * @extends {BaseAPI} + */ +export class ApiFriendMoonyLaV1alpha1FriendPostApi extends BaseAPI { + /** + * Delete All RssFeedSyncLog. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApi + */ + public deleteAllRssFeedSyncLog(options?: RawAxiosRequestConfig) { + return ApiFriendMoonyLaV1alpha1FriendPostApiFp(this.configuration).deleteAllRssFeedSyncLog(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * List friendPost. + * @param {ApiFriendMoonyLaV1alpha1FriendPostApiListFriendPostsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApi + */ + public listFriendPosts(requestParameters: ApiFriendMoonyLaV1alpha1FriendPostApiListFriendPostsRequest = {}, options?: RawAxiosRequestConfig) { + return ApiFriendMoonyLaV1alpha1FriendPostApiFp(this.configuration).listFriendPosts(requestParameters.page, requestParameters.size, requestParameters.labelSelector, requestParameters.fieldSelector, requestParameters.sort, requestParameters.linkName, requestParameters.keyword, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * List RssSyncLog. + * @param {ApiFriendMoonyLaV1alpha1FriendPostApiListRssSyncLogsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApi + */ + public listRssSyncLogs(requestParameters: ApiFriendMoonyLaV1alpha1FriendPostApiListRssSyncLogsRequest = {}, options?: RawAxiosRequestConfig) { + return ApiFriendMoonyLaV1alpha1FriendPostApiFp(this.configuration).listRssSyncLogs(requestParameters.page, requestParameters.size, requestParameters.labelSelector, requestParameters.fieldSelector, requestParameters.sort, requestParameters.keyword, requestParameters.state, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @param {ApiFriendMoonyLaV1alpha1FriendPostApiParsingRssRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApi + */ + public parsingRss(requestParameters: ApiFriendMoonyLaV1alpha1FriendPostApiParsingRssRequest, options?: RawAxiosRequestConfig) { + return ApiFriendMoonyLaV1alpha1FriendPostApiFp(this.configuration).parsingRss(requestParameters.rssUrl, requestParameters.fetchLimitNumber, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @param {ApiFriendMoonyLaV1alpha1FriendPostApiSyncRssFeedRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ApiFriendMoonyLaV1alpha1FriendPostApi + */ + public syncRssFeed(requestParameters: ApiFriendMoonyLaV1alpha1FriendPostApiSyncRssFeedRequest, options?: RawAxiosRequestConfig) { + return ApiFriendMoonyLaV1alpha1FriendPostApiFp(this.configuration).syncRssFeed(requestParameters.name, options).then((request) => request(this.axios, this.basePath)); + } +} + diff --git a/ui/src/api/generated/api/cron-friend-post-v1alpha1-api.ts b/ui/src/api/generated/api/cron-friend-post-v1alpha1-api.ts new file mode 100644 index 0000000..c8bfd6a --- /dev/null +++ b/ui/src/api/generated/api/cron-friend-post-v1alpha1-api.ts @@ -0,0 +1,665 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from '../configuration'; +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; +// Some imports not used depending on template conditions +// @ts-ignore +import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common'; +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base'; +// @ts-ignore +import type { CronFriendPost } from '../models'; +// @ts-ignore +import type { CronFriendPostList } from '../models'; +// @ts-ignore +import type { JsonPatchInner } from '../models'; +/** + * CronFriendPostV1alpha1Api - axios parameter creator + * @export + */ +export const CronFriendPostV1alpha1ApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * Create CronFriendPost + * @param {CronFriendPost} [cronFriendPost] Fresh cronfriendpost + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createCronFriendPost: async (cronFriendPost?: CronFriendPost, options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/apis/friend.moony.la/v1alpha1/cronfriendposts`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(cronFriendPost, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Delete CronFriendPost + * @param {string} name Name of cronfriendpost + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteCronFriendPost: async (name: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists('deleteCronFriendPost', 'name', name) + const localVarPath = `/apis/friend.moony.la/v1alpha1/cronfriendposts/{name}` + .replace(`{${"name"}}`, encodeURIComponent(String(name))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Get CronFriendPost + * @param {string} name Name of cronfriendpost + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getCronFriendPost: async (name: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists('getCronFriendPost', 'name', name) + const localVarPath = `/apis/friend.moony.la/v1alpha1/cronfriendposts/{name}` + .replace(`{${"name"}}`, encodeURIComponent(String(name))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * List CronFriendPost + * @param {number} [page] Page number. Default is 0. + * @param {number} [size] Size number. Default is 0. + * @param {Array} [labelSelector] Label selector. e.g.: hidden!=true + * @param {Array} [fieldSelector] Field selector. e.g.: metadata.name==halo + * @param {Array} [sort] Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listCronFriendPost: async (page?: number, size?: number, labelSelector?: Array, fieldSelector?: Array, sort?: Array, options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/apis/friend.moony.la/v1alpha1/cronfriendposts`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + if (page !== undefined) { + localVarQueryParameter['page'] = page; + } + + if (size !== undefined) { + localVarQueryParameter['size'] = size; + } + + if (labelSelector) { + localVarQueryParameter['labelSelector'] = labelSelector; + } + + if (fieldSelector) { + localVarQueryParameter['fieldSelector'] = fieldSelector; + } + + if (sort) { + localVarQueryParameter['sort'] = sort; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Patch CronFriendPost + * @param {string} name Name of cronfriendpost + * @param {Array} [jsonPatchInner] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + patchCronFriendPost: async (name: string, jsonPatchInner?: Array, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists('patchCronFriendPost', 'name', name) + const localVarPath = `/apis/friend.moony.la/v1alpha1/cronfriendposts/{name}` + .replace(`{${"name"}}`, encodeURIComponent(String(name))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json-patch+json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(jsonPatchInner, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Update CronFriendPost + * @param {string} name Name of cronfriendpost + * @param {CronFriendPost} [cronFriendPost] Updated cronfriendpost + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updateCronFriendPost: async (name: string, cronFriendPost?: CronFriendPost, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists('updateCronFriendPost', 'name', name) + const localVarPath = `/apis/friend.moony.la/v1alpha1/cronfriendposts/{name}` + .replace(`{${"name"}}`, encodeURIComponent(String(name))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(cronFriendPost, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * CronFriendPostV1alpha1Api - functional programming interface + * @export + */ +export const CronFriendPostV1alpha1ApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = CronFriendPostV1alpha1ApiAxiosParamCreator(configuration) + return { + /** + * Create CronFriendPost + * @param {CronFriendPost} [cronFriendPost] Fresh cronfriendpost + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async createCronFriendPost(cronFriendPost?: CronFriendPost, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.createCronFriendPost(cronFriendPost, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['CronFriendPostV1alpha1Api.createCronFriendPost']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Delete CronFriendPost + * @param {string} name Name of cronfriendpost + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deleteCronFriendPost(name: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.deleteCronFriendPost(name, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['CronFriendPostV1alpha1Api.deleteCronFriendPost']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Get CronFriendPost + * @param {string} name Name of cronfriendpost + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getCronFriendPost(name: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getCronFriendPost(name, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['CronFriendPostV1alpha1Api.getCronFriendPost']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * List CronFriendPost + * @param {number} [page] Page number. Default is 0. + * @param {number} [size] Size number. Default is 0. + * @param {Array} [labelSelector] Label selector. e.g.: hidden!=true + * @param {Array} [fieldSelector] Field selector. e.g.: metadata.name==halo + * @param {Array} [sort] Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async listCronFriendPost(page?: number, size?: number, labelSelector?: Array, fieldSelector?: Array, sort?: Array, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.listCronFriendPost(page, size, labelSelector, fieldSelector, sort, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['CronFriendPostV1alpha1Api.listCronFriendPost']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Patch CronFriendPost + * @param {string} name Name of cronfriendpost + * @param {Array} [jsonPatchInner] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async patchCronFriendPost(name: string, jsonPatchInner?: Array, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.patchCronFriendPost(name, jsonPatchInner, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['CronFriendPostV1alpha1Api.patchCronFriendPost']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Update CronFriendPost + * @param {string} name Name of cronfriendpost + * @param {CronFriendPost} [cronFriendPost] Updated cronfriendpost + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async updateCronFriendPost(name: string, cronFriendPost?: CronFriendPost, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.updateCronFriendPost(name, cronFriendPost, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['CronFriendPostV1alpha1Api.updateCronFriendPost']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + } +}; + +/** + * CronFriendPostV1alpha1Api - factory interface + * @export + */ +export const CronFriendPostV1alpha1ApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = CronFriendPostV1alpha1ApiFp(configuration) + return { + /** + * Create CronFriendPost + * @param {CronFriendPostV1alpha1ApiCreateCronFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createCronFriendPost(requestParameters: CronFriendPostV1alpha1ApiCreateCronFriendPostRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.createCronFriendPost(requestParameters.cronFriendPost, options).then((request) => request(axios, basePath)); + }, + /** + * Delete CronFriendPost + * @param {CronFriendPostV1alpha1ApiDeleteCronFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteCronFriendPost(requestParameters: CronFriendPostV1alpha1ApiDeleteCronFriendPostRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.deleteCronFriendPost(requestParameters.name, options).then((request) => request(axios, basePath)); + }, + /** + * Get CronFriendPost + * @param {CronFriendPostV1alpha1ApiGetCronFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getCronFriendPost(requestParameters: CronFriendPostV1alpha1ApiGetCronFriendPostRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.getCronFriendPost(requestParameters.name, options).then((request) => request(axios, basePath)); + }, + /** + * List CronFriendPost + * @param {CronFriendPostV1alpha1ApiListCronFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listCronFriendPost(requestParameters: CronFriendPostV1alpha1ApiListCronFriendPostRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.listCronFriendPost(requestParameters.page, requestParameters.size, requestParameters.labelSelector, requestParameters.fieldSelector, requestParameters.sort, options).then((request) => request(axios, basePath)); + }, + /** + * Patch CronFriendPost + * @param {CronFriendPostV1alpha1ApiPatchCronFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + patchCronFriendPost(requestParameters: CronFriendPostV1alpha1ApiPatchCronFriendPostRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.patchCronFriendPost(requestParameters.name, requestParameters.jsonPatchInner, options).then((request) => request(axios, basePath)); + }, + /** + * Update CronFriendPost + * @param {CronFriendPostV1alpha1ApiUpdateCronFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updateCronFriendPost(requestParameters: CronFriendPostV1alpha1ApiUpdateCronFriendPostRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.updateCronFriendPost(requestParameters.name, requestParameters.cronFriendPost, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * Request parameters for createCronFriendPost operation in CronFriendPostV1alpha1Api. + * @export + * @interface CronFriendPostV1alpha1ApiCreateCronFriendPostRequest + */ +export interface CronFriendPostV1alpha1ApiCreateCronFriendPostRequest { + /** + * Fresh cronfriendpost + * @type {CronFriendPost} + * @memberof CronFriendPostV1alpha1ApiCreateCronFriendPost + */ + readonly cronFriendPost?: CronFriendPost +} + +/** + * Request parameters for deleteCronFriendPost operation in CronFriendPostV1alpha1Api. + * @export + * @interface CronFriendPostV1alpha1ApiDeleteCronFriendPostRequest + */ +export interface CronFriendPostV1alpha1ApiDeleteCronFriendPostRequest { + /** + * Name of cronfriendpost + * @type {string} + * @memberof CronFriendPostV1alpha1ApiDeleteCronFriendPost + */ + readonly name: string +} + +/** + * Request parameters for getCronFriendPost operation in CronFriendPostV1alpha1Api. + * @export + * @interface CronFriendPostV1alpha1ApiGetCronFriendPostRequest + */ +export interface CronFriendPostV1alpha1ApiGetCronFriendPostRequest { + /** + * Name of cronfriendpost + * @type {string} + * @memberof CronFriendPostV1alpha1ApiGetCronFriendPost + */ + readonly name: string +} + +/** + * Request parameters for listCronFriendPost operation in CronFriendPostV1alpha1Api. + * @export + * @interface CronFriendPostV1alpha1ApiListCronFriendPostRequest + */ +export interface CronFriendPostV1alpha1ApiListCronFriendPostRequest { + /** + * Page number. Default is 0. + * @type {number} + * @memberof CronFriendPostV1alpha1ApiListCronFriendPost + */ + readonly page?: number + + /** + * Size number. Default is 0. + * @type {number} + * @memberof CronFriendPostV1alpha1ApiListCronFriendPost + */ + readonly size?: number + + /** + * Label selector. e.g.: hidden!=true + * @type {Array} + * @memberof CronFriendPostV1alpha1ApiListCronFriendPost + */ + readonly labelSelector?: Array + + /** + * Field selector. e.g.: metadata.name==halo + * @type {Array} + * @memberof CronFriendPostV1alpha1ApiListCronFriendPost + */ + readonly fieldSelector?: Array + + /** + * Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. + * @type {Array} + * @memberof CronFriendPostV1alpha1ApiListCronFriendPost + */ + readonly sort?: Array +} + +/** + * Request parameters for patchCronFriendPost operation in CronFriendPostV1alpha1Api. + * @export + * @interface CronFriendPostV1alpha1ApiPatchCronFriendPostRequest + */ +export interface CronFriendPostV1alpha1ApiPatchCronFriendPostRequest { + /** + * Name of cronfriendpost + * @type {string} + * @memberof CronFriendPostV1alpha1ApiPatchCronFriendPost + */ + readonly name: string + + /** + * + * @type {Array} + * @memberof CronFriendPostV1alpha1ApiPatchCronFriendPost + */ + readonly jsonPatchInner?: Array +} + +/** + * Request parameters for updateCronFriendPost operation in CronFriendPostV1alpha1Api. + * @export + * @interface CronFriendPostV1alpha1ApiUpdateCronFriendPostRequest + */ +export interface CronFriendPostV1alpha1ApiUpdateCronFriendPostRequest { + /** + * Name of cronfriendpost + * @type {string} + * @memberof CronFriendPostV1alpha1ApiUpdateCronFriendPost + */ + readonly name: string + + /** + * Updated cronfriendpost + * @type {CronFriendPost} + * @memberof CronFriendPostV1alpha1ApiUpdateCronFriendPost + */ + readonly cronFriendPost?: CronFriendPost +} + +/** + * CronFriendPostV1alpha1Api - object-oriented interface + * @export + * @class CronFriendPostV1alpha1Api + * @extends {BaseAPI} + */ +export class CronFriendPostV1alpha1Api extends BaseAPI { + /** + * Create CronFriendPost + * @param {CronFriendPostV1alpha1ApiCreateCronFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof CronFriendPostV1alpha1Api + */ + public createCronFriendPost(requestParameters: CronFriendPostV1alpha1ApiCreateCronFriendPostRequest = {}, options?: RawAxiosRequestConfig) { + return CronFriendPostV1alpha1ApiFp(this.configuration).createCronFriendPost(requestParameters.cronFriendPost, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Delete CronFriendPost + * @param {CronFriendPostV1alpha1ApiDeleteCronFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof CronFriendPostV1alpha1Api + */ + public deleteCronFriendPost(requestParameters: CronFriendPostV1alpha1ApiDeleteCronFriendPostRequest, options?: RawAxiosRequestConfig) { + return CronFriendPostV1alpha1ApiFp(this.configuration).deleteCronFriendPost(requestParameters.name, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Get CronFriendPost + * @param {CronFriendPostV1alpha1ApiGetCronFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof CronFriendPostV1alpha1Api + */ + public getCronFriendPost(requestParameters: CronFriendPostV1alpha1ApiGetCronFriendPostRequest, options?: RawAxiosRequestConfig) { + return CronFriendPostV1alpha1ApiFp(this.configuration).getCronFriendPost(requestParameters.name, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * List CronFriendPost + * @param {CronFriendPostV1alpha1ApiListCronFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof CronFriendPostV1alpha1Api + */ + public listCronFriendPost(requestParameters: CronFriendPostV1alpha1ApiListCronFriendPostRequest = {}, options?: RawAxiosRequestConfig) { + return CronFriendPostV1alpha1ApiFp(this.configuration).listCronFriendPost(requestParameters.page, requestParameters.size, requestParameters.labelSelector, requestParameters.fieldSelector, requestParameters.sort, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Patch CronFriendPost + * @param {CronFriendPostV1alpha1ApiPatchCronFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof CronFriendPostV1alpha1Api + */ + public patchCronFriendPost(requestParameters: CronFriendPostV1alpha1ApiPatchCronFriendPostRequest, options?: RawAxiosRequestConfig) { + return CronFriendPostV1alpha1ApiFp(this.configuration).patchCronFriendPost(requestParameters.name, requestParameters.jsonPatchInner, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Update CronFriendPost + * @param {CronFriendPostV1alpha1ApiUpdateCronFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof CronFriendPostV1alpha1Api + */ + public updateCronFriendPost(requestParameters: CronFriendPostV1alpha1ApiUpdateCronFriendPostRequest, options?: RawAxiosRequestConfig) { + return CronFriendPostV1alpha1ApiFp(this.configuration).updateCronFriendPost(requestParameters.name, requestParameters.cronFriendPost, options).then((request) => request(this.axios, this.basePath)); + } +} + diff --git a/ui/src/api/generated/api/friend-post-v1alpha1-api.ts b/ui/src/api/generated/api/friend-post-v1alpha1-api.ts new file mode 100644 index 0000000..64e2b0c --- /dev/null +++ b/ui/src/api/generated/api/friend-post-v1alpha1-api.ts @@ -0,0 +1,665 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from '../configuration'; +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; +// Some imports not used depending on template conditions +// @ts-ignore +import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common'; +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base'; +// @ts-ignore +import type { FriendPost } from '../models'; +// @ts-ignore +import type { FriendPostList } from '../models'; +// @ts-ignore +import type { JsonPatchInner } from '../models'; +/** + * FriendPostV1alpha1Api - axios parameter creator + * @export + */ +export const FriendPostV1alpha1ApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * Create FriendPost + * @param {FriendPost} [friendPost] Fresh friendpost + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createFriendPost: async (friendPost?: FriendPost, options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/apis/friend.moony.la/v1alpha1/friendposts`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(friendPost, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Delete FriendPost + * @param {string} name Name of friendpost + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteFriendPost: async (name: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists('deleteFriendPost', 'name', name) + const localVarPath = `/apis/friend.moony.la/v1alpha1/friendposts/{name}` + .replace(`{${"name"}}`, encodeURIComponent(String(name))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Get FriendPost + * @param {string} name Name of friendpost + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getFriendPost: async (name: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists('getFriendPost', 'name', name) + const localVarPath = `/apis/friend.moony.la/v1alpha1/friendposts/{name}` + .replace(`{${"name"}}`, encodeURIComponent(String(name))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * List FriendPost + * @param {number} [page] Page number. Default is 0. + * @param {number} [size] Size number. Default is 0. + * @param {Array} [labelSelector] Label selector. e.g.: hidden!=true + * @param {Array} [fieldSelector] Field selector. e.g.: metadata.name==halo + * @param {Array} [sort] Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listFriendPost: async (page?: number, size?: number, labelSelector?: Array, fieldSelector?: Array, sort?: Array, options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/apis/friend.moony.la/v1alpha1/friendposts`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + if (page !== undefined) { + localVarQueryParameter['page'] = page; + } + + if (size !== undefined) { + localVarQueryParameter['size'] = size; + } + + if (labelSelector) { + localVarQueryParameter['labelSelector'] = labelSelector; + } + + if (fieldSelector) { + localVarQueryParameter['fieldSelector'] = fieldSelector; + } + + if (sort) { + localVarQueryParameter['sort'] = sort; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Patch FriendPost + * @param {string} name Name of friendpost + * @param {Array} [jsonPatchInner] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + patchFriendPost: async (name: string, jsonPatchInner?: Array, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists('patchFriendPost', 'name', name) + const localVarPath = `/apis/friend.moony.la/v1alpha1/friendposts/{name}` + .replace(`{${"name"}}`, encodeURIComponent(String(name))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json-patch+json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(jsonPatchInner, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Update FriendPost + * @param {string} name Name of friendpost + * @param {FriendPost} [friendPost] Updated friendpost + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updateFriendPost: async (name: string, friendPost?: FriendPost, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists('updateFriendPost', 'name', name) + const localVarPath = `/apis/friend.moony.la/v1alpha1/friendposts/{name}` + .replace(`{${"name"}}`, encodeURIComponent(String(name))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(friendPost, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * FriendPostV1alpha1Api - functional programming interface + * @export + */ +export const FriendPostV1alpha1ApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = FriendPostV1alpha1ApiAxiosParamCreator(configuration) + return { + /** + * Create FriendPost + * @param {FriendPost} [friendPost] Fresh friendpost + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async createFriendPost(friendPost?: FriendPost, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.createFriendPost(friendPost, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['FriendPostV1alpha1Api.createFriendPost']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Delete FriendPost + * @param {string} name Name of friendpost + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deleteFriendPost(name: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.deleteFriendPost(name, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['FriendPostV1alpha1Api.deleteFriendPost']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Get FriendPost + * @param {string} name Name of friendpost + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getFriendPost(name: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getFriendPost(name, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['FriendPostV1alpha1Api.getFriendPost']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * List FriendPost + * @param {number} [page] Page number. Default is 0. + * @param {number} [size] Size number. Default is 0. + * @param {Array} [labelSelector] Label selector. e.g.: hidden!=true + * @param {Array} [fieldSelector] Field selector. e.g.: metadata.name==halo + * @param {Array} [sort] Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async listFriendPost(page?: number, size?: number, labelSelector?: Array, fieldSelector?: Array, sort?: Array, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.listFriendPost(page, size, labelSelector, fieldSelector, sort, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['FriendPostV1alpha1Api.listFriendPost']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Patch FriendPost + * @param {string} name Name of friendpost + * @param {Array} [jsonPatchInner] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async patchFriendPost(name: string, jsonPatchInner?: Array, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.patchFriendPost(name, jsonPatchInner, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['FriendPostV1alpha1Api.patchFriendPost']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Update FriendPost + * @param {string} name Name of friendpost + * @param {FriendPost} [friendPost] Updated friendpost + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async updateFriendPost(name: string, friendPost?: FriendPost, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.updateFriendPost(name, friendPost, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['FriendPostV1alpha1Api.updateFriendPost']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + } +}; + +/** + * FriendPostV1alpha1Api - factory interface + * @export + */ +export const FriendPostV1alpha1ApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = FriendPostV1alpha1ApiFp(configuration) + return { + /** + * Create FriendPost + * @param {FriendPostV1alpha1ApiCreateFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createFriendPost(requestParameters: FriendPostV1alpha1ApiCreateFriendPostRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.createFriendPost(requestParameters.friendPost, options).then((request) => request(axios, basePath)); + }, + /** + * Delete FriendPost + * @param {FriendPostV1alpha1ApiDeleteFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteFriendPost(requestParameters: FriendPostV1alpha1ApiDeleteFriendPostRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.deleteFriendPost(requestParameters.name, options).then((request) => request(axios, basePath)); + }, + /** + * Get FriendPost + * @param {FriendPostV1alpha1ApiGetFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getFriendPost(requestParameters: FriendPostV1alpha1ApiGetFriendPostRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.getFriendPost(requestParameters.name, options).then((request) => request(axios, basePath)); + }, + /** + * List FriendPost + * @param {FriendPostV1alpha1ApiListFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listFriendPost(requestParameters: FriendPostV1alpha1ApiListFriendPostRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.listFriendPost(requestParameters.page, requestParameters.size, requestParameters.labelSelector, requestParameters.fieldSelector, requestParameters.sort, options).then((request) => request(axios, basePath)); + }, + /** + * Patch FriendPost + * @param {FriendPostV1alpha1ApiPatchFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + patchFriendPost(requestParameters: FriendPostV1alpha1ApiPatchFriendPostRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.patchFriendPost(requestParameters.name, requestParameters.jsonPatchInner, options).then((request) => request(axios, basePath)); + }, + /** + * Update FriendPost + * @param {FriendPostV1alpha1ApiUpdateFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updateFriendPost(requestParameters: FriendPostV1alpha1ApiUpdateFriendPostRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.updateFriendPost(requestParameters.name, requestParameters.friendPost, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * Request parameters for createFriendPost operation in FriendPostV1alpha1Api. + * @export + * @interface FriendPostV1alpha1ApiCreateFriendPostRequest + */ +export interface FriendPostV1alpha1ApiCreateFriendPostRequest { + /** + * Fresh friendpost + * @type {FriendPost} + * @memberof FriendPostV1alpha1ApiCreateFriendPost + */ + readonly friendPost?: FriendPost +} + +/** + * Request parameters for deleteFriendPost operation in FriendPostV1alpha1Api. + * @export + * @interface FriendPostV1alpha1ApiDeleteFriendPostRequest + */ +export interface FriendPostV1alpha1ApiDeleteFriendPostRequest { + /** + * Name of friendpost + * @type {string} + * @memberof FriendPostV1alpha1ApiDeleteFriendPost + */ + readonly name: string +} + +/** + * Request parameters for getFriendPost operation in FriendPostV1alpha1Api. + * @export + * @interface FriendPostV1alpha1ApiGetFriendPostRequest + */ +export interface FriendPostV1alpha1ApiGetFriendPostRequest { + /** + * Name of friendpost + * @type {string} + * @memberof FriendPostV1alpha1ApiGetFriendPost + */ + readonly name: string +} + +/** + * Request parameters for listFriendPost operation in FriendPostV1alpha1Api. + * @export + * @interface FriendPostV1alpha1ApiListFriendPostRequest + */ +export interface FriendPostV1alpha1ApiListFriendPostRequest { + /** + * Page number. Default is 0. + * @type {number} + * @memberof FriendPostV1alpha1ApiListFriendPost + */ + readonly page?: number + + /** + * Size number. Default is 0. + * @type {number} + * @memberof FriendPostV1alpha1ApiListFriendPost + */ + readonly size?: number + + /** + * Label selector. e.g.: hidden!=true + * @type {Array} + * @memberof FriendPostV1alpha1ApiListFriendPost + */ + readonly labelSelector?: Array + + /** + * Field selector. e.g.: metadata.name==halo + * @type {Array} + * @memberof FriendPostV1alpha1ApiListFriendPost + */ + readonly fieldSelector?: Array + + /** + * Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. + * @type {Array} + * @memberof FriendPostV1alpha1ApiListFriendPost + */ + readonly sort?: Array +} + +/** + * Request parameters for patchFriendPost operation in FriendPostV1alpha1Api. + * @export + * @interface FriendPostV1alpha1ApiPatchFriendPostRequest + */ +export interface FriendPostV1alpha1ApiPatchFriendPostRequest { + /** + * Name of friendpost + * @type {string} + * @memberof FriendPostV1alpha1ApiPatchFriendPost + */ + readonly name: string + + /** + * + * @type {Array} + * @memberof FriendPostV1alpha1ApiPatchFriendPost + */ + readonly jsonPatchInner?: Array +} + +/** + * Request parameters for updateFriendPost operation in FriendPostV1alpha1Api. + * @export + * @interface FriendPostV1alpha1ApiUpdateFriendPostRequest + */ +export interface FriendPostV1alpha1ApiUpdateFriendPostRequest { + /** + * Name of friendpost + * @type {string} + * @memberof FriendPostV1alpha1ApiUpdateFriendPost + */ + readonly name: string + + /** + * Updated friendpost + * @type {FriendPost} + * @memberof FriendPostV1alpha1ApiUpdateFriendPost + */ + readonly friendPost?: FriendPost +} + +/** + * FriendPostV1alpha1Api - object-oriented interface + * @export + * @class FriendPostV1alpha1Api + * @extends {BaseAPI} + */ +export class FriendPostV1alpha1Api extends BaseAPI { + /** + * Create FriendPost + * @param {FriendPostV1alpha1ApiCreateFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof FriendPostV1alpha1Api + */ + public createFriendPost(requestParameters: FriendPostV1alpha1ApiCreateFriendPostRequest = {}, options?: RawAxiosRequestConfig) { + return FriendPostV1alpha1ApiFp(this.configuration).createFriendPost(requestParameters.friendPost, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Delete FriendPost + * @param {FriendPostV1alpha1ApiDeleteFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof FriendPostV1alpha1Api + */ + public deleteFriendPost(requestParameters: FriendPostV1alpha1ApiDeleteFriendPostRequest, options?: RawAxiosRequestConfig) { + return FriendPostV1alpha1ApiFp(this.configuration).deleteFriendPost(requestParameters.name, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Get FriendPost + * @param {FriendPostV1alpha1ApiGetFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof FriendPostV1alpha1Api + */ + public getFriendPost(requestParameters: FriendPostV1alpha1ApiGetFriendPostRequest, options?: RawAxiosRequestConfig) { + return FriendPostV1alpha1ApiFp(this.configuration).getFriendPost(requestParameters.name, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * List FriendPost + * @param {FriendPostV1alpha1ApiListFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof FriendPostV1alpha1Api + */ + public listFriendPost(requestParameters: FriendPostV1alpha1ApiListFriendPostRequest = {}, options?: RawAxiosRequestConfig) { + return FriendPostV1alpha1ApiFp(this.configuration).listFriendPost(requestParameters.page, requestParameters.size, requestParameters.labelSelector, requestParameters.fieldSelector, requestParameters.sort, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Patch FriendPost + * @param {FriendPostV1alpha1ApiPatchFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof FriendPostV1alpha1Api + */ + public patchFriendPost(requestParameters: FriendPostV1alpha1ApiPatchFriendPostRequest, options?: RawAxiosRequestConfig) { + return FriendPostV1alpha1ApiFp(this.configuration).patchFriendPost(requestParameters.name, requestParameters.jsonPatchInner, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Update FriendPost + * @param {FriendPostV1alpha1ApiUpdateFriendPostRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof FriendPostV1alpha1Api + */ + public updateFriendPost(requestParameters: FriendPostV1alpha1ApiUpdateFriendPostRequest, options?: RawAxiosRequestConfig) { + return FriendPostV1alpha1ApiFp(this.configuration).updateFriendPost(requestParameters.name, requestParameters.friendPost, options).then((request) => request(this.axios, this.basePath)); + } +} + diff --git a/ui/src/api/generated/api/rss-feed-sync-log-v1alpha1-api.ts b/ui/src/api/generated/api/rss-feed-sync-log-v1alpha1-api.ts new file mode 100644 index 0000000..0a99c13 --- /dev/null +++ b/ui/src/api/generated/api/rss-feed-sync-log-v1alpha1-api.ts @@ -0,0 +1,665 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from '../configuration'; +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; +// Some imports not used depending on template conditions +// @ts-ignore +import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common'; +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base'; +// @ts-ignore +import type { JsonPatchInner } from '../models'; +// @ts-ignore +import type { RssFeedSyncLog } from '../models'; +// @ts-ignore +import type { RssFeedSyncLogList } from '../models'; +/** + * RssFeedSyncLogV1alpha1Api - axios parameter creator + * @export + */ +export const RssFeedSyncLogV1alpha1ApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * Create RssFeedSyncLog + * @param {RssFeedSyncLog} [rssFeedSyncLog] Fresh rssfeedsynclog + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createRssFeedSyncLog: async (rssFeedSyncLog?: RssFeedSyncLog, options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/apis/friend.moony.la/v1alpha1/rssfeedsynclogs`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(rssFeedSyncLog, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Delete RssFeedSyncLog + * @param {string} name Name of rssfeedsynclog + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteRssFeedSyncLog: async (name: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists('deleteRssFeedSyncLog', 'name', name) + const localVarPath = `/apis/friend.moony.la/v1alpha1/rssfeedsynclogs/{name}` + .replace(`{${"name"}}`, encodeURIComponent(String(name))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Get RssFeedSyncLog + * @param {string} name Name of rssfeedsynclog + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getRssFeedSyncLog: async (name: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists('getRssFeedSyncLog', 'name', name) + const localVarPath = `/apis/friend.moony.la/v1alpha1/rssfeedsynclogs/{name}` + .replace(`{${"name"}}`, encodeURIComponent(String(name))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * List RssFeedSyncLog + * @param {number} [page] Page number. Default is 0. + * @param {number} [size] Size number. Default is 0. + * @param {Array} [labelSelector] Label selector. e.g.: hidden!=true + * @param {Array} [fieldSelector] Field selector. e.g.: metadata.name==halo + * @param {Array} [sort] Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listRssFeedSyncLog: async (page?: number, size?: number, labelSelector?: Array, fieldSelector?: Array, sort?: Array, options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/apis/friend.moony.la/v1alpha1/rssfeedsynclogs`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + if (page !== undefined) { + localVarQueryParameter['page'] = page; + } + + if (size !== undefined) { + localVarQueryParameter['size'] = size; + } + + if (labelSelector) { + localVarQueryParameter['labelSelector'] = labelSelector; + } + + if (fieldSelector) { + localVarQueryParameter['fieldSelector'] = fieldSelector; + } + + if (sort) { + localVarQueryParameter['sort'] = sort; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Patch RssFeedSyncLog + * @param {string} name Name of rssfeedsynclog + * @param {Array} [jsonPatchInner] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + patchRssFeedSyncLog: async (name: string, jsonPatchInner?: Array, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists('patchRssFeedSyncLog', 'name', name) + const localVarPath = `/apis/friend.moony.la/v1alpha1/rssfeedsynclogs/{name}` + .replace(`{${"name"}}`, encodeURIComponent(String(name))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'PATCH', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json-patch+json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(jsonPatchInner, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Update RssFeedSyncLog + * @param {string} name Name of rssfeedsynclog + * @param {RssFeedSyncLog} [rssFeedSyncLog] Updated rssfeedsynclog + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updateRssFeedSyncLog: async (name: string, rssFeedSyncLog?: RssFeedSyncLog, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists('updateRssFeedSyncLog', 'name', name) + const localVarPath = `/apis/friend.moony.la/v1alpha1/rssfeedsynclogs/{name}` + .replace(`{${"name"}}`, encodeURIComponent(String(name))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'PUT', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication basicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration) + + // authentication bearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(rssFeedSyncLog, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * RssFeedSyncLogV1alpha1Api - functional programming interface + * @export + */ +export const RssFeedSyncLogV1alpha1ApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = RssFeedSyncLogV1alpha1ApiAxiosParamCreator(configuration) + return { + /** + * Create RssFeedSyncLog + * @param {RssFeedSyncLog} [rssFeedSyncLog] Fresh rssfeedsynclog + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async createRssFeedSyncLog(rssFeedSyncLog?: RssFeedSyncLog, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.createRssFeedSyncLog(rssFeedSyncLog, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RssFeedSyncLogV1alpha1Api.createRssFeedSyncLog']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Delete RssFeedSyncLog + * @param {string} name Name of rssfeedsynclog + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deleteRssFeedSyncLog(name: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.deleteRssFeedSyncLog(name, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RssFeedSyncLogV1alpha1Api.deleteRssFeedSyncLog']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Get RssFeedSyncLog + * @param {string} name Name of rssfeedsynclog + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getRssFeedSyncLog(name: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getRssFeedSyncLog(name, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RssFeedSyncLogV1alpha1Api.getRssFeedSyncLog']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * List RssFeedSyncLog + * @param {number} [page] Page number. Default is 0. + * @param {number} [size] Size number. Default is 0. + * @param {Array} [labelSelector] Label selector. e.g.: hidden!=true + * @param {Array} [fieldSelector] Field selector. e.g.: metadata.name==halo + * @param {Array} [sort] Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async listRssFeedSyncLog(page?: number, size?: number, labelSelector?: Array, fieldSelector?: Array, sort?: Array, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.listRssFeedSyncLog(page, size, labelSelector, fieldSelector, sort, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RssFeedSyncLogV1alpha1Api.listRssFeedSyncLog']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Patch RssFeedSyncLog + * @param {string} name Name of rssfeedsynclog + * @param {Array} [jsonPatchInner] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async patchRssFeedSyncLog(name: string, jsonPatchInner?: Array, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.patchRssFeedSyncLog(name, jsonPatchInner, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RssFeedSyncLogV1alpha1Api.patchRssFeedSyncLog']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Update RssFeedSyncLog + * @param {string} name Name of rssfeedsynclog + * @param {RssFeedSyncLog} [rssFeedSyncLog] Updated rssfeedsynclog + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async updateRssFeedSyncLog(name: string, rssFeedSyncLog?: RssFeedSyncLog, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.updateRssFeedSyncLog(name, rssFeedSyncLog, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RssFeedSyncLogV1alpha1Api.updateRssFeedSyncLog']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + } +}; + +/** + * RssFeedSyncLogV1alpha1Api - factory interface + * @export + */ +export const RssFeedSyncLogV1alpha1ApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = RssFeedSyncLogV1alpha1ApiFp(configuration) + return { + /** + * Create RssFeedSyncLog + * @param {RssFeedSyncLogV1alpha1ApiCreateRssFeedSyncLogRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createRssFeedSyncLog(requestParameters: RssFeedSyncLogV1alpha1ApiCreateRssFeedSyncLogRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.createRssFeedSyncLog(requestParameters.rssFeedSyncLog, options).then((request) => request(axios, basePath)); + }, + /** + * Delete RssFeedSyncLog + * @param {RssFeedSyncLogV1alpha1ApiDeleteRssFeedSyncLogRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteRssFeedSyncLog(requestParameters: RssFeedSyncLogV1alpha1ApiDeleteRssFeedSyncLogRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.deleteRssFeedSyncLog(requestParameters.name, options).then((request) => request(axios, basePath)); + }, + /** + * Get RssFeedSyncLog + * @param {RssFeedSyncLogV1alpha1ApiGetRssFeedSyncLogRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getRssFeedSyncLog(requestParameters: RssFeedSyncLogV1alpha1ApiGetRssFeedSyncLogRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.getRssFeedSyncLog(requestParameters.name, options).then((request) => request(axios, basePath)); + }, + /** + * List RssFeedSyncLog + * @param {RssFeedSyncLogV1alpha1ApiListRssFeedSyncLogRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listRssFeedSyncLog(requestParameters: RssFeedSyncLogV1alpha1ApiListRssFeedSyncLogRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.listRssFeedSyncLog(requestParameters.page, requestParameters.size, requestParameters.labelSelector, requestParameters.fieldSelector, requestParameters.sort, options).then((request) => request(axios, basePath)); + }, + /** + * Patch RssFeedSyncLog + * @param {RssFeedSyncLogV1alpha1ApiPatchRssFeedSyncLogRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + patchRssFeedSyncLog(requestParameters: RssFeedSyncLogV1alpha1ApiPatchRssFeedSyncLogRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.patchRssFeedSyncLog(requestParameters.name, requestParameters.jsonPatchInner, options).then((request) => request(axios, basePath)); + }, + /** + * Update RssFeedSyncLog + * @param {RssFeedSyncLogV1alpha1ApiUpdateRssFeedSyncLogRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updateRssFeedSyncLog(requestParameters: RssFeedSyncLogV1alpha1ApiUpdateRssFeedSyncLogRequest, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp.updateRssFeedSyncLog(requestParameters.name, requestParameters.rssFeedSyncLog, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * Request parameters for createRssFeedSyncLog operation in RssFeedSyncLogV1alpha1Api. + * @export + * @interface RssFeedSyncLogV1alpha1ApiCreateRssFeedSyncLogRequest + */ +export interface RssFeedSyncLogV1alpha1ApiCreateRssFeedSyncLogRequest { + /** + * Fresh rssfeedsynclog + * @type {RssFeedSyncLog} + * @memberof RssFeedSyncLogV1alpha1ApiCreateRssFeedSyncLog + */ + readonly rssFeedSyncLog?: RssFeedSyncLog +} + +/** + * Request parameters for deleteRssFeedSyncLog operation in RssFeedSyncLogV1alpha1Api. + * @export + * @interface RssFeedSyncLogV1alpha1ApiDeleteRssFeedSyncLogRequest + */ +export interface RssFeedSyncLogV1alpha1ApiDeleteRssFeedSyncLogRequest { + /** + * Name of rssfeedsynclog + * @type {string} + * @memberof RssFeedSyncLogV1alpha1ApiDeleteRssFeedSyncLog + */ + readonly name: string +} + +/** + * Request parameters for getRssFeedSyncLog operation in RssFeedSyncLogV1alpha1Api. + * @export + * @interface RssFeedSyncLogV1alpha1ApiGetRssFeedSyncLogRequest + */ +export interface RssFeedSyncLogV1alpha1ApiGetRssFeedSyncLogRequest { + /** + * Name of rssfeedsynclog + * @type {string} + * @memberof RssFeedSyncLogV1alpha1ApiGetRssFeedSyncLog + */ + readonly name: string +} + +/** + * Request parameters for listRssFeedSyncLog operation in RssFeedSyncLogV1alpha1Api. + * @export + * @interface RssFeedSyncLogV1alpha1ApiListRssFeedSyncLogRequest + */ +export interface RssFeedSyncLogV1alpha1ApiListRssFeedSyncLogRequest { + /** + * Page number. Default is 0. + * @type {number} + * @memberof RssFeedSyncLogV1alpha1ApiListRssFeedSyncLog + */ + readonly page?: number + + /** + * Size number. Default is 0. + * @type {number} + * @memberof RssFeedSyncLogV1alpha1ApiListRssFeedSyncLog + */ + readonly size?: number + + /** + * Label selector. e.g.: hidden!=true + * @type {Array} + * @memberof RssFeedSyncLogV1alpha1ApiListRssFeedSyncLog + */ + readonly labelSelector?: Array + + /** + * Field selector. e.g.: metadata.name==halo + * @type {Array} + * @memberof RssFeedSyncLogV1alpha1ApiListRssFeedSyncLog + */ + readonly fieldSelector?: Array + + /** + * Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported. + * @type {Array} + * @memberof RssFeedSyncLogV1alpha1ApiListRssFeedSyncLog + */ + readonly sort?: Array +} + +/** + * Request parameters for patchRssFeedSyncLog operation in RssFeedSyncLogV1alpha1Api. + * @export + * @interface RssFeedSyncLogV1alpha1ApiPatchRssFeedSyncLogRequest + */ +export interface RssFeedSyncLogV1alpha1ApiPatchRssFeedSyncLogRequest { + /** + * Name of rssfeedsynclog + * @type {string} + * @memberof RssFeedSyncLogV1alpha1ApiPatchRssFeedSyncLog + */ + readonly name: string + + /** + * + * @type {Array} + * @memberof RssFeedSyncLogV1alpha1ApiPatchRssFeedSyncLog + */ + readonly jsonPatchInner?: Array +} + +/** + * Request parameters for updateRssFeedSyncLog operation in RssFeedSyncLogV1alpha1Api. + * @export + * @interface RssFeedSyncLogV1alpha1ApiUpdateRssFeedSyncLogRequest + */ +export interface RssFeedSyncLogV1alpha1ApiUpdateRssFeedSyncLogRequest { + /** + * Name of rssfeedsynclog + * @type {string} + * @memberof RssFeedSyncLogV1alpha1ApiUpdateRssFeedSyncLog + */ + readonly name: string + + /** + * Updated rssfeedsynclog + * @type {RssFeedSyncLog} + * @memberof RssFeedSyncLogV1alpha1ApiUpdateRssFeedSyncLog + */ + readonly rssFeedSyncLog?: RssFeedSyncLog +} + +/** + * RssFeedSyncLogV1alpha1Api - object-oriented interface + * @export + * @class RssFeedSyncLogV1alpha1Api + * @extends {BaseAPI} + */ +export class RssFeedSyncLogV1alpha1Api extends BaseAPI { + /** + * Create RssFeedSyncLog + * @param {RssFeedSyncLogV1alpha1ApiCreateRssFeedSyncLogRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RssFeedSyncLogV1alpha1Api + */ + public createRssFeedSyncLog(requestParameters: RssFeedSyncLogV1alpha1ApiCreateRssFeedSyncLogRequest = {}, options?: RawAxiosRequestConfig) { + return RssFeedSyncLogV1alpha1ApiFp(this.configuration).createRssFeedSyncLog(requestParameters.rssFeedSyncLog, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Delete RssFeedSyncLog + * @param {RssFeedSyncLogV1alpha1ApiDeleteRssFeedSyncLogRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RssFeedSyncLogV1alpha1Api + */ + public deleteRssFeedSyncLog(requestParameters: RssFeedSyncLogV1alpha1ApiDeleteRssFeedSyncLogRequest, options?: RawAxiosRequestConfig) { + return RssFeedSyncLogV1alpha1ApiFp(this.configuration).deleteRssFeedSyncLog(requestParameters.name, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Get RssFeedSyncLog + * @param {RssFeedSyncLogV1alpha1ApiGetRssFeedSyncLogRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RssFeedSyncLogV1alpha1Api + */ + public getRssFeedSyncLog(requestParameters: RssFeedSyncLogV1alpha1ApiGetRssFeedSyncLogRequest, options?: RawAxiosRequestConfig) { + return RssFeedSyncLogV1alpha1ApiFp(this.configuration).getRssFeedSyncLog(requestParameters.name, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * List RssFeedSyncLog + * @param {RssFeedSyncLogV1alpha1ApiListRssFeedSyncLogRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RssFeedSyncLogV1alpha1Api + */ + public listRssFeedSyncLog(requestParameters: RssFeedSyncLogV1alpha1ApiListRssFeedSyncLogRequest = {}, options?: RawAxiosRequestConfig) { + return RssFeedSyncLogV1alpha1ApiFp(this.configuration).listRssFeedSyncLog(requestParameters.page, requestParameters.size, requestParameters.labelSelector, requestParameters.fieldSelector, requestParameters.sort, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Patch RssFeedSyncLog + * @param {RssFeedSyncLogV1alpha1ApiPatchRssFeedSyncLogRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RssFeedSyncLogV1alpha1Api + */ + public patchRssFeedSyncLog(requestParameters: RssFeedSyncLogV1alpha1ApiPatchRssFeedSyncLogRequest, options?: RawAxiosRequestConfig) { + return RssFeedSyncLogV1alpha1ApiFp(this.configuration).patchRssFeedSyncLog(requestParameters.name, requestParameters.jsonPatchInner, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * Update RssFeedSyncLog + * @param {RssFeedSyncLogV1alpha1ApiUpdateRssFeedSyncLogRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RssFeedSyncLogV1alpha1Api + */ + public updateRssFeedSyncLog(requestParameters: RssFeedSyncLogV1alpha1ApiUpdateRssFeedSyncLogRequest, options?: RawAxiosRequestConfig) { + return RssFeedSyncLogV1alpha1ApiFp(this.configuration).updateRssFeedSyncLog(requestParameters.name, requestParameters.rssFeedSyncLog, options).then((request) => request(this.axios, this.basePath)); + } +} + diff --git a/ui/src/api/generated/base.ts b/ui/src/api/generated/base.ts new file mode 100644 index 0000000..34e330d --- /dev/null +++ b/ui/src/api/generated/base.ts @@ -0,0 +1,86 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from './configuration'; +// Some imports not used depending on template conditions +// @ts-ignore +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; + +export const BASE_PATH = "http://localhost:65429".replace(/\/+$/, ""); + +/** + * + * @export + */ +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +/** + * + * @export + * @interface RequestArgs + */ +export interface RequestArgs { + url: string; + options: RawAxiosRequestConfig; +} + +/** + * + * @export + * @class BaseAPI + */ +export class BaseAPI { + protected configuration: Configuration | undefined; + + constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) { + if (configuration) { + this.configuration = configuration; + this.basePath = configuration.basePath ?? basePath; + } + } +}; + +/** + * + * @export + * @class RequiredError + * @extends {Error} + */ +export class RequiredError extends Error { + constructor(public field: string, msg?: string) { + super(msg); + this.name = "RequiredError" + } +} + +interface ServerMap { + [key: string]: { + url: string, + description: string, + }[]; +} + +/** + * + * @export + */ +export const operationServerMap: ServerMap = { +} diff --git a/ui/src/api/generated/common.ts b/ui/src/api/generated/common.ts new file mode 100644 index 0000000..26708e2 --- /dev/null +++ b/ui/src/api/generated/common.ts @@ -0,0 +1,150 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from "./configuration"; +import type { RequestArgs } from "./base"; +import type { AxiosInstance, AxiosResponse } from 'axios'; +import { RequiredError } from "./base"; + +/** + * + * @export + */ +export const DUMMY_BASE_URL = 'https://example.com' + +/** + * + * @throws {RequiredError} + * @export + */ +export const assertParamExists = function (functionName: string, paramName: string, paramValue: unknown) { + if (paramValue === null || paramValue === undefined) { + throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`); + } +} + +/** + * + * @export + */ +export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) { + if (configuration && configuration.apiKey) { + const localVarApiKeyValue = typeof configuration.apiKey === 'function' + ? await configuration.apiKey(keyParamName) + : await configuration.apiKey; + object[keyParamName] = localVarApiKeyValue; + } +} + +/** + * + * @export + */ +export const setBasicAuthToObject = function (object: any, configuration?: Configuration) { + if (configuration && (configuration.username || configuration.password)) { + object["auth"] = { username: configuration.username, password: configuration.password }; + } +} + +/** + * + * @export + */ +export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) { + if (configuration && configuration.accessToken) { + const accessToken = typeof configuration.accessToken === 'function' + ? await configuration.accessToken() + : await configuration.accessToken; + object["Authorization"] = "Bearer " + accessToken; + } +} + +/** + * + * @export + */ +export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) { + if (configuration && configuration.accessToken) { + const localVarAccessTokenValue = typeof configuration.accessToken === 'function' + ? await configuration.accessToken(name, scopes) + : await configuration.accessToken; + object["Authorization"] = "Bearer " + localVarAccessTokenValue; + } +} + +function setFlattenedQueryParams(urlSearchParams: URLSearchParams, parameter: any, key: string = ""): void { + if (parameter == null) return; + if (typeof parameter === "object") { + if (Array.isArray(parameter)) { + (parameter as any[]).forEach(item => setFlattenedQueryParams(urlSearchParams, item, key)); + } + else { + Object.keys(parameter).forEach(currentKey => + setFlattenedQueryParams(urlSearchParams, parameter[currentKey], `${key}${key !== '' ? '.' : ''}${currentKey}`) + ); + } + } + else { + if (urlSearchParams.has(key)) { + urlSearchParams.append(key, parameter); + } + else { + urlSearchParams.set(key, parameter); + } + } +} + +/** + * + * @export + */ +export const setSearchParams = function (url: URL, ...objects: any[]) { + const searchParams = new URLSearchParams(url.search); + setFlattenedQueryParams(searchParams, objects); + url.search = searchParams.toString(); +} + +/** + * + * @export + */ +export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) { + const nonString = typeof value !== 'string'; + const needsSerialization = nonString && configuration && configuration.isJsonMime + ? configuration.isJsonMime(requestOptions.headers['Content-Type']) + : nonString; + return needsSerialization + ? JSON.stringify(value !== undefined ? value : {}) + : (value || ""); +} + +/** + * + * @export + */ +export const toPathString = function (url: URL) { + return url.pathname + url.search + url.hash +} + +/** + * + * @export + */ +export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) { + return >(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { + const axiosRequestArgs = {...axiosArgs.options, url: (axios.defaults.baseURL ? '' : configuration?.basePath ?? basePath) + axiosArgs.url}; + return axios.request(axiosRequestArgs); + }; +} diff --git a/ui/src/api/generated/configuration.ts b/ui/src/api/generated/configuration.ts new file mode 100644 index 0000000..452b67e --- /dev/null +++ b/ui/src/api/generated/configuration.ts @@ -0,0 +1,110 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface ConfigurationParameters { + apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); + username?: string; + password?: string; + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); + basePath?: string; + serverIndex?: number; + baseOptions?: any; + formDataCtor?: new () => any; +} + +export class Configuration { + /** + * parameter for apiKey security + * @param name security name + * @memberof Configuration + */ + apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + username?: string; + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + password?: string; + /** + * parameter for oauth2 security + * @param name security name + * @param scopes oauth2 scope + * @memberof Configuration + */ + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); + /** + * override base path + * + * @type {string} + * @memberof Configuration + */ + basePath?: string; + /** + * override server index + * + * @type {number} + * @memberof Configuration + */ + serverIndex?: number; + /** + * base options for axios calls + * + * @type {any} + * @memberof Configuration + */ + baseOptions?: any; + /** + * The FormData constructor that will be used to create multipart form data + * requests. You can inject this here so that execution environments that + * do not support the FormData class can still run the generated client. + * + * @type {new () => FormData} + */ + formDataCtor?: new () => any; + + constructor(param: ConfigurationParameters = {}) { + this.apiKey = param.apiKey; + this.username = param.username; + this.password = param.password; + this.accessToken = param.accessToken; + this.basePath = param.basePath; + this.serverIndex = param.serverIndex; + this.baseOptions = param.baseOptions; + this.formDataCtor = param.formDataCtor; + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public isJsonMime(mime: string): boolean { + const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i'); + return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json'); + } +} diff --git a/ui/src/api/generated/git_push.sh b/ui/src/api/generated/git_push.sh new file mode 100644 index 0000000..f53a75d --- /dev/null +++ b/ui/src/api/generated/git_push.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="github.com" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=$(git remote) +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' diff --git a/ui/src/api/generated/index.ts b/ui/src/api/generated/index.ts new file mode 100644 index 0000000..7c2845c --- /dev/null +++ b/ui/src/api/generated/index.ts @@ -0,0 +1,18 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export * from "./api"; +export * from "./configuration"; +export * from "./models"; diff --git a/ui/src/api/generated/models/add-operation.ts b/ui/src/api/generated/models/add-operation.ts new file mode 100644 index 0000000..6cbb6cb --- /dev/null +++ b/ui/src/api/generated/models/add-operation.ts @@ -0,0 +1,49 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface AddOperation + */ +export interface AddOperation { + /** + * + * @type {string} + * @memberof AddOperation + */ + 'op': AddOperationOpEnum; + /** + * A JSON Pointer path pointing to the location to move/copy from. + * @type {string} + * @memberof AddOperation + */ + 'path': string; + /** + * Value can be any JSON value + * @type {any} + * @memberof AddOperation + */ + 'value': any; +} + +export const AddOperationOpEnum = { + Add: 'add' +} as const; + +export type AddOperationOpEnum = typeof AddOperationOpEnum[keyof typeof AddOperationOpEnum]; + + diff --git a/ui/src/api/generated/models/channel.ts b/ui/src/api/generated/models/channel.ts new file mode 100644 index 0000000..0bcf738 --- /dev/null +++ b/ui/src/api/generated/models/channel.ts @@ -0,0 +1,51 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { Item } from './item'; + +/** + * + * @export + * @interface Channel + */ +export interface Channel { + /** + * + * @type {string} + * @memberof Channel + */ + 'description'?: string; + /** + * + * @type {Array} + * @memberof Channel + */ + 'items'?: Array; + /** + * + * @type {string} + * @memberof Channel + */ + 'link'?: string; + /** + * + * @type {string} + * @memberof Channel + */ + 'title'?: string; +} + diff --git a/ui/src/api/generated/models/copy-operation.ts b/ui/src/api/generated/models/copy-operation.ts new file mode 100644 index 0000000..e4550e2 --- /dev/null +++ b/ui/src/api/generated/models/copy-operation.ts @@ -0,0 +1,49 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface CopyOperation + */ +export interface CopyOperation { + /** + * A JSON Pointer path pointing to the location to move/copy from. + * @type {string} + * @memberof CopyOperation + */ + 'from': string; + /** + * + * @type {string} + * @memberof CopyOperation + */ + 'op': CopyOperationOpEnum; + /** + * A JSON Pointer path pointing to the location to move/copy from. + * @type {string} + * @memberof CopyOperation + */ + 'path': string; +} + +export const CopyOperationOpEnum = { + Copy: 'copy' +} as const; + +export type CopyOperationOpEnum = typeof CopyOperationOpEnum[keyof typeof CopyOperationOpEnum]; + + diff --git a/ui/src/api/generated/models/cron-friend-post-list.ts b/ui/src/api/generated/models/cron-friend-post-list.ts new file mode 100644 index 0000000..021c294 --- /dev/null +++ b/ui/src/api/generated/models/cron-friend-post-list.ts @@ -0,0 +1,81 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { CronFriendPost } from './cron-friend-post'; + +/** + * + * @export + * @interface CronFriendPostList + */ +export interface CronFriendPostList { + /** + * Indicates whether current page is the first page. + * @type {boolean} + * @memberof CronFriendPostList + */ + 'first': boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof CronFriendPostList + */ + 'hasNext': boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof CronFriendPostList + */ + 'hasPrevious': boolean; + /** + * A chunk of items. + * @type {Array} + * @memberof CronFriendPostList + */ + 'items': Array; + /** + * Indicates whether current page is the last page. + * @type {boolean} + * @memberof CronFriendPostList + */ + 'last': boolean; + /** + * Page number, starts from 1. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof CronFriendPostList + */ + 'page': number; + /** + * Size of each page. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof CronFriendPostList + */ + 'size': number; + /** + * Total elements. + * @type {number} + * @memberof CronFriendPostList + */ + 'total': number; + /** + * Indicates total pages. + * @type {number} + * @memberof CronFriendPostList + */ + 'totalPages': number; +} + diff --git a/ui/src/api/generated/models/cron-friend-post.ts b/ui/src/api/generated/models/cron-friend-post.ts new file mode 100644 index 0000000..e00be1c --- /dev/null +++ b/ui/src/api/generated/models/cron-friend-post.ts @@ -0,0 +1,63 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { CronSpec } from './cron-spec'; +// May contain unused imports in some cases +// @ts-ignore +import type { CronStatus } from './cron-status'; +// May contain unused imports in some cases +// @ts-ignore +import type { Metadata } from './metadata'; + +/** + * + * @export + * @interface CronFriendPost + */ +export interface CronFriendPost { + /** + * + * @type {string} + * @memberof CronFriendPost + */ + 'apiVersion': string; + /** + * + * @type {string} + * @memberof CronFriendPost + */ + 'kind': string; + /** + * + * @type {Metadata} + * @memberof CronFriendPost + */ + 'metadata': Metadata; + /** + * + * @type {CronSpec} + * @memberof CronFriendPost + */ + 'spec'?: CronSpec; + /** + * + * @type {CronStatus} + * @memberof CronFriendPost + */ + 'status'?: CronStatus; +} + diff --git a/ui/src/api/generated/models/cron-spec.ts b/ui/src/api/generated/models/cron-spec.ts new file mode 100644 index 0000000..1f230c1 --- /dev/null +++ b/ui/src/api/generated/models/cron-spec.ts @@ -0,0 +1,54 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface CronSpec + */ +export interface CronSpec { + /** + * + * @type {string} + * @memberof CronSpec + */ + 'cron'?: string; + /** + * + * @type {Array} + * @memberof CronSpec + */ + 'disableSyncList'?: Array; + /** + * + * @type {number} + * @memberof CronSpec + */ + 'successfulRetainLimit'?: number; + /** + * + * @type {boolean} + * @memberof CronSpec + */ + 'suspend'?: boolean; + /** + * + * @type {string} + * @memberof CronSpec + */ + 'timezone'?: string; +} + diff --git a/ui/src/api/generated/models/cron-status.ts b/ui/src/api/generated/models/cron-status.ts new file mode 100644 index 0000000..49409da --- /dev/null +++ b/ui/src/api/generated/models/cron-status.ts @@ -0,0 +1,36 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface CronStatus + */ +export interface CronStatus { + /** + * + * @type {string} + * @memberof CronStatus + */ + 'lastScheduledTimestamp'?: string; + /** + * + * @type {string} + * @memberof CronStatus + */ + 'nextSchedulingTimestamp'?: string; +} + diff --git a/ui/src/api/generated/models/friend-post-list.ts b/ui/src/api/generated/models/friend-post-list.ts new file mode 100644 index 0000000..e347e76 --- /dev/null +++ b/ui/src/api/generated/models/friend-post-list.ts @@ -0,0 +1,81 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { FriendPost } from './friend-post'; + +/** + * + * @export + * @interface FriendPostList + */ +export interface FriendPostList { + /** + * Indicates whether current page is the first page. + * @type {boolean} + * @memberof FriendPostList + */ + 'first': boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof FriendPostList + */ + 'hasNext': boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof FriendPostList + */ + 'hasPrevious': boolean; + /** + * A chunk of items. + * @type {Array} + * @memberof FriendPostList + */ + 'items': Array; + /** + * Indicates whether current page is the last page. + * @type {boolean} + * @memberof FriendPostList + */ + 'last': boolean; + /** + * Page number, starts from 1. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof FriendPostList + */ + 'page': number; + /** + * Size of each page. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof FriendPostList + */ + 'size': number; + /** + * Total elements. + * @type {number} + * @memberof FriendPostList + */ + 'total': number; + /** + * Indicates total pages. + * @type {number} + * @memberof FriendPostList + */ + 'totalPages': number; +} + diff --git a/ui/src/api/generated/models/friend-post-spec.ts b/ui/src/api/generated/models/friend-post-spec.ts new file mode 100644 index 0000000..3ecafca --- /dev/null +++ b/ui/src/api/generated/models/friend-post-spec.ts @@ -0,0 +1,72 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface FriendPostSpec + */ +export interface FriendPostSpec { + /** + * + * @type {string} + * @memberof FriendPostSpec + */ + 'author'?: string; + /** + * + * @type {string} + * @memberof FriendPostSpec + */ + 'authorUrl'?: string; + /** + * + * @type {string} + * @memberof FriendPostSpec + */ + 'description'?: string; + /** + * + * @type {string} + * @memberof FriendPostSpec + */ + 'linkName'?: string; + /** + * + * @type {string} + * @memberof FriendPostSpec + */ + 'logo'?: string; + /** + * + * @type {string} + * @memberof FriendPostSpec + */ + 'postLink'?: string; + /** + * + * @type {string} + * @memberof FriendPostSpec + */ + 'pubDate'?: string; + /** + * + * @type {string} + * @memberof FriendPostSpec + */ + 'title'?: string; +} + diff --git a/ui/src/api/generated/models/friend-post.ts b/ui/src/api/generated/models/friend-post.ts new file mode 100644 index 0000000..edf9366 --- /dev/null +++ b/ui/src/api/generated/models/friend-post.ts @@ -0,0 +1,54 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { FriendPostSpec } from './friend-post-spec'; +// May contain unused imports in some cases +// @ts-ignore +import type { Metadata } from './metadata'; + +/** + * + * @export + * @interface FriendPost + */ +export interface FriendPost { + /** + * + * @type {string} + * @memberof FriendPost + */ + 'apiVersion': string; + /** + * + * @type {string} + * @memberof FriendPost + */ + 'kind': string; + /** + * + * @type {Metadata} + * @memberof FriendPost + */ + 'metadata': Metadata; + /** + * + * @type {FriendPostSpec} + * @memberof FriendPost + */ + 'spec': FriendPostSpec; +} + diff --git a/ui/src/api/generated/models/index.ts b/ui/src/api/generated/models/index.ts new file mode 100644 index 0000000..557e3cf --- /dev/null +++ b/ui/src/api/generated/models/index.ts @@ -0,0 +1,24 @@ +export * from './add-operation'; +export * from './channel'; +export * from './copy-operation'; +export * from './cron-friend-post'; +export * from './cron-friend-post-list'; +export * from './cron-spec'; +export * from './cron-status'; +export * from './friend-post'; +export * from './friend-post-list'; +export * from './friend-post-spec'; +export * from './item'; +export * from './json-patch-inner'; +export * from './link'; +export * from './link-spec'; +export * from './listed-rss-sync-log'; +export * from './listed-rss-sync-log-list'; +export * from './metadata'; +export * from './move-operation'; +export * from './remove-operation'; +export * from './replace-operation'; +export * from './rss-detail'; +export * from './rss-feed-sync-log'; +export * from './rss-feed-sync-log-list'; +export * from './test-operation'; diff --git a/ui/src/api/generated/models/item.ts b/ui/src/api/generated/models/item.ts new file mode 100644 index 0000000..37b6091 --- /dev/null +++ b/ui/src/api/generated/models/item.ts @@ -0,0 +1,60 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface Item + */ +export interface Item { + /** + * + * @type {string} + * @memberof Item + */ + 'author'?: string; + /** + * + * @type {string} + * @memberof Item + */ + 'description'?: string; + /** + * + * @type {string} + * @memberof Item + */ + 'link'?: string; + /** + * + * @type {string} + * @memberof Item + */ + 'pubDate'?: string; + /** + * + * @type {string} + * @memberof Item + */ + 'title'?: string; + /** + * + * @type {string} + * @memberof Item + */ + 'uri'?: string; +} + diff --git a/ui/src/api/generated/models/json-patch-inner.ts b/ui/src/api/generated/models/json-patch-inner.ts new file mode 100644 index 0000000..a63285d --- /dev/null +++ b/ui/src/api/generated/models/json-patch-inner.ts @@ -0,0 +1,41 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { AddOperation } from './add-operation'; +// May contain unused imports in some cases +// @ts-ignore +import type { CopyOperation } from './copy-operation'; +// May contain unused imports in some cases +// @ts-ignore +import type { MoveOperation } from './move-operation'; +// May contain unused imports in some cases +// @ts-ignore +import type { RemoveOperation } from './remove-operation'; +// May contain unused imports in some cases +// @ts-ignore +import type { ReplaceOperation } from './replace-operation'; +// May contain unused imports in some cases +// @ts-ignore +import type { TestOperation } from './test-operation'; + +/** + * @type JsonPatchInner + * @export + */ +export type JsonPatchInner = AddOperation | CopyOperation | MoveOperation | RemoveOperation | ReplaceOperation | TestOperation; + + diff --git a/ui/src/api/generated/models/link-spec.ts b/ui/src/api/generated/models/link-spec.ts new file mode 100644 index 0000000..9b89247 --- /dev/null +++ b/ui/src/api/generated/models/link-spec.ts @@ -0,0 +1,60 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface LinkSpec + */ +export interface LinkSpec { + /** + * + * @type {string} + * @memberof LinkSpec + */ + 'description'?: string; + /** + * + * @type {string} + * @memberof LinkSpec + */ + 'displayName': string; + /** + * + * @type {string} + * @memberof LinkSpec + */ + 'groupName'?: string; + /** + * + * @type {string} + * @memberof LinkSpec + */ + 'logo'?: string; + /** + * + * @type {number} + * @memberof LinkSpec + */ + 'priority'?: number; + /** + * + * @type {string} + * @memberof LinkSpec + */ + 'url': string; +} + diff --git a/ui/src/api/generated/models/link.ts b/ui/src/api/generated/models/link.ts new file mode 100644 index 0000000..057979a --- /dev/null +++ b/ui/src/api/generated/models/link.ts @@ -0,0 +1,54 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { LinkSpec } from './link-spec'; +// May contain unused imports in some cases +// @ts-ignore +import type { Metadata } from './metadata'; + +/** + * + * @export + * @interface Link + */ +export interface Link { + /** + * + * @type {string} + * @memberof Link + */ + 'apiVersion': string; + /** + * + * @type {string} + * @memberof Link + */ + 'kind': string; + /** + * + * @type {Metadata} + * @memberof Link + */ + 'metadata': Metadata; + /** + * + * @type {LinkSpec} + * @memberof Link + */ + 'spec'?: LinkSpec; +} + diff --git a/ui/src/api/generated/models/listed-rss-sync-log-list.ts b/ui/src/api/generated/models/listed-rss-sync-log-list.ts new file mode 100644 index 0000000..6361ec5 --- /dev/null +++ b/ui/src/api/generated/models/listed-rss-sync-log-list.ts @@ -0,0 +1,81 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { ListedRssSyncLog } from './listed-rss-sync-log'; + +/** + * + * @export + * @interface ListedRssSyncLogList + */ +export interface ListedRssSyncLogList { + /** + * Indicates whether current page is the first page. + * @type {boolean} + * @memberof ListedRssSyncLogList + */ + 'first': boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof ListedRssSyncLogList + */ + 'hasNext': boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof ListedRssSyncLogList + */ + 'hasPrevious': boolean; + /** + * A chunk of items. + * @type {Array} + * @memberof ListedRssSyncLogList + */ + 'items': Array; + /** + * Indicates whether current page is the last page. + * @type {boolean} + * @memberof ListedRssSyncLogList + */ + 'last': boolean; + /** + * Page number, starts from 1. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof ListedRssSyncLogList + */ + 'page': number; + /** + * Size of each page. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof ListedRssSyncLogList + */ + 'size': number; + /** + * Total elements. + * @type {number} + * @memberof ListedRssSyncLogList + */ + 'total': number; + /** + * Indicates total pages. + * @type {number} + * @memberof ListedRssSyncLogList + */ + 'totalPages': number; +} + diff --git a/ui/src/api/generated/models/listed-rss-sync-log.ts b/ui/src/api/generated/models/listed-rss-sync-log.ts new file mode 100644 index 0000000..0415af0 --- /dev/null +++ b/ui/src/api/generated/models/listed-rss-sync-log.ts @@ -0,0 +1,42 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { Link } from './link'; +// May contain unused imports in some cases +// @ts-ignore +import type { RssFeedSyncLog } from './rss-feed-sync-log'; + +/** + * A chunk of items. + * @export + * @interface ListedRssSyncLog + */ +export interface ListedRssSyncLog { + /** + * + * @type {Link} + * @memberof ListedRssSyncLog + */ + 'link': Link; + /** + * + * @type {RssFeedSyncLog} + * @memberof ListedRssSyncLog + */ + 'log'?: RssFeedSyncLog; +} + diff --git a/ui/src/api/generated/models/metadata.ts b/ui/src/api/generated/models/metadata.ts new file mode 100644 index 0000000..4c56cf8 --- /dev/null +++ b/ui/src/api/generated/models/metadata.ts @@ -0,0 +1,72 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface Metadata + */ +export interface Metadata { + /** + * + * @type {{ [key: string]: string; }} + * @memberof Metadata + */ + 'annotations'?: { [key: string]: string; }; + /** + * + * @type {string} + * @memberof Metadata + */ + 'creationTimestamp'?: string | null; + /** + * + * @type {string} + * @memberof Metadata + */ + 'deletionTimestamp'?: string | null; + /** + * + * @type {Array} + * @memberof Metadata + */ + 'finalizers'?: Array | null; + /** + * The name field will be generated automatically according to the given generateName field + * @type {string} + * @memberof Metadata + */ + 'generateName'?: string; + /** + * + * @type {{ [key: string]: string; }} + * @memberof Metadata + */ + 'labels'?: { [key: string]: string; }; + /** + * Metadata name + * @type {string} + * @memberof Metadata + */ + 'name': string; + /** + * + * @type {number} + * @memberof Metadata + */ + 'version'?: number | null; +} + diff --git a/ui/src/api/generated/models/move-operation.ts b/ui/src/api/generated/models/move-operation.ts new file mode 100644 index 0000000..92189a9 --- /dev/null +++ b/ui/src/api/generated/models/move-operation.ts @@ -0,0 +1,49 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface MoveOperation + */ +export interface MoveOperation { + /** + * A JSON Pointer path pointing to the location to move/copy from. + * @type {string} + * @memberof MoveOperation + */ + 'from': string; + /** + * + * @type {string} + * @memberof MoveOperation + */ + 'op': MoveOperationOpEnum; + /** + * A JSON Pointer path pointing to the location to move/copy from. + * @type {string} + * @memberof MoveOperation + */ + 'path': string; +} + +export const MoveOperationOpEnum = { + Move: 'move' +} as const; + +export type MoveOperationOpEnum = typeof MoveOperationOpEnum[keyof typeof MoveOperationOpEnum]; + + diff --git a/ui/src/api/generated/models/remove-operation.ts b/ui/src/api/generated/models/remove-operation.ts new file mode 100644 index 0000000..cc4bd53 --- /dev/null +++ b/ui/src/api/generated/models/remove-operation.ts @@ -0,0 +1,43 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface RemoveOperation + */ +export interface RemoveOperation { + /** + * + * @type {string} + * @memberof RemoveOperation + */ + 'op': RemoveOperationOpEnum; + /** + * A JSON Pointer path pointing to the location to move/copy from. + * @type {string} + * @memberof RemoveOperation + */ + 'path': string; +} + +export const RemoveOperationOpEnum = { + Remove: 'remove' +} as const; + +export type RemoveOperationOpEnum = typeof RemoveOperationOpEnum[keyof typeof RemoveOperationOpEnum]; + + diff --git a/ui/src/api/generated/models/replace-operation.ts b/ui/src/api/generated/models/replace-operation.ts new file mode 100644 index 0000000..599d74b --- /dev/null +++ b/ui/src/api/generated/models/replace-operation.ts @@ -0,0 +1,49 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface ReplaceOperation + */ +export interface ReplaceOperation { + /** + * + * @type {string} + * @memberof ReplaceOperation + */ + 'op': ReplaceOperationOpEnum; + /** + * A JSON Pointer path pointing to the location to move/copy from. + * @type {string} + * @memberof ReplaceOperation + */ + 'path': string; + /** + * Value can be any JSON value + * @type {any} + * @memberof ReplaceOperation + */ + 'value': any; +} + +export const ReplaceOperationOpEnum = { + Replace: 'replace' +} as const; + +export type ReplaceOperationOpEnum = typeof ReplaceOperationOpEnum[keyof typeof ReplaceOperationOpEnum]; + + diff --git a/ui/src/api/generated/models/rss-detail.ts b/ui/src/api/generated/models/rss-detail.ts new file mode 100644 index 0000000..7c82133 --- /dev/null +++ b/ui/src/api/generated/models/rss-detail.ts @@ -0,0 +1,33 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { Channel } from './channel'; + +/** + * + * @export + * @interface RssDetail + */ +export interface RssDetail { + /** + * + * @type {Channel} + * @memberof RssDetail + */ + 'channel'?: Channel; +} + diff --git a/ui/src/api/generated/models/rss-feed-sync-log-list.ts b/ui/src/api/generated/models/rss-feed-sync-log-list.ts new file mode 100644 index 0000000..93702a6 --- /dev/null +++ b/ui/src/api/generated/models/rss-feed-sync-log-list.ts @@ -0,0 +1,81 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { RssFeedSyncLog } from './rss-feed-sync-log'; + +/** + * + * @export + * @interface RssFeedSyncLogList + */ +export interface RssFeedSyncLogList { + /** + * Indicates whether current page is the first page. + * @type {boolean} + * @memberof RssFeedSyncLogList + */ + 'first': boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof RssFeedSyncLogList + */ + 'hasNext': boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof RssFeedSyncLogList + */ + 'hasPrevious': boolean; + /** + * A chunk of items. + * @type {Array} + * @memberof RssFeedSyncLogList + */ + 'items': Array; + /** + * Indicates whether current page is the last page. + * @type {boolean} + * @memberof RssFeedSyncLogList + */ + 'last': boolean; + /** + * Page number, starts from 1. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof RssFeedSyncLogList + */ + 'page': number; + /** + * Size of each page. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof RssFeedSyncLogList + */ + 'size': number; + /** + * Total elements. + * @type {number} + * @memberof RssFeedSyncLogList + */ + 'total': number; + /** + * Indicates total pages. + * @type {number} + * @memberof RssFeedSyncLogList + */ + 'totalPages': number; +} + diff --git a/ui/src/api/generated/models/rss-feed-sync-log.ts b/ui/src/api/generated/models/rss-feed-sync-log.ts new file mode 100644 index 0000000..3ee66a0 --- /dev/null +++ b/ui/src/api/generated/models/rss-feed-sync-log.ts @@ -0,0 +1,85 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { Metadata } from './metadata'; + +/** + * + * @export + * @interface RssFeedSyncLog + */ +export interface RssFeedSyncLog { + /** + * + * @type {string} + * @memberof RssFeedSyncLog + */ + 'apiVersion': string; + /** + * + * @type {string} + * @memberof RssFeedSyncLog + */ + 'failureMessage'?: string; + /** + * + * @type {string} + * @memberof RssFeedSyncLog + */ + 'failureReason'?: string; + /** + * + * @type {string} + * @memberof RssFeedSyncLog + */ + 'kind': string; + /** + * + * @type {string} + * @memberof RssFeedSyncLog + */ + 'linkName': string; + /** + * + * @type {Metadata} + * @memberof RssFeedSyncLog + */ + 'metadata': Metadata; + /** + * + * @type {string} + * @memberof RssFeedSyncLog + */ + 'state': RssFeedSyncLogStateEnum; + /** + * + * @type {string} + * @memberof RssFeedSyncLog + */ + 'syncTime': string; +} + +export const RssFeedSyncLogStateEnum = { + Success: 'success', + Failed: 'failed', + Onwait: 'onwait', + Nolink: 'nolink' +} as const; + +export type RssFeedSyncLogStateEnum = typeof RssFeedSyncLogStateEnum[keyof typeof RssFeedSyncLogStateEnum]; + + diff --git a/ui/src/api/generated/models/test-operation.ts b/ui/src/api/generated/models/test-operation.ts new file mode 100644 index 0000000..d7ba2ff --- /dev/null +++ b/ui/src/api/generated/models/test-operation.ts @@ -0,0 +1,49 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.20.11 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface TestOperation + */ +export interface TestOperation { + /** + * + * @type {string} + * @memberof TestOperation + */ + 'op': TestOperationOpEnum; + /** + * A JSON Pointer path pointing to the location to move/copy from. + * @type {string} + * @memberof TestOperation + */ + 'path': string; + /** + * Value can be any JSON value + * @type {any} + * @memberof TestOperation + */ + 'value': any; +} + +export const TestOperationOpEnum = { + Test: 'test' +} as const; + +export type TestOperationOpEnum = typeof TestOperationOpEnum[keyof typeof TestOperationOpEnum]; + + diff --git a/ui/src/api/index.ts b/ui/src/api/index.ts new file mode 100644 index 0000000..b8d1afd --- /dev/null +++ b/ui/src/api/index.ts @@ -0,0 +1,21 @@ +import { axiosInstance } from "@halo-dev/api-client"; +import { + ApiFriendMoonyLaV1alpha1FriendPostApi, + CronFriendPostV1alpha1Api, + FriendPostV1alpha1Api, + RssFeedSyncLogV1alpha1Api + + +} from "./generated"; + +const friendsCoreApiClient = { + cron: new CronFriendPostV1alpha1Api(undefined, "", axiosInstance), + friendPost: new FriendPostV1alpha1Api(undefined, "", axiosInstance), + rssFeedSyncLog: new RssFeedSyncLogV1alpha1Api(undefined, "", axiosInstance) + +}; + +const friendsApiClient = { + friendPost: new ApiFriendMoonyLaV1alpha1FriendPostApi(undefined,"",axiosInstance), +}; +export { friendsCoreApiClient, friendsApiClient }; diff --git a/ui/src/components/RssFeedSyncLogListItem.vue b/ui/src/components/RssFeedSyncLogListItem.vue new file mode 100644 index 0000000..49178f4 --- /dev/null +++ b/ui/src/components/RssFeedSyncLogListItem.vue @@ -0,0 +1,136 @@ + + diff --git a/ui/src/components/filter/LinkFilterDropdown.vue b/ui/src/components/filter/LinkFilterDropdown.vue new file mode 100644 index 0000000..f1e977f --- /dev/null +++ b/ui/src/components/filter/LinkFilterDropdown.vue @@ -0,0 +1,164 @@ + + + diff --git a/ui/src/components/formkit/LinkFormKit.vue b/ui/src/components/formkit/LinkFormKit.vue new file mode 100644 index 0000000..e54bf29 --- /dev/null +++ b/ui/src/components/formkit/LinkFormKit.vue @@ -0,0 +1,53 @@ + + diff --git a/ui/src/editor/BlockActionSeparator.vue b/ui/src/editor/BlockActionSeparator.vue new file mode 100644 index 0000000..70ffcc7 --- /dev/null +++ b/ui/src/editor/BlockActionSeparator.vue @@ -0,0 +1,3 @@ + diff --git a/ui/src/editor/RssView.vue b/ui/src/editor/RssView.vue new file mode 100644 index 0000000..d910cf8 --- /dev/null +++ b/ui/src/editor/RssView.vue @@ -0,0 +1,123 @@ + + + + diff --git a/ui/src/editor/index.ts b/ui/src/editor/index.ts new file mode 100644 index 0000000..d0e3a14 --- /dev/null +++ b/ui/src/editor/index.ts @@ -0,0 +1 @@ +export { default as FriendsRssExtension } from "./rss-edited"; diff --git a/ui/src/editor/rss-edited.ts b/ui/src/editor/rss-edited.ts new file mode 100644 index 0000000..0546593 --- /dev/null +++ b/ui/src/editor/rss-edited.ts @@ -0,0 +1,201 @@ +import { + type Editor, + isActive, + mergeAttributes, + Node, + nodeInputRule, + type Range, + VueNodeViewRenderer, + type EditorState, +} from "@halo-dev/richtext-editor"; +import RssView from "./RssView.vue"; +import { markRaw } from "vue"; +import { ToolboxItem } from "@halo-dev/richtext-editor"; +import MdiShare from "~icons/mdi/share"; +import BlockActionSeparator from "./BlockActionSeparator.vue"; +import MdiDeleteForeverOutline from "~icons/mdi/delete-forever-outline?color=red"; +import { deleteNode } from "../utils/delete-node"; +import LayoutGrid from "@/icon/layout-grid.vue"; +import LayoutList from "@/icon/layout-list.vue"; +import FluentRss20Regular from '~icons/fluent/rss-20-regular'; + +declare module "@halo-dev/richtext-editor" { + interface Commands { + "friends-rss": { + setFriendsRss: (options: { src: string }) => ReturnType; + }; + } +} + +const FriendsRssExtension = Node.create({ + name: "friends-rss", + fakeSelection: true, + + group() { + return "block"; + }, + + addAttributes() { + return { + ...this.parent?.(), + src: { + default: null, + parseHTML: (element) => { + return element.getAttribute("src"); + }, + renderHTML(element) { + return { src: element.src }; + }, + }, + layout: { + default: "list", + parseHTML: (element) => { + return element.getAttribute("layout"); + }, + renderHTML(element) { + return { layout: element.layout }; + }, + }, + }; + }, + + parseHTML() { + return [ + { + tag: "friends-rss", + }, + ]; + }, + + renderHTML({ HTMLAttributes }) { + return ["friends-rss", mergeAttributes(HTMLAttributes)]; + }, + addCommands() { + return { + setFriendsRss: + (options) => + ({ commands }) => { + return commands.insertContent([ + { + type: this.name, + attrs: options, + }, + { + type: "paragraph", + content: "" + }, + ]); + }, + }; + }, + + addInputRules() { + return [ + nodeInputRule({ + find: /^\$friends-rss\$$/, + type: this.type, + getAttributes: (e) => ({ + src: e[1] + }), + }), + ]; + }, + + addNodeView() { + return VueNodeViewRenderer(RssView); + }, + + addOptions() { + return { + getCommandMenuItems() { + return { + priority: 2e2, + icon: markRaw(FluentRss20Regular), + title: "RSS展示", + keywords: ["friends-rss", "rss", "RSS展示"], + command: ({ editor, range }: { editor: Editor; range: Range }) => { + editor + .chain() + .focus() + .deleteRange(range) + .setFriendsRss({ src: "" }) + .run(); + }, + }; + }, + getToolboxItems({ editor }: { editor: Editor }) { + return { + priority: 59, + component: markRaw(ToolboxItem), + props: { + editor, + icon: markRaw(FluentRss20Regular), + title: "RSS展示", + action: () => { + editor + .chain() + .focus() + .setFriendsRss({ src: "" }) + .run(); + }, + }, + }; + }, + getBubbleMenu({ editor }: { editor: Editor }) { + return { + pluginKey: "friends-rss-bubble-menu", + shouldShow: ({ state }: { state: EditorState }) => { + return isActive(state, FriendsRssExtension.name); + }, + items: [ + { + priority: 10, + props: { + icon: markRaw(LayoutList), + title: "列表视图", + isActive: () => editor.isActive(FriendsRssExtension.name,{ layout: "list" }), + action: () => editor.commands.updateAttributes(FriendsRssExtension.name, { layout: "list",}), + }, + }, + { + priority: 20, + props: { + icon: markRaw(LayoutGrid), + title: "网格试图", + isActive: () => editor.isActive(FriendsRssExtension.name,{ layout: "grid" }), + action: () => editor.commands.updateAttributes(FriendsRssExtension.name, { layout: "grid",}), + }, + }, + { + priority: 30, + props: { + icon: markRaw(MdiShare), + title: "打开链接", + action: () => { + window.open(editor.getAttributes(FriendsRssExtension.name).src, "_blank"); + }, + }, + }, + { + priority: 40, + component: markRaw(BlockActionSeparator), + }, + { + priority: 50, + props: { + icon: markRaw(MdiDeleteForeverOutline), + title: "删除", + action: ({ editor }: { editor: Editor }) => { + deleteNode(FriendsRssExtension.name, editor); + }, + }, + }, + ], + }; + }, + } + } + + +}) +export default FriendsRssExtension; diff --git a/ui/src/icon/layout-grid.vue b/ui/src/icon/layout-grid.vue new file mode 100644 index 0000000..0a65531 --- /dev/null +++ b/ui/src/icon/layout-grid.vue @@ -0,0 +1,3 @@ + diff --git a/ui/src/icon/layout-list.vue b/ui/src/icon/layout-list.vue new file mode 100644 index 0000000..2e0597b --- /dev/null +++ b/ui/src/icon/layout-list.vue @@ -0,0 +1,3 @@ + diff --git a/ui/src/index.ts b/ui/src/index.ts new file mode 100644 index 0000000..e8aea52 --- /dev/null +++ b/ui/src/index.ts @@ -0,0 +1,41 @@ +import "./styles/index.css"; +import { VLoading } from "@halo-dev/components"; +import "uno.css"; +import { definePlugin } from "@halo-dev/console-shared"; +import RiBloggerLine from '~icons/ri/blogger-line'; +import { defineAsyncComponent, markRaw } from "vue"; +import {FriendsRssExtension} from "@/editor"; +import '@kunkunyu/fridends-rss'; + +export default definePlugin({ + components: {}, + routes: [ + { + parentName: "Root", + route: { + path: "/friends", + name: "Friends", + component: defineAsyncComponent({ + loader: () => import("@/views/Friend.vue"), + loadingComponent: VLoading, + }), + meta: { + title: "朋友圈", + searchable: true, + permissions: ["plugin:friends:view"], + menu: { + name: "朋友圈", + icon: markRaw(RiBloggerLine), + group: "content", + priority: 20, + }, + }, + }, + }, + ], + extensionPoints: { + "default:editor:extension:create": () => { + return [FriendsRssExtension]; + }, + }, +}); diff --git a/ui/src/styles/index.css b/ui/src/styles/index.css new file mode 100644 index 0000000..00a5c59 --- /dev/null +++ b/ui/src/styles/index.css @@ -0,0 +1,184 @@ +.thyuu-card p { + font-size: 14px; + line-height: 1.5; + margin: 1em 0; +} +:is(.thyuu-card, .thyuu-card-menu, .thyuu-paginate) a { + color: inherit; + text-decoration: none; +} +.thyuu-card { + display: grid; + grid: auto / repeat(auto-fill, minmax(min(25em, 100%), 1fr)); + gap: 1em; + margin: 1em 0; + width: 100%; +} +.thyuu-card.column { + grid: auto / auto; +} +.thyuu-card li { + display: flex; + justify-content: space-between; + align-content: space-between; + align-items: center; + flex-wrap: wrap; + padding: 1em; + gap: 1em; + margin: 0; + background-color: rgb(243 244 246 / var(--tw-bg-opacity)); + border-radius: 1em; + overflow: hidden; +} +.thyuu-card.column li > * { + flex: 1; + min-width: min(20em, 100%); +} +.thyuu-card header { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + gap: 1em .5em; + width: 100%; +} +.thyuu-card header > *:last-child { + margin: 0 0 0 auto; + font-size: 12px; + opacity: .3; +} +.thyuu-card header span { + font-size: 14px; + color: #3c434a; +} +.thyuu-card header img { + border-radius: 50%; +} +.thyuu-card article { + display: flex; + flex-wrap: wrap; + gap: 1em .5em; +} +.thyuu-card article > * { + margin: 0; + display: -webkit-inline-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + text-overflow: ellipsis; + overflow: hidden; +} +.thyuu-card article h4 { + color: #3c434a; + font-size: 14px; + font-weight: 600; +} +.thyuu-card footer { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: .5em; + width: 100%; +} +.thyuu-card footer time { + flex: 1; + font-size: 12px; + opacity: .5; + margin: 0 auto 0 0; +} +.thyuu-card .button { + --btn-color: 200deg 60% 40%; + display: flex; + gap: .5em; + color: hsl(var(--btn-color)); + border: 0; + font-size: 12px; + height: 2.5em; + min-height: 2.5em; + line-height: 1.5em; + padding: .5em 1em; + margin: 0; + transition: .3s; +} +.thyuu-card .button:hover { + color: white; + background: hsl(var(--btn-color, 200deg 60% 40%)); +} +.thyuu-card .button.im { + --btn-color: 0deg 60% 40%; +} +.thyuu-card .button, .thyuu-card .button-secondary { + border-radius: 2em; + background: rgb(255 255 255 / var(--tw-bg-opacity)); + vertical-align: top; +} + +.contact-friends-rss-container { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000); + --tw-ring-opacity: 1; + --tw-ring-color: rgb(229 231 235 / var(--tw-ring-opacity)); + border-radius: 4px; + overflow: hidden; + margin-top: .75em +} + +.contact-friends-rss-container--selected { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000); + --tw-ring-color: inherit +} + +.contact-friends-rss-nav { + border-bottom: 1px #e7e7e7 solid; + display: flex; + padding: 5px 10px; + align-items: center +} + +.contact-friends-rss-nav-start { + flex: 1; + display: flex; + align-items: center; + gap: 10px; + font-size: 14px +} + +.contact-friends-rss-nav-end { + justify-content: flex-end +} + +.contact-friends-rss-preview { + padding: 5px 10px +} +.halo-rich-text-editor .editor-content .ProseMirror .block-rss { + list-style: none; + padding: 0; +} +ul.block-rss.block-rss { + box-sizing: border-box; +} +ul.block-rss.is-grid { + display: flex; + flex-wrap: wrap; + list-style: none; + padding: 0; +} + +ul.block-rss.is-grid li { + margin: 0 1em 1em 0; + width: 100% +} + +@media (min-width: 600px) { + ul.block-rss.columns-2 li { + width:calc(50% - 1em) + } +} + +.friends-rss-editor-block__actions-separator { + @apply h-5 bg-slate-200 mx-1.5; + width: 1px; +} diff --git a/ui/src/types/index.ts b/ui/src/types/index.ts new file mode 100644 index 0000000..0f55c82 --- /dev/null +++ b/ui/src/types/index.ts @@ -0,0 +1,23 @@ +import type {Link} from "@/api/generated"; + +export interface LinkList { + + 'first': boolean; + + 'hasNext': boolean; + + 'hasPrevious': boolean; + + 'items': Array; + + 'last': boolean; + + 'page': number; + + 'size': number; + + 'total': number; + + 'totalPages': number; +} + diff --git a/ui/src/utils/date.ts b/ui/src/utils/date.ts new file mode 100644 index 0000000..39def32 --- /dev/null +++ b/ui/src/utils/date.ts @@ -0,0 +1,63 @@ +import dayjs from "dayjs"; +import "dayjs/locale/zh-cn"; +import timezone from "dayjs/plugin/timezone"; +import TimeAgo from "javascript-time-ago"; +import zh from "javascript-time-ago/locale/zh"; + +dayjs.extend(timezone); + +dayjs.locale("zh-cn"); + +TimeAgo.addDefaultLocale(zh); + +export function formatDatetime(date: string | Date | undefined | null): string { + if (!date) { + return ""; + } + return dayjs(date).format("YYYY-MM-DD HH:mm"); +} + +export function timeAgo( + date: string | Date | number | undefined | null +): string { + if (!date) { + return ""; + } + + const currentDate = new Date(); + const inputDate = new Date(date); + + // 365天 * 24小时 * 60分钟 * 60秒 * 1000毫秒 + const oneYearInMilliseconds = 365 * 24 * 60 * 60 * 1000; + + if (currentDate.getTime() - inputDate.getTime() > oneYearInMilliseconds) { + return dayjs(date).format("YYYY-MM-DD"); + } + + const timeAgo = new TimeAgo("zh"); + + return timeAgo.format(new Date(date)); +} + + +export function calculateTimeDifferenceInSeconds( + startTimestamp: string | Date | number | undefined | null, + completionTimestamp: string | Date | number | undefined | null +): string { + if (!startTimestamp || !completionTimestamp) { + return "Invalid input"; + } + + // 将输入转换为 Date 对象 + const startDate = new Date(startTimestamp); + const completionDate = new Date(completionTimestamp); + + // 计算时间差(以毫秒为单位) + const differenceInMilliseconds = completionDate.getTime() - startDate.getTime(); + + // 将时间差转换为秒 + const differenceInSeconds = differenceInMilliseconds / 1000; + + // 返回时间差的整数部分 + return Math.floor(differenceInSeconds).toString(); +} diff --git a/ui/src/utils/delete-node.ts b/ui/src/utils/delete-node.ts new file mode 100644 index 0000000..93fb1c3 --- /dev/null +++ b/ui/src/utils/delete-node.ts @@ -0,0 +1,47 @@ +import type { Editor } from "@halo-dev/richtext-editor"; + +export const deleteNode = (nodeType: string, editor: Editor) => { + const { state } = editor; + const $pos = state.selection.$anchor; + let done = false; + + if ($pos.depth) { + for (let d = $pos.depth; d > 0; d--) { + const node = $pos.node(d); + if (node.type.name === nodeType) { + // @ts-ignore + if (editor.dispatchTransaction) + // @ts-ignore + editor.dispatchTransaction( + state.tr.delete($pos.before(d), $pos.after(d)).scrollIntoView() + ); + done = true; + } + } + } else { + // @ts-ignore + const node = state.selection.node; + if (node && node.type.name === nodeType) { + editor.chain().deleteSelection().run(); + done = true; + } + } + + if (!done) { + const pos = $pos.pos; + + if (pos) { + const node = state.tr.doc.nodeAt(pos); + + if (node && node.type.name === nodeType) { + // @ts-ignore + if (editor.dispatchTransaction) + // @ts-ignore + editor.dispatchTransaction(state.tr.delete(pos, pos + node.nodeSize)); + done = true; + } + } + } + + return done; +}; diff --git a/ui/src/utils/focus.ts b/ui/src/utils/focus.ts new file mode 100644 index 0000000..43995a7 --- /dev/null +++ b/ui/src/utils/focus.ts @@ -0,0 +1,14 @@ +export function setFocus(id: string) { + const inputElement = document.getElementById(id); + if ( + inputElement instanceof HTMLInputElement || + inputElement instanceof HTMLTextAreaElement + ) { + const timer = setTimeout(() => { + const end = inputElement.value.length; + inputElement.setSelectionRange(end, end); + inputElement?.focus(); + clearTimeout(timer); + }, 0); + } +} diff --git a/ui/src/views/CronTab.vue b/ui/src/views/CronTab.vue new file mode 100644 index 0000000..1a35cbd --- /dev/null +++ b/ui/src/views/CronTab.vue @@ -0,0 +1,161 @@ + + + + + diff --git a/ui/src/views/Friend.vue b/ui/src/views/Friend.vue new file mode 100644 index 0000000..87221c2 --- /dev/null +++ b/ui/src/views/Friend.vue @@ -0,0 +1,116 @@ + + + diff --git a/ui/src/views/FriendPostTab.vue b/ui/src/views/FriendPostTab.vue new file mode 100644 index 0000000..ce456e5 --- /dev/null +++ b/ui/src/views/FriendPostTab.vue @@ -0,0 +1,229 @@ + + + diff --git a/ui/src/views/RssFeedSyncLogTab.vue b/ui/src/views/RssFeedSyncLogTab.vue new file mode 100644 index 0000000..34f5af2 --- /dev/null +++ b/ui/src/views/RssFeedSyncLogTab.vue @@ -0,0 +1,134 @@ + + diff --git a/ui/tsconfig.json b/ui/tsconfig.json new file mode 100644 index 0000000..3db79ce --- /dev/null +++ b/ui/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["./env.d.ts", "./src/**/*", "./src/**/*.vue"], + "exclude": ["./src/**/__tests__/*"], + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + }, + "types": ["unplugin-icons/types/vue"] + } +} diff --git a/ui/uno.config.ts b/ui/uno.config.ts new file mode 100644 index 0000000..9d6cc36 --- /dev/null +++ b/ui/uno.config.ts @@ -0,0 +1,7 @@ + +import { defineConfig, presetWind3, transformerCompileClass } from "unocss"; + +export default defineConfig({ + presets: [presetWind3()], + transformers: [transformerCompileClass()], +});