kasy-cli 1.21.8 → 1.22.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.
- package/lib/commands/add.js +93 -80
- package/lib/commands/configure.js +100 -32
- package/lib/commands/doctor.js +28 -2
- package/lib/commands/new.js +86 -38
- package/lib/commands/notifications.js +1 -1
- package/lib/commands/remove.js +43 -15
- package/lib/commands/run.js +2 -2
- package/lib/commands/update.js +2 -2
- package/lib/scaffold/CHANGELOG.json +14 -0
- package/lib/scaffold/backends/api/generator.js +14 -14
- package/lib/scaffold/backends/api/patch/README.md +83 -0
- package/lib/scaffold/backends/api/patch/lib/core/data/api/storage_api.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/core/data/entities/user_entity.dart +5 -0
- package/lib/scaffold/backends/api/patch/lib/{environnements.dart → environments.dart} +3 -11
- package/lib/scaffold/backends/api/patch/lib/features/ai_chat/api/ai_chat_api.dart +108 -0
- package/lib/scaffold/backends/api/patch/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +51 -0
- package/lib/scaffold/backends/api/patch/lib/features/{llm_chat/api/llm_chat_message_entity.dart → ai_chat/api/ai_chat_message_entity.dart} +5 -5
- package/lib/scaffold/backends/api/patch/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +71 -38
- package/lib/scaffold/backends/api/patch/lib/features/authentication/api/authentication_api.dart +2 -2
- package/lib/scaffold/backends/api/patch/lib/features/feedbacks/api/feature_request_api.dart +54 -0
- package/lib/scaffold/backends/api/patch/lib/features/onboarding/models/user_info.dart +16 -16
- package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
- package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
- package/lib/scaffold/backends/api/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +100 -0
- package/lib/scaffold/backends/api/patch/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +13 -0
- package/lib/scaffold/backends/api/patch/lib/features/subscriptions/api/stripe_backend_api.dart +60 -0
- package/lib/scaffold/backends/api/patch/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
- package/lib/scaffold/backends/api/pubspec.yaml.tpl +4 -5
- package/lib/scaffold/backends/firebase/deploy.js +87 -13
- package/lib/scaffold/backends/firebase/enable-auth-via-cli.js +14 -6
- package/lib/scaffold/backends/firebase/generator.js +5 -5
- package/lib/scaffold/backends/firebase/setup-from-scratch.js +69 -45
- package/lib/scaffold/backends/firebase/tokens.js +4 -4
- package/lib/scaffold/backends/supabase/deploy.js +63 -11
- package/lib/scaffold/backends/supabase/edge-functions/admin-list-users/index.ts +149 -0
- package/lib/scaffold/backends/supabase/edge-functions/{llm-chat → ai-chat}/index.ts +19 -19
- package/lib/scaffold/backends/supabase/edge-functions/revenuecat-webhook/index.ts +2 -0
- package/lib/scaffold/backends/supabase/edge-functions/stripe-create-checkout-session/index.ts +123 -0
- package/lib/scaffold/backends/supabase/edge-functions/stripe-create-portal-session/index.ts +97 -0
- package/lib/scaffold/backends/supabase/edge-functions/stripe-list-prices/index.ts +83 -0
- package/lib/scaffold/backends/supabase/edge-functions/stripe-webhook/index.ts +138 -0
- package/lib/scaffold/backends/supabase/generator.js +17 -17
- package/lib/scaffold/backends/supabase/migrations/20240101000009_ai_messages.sql +50 -0
- package/lib/scaffold/backends/supabase/migrations/20240101000012_stripe_customers.sql +36 -0
- package/lib/scaffold/backends/supabase/migrations/20240101000013_admin_role.sql +62 -0
- package/lib/scaffold/backends/supabase/patch/lib/core/data/entities/user_entity.dart +4 -0
- package/lib/scaffold/backends/supabase/patch/lib/{environnements.dart → environments.dart} +3 -13
- package/lib/scaffold/backends/supabase/patch/lib/features/ai_chat/api/ai_chat_api.dart +95 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +52 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/{llm_chat/api/llm_chat_message_entity.dart → ai_chat/api/ai_chat_message_entity.dart} +6 -6
- package/lib/scaffold/backends/supabase/patch/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +63 -35
- package/lib/scaffold/backends/supabase/patch/lib/features/authentication/api/authentication_api.dart +1 -1
- package/lib/scaffold/backends/supabase/patch/lib/features/feedbacks/api/feature_request_api.dart +46 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/models/user_info.dart +16 -16
- package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
- package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
- package/lib/scaffold/backends/supabase/patch/lib/features/settings/ui/components/admin/admin_users_api.dart +93 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +13 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/subscriptions/api/stripe_backend_api.dart +52 -0
- package/lib/scaffold/backends/supabase/patch/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
- package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +4 -5
- package/lib/scaffold/backends/supabase/tokens.js +3 -3
- package/lib/scaffold/catalog.js +9 -11
- package/lib/scaffold/generate.js +45 -31
- package/lib/scaffold/shared/generator-utils.js +188 -81
- package/lib/scaffold/shared/sort-imports.js +191 -0
- package/lib/scaffold/shared/template-strings.js +3 -3
- package/lib/utils/checks.js +2 -2
- package/lib/utils/i18n/messages-en.js +50 -35
- package/lib/utils/i18n/messages-es.js +50 -35
- package/lib/utils/i18n/messages-pt.js +52 -37
- package/lib/utils/updates.js +15 -15
- package/package.json +1 -1
- package/templates/firebase/.env.example +2 -2
- package/templates/firebase/android/app/src/main/res/drawable/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-v21/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-v21/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/values-night-v31/styles.xml +1 -1
- package/templates/firebase/android/app/src/main/res/values-v31/styles.xml +1 -1
- package/templates/firebase/assets/images/logo_wordmark_dark.png +0 -0
- package/templates/firebase/assets/images/logo_wordmark_light.png +0 -0
- package/templates/firebase/docs/revenuecat-setup.es.md +1 -1
- package/templates/firebase/docs/revenuecat-setup.pt.md +1 -1
- package/templates/firebase/firestore.rules +24 -5
- package/templates/firebase/functions/package-lock.json +22 -1
- package/templates/firebase/functions/package.json +2 -1
- package/templates/firebase/functions/src/admin/functions.ts +113 -0
- package/templates/firebase/functions/src/{llm_chat → ai_chat}/index.ts +16 -16
- package/templates/firebase/functions/src/index.ts +8 -2
- package/templates/firebase/functions/src/notifications/device_triggers.ts +2 -2
- package/templates/firebase/functions/src/notifications/triggers.ts +3 -3
- package/templates/firebase/functions/src/subscriptions/models/subscription_status.ts +2 -0
- package/templates/firebase/functions/src/subscriptions/stripe_functions.ts +222 -0
- package/templates/firebase/functions/src/subscriptions/subscriptions_functions.ts +2 -2
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png +0 -0
- package/templates/firebase/lib/components/components.dart +4 -1
- package/templates/firebase/lib/components/kasy_app_bar.dart +22 -7
- package/templates/firebase/lib/components/kasy_avatar.dart +7 -6
- package/templates/firebase/lib/components/kasy_button.dart +23 -99
- package/templates/firebase/lib/components/kasy_dialog.dart +11 -11
- package/templates/firebase/lib/components/kasy_image_viewer.dart +84 -0
- package/templates/firebase/lib/components/{kasy_sidebar_pro.dart → kasy_sidebar.dart} +692 -425
- package/templates/firebase/lib/components/kasy_status_tag.dart +91 -0
- package/templates/firebase/lib/components/kasy_text_area.dart +1 -3
- package/templates/firebase/lib/components/kasy_text_field.dart +5 -6
- package/templates/firebase/lib/components/kasy_text_field_otp.dart +1 -3
- package/templates/firebase/lib/components/kasy_toast.dart +2 -2
- package/templates/firebase/lib/components/kasy_web_header.dart +209 -0
- package/templates/firebase/lib/core/ads/ads_provider.dart +1 -1
- package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +57 -4
- package/templates/firebase/lib/core/bottom_menu/bottom_router.dart +19 -91
- package/templates/firebase/lib/core/bottom_menu/kasy_bottom_bar_factory.dart +196 -96
- package/templates/firebase/lib/core/bottom_menu/web_content_wrapper.dart +41 -0
- package/templates/firebase/lib/core/config/app_env.dart +5 -11
- package/templates/firebase/lib/core/config/features.dart +5 -4
- package/templates/firebase/lib/core/data/api/analytics_api.dart +1 -1
- package/templates/firebase/lib/core/data/api/http_client.dart +1 -1
- package/templates/firebase/lib/core/data/entities/user_entity.dart +3 -0
- package/templates/firebase/lib/core/data/models/entitlement.dart +35 -0
- package/templates/firebase/lib/core/data/models/subscription.dart +13 -186
- package/templates/firebase/lib/core/data/models/user.dart +11 -0
- package/templates/firebase/lib/core/data/repositories/user_repository.dart +1 -1
- package/templates/firebase/lib/core/home_widgets/home_widget_background_task.dart +1 -1
- package/templates/firebase/lib/core/home_widgets/home_widget_mywidget_service.dart +1 -1
- package/templates/firebase/lib/core/icons/kasy_icons.dart +31 -1
- package/templates/firebase/lib/core/rating/api/rating_api.dart +2 -2
- package/templates/firebase/lib/core/states/logout_action.dart +25 -0
- package/templates/firebase/lib/core/states/user_state_notifier.dart +3 -3
- package/templates/firebase/lib/core/theme/colors.dart +488 -188
- package/templates/firebase/lib/core/theme/radius.dart +22 -11
- package/templates/firebase/lib/core/theme/shadows.dart +66 -0
- package/templates/firebase/lib/core/theme/texts.dart +75 -41
- package/templates/firebase/lib/core/theme/universal_theme.dart +9 -4
- package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +5 -4
- package/templates/firebase/lib/core/web_viewport_scale.dart +52 -0
- package/templates/firebase/lib/core/widgets/kasy_brand_badge.dart +118 -0
- package/templates/firebase/lib/core/widgets/kasy_brand_logo.dart +40 -0
- package/templates/firebase/lib/core/widgets/kasy_focus_ring.dart +125 -0
- package/templates/firebase/lib/core/widgets/kasy_hover.dart +33 -13
- package/templates/firebase/lib/core/widgets/kasy_pressable_depth.dart +18 -13
- package/templates/firebase/lib/{environnements.dart → environments.dart} +3 -14
- package/templates/firebase/lib/features/ai_chat/ai_chat_page.dart +159 -0
- package/templates/firebase/lib/features/ai_chat/api/ai_chat_api.dart +107 -0
- package/templates/firebase/lib/features/ai_chat/api/ai_chat_conversation_entity.dart +64 -0
- package/templates/firebase/lib/features/{llm_chat/api/llm_chat_message_entity.dart → ai_chat/api/ai_chat_message_entity.dart} +6 -6
- package/templates/firebase/lib/features/{llm_chat/providers/llm_chat_notifier.dart → ai_chat/providers/ai_chat_notifier.dart} +73 -38
- package/templates/firebase/lib/features/ai_chat/providers/ai_conversations_notifier.dart +103 -0
- package/templates/firebase/lib/features/{llm_chat/ui/widgets/llm_chat_avatars.dart → ai_chat/ui/widgets/ai_chat_avatars.dart} +13 -13
- package/templates/firebase/lib/features/{llm_chat/ui/widgets/llm_chat_composer.dart → ai_chat/ui/widgets/ai_chat_composer.dart} +10 -10
- package/templates/firebase/lib/features/{llm_chat/llm_chat_page.dart → ai_chat/ui/widgets/ai_chat_conversation_view.dart} +80 -67
- package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_list.dart +285 -0
- package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_tile.dart +163 -0
- package/templates/firebase/lib/features/authentication/api/authentication_api.dart +52 -13
- package/templates/firebase/lib/features/authentication/api/popup_dismiss_watcher.dart +12 -0
- package/templates/firebase/lib/features/authentication/api/popup_dismiss_watcher_web.dart +35 -0
- package/templates/firebase/lib/features/authentication/ui/recover_password_page.dart +108 -68
- package/templates/firebase/lib/features/authentication/ui/signin_page.dart +38 -51
- package/templates/firebase/lib/features/authentication/ui/signup_page.dart +38 -51
- package/templates/firebase/lib/features/authentication/ui/widgets/auth_card_scaffold.dart +118 -0
- package/templates/firebase/lib/features/authentication/ui/widgets/recover_password_result.dart +61 -44
- package/templates/firebase/lib/features/feedbacks/api/feature_request_api.dart +32 -0
- package/templates/firebase/lib/features/feedbacks/models/feedback_state.dart +5 -5
- package/templates/firebase/lib/features/feedbacks/providers/feedback_page_notifier.dart +2 -2
- package/templates/firebase/lib/features/home/design_system_page.dart +808 -170
- package/templates/firebase/lib/features/home/home_components_page.dart +6 -3
- package/templates/firebase/lib/features/home/home_components_preview_page.dart +6 -6
- package/templates/firebase/lib/features/home/home_components_preview_registry.dart +325 -186
- package/templates/firebase/lib/features/home/home_feed.dart +289 -0
- package/templates/firebase/lib/features/home/home_image_grid.dart +355 -0
- package/templates/firebase/lib/features/home/home_page.dart +11 -250
- package/templates/firebase/lib/features/{local_reminder → local_reminders}/providers/reminder_notifier.dart +1 -1
- package/templates/firebase/lib/features/{local_reminder → local_reminders}/ui/reminder_page.dart +2 -2
- package/templates/firebase/lib/features/notifications/shared/att_permission.dart +1 -1
- package/templates/firebase/lib/features/notifications/ui/request_notification_permission.dart +25 -61
- package/templates/firebase/lib/features/notifications/ui/widgets/permission_request_view.dart +117 -0
- package/templates/firebase/lib/features/onboarding/models/user_info.dart +16 -16
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_att_setup.dart +4 -3
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_features.dart +7 -9
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_loader.dart +71 -48
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +3 -2
- package/templates/firebase/lib/features/onboarding/ui/components/onboarding_questions.dart +5 -5
- package/templates/firebase/lib/features/onboarding/ui/onboarding_page.dart +4 -4
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_background.dart +4 -2
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_feature.dart +39 -121
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart +105 -70
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_module_mockups.dart +639 -0
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_progress.dart +62 -50
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +38 -28
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_step_header.dart +75 -0
- package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_sticky_footer.dart +16 -5
- package/templates/firebase/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +26 -22
- package/templates/firebase/lib/features/settings/settings_page.dart +601 -90
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_page.dart +1193 -0
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_paywalls.dart +1 -1
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_routes.dart +2 -3
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_api.dart +86 -0
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_users_tab.dart +1215 -0
- package/templates/firebase/lib/features/settings/ui/components/admin/send_push_notification_page.dart +236 -0
- package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +3 -3
- package/templates/firebase/lib/features/settings/ui/widgets/kasy_user_avatar.dart +77 -0
- package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +1 -0
- package/templates/firebase/lib/features/{subscription → subscriptions}/api/entities/subscription_entity.dart +17 -0
- package/templates/firebase/lib/features/{subscription → subscriptions}/api/inapp_subscription_api.dart +67 -46
- package/templates/firebase/lib/features/subscriptions/api/revenuecat_product.dart +189 -0
- package/templates/firebase/lib/features/subscriptions/api/stripe_backend_api.dart +55 -0
- package/templates/firebase/lib/features/subscriptions/api/stripe_payment_api.dart +82 -0
- package/templates/firebase/lib/features/subscriptions/api/stripe_product.dart +178 -0
- package/templates/firebase/lib/features/{subscription → subscriptions}/api/subscription_api.dart +1 -1
- package/templates/firebase/lib/features/subscriptions/api/subscription_payment_api.dart +53 -0
- package/templates/firebase/lib/features/subscriptions/api/subscription_payment_api_provider.dart +21 -0
- package/templates/firebase/lib/features/{subscription → subscriptions}/providers/premium_page_provider.dart +9 -6
- package/templates/firebase/lib/features/{subscription → subscriptions}/repositories/subscription_repository.dart +26 -30
- package/{lib/scaffold/backends/supabase/patch/lib/features/subscription → templates/firebase/lib/features/subscriptions}/shared/maybeshow_premium.dart +0 -2
- package/templates/firebase/lib/features/subscriptions/shared/subscription_management.dart +45 -0
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/active_premium_content.dart +28 -4
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_minimal.dart +7 -7
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_row.dart +8 -8
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/paywall_with_switch.dart +7 -7
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/premium_content.dart +7 -7
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/component/premium_page_factory.dart +5 -5
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/premium_page.dart +5 -5
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/comparison_table.dart +1 -1
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/paywall_empty_state.dart +4 -4
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_bottom_menu.dart +3 -4
- package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/selectable_col.dart +1 -1
- package/templates/firebase/lib/i18n/en.i18n.json +171 -46
- package/templates/firebase/lib/i18n/es.i18n.json +175 -50
- package/templates/firebase/lib/i18n/pt.i18n.json +166 -41
- package/templates/firebase/lib/main.dart +6 -3
- package/templates/firebase/lib/router.dart +15 -23
- package/templates/firebase/pubspec.yaml +4 -5
- package/templates/firebase/test/core/data/repositories/user_repository_test.dart +3 -3
- package/templates/firebase/test/core/states/user_state_notifier_test.dart +4 -4
- package/templates/firebase/test/core/widgets/focus_ring_shape_test.dart +55 -0
- package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_inapp_subscription_api.dart +11 -4
- package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_revenuecat_product.dart +1 -0
- package/templates/firebase/test/features/{subscription → subscriptions}/api/fake_subscription_api.dart +2 -2
- package/templates/firebase/test/features/{subscription → subscriptions}/subscription_page_test.dart +4 -4
- package/templates/firebase/test/test_utils.dart +6 -6
- package/templates/firebase/web/index.html +5 -2
- package/lib/scaffold/backends/api/patch/lib/features/llm_chat/api/llm_chat_api.dart +0 -58
- package/lib/scaffold/backends/api/patch/lib/features/subscription/shared/maybeshow_premium.dart +0 -86
- package/lib/scaffold/backends/supabase/migrations/20240101000009_llm_messages.sql +0 -22
- package/lib/scaffold/backends/supabase/patch/lib/features/llm_chat/api/llm_chat_api.dart +0 -47
- package/templates/firebase/assets/images/onboarding/authentication-login-template.jpg +0 -0
- package/templates/firebase/assets/images/onboarding/img2.jpg +0 -0
- package/templates/firebase/assets/images/onboarding/img3.jpg +0 -0
- package/templates/firebase/assets/images/onboarding/notifications.png +0 -0
- package/templates/firebase/assets/images/onboarding/purchase.png +0 -0
- package/templates/firebase/lib/core/sidebar/kasy_sidebar.dart +0 -2021
- package/templates/firebase/lib/features/authentication/ui/widgets/auth_brand.dart +0 -35
- package/templates/firebase/lib/features/home/home_features_page.dart +0 -207
- package/templates/firebase/lib/features/llm_chat/api/llm_chat_api.dart +0 -50
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_bottom_sheet.dart +0 -316
- package/templates/firebase/lib/features/subscription/shared/maybeshow_premium.dart +0 -85
- /package/templates/firebase/lib/features/{local_reminder → local_reminders}/repositories/reminder_preferences.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/providers/models/premium_state.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/feature_line.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_background.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_background_gradient.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_banner.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_card.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_close_button.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/premium_feature.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/selectable_row.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/trial_switcher.dart +0 -0
- /package/templates/firebase/lib/features/{subscription → subscriptions}/ui/widgets/unsubscribe_feedback_popup.dart +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Kasy CLI i18n messages —
|
|
4
|
+
* Kasy CLI i18n messages — Portuguese.
|
|
5
5
|
*
|
|
6
6
|
* One entry per user-facing string. Keys are dotted paths describing
|
|
7
7
|
* where the string is used (cli.help.*, run.*, ios.*, etc.).
|
|
@@ -43,7 +43,7 @@ module.exports = {
|
|
|
43
43
|
'cli.command.setup.langName': 'idioma',
|
|
44
44
|
'cli.command.setup.langOption': 'Idioma dos prompts (en, pt, es)',
|
|
45
45
|
'cli.command.setup.backendOption': 'Adapter de backend (firebase, supabase, api)',
|
|
46
|
-
'cli.command.setup.featuresOption': 'Features opcionais separadas por virgula (web,widget,
|
|
46
|
+
'cli.command.setup.featuresOption': 'Features opcionais separadas por virgula (web,widget,ai_chat,revenuecat,ci)',
|
|
47
47
|
'cli.command.help.paramName': 'comando',
|
|
48
48
|
'cli.command.doctor.description': 'Verifica se o seu computador está pronto para rodar a Kasy',
|
|
49
49
|
'cli.command.modules.description': 'Mostra o que já vem incluso e o que você pode adicionar',
|
|
@@ -109,15 +109,15 @@ module.exports = {
|
|
|
109
109
|
'modules.backend.supabase.description': 'Adapter de backend Supabase',
|
|
110
110
|
'modules.backend.api.description': 'Adapter de backend API REST/GraphQL',
|
|
111
111
|
'modules.feature.sentry.description': 'Rastreamento de erros e crashes com Sentry',
|
|
112
|
-
'modules.feature.analytics.description': 'Rastreamento de eventos de analytics (
|
|
112
|
+
'modules.feature.analytics.description': 'Rastreamento de eventos de analytics (Mixpanel)',
|
|
113
113
|
'modules.feature.facebook.description': 'Login do Facebook e rastreamento de eventos Meta Ads (vem sempre junto)',
|
|
114
114
|
'modules.feature.revenuecat.description': 'Habilita pagamentos reais na tela de Subscriptions',
|
|
115
115
|
'modules.feature.onboarding.description': 'Fluxo de boas-vindas mostrado no primeiro acesso',
|
|
116
116
|
'modules.feature.web.description': 'Suporte Web/PWA',
|
|
117
117
|
'modules.feature.widget.description': 'Widget de tela inicial iOS/Android',
|
|
118
|
-
'modules.feature.
|
|
118
|
+
'modules.feature.ai_chat.description': 'Tela de chat com IA usando OpenAI ou Gemini',
|
|
119
119
|
'modules.feature.feedback.description': 'Pedidos e votacao de features dentro do app',
|
|
120
|
-
'modules.feature.
|
|
120
|
+
'modules.feature.local_reminders.description': 'Lembretes locais agendados pelo usuario (sem servidor)',
|
|
121
121
|
'modules.feature.ci.description': 'CI/CD: GitHub/GitLab (testes + build) + Codemagic (publicação nas lojas)',
|
|
122
122
|
'checks.checking': 'Verificando {name}...',
|
|
123
123
|
'checks.found': '{name} encontrado',
|
|
@@ -268,6 +268,9 @@ module.exports = {
|
|
|
268
268
|
'new.supabase.created': 'Projeto criado com sucesso.',
|
|
269
269
|
'new.supabase.createFailed': 'Não foi possível criar o projeto',
|
|
270
270
|
'new.supabase.loginHint': 'Execute: supabase login. Depois informe a URL e chave do seu projeto existente.',
|
|
271
|
+
'new.supabase.createFailed.freeLimit': 'Seu plano grátis do Supabase atingiu o limite de projetos nesta organização.',
|
|
272
|
+
'new.supabase.freeLimit.title': 'Como liberar',
|
|
273
|
+
'new.supabase.freeLimit.options': '• Apague um projeto que você não usa mais\n• Escolha outra organização (se você tiver mais de uma)\n• Ou faça upgrade do plano no painel do Supabase\nDepois rode o kasy new de novo.',
|
|
271
274
|
'new.supabase.setup': 'Vinculando projeto e publicando…',
|
|
272
275
|
'new.supabase.setupManual': 'Execute manualmente: supabase link, supabase db push, supabase functions deploy',
|
|
273
276
|
'new.supabase.passwordSaved': 'Senha do banco gerada automaticamente e salva em .kasy/supabase.json (gitignorado).',
|
|
@@ -301,7 +304,7 @@ module.exports = {
|
|
|
301
304
|
'validate.fail': 'falhou',
|
|
302
305
|
'validate.passed': 'aprovado',
|
|
303
306
|
|
|
304
|
-
// ──
|
|
307
|
+
// ── Firebase flow ────────────────────────────────────────────────────
|
|
305
308
|
'cli.command.firebase.description': '🔥 Criar app Flutter completo com Firebase (recomendado)',
|
|
306
309
|
'new.banner': '✨ Novo app Flutter',
|
|
307
310
|
'new.subtitle': 'Escolha o backend: Firebase, Supabase ou API REST.',
|
|
@@ -345,8 +348,10 @@ module.exports = {
|
|
|
345
348
|
'configure.section.sentry': 'Sentry (Crash Reports)',
|
|
346
349
|
'configure.section.mixpanel': 'Mixpanel (Analytics)',
|
|
347
350
|
'configure.section.revenuecat': 'RevenueCat (Assinaturas)',
|
|
351
|
+
'configure.section.stripe': 'Stripe (Pagamentos na Web)',
|
|
352
|
+
'configure.stripe.apiNote': 'Backend API: configure STRIPE_SECRET_KEY + STRIPE_WEBHOOK_SECRET no seu próprio servidor (veja Stripe-Guia.md). A CLI não tem onde guardar.',
|
|
348
353
|
'configure.section.facebook': 'Facebook (Login + Ads)',
|
|
349
|
-
'configure.section.
|
|
354
|
+
'configure.section.aiChat': 'AI Chat',
|
|
350
355
|
'configure.savedFacebook': 'Credenciais do Facebook gravadas em Info.plist e strings.xml.',
|
|
351
356
|
'configure.facebookPlistMissing': 'ios/Runner/Info.plist não encontrado — Facebook iOS não foi atualizado.',
|
|
352
357
|
'configure.facebookStringsMissing': 'android/.../strings.xml não encontrado — Facebook Android não foi atualizado.',
|
|
@@ -501,7 +506,13 @@ module.exports = {
|
|
|
501
506
|
'doctor.revenuecat.prefixMismatch': 'Chave com prefixo errado: {key} deveria começar com {expected}',
|
|
502
507
|
'doctor.revenuecat.keysTest': 'Apenas chaves Test Store (test_) configuradas — releases na loja exigem appl_/goog_, senão o app crasha',
|
|
503
508
|
'doctor.revenuecat.webhookUrlSupabase': 'URL do webhook (cole no RevenueCat → Integrations → Webhooks)',
|
|
504
|
-
'doctor.revenuecat.webhookUrlFirebase': 'URL do webhook: Firebase Console → Functions →
|
|
509
|
+
'doctor.revenuecat.webhookUrlFirebase': 'URL do webhook: Firebase Console → Functions → subscriptions-revenuecatWebhook',
|
|
510
|
+
'doctor.stripe.title': 'Stripe (Pagamentos na Web)',
|
|
511
|
+
'doctor.stripe.serverSide': 'Só no servidor — nenhuma chave Stripe vai no app (Checkout hospedado).',
|
|
512
|
+
'doctor.stripe.secretsFirebase': 'Defina STRIPE_SECRET_KEY + STRIPE_WEBHOOK_SECRET com `kasy configure stripe`, depois `kasy deploy`.',
|
|
513
|
+
'doctor.stripe.secretsSupabase': 'Secrets via `supabase secrets set` (feito no `kasy new` se informadas).',
|
|
514
|
+
'doctor.stripe.secretsApi': 'Configure STRIPE_SECRET_KEY + STRIPE_WEBHOOK_SECRET no seu servidor (veja Stripe-Guia.md).',
|
|
515
|
+
'doctor.stripe.webhookUrl': 'URL do webhook Stripe (adicione em Stripe → Developers → Webhooks)',
|
|
505
516
|
|
|
506
517
|
'update.iosRelease.success': 'Arquivos de release iOS atualizados',
|
|
507
518
|
'add.iosRelease.success': 'Arquivos de release iOS adicionados',
|
|
@@ -548,14 +559,15 @@ module.exports = {
|
|
|
548
559
|
'new.modules.header.feedback': '── Feedback (Firebase + Supabase) ──',
|
|
549
560
|
'new.modules.header.ci': '── CI/CD ──',
|
|
550
561
|
'new.modules.header.monetization': '── Monetizacao ──',
|
|
551
|
-
'new.firebase.module.revenuecat': '💰 RevenueCat (
|
|
562
|
+
'new.firebase.module.revenuecat': '💰 RevenueCat (assinaturas mobile)',
|
|
563
|
+
'new.firebase.module.stripe': '🌐 Stripe (assinaturas na web)',
|
|
552
564
|
'new.firebase.module.sentry': '🚨 Crash Reports (Sentry)',
|
|
553
565
|
'new.firebase.module.analytics': '📊 Analytics (Mixpanel)',
|
|
554
566
|
'new.firebase.module.facebook': '👤 Facebook (Login + Ads)',
|
|
555
567
|
'new.firebase.module.web': '🌐 Web Support (PWA)',
|
|
556
568
|
'new.firebase.module.widget': '📱 Home Widget (iOS/Android)',
|
|
557
|
-
'new.firebase.module.
|
|
558
|
-
'new.firebase.module.
|
|
569
|
+
'new.firebase.module.ai_chat': '🤖 AI Chat (OpenAI/Gemini)',
|
|
570
|
+
'new.firebase.module.local_reminders': '🔔 Local Reminders (sem servidor)',
|
|
559
571
|
'new.firebase.module.feedback': '💬 Feature Requests (requer Firebase ou Supabase)',
|
|
560
572
|
'new.firebase.module.ci': '⚙️ CI/CD (GitHub + Codemagic)',
|
|
561
573
|
'new.firebase.module.onboarding': '👋 Onboarding (fluxo de boas-vindas)',
|
|
@@ -579,10 +591,12 @@ module.exports = {
|
|
|
579
591
|
'new.firebase.q.revenuecat.ios': 'Chave de API RevenueCat para iOS',
|
|
580
592
|
'new.firebase.q.paywall': 'Qual estilo de paywall?',
|
|
581
593
|
'new.firebase.q.paywall.hint': 'Layout da tela de assinaturas — pode alterar depois no RevenueCat',
|
|
582
|
-
'new.firebase.q.
|
|
583
|
-
'new.firebase.q.
|
|
584
|
-
'new.firebase.q.
|
|
585
|
-
'new.firebase.
|
|
594
|
+
'new.firebase.q.stripe.secretKey': 'Stripe Secret Key (sk_test_… / sk_live_…) — opcional, configure depois',
|
|
595
|
+
'new.firebase.q.stripe.webhookSecret': 'Stripe Webhook Signing Secret (whsec_…) — opcional, configure depois',
|
|
596
|
+
'new.firebase.q.stripe.webhookSecret.hint': 'Em Stripe → Developers → Webhooks → seu endpoint',
|
|
597
|
+
'new.firebase.q.stripe.webhookSecret.invalid': 'O signing secret do webhook deve começar com whsec_.',
|
|
598
|
+
'new.firebase.q.stripe.productId': 'Stripe Product ID (prod_…) — opcional, limita o paywall a um produto',
|
|
599
|
+
'new.firebase.q.stripe.productId.hint': 'Deixe em branco para listar todos os preços recorrentes ativos',
|
|
586
600
|
'new.firebase.q.sentry.dsn': 'DSN do Sentry (deixe em branco para configurar depois)',
|
|
587
601
|
'new.firebase.q.sentry.dsn.invalid': 'DSN inválido. Formato esperado: https://chave@host/project-id',
|
|
588
602
|
'new.firebase.q.mixpanel.token': 'Token do Mixpanel (deixe em branco para configurar depois)',
|
|
@@ -594,7 +608,6 @@ module.exports = {
|
|
|
594
608
|
'new.firebase.q.revenuecat.android.required': 'Chave Android do RevenueCat é obrigatória.',
|
|
595
609
|
'new.firebase.q.revenuecat.ios.required': 'Chave iOS do RevenueCat é obrigatória.',
|
|
596
610
|
'new.firebase.q.revenuecat.metaDataset.invalid': 'Pixel ID deve ser numerico (ex: 1234567890).',
|
|
597
|
-
'new.firebase.q.revenuecat.webKey.invalid': 'Chave Web Billing deve começar com rcb_ (ex: rcb_sb_xxx ou rcb_xxx).',
|
|
598
611
|
|
|
599
612
|
'new.firebase.confirm.title': 'Resumo da configuração:',
|
|
600
613
|
'new.firebase.confirm.app': 'App',
|
|
@@ -707,6 +720,8 @@ module.exports = {
|
|
|
707
720
|
'new.outdated.upgraded': 'kasy atualizado! Rode kasy new novamente.',
|
|
708
721
|
'new.success.title': 'Projeto criado com sucesso!',
|
|
709
722
|
'new.success.featuresInstalled': 'Recursos ativados:',
|
|
723
|
+
'new.success.bundleId': 'Identificador do app (bundle ID)',
|
|
724
|
+
'new.success.bundleId.hint': 'Identificador único do seu app no Android, iOS e Firebase (push).',
|
|
710
725
|
'new.success.nextSteps': 'Proximos passos:',
|
|
711
726
|
'new.success.step.cd': 'Entre na pasta do projeto:',
|
|
712
727
|
'new.success.step.deploy': 'Suba o servidor pro Firebase (banco + funções):',
|
|
@@ -914,7 +929,7 @@ module.exports = {
|
|
|
914
929
|
'notifications.error.notKasyProject': 'kit_setup.json não encontrado. Execute dentro de um projeto Kasy.',
|
|
915
930
|
'notifications.error.noI18n': 'lib/i18n/*.i18n.json não encontrado neste projeto.',
|
|
916
931
|
'notifications.error.noFeatures': 'lib/core/config/features.dart não encontrado.',
|
|
917
|
-
'notifications.error.notEnabled': 'Notificações locais desativadas. Execute: kasy add
|
|
932
|
+
'notifications.error.notEnabled': 'Notificações locais desativadas. Execute: kasy add local_reminders',
|
|
918
933
|
'notifications.error.cancelled': 'Cancelado.',
|
|
919
934
|
'notifications.prompt.intro': 'Textos de notificação local',
|
|
920
935
|
'notifications.prompt.hint': 'Demo Home = teste instantâneo em Features. Lembrete = alerta agendado em Configurações.',
|
|
@@ -957,26 +972,26 @@ module.exports = {
|
|
|
957
972
|
// Legacy keys — kept for compatibility with old scripts.
|
|
958
973
|
'add.prompt.rcAndroidKey': 'RevenueCat Android API key (deixe em branco para configurar depois):',
|
|
959
974
|
'add.prompt.rcIosKey': 'RevenueCat iOS API key (deixe em branco para configurar depois):',
|
|
960
|
-
'add.note.facebook': 'Adicione seu Facebook App ID e token no .vscode/launch.json (FB_APP_ID,
|
|
961
|
-
'new.q.
|
|
962
|
-
'new.q.
|
|
963
|
-
'add.
|
|
964
|
-
'add.prompt.
|
|
965
|
-
'add.prompt.
|
|
966
|
-
'add.prompt.
|
|
967
|
-
'add.prompt.
|
|
968
|
-
'add.
|
|
969
|
-
'add.
|
|
970
|
-
'add.
|
|
971
|
-
'add.
|
|
972
|
-
'add.
|
|
973
|
-
'add.
|
|
974
|
-
'add.
|
|
975
|
-
'add.
|
|
976
|
-
'add.
|
|
977
|
-
'add.
|
|
978
|
-
'add.
|
|
979
|
-
'add.
|
|
975
|
+
'add.note.facebook': 'Adicione seu Facebook App ID e token no .vscode/launch.json (FB_APP_ID, FB_CLIENT_TOKEN).',
|
|
976
|
+
'new.q.ai_chat.configureNow': 'Configurar o agente de Chat IA agora?',
|
|
977
|
+
'new.q.ai_chat.configureNow.hint': 'Pode pular e executar "kasy add ai_chat" depois',
|
|
978
|
+
'add.ai_chat.reconfigure': 'Feature já ativa — reconfigurando apenas as credenciais.',
|
|
979
|
+
'add.prompt.aiProvider': 'Provedor de IA:',
|
|
980
|
+
'add.prompt.aiSystemPrompt': 'Instrução do agente — system prompt (deixe em branco para nenhuma):\n Exemplo: "Você e um assistente de suporte do app Fitsync. Responda apenas sobre treinos."\n >',
|
|
981
|
+
'add.prompt.aiApiKey': 'Chave de API (OpenAI ou Gemini) — fica no servidor, nunca no app (deixe em branco para configurar depois):',
|
|
982
|
+
'add.prompt.aiEndpoint': 'URL do seu endpoint IA (deixe em branco para configurar depois):',
|
|
983
|
+
'add.ai_chat.settingSecret': 'Salvando chave de API como secret no servidor...',
|
|
984
|
+
'add.ai_chat.secretSet': 'Chave de API salva como secret',
|
|
985
|
+
'add.ai_chat.secretFailed': 'Não foi possível salvar o secret automaticamente — configure manualmente (veja instruções abaixo)',
|
|
986
|
+
'add.ai_chat.skipSecret': 'Chave de API ignorada — configure antes do deploy via CLI do servidor',
|
|
987
|
+
'add.ai_chat.deploying': 'Fazendo deploy da função IA no servidor...',
|
|
988
|
+
'add.ai_chat.deployed': 'Função IA deployada com sucesso',
|
|
989
|
+
'add.ai_chat.deployFailed': 'Deploy automático falhou — faca o deploy manualmente (veja instruções abaixo)',
|
|
990
|
+
'add.ai_chat.nextSteps.firebase': '\n Proximos passos:\n 1. O AI_CHAT_ENDPOINT no .vscode/launch.json já foi preenchido.\n 2. Rode o app: kasy run\n',
|
|
991
|
+
'add.ai_chat.nextSteps.firebase.deployFailed': '\n Proximos passos:\n 1. Deploy manual: firebase deploy --only functions:aiChat\n 2. O AI_CHAT_ENDPOINT no .vscode/launch.json já foi preenchido.\n 3. Rode o app: kasy run\n',
|
|
992
|
+
'add.ai_chat.nextSteps.supabase': '\n Proximos passos:\n 1. O AI_CHAT_ENDPOINT no .vscode/launch.json já foi preenchido.\n 2. Rode o app: kasy run\n',
|
|
993
|
+
'add.ai_chat.nextSteps.supabase.deployFailed': '\n Proximos passos:\n 1. Deploy manual: supabase functions deploy ai-chat --no-verify-jwt\n 2. O AI_CHAT_ENDPOINT no .vscode/launch.json já foi preenchido.\n 3. Rode o app: kasy run\n',
|
|
994
|
+
'add.ai_chat.nextSteps.api': '\n Proximos passos:\n 1. Crie um endpoint no seu servidor que aceite {message, history} e chame sua IA.\n 2. Atualize AI_CHAT_ENDPOINT no .vscode/launch.json com a URL do seu endpoint.\n 3. Rode o app: kasy run\n',
|
|
980
995
|
'cli.command.remove.description': 'Remove algo que você não usa mais (ex: kasy remove sentry)',
|
|
981
996
|
'remove.error.noModule': 'Informe o nome da feature. Uso: kasy remove <feature>',
|
|
982
997
|
'remove.error.notKasyProject': 'kit_setup.json não encontrado. Execute este comando dentro de um projeto Kasy.',
|
package/lib/utils/updates.js
CHANGED
|
@@ -8,9 +8,9 @@ const pkg = require('../../package.json');
|
|
|
8
8
|
const ui = require('./ui');
|
|
9
9
|
const { CONFIG_PATH } = require('./license');
|
|
10
10
|
|
|
11
|
-
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24
|
|
11
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
12
12
|
|
|
13
|
-
// ──
|
|
13
|
+
// ── Config helpers ────────────────────────────────────────────────────────
|
|
14
14
|
|
|
15
15
|
function readConfig() {
|
|
16
16
|
if (!existsSync(CONFIG_PATH)) return {};
|
|
@@ -22,7 +22,7 @@ function writeConfig(config) {
|
|
|
22
22
|
writeJsonSync(CONFIG_PATH, config, { spaces: 2 });
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
// ──
|
|
25
|
+
// ── Minimal semver comparison (no external deps) ─────────────────────────────
|
|
26
26
|
|
|
27
27
|
function isNewer(remote, local) {
|
|
28
28
|
const parse = (v) => String(v).replace(/^v/, '').split('.').map(Number);
|
|
@@ -33,7 +33,7 @@ function isNewer(remote, local) {
|
|
|
33
33
|
return rPat > lPat;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
// ──
|
|
36
|
+
// ── Async fetch (fires in the background) ─────────────────────────────────
|
|
37
37
|
|
|
38
38
|
function fetchLatestVersion() {
|
|
39
39
|
return new Promise((resolve) => {
|
|
@@ -54,19 +54,19 @@ function fetchLatestVersion() {
|
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
// ──
|
|
57
|
+
// ── Main function ─────────────────────────────────────────────────────────
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
60
|
+
* Shows an update notice (based on the last-check cache — zero latency).
|
|
61
|
+
* Triggers a background cache refresh if 24h have passed.
|
|
62
|
+
* Never throws — fails silently in any scenario.
|
|
63
63
|
*/
|
|
64
64
|
async function checkForUpdates() {
|
|
65
65
|
try {
|
|
66
66
|
const config = readConfig();
|
|
67
67
|
const cache = config.updateCheck || {};
|
|
68
68
|
|
|
69
|
-
//
|
|
69
|
+
// Show a notice based on the cache — does not wait for the network
|
|
70
70
|
if (cache.latestVersion && isNewer(cache.latestVersion, pkg.version)) {
|
|
71
71
|
console.log(
|
|
72
72
|
'\n' +
|
|
@@ -79,7 +79,7 @@ async function checkForUpdates() {
|
|
|
79
79
|
);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
//
|
|
82
|
+
// Refresh the cache in the background every 24h — does not block the CLI
|
|
83
83
|
const now = Date.now();
|
|
84
84
|
if (!cache.checkedAt || now - cache.checkedAt > CHECK_INTERVAL_MS) {
|
|
85
85
|
fetchLatestVersion()
|
|
@@ -91,14 +91,14 @@ async function checkForUpdates() {
|
|
|
91
91
|
.catch(() => {});
|
|
92
92
|
}
|
|
93
93
|
} catch {
|
|
94
|
-
//
|
|
94
|
+
// Never block the CLI because of an update check
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
99
|
+
* Called before kasy new. If there is a newer version in the cache,
|
|
100
|
+
* shows a notice and asks whether to continue or exit to update.
|
|
101
|
+
* Never blocks — if the user ignores it, the flow continues normally.
|
|
102
102
|
*/
|
|
103
103
|
async function warnIfOutdatedBeforeNew(t) {
|
|
104
104
|
try {
|
|
@@ -128,7 +128,7 @@ async function warnIfOutdatedBeforeNew(t) {
|
|
|
128
128
|
process.exit(result.status ?? 0);
|
|
129
129
|
}
|
|
130
130
|
} catch {
|
|
131
|
-
//
|
|
131
|
+
// Never block kasy new because of the version notice
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
|
package/package.json
CHANGED
|
@@ -6,8 +6,8 @@ ENV=dev
|
|
|
6
6
|
# Firebase Cloud Functions (região + project id do seu projeto)
|
|
7
7
|
BACKEND_URL=https://europe-west1-YOUR_PROJECT_ID.cloudfunctions.net
|
|
8
8
|
|
|
9
|
-
#
|
|
10
|
-
|
|
9
|
+
# AI Chat — proxy Cloud Function (AI_API_KEY fica no Firebase Secret)
|
|
10
|
+
AI_CHAT_ENDPOINT=https://europe-west1-YOUR_PROJECT_ID.cloudfunctions.net/aiChat
|
|
11
11
|
|
|
12
12
|
# RevenueCat
|
|
13
13
|
RC_ANDROID_API_KEY=YOUR_REVENUECAT_ANDROID_KEY
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<item name="android:windowFullscreen">true</item>
|
|
7
7
|
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
|
8
8
|
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
|
9
|
-
<item name="android:windowSplashScreenBackground">#
|
|
9
|
+
<item name="android:windowSplashScreenBackground">#0C0A14</item>
|
|
10
10
|
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
|
|
11
11
|
</style>
|
|
12
12
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<item name="android:windowFullscreen">true</item>
|
|
7
7
|
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
|
8
8
|
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
|
|
9
|
-
<item name="android:windowSplashScreenBackground">#
|
|
9
|
+
<item name="android:windowSplashScreenBackground">#FAF9FC</item>
|
|
10
10
|
<item name="android:windowSplashScreenAnimatedIcon">@drawable/android12splash</item>
|
|
11
11
|
</style>
|
|
12
12
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
|
Binary file
|
|
Binary file
|
|
@@ -88,7 +88,7 @@ Encuentra la URL de la función:
|
|
|
88
88
|
| Backend | Dónde encontrar la URL |
|
|
89
89
|
|---------|------------------------|
|
|
90
90
|
| **Supabase** | `https://TU_PROJECT_REF.supabase.co/functions/v1/revenuecat-webhook` |
|
|
91
|
-
| **Firebase** | [Firebase Console → Functions](https://console.firebase.google.com/project/_/functions) → `
|
|
91
|
+
| **Firebase** | [Firebase Console → Functions](https://console.firebase.google.com/project/_/functions) → `subscriptions-revenuecatWebhook` → copia la URL |
|
|
92
92
|
|
|
93
93
|
Regístrala en RevenueCat:
|
|
94
94
|
|
|
@@ -88,7 +88,7 @@ Encontre a URL da função:
|
|
|
88
88
|
| Backend | Onde encontrar a URL |
|
|
89
89
|
|---------|---------------------|
|
|
90
90
|
| **Supabase** | `https://SEU_PROJECT_REF.supabase.co/functions/v1/revenuecat-webhook` |
|
|
91
|
-
| **Firebase** | [Firebase Console → Functions](https://console.firebase.google.com/project/_/functions) → `
|
|
91
|
+
| **Firebase** | [Firebase Console → Functions](https://console.firebase.google.com/project/_/functions) → `subscriptions-revenuecatWebhook` → copie a URL |
|
|
92
92
|
|
|
93
93
|
Registre no RevenueCat:
|
|
94
94
|
|
|
@@ -2,8 +2,22 @@ rules_version = '2';
|
|
|
2
2
|
service cloud.firestore {
|
|
3
3
|
match /databases/{database}/documents {
|
|
4
4
|
|
|
5
|
+
// True when the signed-in user has role == "admin" on their OWN user doc.
|
|
6
|
+
// `role` is writable only server-side (see the users rules below), so a
|
|
7
|
+
// client cannot forge it — this check is trustworthy and server-evaluated.
|
|
8
|
+
function isAdmin() {
|
|
9
|
+
return request.auth != null
|
|
10
|
+
&& exists(/databases/$(database)/documents/users/$(request.auth.uid))
|
|
11
|
+
&& get(/databases/$(database)/documents/users/$(request.auth.uid)).data.get('role', '') == 'admin';
|
|
12
|
+
}
|
|
13
|
+
|
|
5
14
|
match /users/{id} {
|
|
6
|
-
allow read,
|
|
15
|
+
allow read, delete: if request.auth.uid != null && request.auth.uid == id;
|
|
16
|
+
// Owners update their own profile, but NEVER their own `role`
|
|
17
|
+
// (no self-promotion to admin). `role` is set only server-side
|
|
18
|
+
// (Cloud Function / console), never from the client.
|
|
19
|
+
allow update: if request.auth.uid != null && request.auth.uid == id
|
|
20
|
+
&& !request.resource.data.diff(resource.data).affectedKeys().hasAny(['role']);
|
|
7
21
|
allow create: if false;
|
|
8
22
|
}
|
|
9
23
|
match /users/{id}/{document=**} {
|
|
@@ -23,10 +37,15 @@ service cloud.firestore {
|
|
|
23
37
|
allow create: if request.auth.uid != null
|
|
24
38
|
&& request.resource.data.active == false
|
|
25
39
|
&& request.resource.data.votes == 0;
|
|
40
|
+
// Either a normal ±1 vote by any signed-in user, OR full moderation
|
|
41
|
+
// (toggle visibility, edit translations) by an admin.
|
|
26
42
|
allow update: if request.auth.uid != null
|
|
27
|
-
&&
|
|
28
|
-
|
|
29
|
-
|
|
43
|
+
&& (
|
|
44
|
+
(request.resource.data.diff(resource.data).affectedKeys().hasOnly(['votes', 'last_update_date'])
|
|
45
|
+
&& (request.resource.data.votes == resource.data.votes + 1
|
|
46
|
+
|| request.resource.data.votes == resource.data.votes - 1))
|
|
47
|
+
|| isAdmin()
|
|
48
|
+
);
|
|
30
49
|
allow delete: if false;
|
|
31
50
|
}
|
|
32
51
|
match /feature_requests/{featureId}/votes/{id} {
|
|
@@ -48,4 +67,4 @@ service cloud.firestore {
|
|
|
48
67
|
allow read, write: if false;
|
|
49
68
|
}
|
|
50
69
|
}
|
|
51
|
-
}
|
|
70
|
+
}
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
"name": "functions",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"firebase-admin": "^13.6.0",
|
|
10
|
-
"firebase-functions": "^7.1.1"
|
|
10
|
+
"firebase-functions": "^7.1.1",
|
|
11
|
+
"stripe": "^18.5.0"
|
|
11
12
|
},
|
|
12
13
|
"devDependencies": {
|
|
13
14
|
"@types/jest": "^29.5.14",
|
|
@@ -6239,6 +6240,26 @@
|
|
|
6239
6240
|
"url": "https://github.com/sponsors/sindresorhus"
|
|
6240
6241
|
}
|
|
6241
6242
|
},
|
|
6243
|
+
"node_modules/stripe": {
|
|
6244
|
+
"version": "18.5.0",
|
|
6245
|
+
"resolved": "https://registry.npmjs.org/stripe/-/stripe-18.5.0.tgz",
|
|
6246
|
+
"integrity": "sha512-Hp+wFiEQtCB0LlNgcFh5uVyKznpDjzyUZ+CNVEf+I3fhlYvh7rZruIg+jOwzJRCpy0ZTPMjlzm7J2/M2N6d+DA==",
|
|
6247
|
+
"license": "MIT",
|
|
6248
|
+
"dependencies": {
|
|
6249
|
+
"qs": "^6.11.0"
|
|
6250
|
+
},
|
|
6251
|
+
"engines": {
|
|
6252
|
+
"node": ">=12.*"
|
|
6253
|
+
},
|
|
6254
|
+
"peerDependencies": {
|
|
6255
|
+
"@types/node": ">=12.x.x"
|
|
6256
|
+
},
|
|
6257
|
+
"peerDependenciesMeta": {
|
|
6258
|
+
"@types/node": {
|
|
6259
|
+
"optional": true
|
|
6260
|
+
}
|
|
6261
|
+
}
|
|
6262
|
+
},
|
|
6242
6263
|
"node_modules/strnum": {
|
|
6243
6264
|
"version": "2.2.0",
|
|
6244
6265
|
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz",
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {onCall, HttpsError} from "firebase-functions/v2/https";
|
|
2
|
+
import * as admin from "firebase-admin";
|
|
3
|
+
import {Logger} from "../core/logger/logger";
|
|
4
|
+
|
|
5
|
+
// In-memory cap: the admin console loads up to this many of the most recent
|
|
6
|
+
// users in ONE call, then searches / sorts / paginates entirely on the client
|
|
7
|
+
// for an instant experience (no extra reads when typing or paging). Apps that
|
|
8
|
+
// outgrow this should add a dedicated search index (Algolia/Typesense) — see
|
|
9
|
+
// README. Reads are admin-only and bounded, so the cost stays predictable.
|
|
10
|
+
const MAX_SCAN = 1000;
|
|
11
|
+
|
|
12
|
+
// getAll() batch size for the subscription lookups (Firestore caps a single
|
|
13
|
+
// getAll at a few hundred refs comfortably; chunking keeps it safe).
|
|
14
|
+
const SUB_CHUNK = 200;
|
|
15
|
+
|
|
16
|
+
// A user counts as a subscriber when their subscription is currently usable.
|
|
17
|
+
const ACTIVE_SUBSCRIPTION_STATUSES = ["ACTIVE", "LIFETIME"];
|
|
18
|
+
|
|
19
|
+
interface AdminUser {
|
|
20
|
+
id: string;
|
|
21
|
+
email: string | null;
|
|
22
|
+
name: string | null;
|
|
23
|
+
createdAt: number | null; // epoch millis
|
|
24
|
+
subscriber: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Lists app users for the admin console.
|
|
29
|
+
*
|
|
30
|
+
* Security: the caller must be authenticated AND have `role == "admin"` on their
|
|
31
|
+
* own `users/{uid}` document. Clients cannot read the whole users collection
|
|
32
|
+
* (Firestore rules), so this runs with Admin privileges and gates on the role.
|
|
33
|
+
*
|
|
34
|
+
* Returns the most-recent users (bounded to MAX_SCAN) with their subscriber
|
|
35
|
+
* status already resolved. The client (admin_users_tab.dart) does the search,
|
|
36
|
+
* sort and pagination locally so those interactions are instant. Shape:
|
|
37
|
+
* { users: AdminUser[], totalUsers: number, truncated: boolean }
|
|
38
|
+
* - totalUsers: the true size of the collection (for an honest count).
|
|
39
|
+
* - truncated: true when the collection is larger than what we returned, so
|
|
40
|
+
* the UI can warn that search only covers the loaded set.
|
|
41
|
+
*/
|
|
42
|
+
export const listUsers = onCall(async (request) => {
|
|
43
|
+
if (!request.auth) {
|
|
44
|
+
throw new HttpsError("unauthenticated", "Authentication required");
|
|
45
|
+
}
|
|
46
|
+
const callerUid = request.auth.uid;
|
|
47
|
+
const db = admin.firestore();
|
|
48
|
+
const logger = new Logger("listUsers");
|
|
49
|
+
|
|
50
|
+
// Admin gate: the caller's own user doc must carry role == "admin".
|
|
51
|
+
const callerDoc = await db.collection("users").doc(callerUid).get();
|
|
52
|
+
if (!callerDoc.exists || callerDoc.get("role") !== "admin") {
|
|
53
|
+
throw new HttpsError("permission-denied", "Admin role required");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const usersRef = db.collection("users");
|
|
58
|
+
|
|
59
|
+
// Bounded fetch — the client does the search/sort/paginate.
|
|
60
|
+
//
|
|
61
|
+
// IMPORTANT: do NOT order this query by "creation_date". A Firestore
|
|
62
|
+
// orderBy silently DROPS every document that lacks the ordered field, and
|
|
63
|
+
// some user docs have no creation_date (e.g. accounts whose doc was created
|
|
64
|
+
// client-side during anonymous→social linking; the auth trigger then only
|
|
65
|
+
// patches the email and never backfills creation_date). Ordering by it
|
|
66
|
+
// would hide exactly those real, active users. We fetch unordered and sort
|
|
67
|
+
// in memory below (newest first, undated last) so every user is returned.
|
|
68
|
+
const snapshot = await usersRef.limit(MAX_SCAN).get();
|
|
69
|
+
const docs = snapshot.docs;
|
|
70
|
+
|
|
71
|
+
// Subscriber status for the whole batch, in chunked getAll() reads.
|
|
72
|
+
const activeSubscribers = new Set<string>();
|
|
73
|
+
for (let i = 0; i < docs.length; i += SUB_CHUNK) {
|
|
74
|
+
const chunk = docs.slice(i, i + SUB_CHUNK);
|
|
75
|
+
const refs = chunk.map((d) => db.collection("subscriptions").doc(d.id));
|
|
76
|
+
const snaps = await db.getAll(...refs);
|
|
77
|
+
for (const s of snaps) {
|
|
78
|
+
if (
|
|
79
|
+
s.exists &&
|
|
80
|
+
ACTIVE_SUBSCRIPTION_STATUSES.includes(s.get("status"))
|
|
81
|
+
) {
|
|
82
|
+
activeSubscribers.add(s.id);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const users: AdminUser[] = docs.map((d) => {
|
|
88
|
+
const createdAt = d.get("creation_date");
|
|
89
|
+
return {
|
|
90
|
+
id: d.id,
|
|
91
|
+
email: (d.get("email") as string) || null,
|
|
92
|
+
name: (d.get("name") as string) || null,
|
|
93
|
+
createdAt: createdAt ? createdAt.toMillis() : null,
|
|
94
|
+
subscriber: activeSubscribers.has(d.id),
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Newest first; users without a creation_date sort to the end.
|
|
99
|
+
users.sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0));
|
|
100
|
+
|
|
101
|
+
const countSnap = await usersRef.count().get();
|
|
102
|
+
const totalUsers = countSnap.data().count;
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
users,
|
|
106
|
+
totalUsers,
|
|
107
|
+
truncated: totalUsers > users.length,
|
|
108
|
+
};
|
|
109
|
+
} catch (e) {
|
|
110
|
+
logger.error(`listUsers error: ${e}`);
|
|
111
|
+
throw new HttpsError("internal", "Failed to list users");
|
|
112
|
+
}
|
|
113
|
+
});
|