archsight 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Dockerfile +5 -4
- data/README.md +44 -59
- data/chart/archsight/Chart.yaml +6 -0
- data/chart/archsight/README.md +3 -0
- data/chart/archsight/templates/NOTES.txt +22 -0
- data/chart/archsight/templates/_helpers.tpl +62 -0
- data/chart/archsight/templates/deployment.yaml +114 -0
- data/chart/archsight/templates/ingress.yaml +56 -0
- data/chart/archsight/templates/resources-configmap.yaml +10 -0
- data/chart/archsight/templates/resources-pvc.yaml +23 -0
- data/chart/archsight/templates/service.yaml +15 -0
- data/chart/archsight/templates/serviceaccount.yaml +12 -0
- data/chart/archsight/values.yaml +162 -0
- data/docs/architecture.md +39 -0
- data/docs/docker.md +49 -0
- data/{lib/archsight/web/doc → docs}/import.md +10 -2
- data/{lib/archsight/web/doc → docs}/index.md.erb +3 -1
- data/docs/kubernetes.md +149 -0
- data/docs/licenses.md +307 -0
- data/lib/archsight/analysis/executor.rb +0 -10
- data/lib/archsight/annotations/annotation.rb +85 -36
- data/lib/archsight/annotations/architecture_annotations.rb +1 -34
- data/lib/archsight/annotations/computed.rb +1 -1
- data/lib/archsight/annotations/generated_annotations.rb +6 -3
- data/lib/archsight/annotations/git_annotations.rb +8 -4
- data/lib/archsight/annotations/interface_annotations.rb +35 -0
- data/lib/archsight/cli.rb +4 -2
- data/lib/archsight/editor/content_hasher.rb +37 -0
- data/lib/archsight/editor/file_writer.rb +79 -0
- data/lib/archsight/editor.rb +237 -0
- data/lib/archsight/graph.rb +1 -51
- data/lib/archsight/helpers.rb +0 -20
- data/lib/archsight/import/handlers/github.rb +16 -6
- data/lib/archsight/import/handlers/gitlab.rb +28 -10
- data/lib/archsight/import/handlers/repository.rb +56 -6
- data/lib/archsight/import/handlers/rest_api.rb +13 -1
- data/lib/archsight/import/license_analyzer.rb +650 -0
- data/lib/archsight/import/team_matcher.rb +111 -61
- data/lib/archsight/linter.rb +1 -1
- data/lib/archsight/mcp/base.rb +11 -0
- data/lib/archsight/mcp/execute_analysis_tool.rb +100 -0
- data/lib/archsight/mcp.rb +1 -0
- data/lib/archsight/renderer.rb +4 -4
- data/lib/archsight/resources/analysis.rb +1 -17
- data/lib/archsight/resources/application_interface.rb +1 -5
- data/lib/archsight/resources/base.rb +14 -14
- data/lib/archsight/resources/business_actor.rb +18 -3
- data/lib/archsight/resources/technology_artifact.rb +48 -0
- data/lib/archsight/resources/technology_interface.rb +1 -1
- data/lib/archsight/resources/technology_service.rb +5 -0
- data/lib/archsight/version.rb +1 -1
- data/lib/archsight/web/api/docs.rb +37 -2
- data/lib/archsight/web/api/json_helpers.rb +79 -13
- data/lib/archsight/web/api/openapi/spec.yaml +699 -0
- data/lib/archsight/web/api/routes.rb +23 -0
- data/lib/archsight/web/application.rb +48 -128
- data/lib/archsight/web/editor/form_builder.rb +100 -0
- data/lib/archsight/web/editor/helpers.rb +150 -0
- data/lib/archsight/web/editor/routes.rb +166 -0
- data/lib/archsight/web/public/vue/ApiDocsPage-B1RqTNqh.js +1 -0
- data/lib/archsight/web/public/vue/ApiDocsPage-DhNTOH4o.css +1 -0
- data/lib/archsight/web/public/vue/DocPage-DzwBgBd4.js +1 -0
- data/lib/archsight/web/public/vue/EditorPage-D_miHSv4.js +34 -0
- data/lib/archsight/web/public/vue/EditorPage-Dq0MuTnp.css +1 -0
- data/lib/archsight/web/public/vue/ErrorPage-CQQtPey3.js +2 -0
- data/lib/archsight/web/public/vue/ErrorPage-CwPT3JUr.css +1 -0
- data/lib/archsight/web/public/vue/GraphView-DRcIqAiR.css +1 -0
- data/lib/archsight/web/public/vue/GraphView-T9jFH_qg.js +1 -0
- data/lib/archsight/web/public/vue/InstanceRouter-1Sm-CRhf.js +2 -0
- data/lib/archsight/web/public/vue/InstanceRouter-BJkDRXZY.css +1 -0
- data/lib/archsight/web/public/vue/KindList-JA_L_-Cz.js +1 -0
- data/lib/archsight/web/public/vue/ResourceList-8iqavWdg.js +1 -0
- data/lib/archsight/web/public/vue/ResourceList-DP-z-j71.css +1 -0
- data/lib/archsight/web/public/vue/SearchResults-BGHbg48-.css +1 -0
- data/lib/archsight/web/public/vue/SearchResults-BdgFeHcm.js +1 -0
- data/lib/archsight/web/public/vue/_basePickBy-CVgieyx-.js +1 -0
- data/lib/archsight/web/public/vue/_baseUniq-BNfrOSaP.js +1 -0
- data/lib/archsight/web/public/vue/architectureDiagram-VXUJARFQ-CJXNpTr5.js +36 -0
- data/lib/archsight/web/public/vue/blockDiagram-VD42YOAC-B5488Hes.js +122 -0
- data/lib/archsight/web/public/vue/c4Diagram-YG6GDRKO-eYY3hprM.js +10 -0
- data/lib/archsight/web/public/vue/chunk-4BX2VUAB-ZoXeL4D1.js +1 -0
- data/lib/archsight/web/public/vue/chunk-55IACEB6-rNtQYnu_.js +1 -0
- data/lib/archsight/web/public/vue/chunk-B4BG7PRW-DolAeVV9.js +165 -0
- data/lib/archsight/web/public/vue/chunk-DI55MBZ5-DnN0f_hj.js +220 -0
- data/lib/archsight/web/public/vue/chunk-FMBD7UC4-BQWOCMuR.js +15 -0
- data/lib/archsight/web/public/vue/chunk-QN33PNHL-DId301Kb.js +1 -0
- data/lib/archsight/web/public/vue/chunk-QZHKN3VN-xbY0NLgv.js +1 -0
- data/lib/archsight/web/public/vue/chunk-TZMSLE5B-CgF9_37b.js +1 -0
- data/lib/archsight/web/public/vue/classDiagram-2ON5EDUG-jGlvI-Za.js +1 -0
- data/lib/archsight/web/public/vue/classDiagram-v2-WZHVMYZB-jGlvI-Za.js +1 -0
- data/lib/archsight/web/public/vue/clone-6iRPe1-W.js +1 -0
- data/lib/archsight/web/public/vue/cose-bilkent-S5V4N54A-CB9Zfu50.js +1 -0
- data/lib/archsight/web/public/vue/cytoscape.esm-5J0xJHOV.js +321 -0
- data/lib/archsight/web/public/vue/dagre-6UL2VRFP-BqkmE-LI.js +4 -0
- data/lib/archsight/web/public/vue/diagram-PSM6KHXK-CKBfqtw3.js +24 -0
- data/lib/archsight/web/public/vue/diagram-QEK2KX5R-B78rOlvK.js +43 -0
- data/lib/archsight/web/public/vue/diagram-S2PKOQOG-BlXC6Cia.js +24 -0
- data/lib/archsight/web/public/vue/erDiagram-Q2GNP2WA-BnliyziJ.js +60 -0
- data/lib/archsight/web/public/vue/flowDiagram-NV44I4VS-wuqPowTd.js +162 -0
- data/lib/archsight/web/public/vue/ganttDiagram-JELNMOA3-GSffAIH3.js +267 -0
- data/lib/archsight/web/public/vue/gitGraphDiagram-V2S2FVAM-OA7VyugW.js +65 -0
- data/lib/archsight/web/public/vue/graph-BXHAtA0S.js +1 -0
- data/lib/archsight/web/public/vue/graphviz-CJms5bxZ.js +13 -0
- data/lib/archsight/web/public/vue/index-DsEsN0_K.js +2 -0
- data/lib/archsight/web/public/vue/index-Tiu4C-Sb.css +1 -0
- data/lib/archsight/web/public/vue/infoDiagram-HS3SLOUP-nlVe2qgv.js +2 -0
- data/lib/archsight/web/public/vue/journeyDiagram-XKPGCS4Q-CtTIcKwf.js +139 -0
- data/lib/archsight/web/public/vue/kanban-definition-3W4ZIXB7-837KX0sW.js +89 -0
- data/lib/archsight/web/public/vue/katex-C-M49wc6.js +261 -0
- data/lib/archsight/web/public/vue/layout-DtE0QdL6.js +1 -0
- data/lib/archsight/web/public/vue/mermaid-DpPHPFQh.js +250 -0
- data/lib/archsight/web/public/vue/mindmap-definition-VGOIOE7T-9gLF2AoY.js +68 -0
- data/lib/archsight/web/public/vue/pieDiagram-ADFJNKIX-CyCNgw3u.js +30 -0
- data/lib/archsight/web/public/vue/quadrantDiagram-AYHSOK5B-CkPh8g02.js +7 -0
- data/lib/archsight/web/public/vue/requirementDiagram-UZGBJVZJ-Dkt6OSlY.js +64 -0
- data/lib/archsight/web/public/vue/sankeyDiagram-TZEHDZUN-BqprTk8x.js +10 -0
- data/lib/archsight/web/public/vue/sequenceDiagram-WL72ISMW-CTmTe1FQ.js +145 -0
- data/lib/archsight/web/public/vue/stateDiagram-FKZM4ZOC-CphqmkEU.js +1 -0
- data/lib/archsight/web/public/vue/stateDiagram-v2-4FDKWEC3-CxaDW5sW.js +1 -0
- data/lib/archsight/web/public/vue/timeline-definition-IT6M3QCI-CSQUZkyE.js +61 -0
- data/lib/archsight/web/public/vue/treemap-GDKQZRPO-DTojm7Yr.js +162 -0
- data/lib/archsight/web/public/{css/graph.css → vue/useGraphviz-A5s4h76R.js} +2 -1
- data/lib/archsight/web/public/vue/useHighlight-C6Kb5G3l.js +10 -0
- data/lib/archsight/web/public/vue/useMermaid-DqxTrLRB.js +1 -0
- data/lib/archsight/web/public/vue/usePanZoom-BybZ_rfh.js +11 -0
- data/lib/archsight/web/public/vue/xychartDiagram-PRI3JC2R-B1ZJZtDC.js +7 -0
- data/lib/archsight/web/public/vue.html +15 -0
- data/media/artifact.jpg +0 -0
- data/media/service.jpg +0 -0
- metadata +104 -77
- data/lib/archsight/web/public/css/artifact.css +0 -995
- data/lib/archsight/web/public/css/base.css +0 -201
- data/lib/archsight/web/public/css/highlight.min.css +0 -10
- data/lib/archsight/web/public/css/iconoir.css +0 -22
- data/lib/archsight/web/public/css/instance.css +0 -818
- data/lib/archsight/web/public/css/layout.css +0 -421
- data/lib/archsight/web/public/css/mermaid-layers.css +0 -188
- data/lib/archsight/web/public/css/pico.min.css +0 -4
- data/lib/archsight/web/public/img/archimate.png +0 -0
- data/lib/archsight/web/public/img/togaf-high-level.png +0 -0
- data/lib/archsight/web/public/js/graph-zoom.js +0 -18
- data/lib/archsight/web/public/js/highlight.min.js +0 -3899
- data/lib/archsight/web/public/js/htmx.min.js +0 -1
- data/lib/archsight/web/public/js/mermaid-init.js +0 -88
- data/lib/archsight/web/public/js/mermaid.min.js +0 -2811
- data/lib/archsight/web/public/js/sparkline.js +0 -42
- data/lib/archsight/web/public/js/svg-pan-zoom.min.js +0 -3
- data/lib/archsight/web/public/js/svg-zoom-controls.js +0 -93
- data/lib/archsight/web/views/api_docs.erb +0 -19
- data/lib/archsight/web/views/index.haml +0 -12
- data/lib/archsight/web/views/partials/artifact/_activity.haml +0 -55
- data/lib/archsight/web/views/partials/artifact/_agentic.haml +0 -25
- data/lib/archsight/web/views/partials/artifact/_deployment.haml +0 -29
- data/lib/archsight/web/views/partials/artifact/_git_info.haml +0 -16
- data/lib/archsight/web/views/partials/artifact/_language_stats.haml +0 -53
- data/lib/archsight/web/views/partials/artifact/_links.haml +0 -24
- data/lib/archsight/web/views/partials/artifact/_project_estimate.haml +0 -32
- data/lib/archsight/web/views/partials/artifact/_repositories.haml +0 -55
- data/lib/archsight/web/views/partials/artifact/_team.haml +0 -83
- data/lib/archsight/web/views/partials/artifact/_workflow.haml +0 -69
- data/lib/archsight/web/views/partials/components/_activity.haml +0 -37
- data/lib/archsight/web/views/partials/components/_git.haml +0 -17
- data/lib/archsight/web/views/partials/components/_jira.haml +0 -18
- data/lib/archsight/web/views/partials/components/_languages.haml +0 -29
- data/lib/archsight/web/views/partials/components/_owner.haml +0 -15
- data/lib/archsight/web/views/partials/components/_repositories.haml +0 -37
- data/lib/archsight/web/views/partials/components/_status.haml +0 -23
- data/lib/archsight/web/views/partials/instance/_analysis_detail.haml +0 -74
- data/lib/archsight/web/views/partials/instance/_analysis_result.haml +0 -64
- data/lib/archsight/web/views/partials/instance/_detail.haml +0 -103
- data/lib/archsight/web/views/partials/instance/_graph.haml +0 -6
- data/lib/archsight/web/views/partials/instance/_import_detail.haml +0 -87
- data/lib/archsight/web/views/partials/instance/_list.haml +0 -84
- data/lib/archsight/web/views/partials/instance/_relations.haml +0 -43
- data/lib/archsight/web/views/partials/instance/_requirements.haml +0 -41
- data/lib/archsight/web/views/partials/instance/_view_detail.haml +0 -57
- data/lib/archsight/web/views/partials/layout/_content.haml +0 -44
- data/lib/archsight/web/views/partials/layout/_error.haml +0 -22
- data/lib/archsight/web/views/partials/layout/_head.haml +0 -24
- data/lib/archsight/web/views/partials/layout/_navigation.haml +0 -21
- data/lib/archsight/web/views/partials/layout/_sidebar.haml +0 -27
- data/lib/archsight/web/views/search.haml +0 -53
- /data/{lib/archsight/web/doc → docs}/archimate.md +0 -0
- /data/{lib/archsight/web/doc → docs}/computed_annotations.md +0 -0
- /data/{lib/archsight/web/doc → docs}/icons.md +0 -0
- /data/{lib/archsight/web/doc → docs}/modeling.md +0 -0
- /data/{lib/archsight/web/doc → docs}/search.md +0 -0
- /data/{lib/archsight/web/doc → docs}/togaf.md +0 -0
- /data/{lib/archsight/web/doc → docs}/tool.md +0 -0
|
@@ -11,7 +11,42 @@ module Archsight::Web::API; end
|
|
|
11
11
|
module Archsight::Web::API::Docs
|
|
12
12
|
extend Sinatra::Extension
|
|
13
13
|
|
|
14
|
-
get "/api
|
|
15
|
-
|
|
14
|
+
get "/docs/api" do
|
|
15
|
+
serve_vue
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# GET /api/v1/docs/resources/:filename — auto-generated resource docs
|
|
19
|
+
get "/api/v1/docs/resources/:filename" do
|
|
20
|
+
filename = params["filename"].gsub(/[^a-zA-Z0-9_-]/, "")
|
|
21
|
+
kind_name = filename.split("_").map(&:capitalize).join
|
|
22
|
+
|
|
23
|
+
begin
|
|
24
|
+
content = Archsight::Documentation.generate(kind_name)
|
|
25
|
+
content_type :html
|
|
26
|
+
"<article>#{markdown(content)}</article>"
|
|
27
|
+
rescue StandardError
|
|
28
|
+
halt 404, "Documentation not found"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# GET /api/v1/docs/:filename — static markdown/ERB docs
|
|
33
|
+
get "/api/v1/docs/:filename" do
|
|
34
|
+
filename = params["filename"].gsub(/[^a-zA-Z0-9_-]/, "")
|
|
35
|
+
|
|
36
|
+
doc_dir = File.expand_path("../../../../docs", __dir__)
|
|
37
|
+
erb_path = File.join(doc_dir, "#{filename}.md.erb")
|
|
38
|
+
md_path = File.join(doc_dir, "#{filename}.md")
|
|
39
|
+
|
|
40
|
+
content = if File.exist?(erb_path)
|
|
41
|
+
template = ERB.new(File.read(erb_path))
|
|
42
|
+
template.result(binding)
|
|
43
|
+
elsif File.exist?(md_path)
|
|
44
|
+
File.read(md_path)
|
|
45
|
+
else
|
|
46
|
+
halt 404, "Documentation not found"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
content_type :html
|
|
50
|
+
"<article>#{markdown(content)}</article>"
|
|
16
51
|
end
|
|
17
52
|
end
|
|
@@ -22,12 +22,19 @@ module Archsight::Web::API::JsonHelpers
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def resource_summary(resource, output:, omit_kind: false)
|
|
25
|
-
case output
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
result = case output
|
|
26
|
+
when "brief"
|
|
27
|
+
Archsight::MCP.brief_summary(resource, omit_kind: omit_kind)
|
|
28
|
+
when "annotations"
|
|
29
|
+
Archsight::MCP.annotations_summary(resource, omit_kind: omit_kind)
|
|
30
|
+
else # "complete"
|
|
31
|
+
summary = Archsight::MCP.complete_summary(resource, omit_kind: omit_kind)
|
|
32
|
+
summary[:spec] = serialize_spec(summary[:spec]) if summary[:spec]
|
|
33
|
+
summary
|
|
34
|
+
end
|
|
35
|
+
result[:icon] = resource.class.icon
|
|
36
|
+
result[:layer] = resource.class.layer
|
|
37
|
+
result
|
|
31
38
|
end
|
|
32
39
|
|
|
33
40
|
def paginate(collection, limit:, offset:)
|
|
@@ -76,17 +83,29 @@ module Archsight::Web::API::JsonHelpers
|
|
|
76
83
|
}
|
|
77
84
|
end
|
|
78
85
|
|
|
86
|
+
MARKDOWN_ANNOTATION_KEYS = Set["architecture/description"].freeze
|
|
87
|
+
|
|
79
88
|
def build_instance_response(kind, instance)
|
|
80
89
|
{
|
|
81
90
|
kind: kind,
|
|
82
91
|
name: instance.name,
|
|
83
|
-
metadata: { annotations: instance.annotations },
|
|
92
|
+
metadata: { annotations: render_annotations(instance.annotations) },
|
|
84
93
|
spec: serialize_spec(instance.spec),
|
|
85
94
|
relations: extract_relations(instance),
|
|
86
95
|
references: extract_references(instance)
|
|
87
96
|
}
|
|
88
97
|
end
|
|
89
98
|
|
|
99
|
+
def render_annotations(annotations)
|
|
100
|
+
annotations.each_with_object({}) do |(key, value), result|
|
|
101
|
+
result[key] = if MARKDOWN_ANNOTATION_KEYS.include?(key) && value.is_a?(String)
|
|
102
|
+
markdown(value)
|
|
103
|
+
else
|
|
104
|
+
value
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
90
109
|
def serialize_spec(spec)
|
|
91
110
|
spec.transform_values do |kinds|
|
|
92
111
|
next kinds unless kinds.is_a?(Hash)
|
|
@@ -110,15 +129,14 @@ module Archsight::Web::API::JsonHelpers
|
|
|
110
129
|
}
|
|
111
130
|
end
|
|
112
131
|
|
|
113
|
-
def build_search_response(query, results,
|
|
132
|
+
def build_search_response(query, results, _parsed_query, query_time_ms)
|
|
114
133
|
limit, offset = parse_pagination_params
|
|
115
134
|
output = parse_output_param
|
|
116
|
-
omit_kind = !parsed_query.kind_filter.nil?
|
|
117
135
|
sorted = results.sort_by(&:name)
|
|
118
136
|
pagination = paginate(sorted, limit: limit, offset: offset)
|
|
119
137
|
|
|
120
138
|
instances = pagination[:items].map do |r|
|
|
121
|
-
resource_summary(r, output: output, omit_kind:
|
|
139
|
+
resource_summary(r, output: output, omit_kind: false)
|
|
122
140
|
end
|
|
123
141
|
|
|
124
142
|
{
|
|
@@ -135,17 +153,65 @@ module Archsight::Web::API::JsonHelpers
|
|
|
135
153
|
def extract_relations(instance)
|
|
136
154
|
relations = {}
|
|
137
155
|
|
|
138
|
-
instance.class.relations.each do |verb,
|
|
139
|
-
rels = instance.relations(verb,
|
|
156
|
+
instance.class.relations.each do |verb, spec_key, kind|
|
|
157
|
+
rels = instance.relations(verb, spec_key).map(&:name)
|
|
140
158
|
next if rels.empty?
|
|
141
159
|
|
|
142
160
|
relations[verb] ||= {}
|
|
143
|
-
relations[verb][
|
|
161
|
+
relations[verb][kind] = rels
|
|
144
162
|
end
|
|
145
163
|
|
|
146
164
|
relations
|
|
147
165
|
end
|
|
148
166
|
|
|
167
|
+
def build_filters_response(kind)
|
|
168
|
+
db.filters_for_kind(kind).map do |annotation, values|
|
|
169
|
+
{
|
|
170
|
+
key: annotation.key,
|
|
171
|
+
title: annotation.title,
|
|
172
|
+
description: annotation.description,
|
|
173
|
+
filter_type: annotation.filter.to_s,
|
|
174
|
+
values: values
|
|
175
|
+
}
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def build_analysis_result(result)
|
|
180
|
+
data = {
|
|
181
|
+
name: result.name,
|
|
182
|
+
success: result.success?,
|
|
183
|
+
has_findings: result.has_findings?,
|
|
184
|
+
duration: result.duration,
|
|
185
|
+
sections: result.sections.map { |s| serialize_analysis_section(s) }
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if result.failed?
|
|
189
|
+
data[:error] = result.error
|
|
190
|
+
data[:error_backtrace] = result.error_backtrace if result.error_backtrace&.any?
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
data
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def serialize_analysis_section(section)
|
|
197
|
+
case section[:type]
|
|
198
|
+
when :heading
|
|
199
|
+
{ type: "heading", level: section[:level], text: section[:text] }
|
|
200
|
+
when :text
|
|
201
|
+
{ type: "text", content: section[:content] }
|
|
202
|
+
when :message
|
|
203
|
+
{ type: "message", level: section[:level].to_s, message: section[:message] }
|
|
204
|
+
when :table
|
|
205
|
+
{ type: "table", headers: section[:headers], rows: section[:rows] }
|
|
206
|
+
when :list
|
|
207
|
+
{ type: "list", items: section[:items] }
|
|
208
|
+
when :code
|
|
209
|
+
{ type: "code", lang: section[:lang], content: section[:content] }
|
|
210
|
+
else
|
|
211
|
+
{ type: section[:type].to_s }
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
149
215
|
def extract_references(instance)
|
|
150
216
|
references = {}
|
|
151
217
|
|