solara 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (175) hide show
  1. checksums.yaml +7 -0
  2. data/bin/solara +18 -0
  3. data/solara/lib/.DS_Store +0 -0
  4. data/solara/lib/core/.DS_Store +0 -0
  5. data/solara/lib/core/aliases/alias_generator.rb +128 -0
  6. data/solara/lib/core/aliases/alias_generator_manager.rb +28 -0
  7. data/solara/lib/core/aliases/solara_terminal_setup.rb +103 -0
  8. data/solara/lib/core/brands/brand_onboarder.rb +46 -0
  9. data/solara/lib/core/brands/brand_switcher.rb +204 -0
  10. data/solara/lib/core/brands/brands_manager.rb +154 -0
  11. data/solara/lib/core/dashboard/.DS_Store +0 -0
  12. data/solara/lib/core/dashboard/brand/.DS_Store +0 -0
  13. data/solara/lib/core/dashboard/brand/BrandDetail.js +11 -0
  14. data/solara/lib/core/dashboard/brand/BrandDetailController.js +361 -0
  15. data/solara/lib/core/dashboard/brand/BrandDetailModel.js +155 -0
  16. data/solara/lib/core/dashboard/brand/BrandDetailView.js +245 -0
  17. data/solara/lib/core/dashboard/brand/brand.html +477 -0
  18. data/solara/lib/core/dashboard/brand/source/BrandLocalSource.js +123 -0
  19. data/solara/lib/core/dashboard/brand/source/BrandRemoteSource.js +260 -0
  20. data/solara/lib/core/dashboard/brands/Brands.js +10 -0
  21. data/solara/lib/core/dashboard/brands/BrandsController.js +155 -0
  22. data/solara/lib/core/dashboard/brands/BrandsModel.js +136 -0
  23. data/solara/lib/core/dashboard/brands/BrandsView.js +136 -0
  24. data/solara/lib/core/dashboard/brands/brands.html +345 -0
  25. data/solara/lib/core/dashboard/component/AddFieldSheet.js +212 -0
  26. data/solara/lib/core/dashboard/component/AliasesBottomSheet.js +128 -0
  27. data/solara/lib/core/dashboard/component/BrandOptionsBottomSheet.js +130 -0
  28. data/solara/lib/core/dashboard/component/ConfirmationDialog.js +103 -0
  29. data/solara/lib/core/dashboard/component/MessageBottomSheet.js +80 -0
  30. data/solara/lib/core/dashboard/component/OnboardBrandBottomSheet.js +214 -0
  31. data/solara/lib/core/dashboard/dashboard_manager.rb +19 -0
  32. data/solara/lib/core/dashboard/dashboard_server.rb +132 -0
  33. data/solara/lib/core/dashboard/handler/base_handler.rb +25 -0
  34. data/solara/lib/core/dashboard/handler/brand_alisases_handler.rb +33 -0
  35. data/solara/lib/core/dashboard/handler/brand_configurations_handler.rb +18 -0
  36. data/solara/lib/core/dashboard/handler/brand_configurations_manager.rb +73 -0
  37. data/solara/lib/core/dashboard/handler/brand_icon_handler.rb +20 -0
  38. data/solara/lib/core/dashboard/handler/brand_section_handler.rb +20 -0
  39. data/solara/lib/core/dashboard/handler/brands_handler.rb +14 -0
  40. data/solara/lib/core/dashboard/handler/current_brand_handler.rb +18 -0
  41. data/solara/lib/core/dashboard/handler/doctor_handler.rb +39 -0
  42. data/solara/lib/core/dashboard/handler/edit_section_handler.rb +55 -0
  43. data/solara/lib/core/dashboard/handler/offboard_brand_handler.rb +34 -0
  44. data/solara/lib/core/dashboard/handler/onboard_brand_handler.rb +53 -0
  45. data/solara/lib/core/dashboard/handler/redirect_handler.rb +12 -0
  46. data/solara/lib/core/dashboard/handler/switch_handler.rb +25 -0
  47. data/solara/lib/core/dashboard/index.html +36 -0
  48. data/solara/lib/core/dashboard/local.html +41 -0
  49. data/solara/lib/core/dashboard/res/favicon/android-chrome-192x192.png +0 -0
  50. data/solara/lib/core/dashboard/res/favicon/android-chrome-512x512.png +0 -0
  51. data/solara/lib/core/dashboard/res/favicon/apple-touch-icon.png +0 -0
  52. data/solara/lib/core/dashboard/res/favicon/favicon-16x16.png +0 -0
  53. data/solara/lib/core/dashboard/res/favicon/favicon-32x32.png +0 -0
  54. data/solara/lib/core/dashboard/res/favicon/favicon.ico +0 -0
  55. data/solara/lib/core/dashboard/res/favicon/site.webmanifest +1 -0
  56. data/solara/lib/core/dashboard/solara.png +0 -0
  57. data/solara/lib/core/doctor/brand_doctor.rb +94 -0
  58. data/solara/lib/core/doctor/doctor_manager.rb +35 -0
  59. data/solara/lib/core/doctor/project_doctor.rb +8 -0
  60. data/solara/lib/core/doctor/schema/brand_configurations.json +60 -0
  61. data/solara/lib/core/doctor/schema/platform/android/android_config.json +23 -0
  62. data/solara/lib/core/doctor/schema/platform/android/android_signing.json +23 -0
  63. data/solara/lib/core/doctor/schema/platform/ios/ios_config.json +27 -0
  64. data/solara/lib/core/doctor/schema/platform/ios/ios_signing.json +27 -0
  65. data/solara/lib/core/doctor/schema/platform/shared/theme.json +48 -0
  66. data/solara/lib/core/doctor/validator/brand_settings_validator.rb +55 -0
  67. data/solara/lib/core/doctor/validator/brand_settings_validator_manager.rb +82 -0
  68. data/solara/lib/core/doctor/validator/directory_structure_validator.rb +38 -0
  69. data/solara/lib/core/doctor/validator/file_structure_validator.rb +37 -0
  70. data/solara/lib/core/doctor/validator/json_file_validator.rb +21 -0
  71. data/solara/lib/core/doctor/validator/json_schema_validator.rb +32 -0
  72. data/solara/lib/core/doctor/validator/project_filesystem_validator.rb +70 -0
  73. data/solara/lib/core/doctor/validator/template/android_template_validation_config.yml +51 -0
  74. data/solara/lib/core/doctor/validator/template/flutter_template_validation_config.yml +53 -0
  75. data/solara/lib/core/doctor/validator/template/ios_template_validation_config.yml +51 -0
  76. data/solara/lib/core/doctor/validator/template/template_validator.rb +108 -0
  77. data/solara/lib/core/doctor/validator/validation_strategy.rb +7 -0
  78. data/solara/lib/core/scripts/brand_config_generator.rb +245 -0
  79. data/solara/lib/core/scripts/brand_config_manager.rb +90 -0
  80. data/solara/lib/core/scripts/brand_exporter.rb +38 -0
  81. data/solara/lib/core/scripts/brand_importer.rb +84 -0
  82. data/solara/lib/core/scripts/brand_offboarder.rb +19 -0
  83. data/solara/lib/core/scripts/brand_resources_manager.rb +77 -0
  84. data/solara/lib/core/scripts/directory_creator.rb +22 -0
  85. data/solara/lib/core/scripts/file_manager.rb +90 -0
  86. data/solara/lib/core/scripts/file_path.rb +327 -0
  87. data/solara/lib/core/scripts/folder_copier.rb +41 -0
  88. data/solara/lib/core/scripts/gitignore_manager.rb +54 -0
  89. data/solara/lib/core/scripts/interactive_file_system_validator.rb +110 -0
  90. data/solara/lib/core/scripts/platform/android/android_manifest_switcher.rb +24 -0
  91. data/solara/lib/core/scripts/platform/android/android_strings_switcher.rb +39 -0
  92. data/solara/lib/core/scripts/platform/android/gradle_switcher.rb +233 -0
  93. data/solara/lib/core/scripts/platform/android/properties_generator.rb +31 -0
  94. data/solara/lib/core/scripts/platform/ios/ios_file_path_manager.rb +109 -0
  95. data/solara/lib/core/scripts/platform/ios/ios_plist_manager.rb +42 -0
  96. data/solara/lib/core/scripts/platform/ios/xcconfig_generator.rb +44 -0
  97. data/solara/lib/core/scripts/platform/ios/xcode_asset_manager.rb +56 -0
  98. data/solara/lib/core/scripts/platform/ios/xcode_project_manager.rb +82 -0
  99. data/solara/lib/core/scripts/platform/ios/xcode_project_switcher.rb +130 -0
  100. data/solara/lib/core/scripts/project_settings_manager.rb +39 -0
  101. data/solara/lib/core/scripts/solara_logger.rb +103 -0
  102. data/solara/lib/core/scripts/solara_settings_manager.rb +73 -0
  103. data/solara/lib/core/scripts/solara_status_manager.rb +55 -0
  104. data/solara/lib/core/scripts/solara_version_manager.rb +42 -0
  105. data/solara/lib/core/scripts/strings_xml_manager.rb +22 -0
  106. data/solara/lib/core/scripts/terminal_input_manager.rb +22 -0
  107. data/solara/lib/core/scripts/theme_generator.rb +250 -0
  108. data/solara/lib/core/scripts/yaml_manager.rb +72 -0
  109. data/solara/lib/core/solara_configurator.rb +15 -0
  110. data/solara/lib/core/template/brands/android/android_config.json +8 -0
  111. data/solara/lib/core/template/brands/android/android_signing.json +6 -0
  112. data/solara/lib/core/template/brands/android/res/.DS_Store +0 -0
  113. data/solara/lib/core/template/brands/android/res/mipmap-hdpi/ic_launcher.png +0 -0
  114. data/solara/lib/core/template/brands/android/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  115. data/solara/lib/core/template/brands/android/res/mipmap-mdpi/ic_launcher.png +0 -0
  116. data/solara/lib/core/template/brands/android/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  117. data/solara/lib/core/template/brands/android/res/mipmap-xhdpi/ic_launcher.png +0 -0
  118. data/solara/lib/core/template/brands/android/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  119. data/solara/lib/core/template/brands/android/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  120. data/solara/lib/core/template/brands/android/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  121. data/solara/lib/core/template/brands/android/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  122. data/solara/lib/core/template/brands/android/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  123. data/solara/lib/core/template/brands/brands.json +4 -0
  124. data/solara/lib/core/template/brands/ios/assets/.DS_Store +0 -0
  125. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/100.png +0 -0
  126. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/102.png +0 -0
  127. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/1024.png +0 -0
  128. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/114.png +0 -0
  129. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/120.png +0 -0
  130. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/128.png +0 -0
  131. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/144.png +0 -0
  132. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/152.png +0 -0
  133. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/16.png +0 -0
  134. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/167.png +0 -0
  135. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/172.png +0 -0
  136. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/180.png +0 -0
  137. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/196.png +0 -0
  138. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/20.png +0 -0
  139. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/216.png +0 -0
  140. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/256.png +0 -0
  141. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/29.png +0 -0
  142. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/32.png +0 -0
  143. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/40.png +0 -0
  144. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/48.png +0 -0
  145. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/50.png +0 -0
  146. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/512.png +0 -0
  147. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/55.png +0 -0
  148. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/57.png +0 -0
  149. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/58.png +0 -0
  150. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/60.png +0 -0
  151. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/64.png +0 -0
  152. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/66.png +0 -0
  153. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/72.png +0 -0
  154. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/76.png +0 -0
  155. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/80.png +0 -0
  156. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/87.png +0 -0
  157. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/88.png +0 -0
  158. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/92.png +0 -0
  159. data/solara/lib/core/template/brands/ios/assets/AppIcon.appiconset/Contents.json +1 -0
  160. data/solara/lib/core/template/brands/ios/ios_config.json +7 -0
  161. data/solara/lib/core/template/brands/ios/ios_signing.json +7 -0
  162. data/solara/lib/core/template/brands/shared/.DS_Store +0 -0
  163. data/solara/lib/core/template/brands/shared/brand_config.json +2 -0
  164. data/solara/lib/core/template/brands/shared/theme.json +46 -0
  165. data/solara/lib/core/template/config/android_template_config.json +57 -0
  166. data/solara/lib/core/template/config/flutter_template_config.json +62 -0
  167. data/solara/lib/core/template/config/ios_template_config.json +57 -0
  168. data/solara/lib/core/template/project_template_generator.rb +63 -0
  169. data/solara/lib/platform_detector.rb +84 -0
  170. data/solara/lib/solara/cli.rb +5 -0
  171. data/solara/lib/solara/version.rb +3 -0
  172. data/solara/lib/solara.rb +238 -0
  173. data/solara/lib/solara_initializer.rb +44 -0
  174. data/solara/lib/solara_manager.rb +73 -0
  175. metadata +346 -0
@@ -0,0 +1,25 @@
1
+ class BaseHandler
2
+ def initialize(server, dashboard_server)
3
+ @server = server
4
+ @dashboard_server = dashboard_server
5
+ end
6
+
7
+ def mount
8
+ raise NotImplementedError, "Subclasses must implement the 'mount' method"
9
+ end
10
+
11
+ protected
12
+
13
+ def handle_error(res, error, message, status = 500)
14
+ Solara.logger.failure("#{message}: #{error.message}")
15
+ res.status = status
16
+ res.body = JSON.generate({ success: false, error: "#{message}: #{error.message}" })
17
+ res.content_type = 'application/json'
18
+ end
19
+
20
+ def method_not_allowed(res)
21
+ res.status = 405
22
+ res.body = JSON.generate({ success: false, error: 'Method Not Allowed' })
23
+ res.content_type = 'application/json'
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ class BrandAliasesHandler < BaseHandler
2
+ def mount
3
+ @server.mount_proc('/brand/aliases') do |req, res|
4
+ if req.request_method == 'GET'
5
+ begin
6
+ query = CGI.parse(req.query_string)
7
+ brand_key = query['brand_key']&.first
8
+
9
+ if brand_key
10
+ aliases = get_brand_aliases(brand_key)
11
+ res.status = 200
12
+ res.body = JSON.generate({ success: true, aliases: aliases })
13
+ else
14
+ res.status = 400
15
+ res.body = JSON.generate({ success: false, error: 'Missing brand_key parameter' })
16
+ end
17
+ rescue StandardError => e
18
+ handle_error(res, e, "Error fetching brand aliases")
19
+ end
20
+ else
21
+ method_not_allowed(res)
22
+ end
23
+ res.content_type = 'application/json'
24
+ end
25
+ end
26
+
27
+ def get_brand_aliases(brand_key)
28
+ AliasGeneratorManager.aliases_json
29
+ rescue StandardError => e
30
+ Solara.logger.failure("Error getting brand aliases: #{e.message}")
31
+ raise
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ class BrandConfigurationsHandler < BaseHandler
2
+ def mount
3
+ @server.mount_proc('/configurations') do |req, res|
4
+ begin
5
+ query = CGI.parse(req.query_string)
6
+ brand_key = query['brand_key']&.first
7
+
8
+ response_data = BrandsManager.instance.brand_with_configurations(brand_key)
9
+ res.body = JSON.generate({ success: true, message: "Configurations response", result: response_data })
10
+ res['Content-Type'] = 'application/json'
11
+ rescue StandardError => e
12
+ handle_error(res, e, "Error in brand configurations handler")
13
+ end
14
+ end
15
+ end
16
+
17
+ end
18
+
@@ -0,0 +1,73 @@
1
+ class BrandConfigurationsManager
2
+
3
+ def initialize(brand_key)
4
+ @brand_key = brand_key
5
+ end
6
+
7
+ def template_with_key(key)
8
+ templates.select { |section| section[:key] === key }.first
9
+ end
10
+
11
+ def templates
12
+ [
13
+ {
14
+ key: 'theme',
15
+ name: 'Theme Configuration',
16
+ input_type: 'color',
17
+ path: FilePath.brand_theme(@brand_key)
18
+ },
19
+ {
20
+ key: 'brand_config',
21
+ name: 'Brand Configuration',
22
+ input_type: 'text',
23
+ path: FilePath.brand_config(@brand_key)
24
+ },
25
+ {
26
+ key: 'android_config',
27
+ name: 'Android Platform Configuration',
28
+ input_type: 'text',
29
+ path: FilePath.android_brand_config(@brand_key)
30
+ },
31
+ {
32
+ key: 'android_signing',
33
+ name: 'Android Signing',
34
+ input_type: 'text',
35
+ path: FilePath.brand_signing(@brand_key, Platform::Android)
36
+ },
37
+ {
38
+ key: 'ios_config',
39
+ name: 'iOS Platform Configuration',
40
+ input_type: 'text',
41
+ path: FilePath.ios_config(@brand_key)
42
+ },
43
+ {
44
+ key: 'ios_signing',
45
+ name: 'iOS Signing',
46
+ input_type: 'text',
47
+ path: FilePath.brand_signing(@brand_key, Platform::IOS)
48
+ }
49
+ ]
50
+ end
51
+
52
+ def create
53
+ config_templates = templates
54
+
55
+ config_templates.map do |template|
56
+ create_config_item(
57
+ template[:key],
58
+ template[:name],
59
+ template[:input_type],
60
+ template[:path].tap { |p| File.expand_path(p) }
61
+ )
62
+ end
63
+ end
64
+
65
+ def create_config_item(key, name, input_type, path)
66
+ {
67
+ key: key,
68
+ name: name,
69
+ inputType: input_type,
70
+ content: JSON.parse(File.read(path)),
71
+ }
72
+ end
73
+ end
@@ -0,0 +1,20 @@
1
+ class BrandIconHandler < BaseHandler
2
+ def mount
3
+ @server.mount_proc("/brand/icon") do |req, res|
4
+ begin
5
+ brand_key = req.query['brand_key']
6
+ filepath = FilePath.launcher_icon(brand_key)
7
+ if File.exist?(filepath)
8
+ res.body = File.read(filepath)
9
+ res['Content-Type'] = 'image/png' # Adjust as necessary
10
+ else
11
+ res.status = 404
12
+ res.body = JSON.generate({ error: "Icon not found for brand: #{brand_key}" })
13
+ res['Content-Type'] = 'application/json'
14
+ end
15
+ rescue StandardError => e
16
+ handle_error(res, e, "Error fetching brand icon")
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ class BrandSectionHandler < BaseHandler
2
+ def mount
3
+ @server.mount_proc("/section") do |req, res|
4
+ begin
5
+ query = CGI.parse(req.query_string)
6
+ path = query['path']&.first
7
+ if File.exist?(path)
8
+ res.body = File.read(path)
9
+ res['Content-Type'] = 'application/json'
10
+ else
11
+ res.status = 404
12
+ res.body = JSON.generate({ error: "Config file not found: #{path}" })
13
+ res['Content-Type'] = 'application/json'
14
+ end
15
+ rescue StandardError => e
16
+ handle_error(res, e, "Error in brand section handler")
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ class BrandsHandler < BaseHandler
2
+ def mount
3
+ @server.mount_proc('/brands.json') do |req, res|
4
+ begin
5
+ brands_list = BrandsManager.instance.brands_list
6
+ json = JSON.generate(brands_list)
7
+ res.body = json
8
+ res['Content-Type'] = 'application/json'
9
+ rescue StandardError => e
10
+ handle_error(res, e, "Error fetching brands list")
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ class CurrentBrandHandler < BaseHandler
2
+ def mount
3
+ @server.mount_proc('/brand/current') do |req, res|
4
+ begin
5
+ current_brand = BrandsManager.instance.current_brand
6
+ if current_brand
7
+ res.status = 200
8
+ res.body = JSON.generate(current_brand)
9
+ else
10
+ res.status = 204 # No Content
11
+ end
12
+ res.content_type = 'application/json'
13
+ rescue StandardError => e
14
+ handle_error(res, e, "Error fetching current brand")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,39 @@
1
+ class DoctorHandler < BaseHandler
2
+ def mount
3
+ @server.mount_proc('/brand/doctor') do |req, res|
4
+ if req.request_method == 'GET'
5
+ begin
6
+ query = CGI.parse(req.query_string)
7
+ brand_key = query['brand_key']&.first
8
+
9
+ if brand_key
10
+ doctor_result = run_doctor(brand_key)
11
+ res.status = 200
12
+ res.body = JSON.generate({ success: true, result: doctor_result })
13
+ else
14
+ res.status = 400
15
+ res.body = JSON.generate({ success: false, error: 'Missing brand_key parameter' })
16
+ end
17
+ rescue StandardError => e
18
+ handle_error(res, e, "Error running doctor")
19
+ end
20
+ else
21
+ method_not_allowed(res)
22
+ end
23
+ res.content_type = 'application/json'
24
+ end
25
+ end
26
+
27
+ def run_doctor(brand_key)
28
+ errors = SolaraManager.new.doctor(brand_key)
29
+ {
30
+ message: "Health check completed.",
31
+ brand_key: brand_key,
32
+ passed: errors.empty?,
33
+ errors: errors
34
+ }
35
+ rescue StandardError => e
36
+ Solara.logger.failure("Error running doctor: #{e.message}")
37
+ raise
38
+ end
39
+ end
@@ -0,0 +1,55 @@
1
+ Dir['*.rb'].each { |file| require_relative file }
2
+
3
+ class EditSectionHandler < BaseHandler
4
+ def mount
5
+ @server.mount_proc('/section/edit') do |req, res|
6
+ if req.request_method == 'POST'
7
+ begin
8
+ request_payload = JSON.parse(req.body)
9
+ brand_key = request_payload['brand_key']
10
+ key = request_payload['key']
11
+ data = request_payload['data']
12
+
13
+ update_section(key, data, brand_key)
14
+ set_current_brand_content_changed(brand_key, true)
15
+
16
+ res.status = 200
17
+ res.body = JSON.generate({ success: true, message: "Configuration for #{key} updated successfully" })
18
+ res.content_type = 'application/json'
19
+ rescue JSON::ParserError => e
20
+ handle_error(res, e, "Invalid JSON in request body", 400)
21
+ rescue StandardError => e
22
+ handle_error(res, e, "Error updating configuration")
23
+ end
24
+ else
25
+ method_not_allowed(res)
26
+ end
27
+ end
28
+ end
29
+
30
+ def update_section(key, data, brand_key)
31
+ template = BrandConfigurationsManager.new(brand_key).template_with_key(key)
32
+
33
+ path = template[:path]
34
+ if File.exist?(path)
35
+ File.write(path, JSON.pretty_generate(data))
36
+ Solara.logger.debug("Updated Config for #{path}: #{data}")
37
+ else
38
+ raise "Config file not found: #{path}"
39
+ end
40
+ rescue StandardError => e
41
+ Solara.logger.failure("Error updating section: #{e.message}")
42
+ raise
43
+ end
44
+
45
+ def update_theme(path, data)
46
+ json = JSON.parse(File.read(path))
47
+ json['colors'] = data
48
+ File.write(path, JSON.pretty_generate(json))
49
+ end
50
+
51
+ def set_current_brand_content_changed(brand_key, changed)
52
+ BrandsManager.instance.set_current_brand_content_changed(brand_key, changed)
53
+ end
54
+
55
+ end
@@ -0,0 +1,34 @@
1
+ class OffboardBrandHandler < BaseHandler
2
+ def mount
3
+ @server.mount_proc('/brand/offboard') do |req, res|
4
+ if req.request_method == 'GET'
5
+ begin
6
+ query = CGI.parse(req.query_string)
7
+ brand_key = query['brand_key']&.first
8
+
9
+ if brand_key
10
+ offboard(brand_key)
11
+ res.status = 200
12
+ res.body = JSON.generate({ success: true, message: "Deleted brand: #{brand_key}" })
13
+ else
14
+ res.status = 400
15
+ res.body = JSON.generate({ success: false, error: 'Missing brand_key parameter' })
16
+ end
17
+ rescue StandardError => e
18
+ handle_error(res, e, "Error deleting brand")
19
+ end
20
+ else
21
+ method_not_allowed(res)
22
+ end
23
+ res.content_type = 'application/json'
24
+ end
25
+ end
26
+
27
+ def offboard(brand_key)
28
+ SolaraManager.new.offboard(brand_key, confirm: false)
29
+ rescue StandardError => e
30
+ Solara.logger.failure("Error deleting brand: #{e.message}")
31
+ raise
32
+ end
33
+
34
+ end
@@ -0,0 +1,53 @@
1
+ class OnboardBrandHandler < BaseHandler
2
+ def mount
3
+ @server.mount_proc('/brand/onboard') do |req, res|
4
+ if req.request_method == 'POST'
5
+ begin
6
+ data = JSON.parse(req.body)
7
+ brand_name = data['brand_name']
8
+ brand_key = data['brand_key']
9
+ clone_brand_key = data['clone_brand_key']
10
+
11
+ if brand_key
12
+ result = onboard_brand(brand_name, brand_key, clone_brand_key)
13
+ res.status = result[:success] ? 200 : 409 # 409 Conflict for existing brand
14
+ res.body = JSON.generate(result)
15
+ else
16
+ res.status = 400
17
+ res.body = JSON.generate({ success: false, error: 'Missing brand_key parameter' })
18
+ end
19
+ rescue JSON::ParserError => e
20
+ handle_error(res, e, "Invalid JSON in request body", 400)
21
+ rescue StandardError => e
22
+ handle_error(res, e, "Error adding brand")
23
+ end
24
+ else
25
+ method_not_allowed(res)
26
+ end
27
+ res.content_type = 'application/json'
28
+ end
29
+
30
+ def onboard_brand(brand_name, brand_key, clone_brand_key = nil)
31
+ if BrandsManager.instance.exists(brand_key)
32
+ return { success: false, message: "Brand with key (#{brand_key}) already added!" }
33
+ end
34
+ SolaraManager.new.onboard(brand_key, brand_name, clone_brand_key: clone_brand_key, open_dashboard: false)
35
+ { success: true, message: "Brand added successfully" }
36
+ rescue StandardError => e
37
+ Solara.logger.failure("Error adding brand: #{e.message}")
38
+ raise
39
+ end
40
+
41
+ end
42
+
43
+ def onboard_brand(brand_name, brand_key, clone_brand_key = nil)
44
+ if BrandsManager.instance.exists(brand_key)
45
+ return { success: false, message: "Brand with key (#{brand_key}) already added!" }
46
+ end
47
+ SolaraManager.new.onboard(brand_key, brand_name, clone_brand_key: clone_brand_key, open_dashboard: false)
48
+ { success: true, message: "Brand added successfully" }
49
+ rescue StandardError => e
50
+ Solara.logger.failure("Error adding brand: #{e.message}")
51
+ raise
52
+ end
53
+ end
@@ -0,0 +1,12 @@
1
+ class RedirectHandler < BaseHandler
2
+ def mount
3
+ @server.mount_proc('/redirect') do |req, res|
4
+ begin
5
+ res.content_type = 'application/json'
6
+ res.body = JSON.generate({ redirect_url: @dashboard_server.home })
7
+ rescue StandardError => e
8
+ handle_error(res, e, "Error in redirect handler")
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,25 @@
1
+ class SwitchToBrandHandler < BaseHandler
2
+ def mount
3
+ @server.mount_proc('/switch') do |req, res|
4
+ if req.request_method == 'POST'
5
+ begin
6
+ request_payload = JSON.parse(req.body)
7
+ brand_key = request_payload['brand_key']
8
+
9
+ result = SolaraManager.new.switch(brand_key)
10
+ Solara.logger.debug(result)
11
+
12
+ res.status = 200
13
+ res.body = JSON.generate({ success: true, message: "Switched to brand: #{brand_key}", result: result })
14
+ res.content_type = 'application/json'
15
+ rescue JSON::ParserError => e
16
+ handle_error(res, e, "Invalid JSON in request body", 400)
17
+ rescue StandardError => e
18
+ handle_error(res, e, "Error switching to brand")
19
+ end
20
+ else
21
+ method_not_allowed(res)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,36 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Redirecting...</title>
7
+ <style>
8
+ body, html {
9
+ height: 100%;
10
+ margin: 0;
11
+ display: flex;
12
+ justify-content: center;
13
+ align-items: center;
14
+ background-color: #f0f0f0;
15
+ }
16
+ .container {
17
+ text-align: center;
18
+ }
19
+ img {
20
+ max-width: 50%;
21
+ max-height: 80vh;
22
+ object-fit: contain;
23
+ }
24
+ </style>
25
+ <script>
26
+ setTimeout(() => {
27
+ window.location.href = 'brand/brand.html?source=remote';
28
+ }, 500);
29
+ </script>
30
+ </head>
31
+ <body>
32
+ <div class="container">
33
+ <img src="solara.png" alt="Logo Image">
34
+ </div>
35
+ </body>
36
+ </html>
@@ -0,0 +1,41 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Redirecting...</title>
7
+ <style>
8
+ body, html {
9
+ height: 100%;
10
+ margin: 0;
11
+ display: flex;
12
+ justify-content: center;
13
+ align-items: center;
14
+ background-color: #f0f0f0;
15
+ }
16
+ .container {
17
+ text-align: center;
18
+ }
19
+ img {
20
+ max-width: 50%;
21
+ max-height: 80vh;
22
+ object-fit: contain;
23
+ }
24
+ </style>
25
+ <script>
26
+ fetch('/redirect')
27
+ .then(response => response.json())
28
+ .then(data => {
29
+ setTimeout(() => {
30
+ window.location.href = data.redirect_url;
31
+ }, 500);
32
+ })
33
+ .catch(error => console.error('Error:', error));
34
+ </script>
35
+ </head>
36
+ <body>
37
+ <div class="container">
38
+ <img src="solara.png" alt="Logo Image">
39
+ </div>
40
+ </body>
41
+ </html>
@@ -0,0 +1 @@
1
+ {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
@@ -0,0 +1,94 @@
1
+ Dir.glob("#{__dir__}/*.rb").each { |file| require file }
2
+ Dir.glob("#{__dir__}/../../.solara/core/brands/*.rb").each { |file| require file }
3
+ Dir.glob("#{__dir__}/validator/template/*.rb").each { |file| require file }
4
+
5
+ class Issue < StandardError
6
+ ERROR = 'ERROR'
7
+ WARNING = 'WARNING'
8
+
9
+ attr_reader :type, :error
10
+
11
+ def initialize(type, error)
12
+ @type = type
13
+ @error = error
14
+ end
15
+
16
+ def to_s
17
+ "#{@type}: #{@error}"
18
+ end
19
+
20
+ def <=>(other)
21
+ [type, error] <=> [other.type, other.error]
22
+ end
23
+
24
+ def self.error(error)
25
+ Issue.new(ERROR, error)
26
+ end
27
+
28
+ def self.warning(error)
29
+ Issue.new(WARNING, error)
30
+ end
31
+ end
32
+
33
+ class BrandDoctor
34
+ def initialize
35
+ end
36
+
37
+ def visit(brand_keys = [], print_logs: true)
38
+ keys = brand_keys.empty? ? BrandsManager.instance.brands_list.map { |brand| brand['key'] } : brand_keys
39
+ issues = []
40
+
41
+ keys.each do |brand_key|
42
+ validator = TemplateValidator.new(FilePath.brand(brand_key), FilePath.template_validation_config)
43
+ errors = validator.validate
44
+
45
+ errors.each { |error|
46
+ issues << Issue.error(error)
47
+ }
48
+ end
49
+
50
+ # Validate settings only if all validations so far are passed to avoid files issues
51
+ if issues.select { |issue| issue.type == Issue::ERROR }.empty?
52
+ issues += BrandSettingsValidatorManager.new.validate
53
+ end
54
+
55
+ if print_logs && !issues.empty?
56
+ Solara.logger.title("Healt Check Result")
57
+
58
+ issues.sort!.each { |issue|
59
+ case issue.type
60
+ when Issue::ERROR
61
+ Solara.logger.failure(issue.to_s)
62
+ when Issue::WARNING
63
+ Solara.logger.warn(issue.to_s)
64
+ else
65
+ Solara.logger.failure(issue.to_s)
66
+ issues.sort!
67
+ end
68
+ }
69
+ end
70
+
71
+ issues.sort!
72
+ end
73
+
74
+ def validate_brand(brand_key, strategies = [])
75
+ Solara.logger.start_step("Brand Health Check: #{brand_key}")
76
+
77
+ project_path = FilePath.brand(brand_key)
78
+ issues = []
79
+
80
+ strategies.each do |strategy|
81
+ Solara.logger.debug("Running #{strategy.class.name}...")
82
+ begin
83
+ strategy.validate(project_path)
84
+ Solara.logger.debug("#{strategy.class.name} passed.")
85
+ rescue => e
86
+ issues << Issue.error("#{e.message} (#{strategy.class.name})")
87
+ end
88
+ end
89
+
90
+ Solara.logger.end_step("Brand Health Check #{brand_key}")
91
+ issues
92
+ end
93
+ end
94
+