diff --git a/README.md b/README.md index 4a44095..2d6bacf 100644 --- a/README.md +++ b/README.md @@ -78,9 +78,10 @@ When utilizing the `nitric new` command to initiate a new project, the available ### Dart -| Name | Description | Features | -| ---------------------------------- | ---------------- | -------- | -| [dart-starter](./v1/dart-starter/) | REST API Starter | APIs | +| Name | Description | Features | +| ---------------------------------- | ----------------------------------------------- | ---------------------- | +| [dart-starter](./v1/dart-starter/) | REST API Starter | APIs | +| [flutter](./v1/flutter/) | Basic Flutter Application with a Nitric backend | APIs, Key Value Stores | ### Go diff --git a/v1/flutter/.gitignore b/v1/flutter/.gitignore new file mode 100644 index 0000000..29a3a50 --- /dev/null +++ b/v1/flutter/.gitignore @@ -0,0 +1,43 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/v1/flutter/.metadata b/v1/flutter/.metadata new file mode 100644 index 0000000..9d32c61 --- /dev/null +++ b/v1/flutter/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "b0850beeb25f6d5b10426284f506557f66181b36" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: b0850beeb25f6d5b10426284f506557f66181b36 + base_revision: b0850beeb25f6d5b10426284f506557f66181b36 + - platform: android + create_revision: b0850beeb25f6d5b10426284f506557f66181b36 + base_revision: b0850beeb25f6d5b10426284f506557f66181b36 + - platform: ios + create_revision: b0850beeb25f6d5b10426284f506557f66181b36 + base_revision: b0850beeb25f6d5b10426284f506557f66181b36 + - platform: linux + create_revision: b0850beeb25f6d5b10426284f506557f66181b36 + base_revision: b0850beeb25f6d5b10426284f506557f66181b36 + - platform: macos + create_revision: b0850beeb25f6d5b10426284f506557f66181b36 + base_revision: b0850beeb25f6d5b10426284f506557f66181b36 + - platform: web + create_revision: b0850beeb25f6d5b10426284f506557f66181b36 + base_revision: b0850beeb25f6d5b10426284f506557f66181b36 + - platform: windows + create_revision: b0850beeb25f6d5b10426284f506557f66181b36 + base_revision: b0850beeb25f6d5b10426284f506557f66181b36 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/v1/flutter/README.md b/v1/flutter/README.md new file mode 100644 index 0000000..775117a --- /dev/null +++ b/v1/flutter/README.md @@ -0,0 +1,64 @@ +

+ + Nitric Logo + +

+ +

+ A fast & fun way to build portable cloud-native applications +

+ +

+ GitHub release (latest SemVer) + + Twitter Follow + + Discord +

+ +## Project Description + +A basic Flutter application that uses Nitric as the backend service for the API and key value collections. + +## Running this project + +To run this project you'll need the [Nitric CLI](https://nitric.io/docs/installation) installed, then you can use the CLI commands to run, build or deploy the project. + +You'll also want to make sure the project's required dependencies have been installed. + +```bash +# install dependencies +dart pub get + +# run locally +nitric start +``` + +## About Nitric + +[Nitric](https://nitric.io) is a framework for rapid development of cloud-native and serverless applications. Define your apps in terms of the resources they need, then write the code for serverless function based APIs, event subscribers and scheduled jobs. + +Apps built with Nitric can be deployed to AWS, Azure or Google Cloud all from the same code base so you can focus on your products, not your cloud provider. + +Nitric makes it easy to: + +- Create smart serverless functions and APIs +- Build reliable distributed apps that use events and/or queues +- Securely store and retrieve secrets +- Read and write files from buckets + +## Documentation + +The full documentation is available at [nitric.io/docs](https://nitric.io/docs). + +We're completely open-source and encourage [code contributions](https://nitric.io/docs/contributions). + +## Get in touch + +- Jump into our [Discord server](https://nitric.io/chat) + +- Ask questions in [GitHub discussions](https://github.com/nitrictech/nitric/discussions) + +- Find us on [Twitter](https://twitter.com/nitric_io) + +- Send us an [email](mailto:maintainers@nitric.io) diff --git a/v1/flutter/analysis_options.yaml b/v1/flutter/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/v1/flutter/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/v1/flutter/docker/flutter.dockerfile b/v1/flutter/docker/flutter.dockerfile new file mode 100644 index 0000000..fafdfae --- /dev/null +++ b/v1/flutter/docker/flutter.dockerfile @@ -0,0 +1,40 @@ +FROM dart:stable AS build + +# The Nitric CLI will provide the HANDLER arg with the location of our service +ARG HANDLER +WORKDIR /app + +ENV DEBIAN_FRONTEND=noninteractive + +# download Flutter SDK from Flutter Github repo +RUN git clone https://github.com/flutter/flutter.git /usr/local/flutter + +ENV DEBIAN_FRONTEND=dialog + +# Set flutter environment path +ENV PATH="/usr/local/flutter/bin:/usr/local/flutter/bin/cache/dart-sdk/bin:${PATH}" + +# Run flutter doctor +RUN flutter doctor + +# Resolve app dependencies. +COPY pubspec.* ./ +RUN flutter pub get + +# Ensure the ./bin folder exists +RUN mkdir -p ./bin + +# Copy app source code and AOT compile it. +COPY . . +# Ensure packages are still up-to-date if anything has changed +RUN flutter pub get --offline +# Compile the dart service into an exe +RUN dart compile exe ./${HANDLER} -o bin/main + +# Start from scratch and copy in the necessary runtime files +FROM alpine + +COPY --from=build /runtime/ / +COPY --from=build /app/bin/main /app/bin/ + +ENTRYPOINT ["/app/bin/main"] \ No newline at end of file diff --git a/v1/flutter/docker/flutter.dockerignore b/v1/flutter/docker/flutter.dockerignore new file mode 100644 index 0000000..6f67a3d --- /dev/null +++ b/v1/flutter/docker/flutter.dockerignore @@ -0,0 +1,13 @@ +build +test +.nitric +.idea +.dart_tool +.git +docker +android +ios +linux +macos +web +windows \ No newline at end of file diff --git a/v1/flutter/lib/cors.dart b/v1/flutter/lib/cors.dart new file mode 100644 index 0000000..638744b --- /dev/null +++ b/v1/flutter/lib/cors.dart @@ -0,0 +1,23 @@ +import 'package:nitric_sdk/nitric.dart'; + +/// Handle Preflight Options requests by returning status 200 to the requests +Future optionsHandler(HttpContext ctx) async { + ctx.res.headers["Content-Type"] = ["text/html; charset=ascii"]; + ctx.res.body = "OK"; + + return ctx.next(); +} + +/// Add CORS headers to responses +Future addCors(HttpContext ctx) async { + ctx.res.headers["Access-Control-Allow-Origin"] = ["*"]; + ctx.res.headers["Access-Control-Allow-Headers"] = [ + "Origin, X-Requested-With, Content-Type, Accept, Authorization", + ]; + ctx.res.headers["Access-Control-Allow-Methods"] = [ + "GET, PUT, POST, PATCH, OPTIONS, DELETE", + ]; + ctx.res.headers["Access-Control-Max-Age"] = ["7200"]; + + return ctx.next(); +} diff --git a/v1/flutter/lib/favorite.dart b/v1/flutter/lib/favorite.dart new file mode 100644 index 0000000..62470f7 --- /dev/null +++ b/v1/flutter/lib/favorite.dart @@ -0,0 +1,13 @@ +class Favorite { + /// The name of the favorite + String name; + + Favorite(this.name); + + /// Convert a json decodable map to a Favorite object + Favorite.fromJson(Map json) : name = json['name']; + + /// Convert a Favorite object to a json encodable + static Map toJson(Favorite favorite) => + {'name': favorite.name}; +} diff --git a/v1/flutter/lib/main.dart b/v1/flutter/lib/main.dart new file mode 100644 index 0000000..51a456e --- /dev/null +++ b/v1/flutter/lib/main.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:word_generator/pages/home.dart'; +import 'package:word_generator/providers/favorites.dart'; +import 'package:word_generator/providers/word.dart'; + +void main() => runApp(Application()); + +class Application extends StatelessWidget { + const Application({super.key}); + @override + Widget build(BuildContext context) { + return MultiProvider( + providers: [ + ChangeNotifierProvider(create: (context) => FavoritesProvider()), + ChangeNotifierProvider(create: (context) => WordProvider()), + ], + child: MaterialApp( + title: 'Word Generator App', + theme: ThemeData( + useMaterial3: true, + colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), + ), + home: HomePage(), // <-- Change here + ), + ); + } +} diff --git a/v1/flutter/lib/pages/favorites.dart b/v1/flutter/lib/pages/favorites.dart new file mode 100644 index 0000000..80e599c --- /dev/null +++ b/v1/flutter/lib/pages/favorites.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:word_generator/providers/favorites.dart'; + +class FavoritesPage extends StatelessWidget { + const FavoritesPage({super.key}); + + @override + Widget build(BuildContext context) { + var favorites = context.watch(); + + // If the favorites list is still loading then show a spinning circle. + if (favorites.isLoading) { + return const Center( + child: SizedBox( + width: 40, + height: 40, + child: CircularProgressIndicator(color: Colors.blue), + )); + } + + // Otherwise return a list of all the favorites + return ListView( + children: [ + Padding( + padding: const EdgeInsets.all(20), + // Display how many favorites there are + child: Text('You have ' + '${favorites.favorites.length} favorites:'), + ), + // Create a list tile for every favorite in the list of favorites + for (var favorite in favorites.favorites) + ListTile( + leading: Icon(Icons.favorite), // <- A heart icon + title: Text(favorite.name), + ), + ], + ); + } +} diff --git a/v1/flutter/lib/pages/generator.dart b/v1/flutter/lib/pages/generator.dart new file mode 100644 index 0000000..3b48167 --- /dev/null +++ b/v1/flutter/lib/pages/generator.dart @@ -0,0 +1,147 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:word_generator/providers/favorites.dart'; +import 'package:word_generator/providers/word.dart'; + +class GeneratorPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + final style = theme.textTheme.displayMedium!.copyWith( + color: theme.colorScheme.onPrimary, + ); + + final favorites = context.watch(); + final words = context.watch(); + + IconData icon = Icons.favorite_border; + + if (favorites.hasFavorite(words.current)) { + icon = Icons.favorite; + } + + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Expanded( + // <- allows the list to extend to the top of the page + flex: 3, + child: HistoryListView(), // <- Add the history list view here + ), + const SizedBox(height: 10), + Card( + color: theme.colorScheme.primary, + child: Padding( + padding: const EdgeInsets.all(20), + child: AnimatedSize( + duration: const Duration(milliseconds: 200), + child: MergeSemantics( + child: Wrap( + children: [ + Text( + words.current.first, + style: style.copyWith(fontWeight: FontWeight.w200), + ), + Text( + words.current.second, + style: style.copyWith(fontWeight: FontWeight.bold), + ) + ], + ), + ), + ), + ), + ), + const SizedBox(height: 10), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + ElevatedButton.icon( + onPressed: () { + favorites.toggleFavorite(words.current); + }, + icon: Icon(icon), + label: const Text('Like'), + ), + const SizedBox(width: 10), + ElevatedButton( + onPressed: () { + words.getNext(); + }, + child: const Text('Next'), + ), + ], + ), + const Spacer(flex: 2), + ], + ), + ); + } +} + +class HistoryListView extends StatefulWidget { + const HistoryListView({super.key}); + + @override + State createState() => _HistoryListViewState(); +} + +class _HistoryListViewState extends State { + final _key = GlobalKey(); + + // Create a mask which will make a fade out appearance by making a linear gradient of transparent -> opaque. + static const Gradient _maskingGradient = LinearGradient( + colors: [Colors.transparent, Colors.black], + stops: [0.0, 0.5], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ); + + @override + Widget build(BuildContext context) { + final favorites = context.watch(); + final words = context.watch(); + + // Set the key of the animated list to the WordProvider GlobalKey so it can be manipulated from there + // Not recommended for a production app as it can slow performance... + // Read more here: https://api.flutter.dev/flutter/widgets/GlobalKey-class.html + words.historyListKey = _key; + + return ShaderMask( + shaderCallback: (bounds) => _maskingGradient.createShader(bounds), + // This blend mode takes the opacity of the shader (i.e. our gradient) + // and applies it to the destination (i.e. our animated list). + blendMode: BlendMode.dstIn, + child: AnimatedList( + key: _key, + // Reverse the list so the latest is on the bottom + reverse: true, + padding: const EdgeInsets.only(top: 100), + initialItemCount: words.history.length, + // Build each item in the list, will be run initially and when a new word pair is added. + itemBuilder: (context, index, animation) { + final pair = words.history[index]; + return SizeTransition( + sizeFactor: animation, + child: Center( + child: TextButton.icon( + onPressed: () { + favorites.toggleFavorite(pair); + }, + // If the word pair was favorited, show a heart next to it + icon: favorites.hasFavorite(pair) + ? const Icon(Icons.favorite, size: 12) + : const SizedBox(), + label: Text( + pair.asLowerCase, + ), + ), + ), + ); + }, + ), + ); + } +} diff --git a/v1/flutter/lib/pages/home.dart b/v1/flutter/lib/pages/home.dart new file mode 100644 index 0000000..ca44b81 --- /dev/null +++ b/v1/flutter/lib/pages/home.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:word_generator/providers/favorites.dart'; +import 'favorites.dart'; +import 'generator.dart'; + +class HomePage extends StatefulWidget { + const HomePage({super.key}); + + @override + State createState() => _HomePageState(); +} + +class _HomePageState extends State { + var selectedIndex = 0; + @override + void initState() { + super.initState(); + context.read().fetchData(); + } + + @override + Widget build(BuildContext context) { + var colorScheme = Theme.of(context).colorScheme; + Widget page; + switch (selectedIndex) { + case 0: + page = GeneratorPage(); + case 1: + page = FavoritesPage(); + default: + throw UnimplementedError('no widget for $selectedIndex'); + } + // The container for the current page, with its background color + // and subtle switching animation. + var mainArea = ColoredBox( + color: colorScheme.surfaceContainerHighest, + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: page, + ), + ); + return Scaffold( + body: LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxWidth < 450) { + return Column( + children: [ + Expanded(child: mainArea), + SafeArea( + child: BottomNavigationBar( + items: const [ + BottomNavigationBarItem( + icon: Icon(Icons.home), + label: 'Home', + ), + BottomNavigationBarItem( + icon: Icon(Icons.favorite), + label: 'Favorites', + ), + ], + currentIndex: selectedIndex, + onTap: (value) { + setState(() { + selectedIndex = value; + }); + }, + ), + ) + ], + ); + } else { + return Row( + children: [ + SafeArea( + child: NavigationRail( + extended: constraints.maxWidth >= 600, + destinations: [ + NavigationRailDestination( + icon: Icon(Icons.home), + label: Text('Home'), + ), + NavigationRailDestination( + icon: Icon(Icons.favorite), + label: Text('Favorites'), + ), + ], + selectedIndex: selectedIndex, + onDestinationSelected: (value) { + setState(() { + selectedIndex = value; + }); + }, + ), + ), + Expanded(child: mainArea), + ], + ); + } + }, + ), + ); + } +} diff --git a/v1/flutter/lib/providers/favorites.dart b/v1/flutter/lib/providers/favorites.dart new file mode 100644 index 0000000..aa6ec2c --- /dev/null +++ b/v1/flutter/lib/providers/favorites.dart @@ -0,0 +1,78 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:english_words/english_words.dart'; +import 'package:word_generator/favorite.dart'; + +class FavoritesProvider extends ChangeNotifier { + final baseApiUrl = "http://localhost:4001"; + + List _favorites = []; + bool _isLoading = false; + + /// Get a list of active favorite + List get favorites => _favorites; + + /// Check whether the data is loading or not + bool get isLoading => _isLoading; + + /// Updates the list of favorites whilst returning a Future with the list of favorites. + /// Sets isLoading to true when the favorites have been fetched + Future> fetchData() async { + _isLoading = true; + notifyListeners(); + + final response = await http.get(Uri.parse("$baseApiUrl/favorites")); + + if (response.statusCode == 200) { + // Decode the json data into an iterable list of unknown objects + Iterable rawFavorites = jsonDecode(response.body); + + // Map over the iterable, converting it to a list of Favorite objects + _favorites = List.from( + rawFavorites.map((model) => Favorite.fromJson(model))); + } else { + throw Exception('Failed to load data'); + } + + _isLoading = false; + notifyListeners(); + + return _favorites; + } + + bool hasFavorite(WordPair pair) { + if (isLoading) { + return false; + } + + return _favorites.any((f) => f.name == pair.asLowerCase); + } + + /// Toggles whether a favorite being liked or unliked. + Future toggleFavorite(WordPair pair) async { + // Convert the word pair into a json encoded + final encodedFavorites = + jsonEncode(Favorite.toJson(Favorite(pair.asLowerCase))); + + // Makes a post request to the toggle favorite route. + final response = await http.post(Uri.parse("$baseApiUrl/favorite"), + body: encodedFavorites); + + // If the response doesn't respond with OK, throw an error + if (response.statusCode != 200) { + throw Exception("Failed to add favorite: ${response.body}"); + } + + // If it was successfully removed update favorites + if (hasFavorite(pair)) { + // Remove the favorite for + _favorites.removeWhere((f) => f.name == pair.asLowerCase); + } else { + _favorites.add(Favorite(pair.asLowerCase)); + } + + notifyListeners(); + } +} diff --git a/v1/flutter/lib/providers/word.dart b/v1/flutter/lib/providers/word.dart new file mode 100644 index 0000000..e542235 --- /dev/null +++ b/v1/flutter/lib/providers/word.dart @@ -0,0 +1,25 @@ +import 'package:english_words/english_words.dart'; +import 'package:flutter/material.dart'; + +class WordProvider extends ChangeNotifier { + // The current word pair + var current = WordPair.random(); + // A list of all generated word pairs + var history = []; + + // A key that is used to get a reference to the history list state + GlobalKey? historyListKey; + + // Generate a new word pair and notify the listeners + void getNext() { + // Add the current pair to the start of the history list + history.insert(0, current); + + // Adds space to the start of the animated list and triggers an animation to start + var animatedList = historyListKey?.currentState as AnimatedListState?; + animatedList?.insertItem(0); + + current = WordPair.random(); + notifyListeners(); + } +} diff --git a/v1/flutter/lib/services/main.dart b/v1/flutter/lib/services/main.dart new file mode 100644 index 0000000..8af4b29 --- /dev/null +++ b/v1/flutter/lib/services/main.dart @@ -0,0 +1,58 @@ +import 'dart:convert'; +import 'package:word_generator/cors.dart'; +import 'package:word_generator/favorite.dart'; + +import 'package:nitric_sdk/nitric.dart'; + +void main() async { + final api = Nitric.api("main", opts: ApiOptions(middlewares: [addCors])); + + final favoritesKV = Nitric.kv("favorites").allow([ + KeyValueStorePermission.get, + KeyValueStorePermission.set, + KeyValueStorePermission.delete + ]); + + api.options("/favorites", optionsHandler); + api.get("/favorites", (ctx) async { + var keys = await favoritesKV.keys(); + + var favorites = await keys.asyncMap((k) async { + final favorite = await favoritesKV.get(k); + + return favorite; + }).toList(); + + ctx.res.body = jsonEncode(favorites); + + return ctx; + }); + + api.options("/favorite", optionsHandler); + api.post("/favorite", (ctx) async { + final req = ctx.req.json(); + + final favorite = Favorite.fromJson(req); + + var exists = false; + + final keys = await favoritesKV.keys(prefix: favorite.name); + + await for (final key in keys) { + if (key == favorite.name) { + exists = true; + } + } + + // if it exists delete and return + if (exists) { + await favoritesKV.delete(favorite.name); + + return ctx; + } + + await favoritesKV.set(favorite.name, Favorite.toJson(favorite)); + + return ctx; + }); +} diff --git a/v1/flutter/nitric-spec.json b/v1/flutter/nitric-spec.json new file mode 100644 index 0000000..42da14a --- /dev/null +++ b/v1/flutter/nitric-spec.json @@ -0,0 +1,61 @@ +{ + "resources": [ + { + "id": { + "name": "main" + }, + "api": { + "openapi": "{\"components\":{},\"info\":{\"title\":\"main\",\"version\":\"v1\"},\"openapi\":\"3.0.1\",\"paths\":{\"/favourite\":{\"options\":{\"operationId\":\"favouriteoptions\",\"responses\":{\"default\":{\"description\":\"\"}},\"security\":[],\"x-nitric-target\":{\"name\":\"backend_lib-services-main\",\"type\":\"function\"}},\"post\":{\"operationId\":\"favouritepost\",\"responses\":{\"default\":{\"description\":\"\"}},\"security\":[],\"x-nitric-target\":{\"name\":\"backend_lib-services-main\",\"type\":\"function\"}}},\"/favourites\":{\"get\":{\"operationId\":\"favouritesget\",\"responses\":{\"default\":{\"description\":\"\"}},\"security\":[],\"x-nitric-target\":{\"name\":\"backend_lib-services-main\",\"type\":\"function\"}},\"options\":{\"operationId\":\"favouritesoptions\",\"responses\":{\"default\":{\"description\":\"\"}},\"security\":[],\"x-nitric-target\":{\"name\":\"backend_lib-services-main\",\"type\":\"function\"}}}}}" + } + }, + { + "id": { + "type": "KeyValueStore", + "name": "favourites" + }, + "keyValueStore": {} + }, + { + "id": { + "type": "Policy", + "name": "4e19479ef94af74f894d83df7315494c" + }, + "policy": { + "principals": [ + { + "id": { + "type": "Service", + "name": "backend_lib-services-main" + } + } + ], + "actions": [ + "KeyValueStoreRead", + "KeyValueStoreWrite", + "KeyValueStoreDelete" + ], + "resources": [ + { + "id": { + "type": "KeyValueStore", + "name": "favourites" + } + } + ] + } + }, + { + "id": { + "type": "Service", + "name": "backend_lib-services-main" + }, + "service": { + "image": { + "uri": "backend_lib-services-main" + }, + "workers": 1, + "type": "default" + } + } + ] +} \ No newline at end of file diff --git a/v1/flutter/nitric.dev.yaml b/v1/flutter/nitric.dev.yaml new file mode 100755 index 0000000..cbae95e --- /dev/null +++ b/v1/flutter/nitric.dev.yaml @@ -0,0 +1,68 @@ +# The nitric provider to use +provider: nitric/aws@1.11.1 +# The target aws region to deploy to +# See available regions: +# https://docs.aws.amazon.com/general/latest/gr/lambda-service.html +region: us-east-1 +# Optional Configuration Below + +# The timezone that deployed schedules will run with +# Format is in tz identifiers: +# https://en.wikipedia.org/wiki/List_of_tz_database_time_zones +# schedule-timezone: Australia/Sydney # Available since v0.27.0 + +# Import existing AWS Resources +# Currently only secrets are supported +# Available since v0.28.0 +# import: +# # A name ARN map of secrets, where the name matches the nitric name of the secret you would like to import +# secrets: # Available since v0.28.0 +# # In typescript this would import the provided secret reference for a secret declared as +# # const mySecret = secret('my-secret'); +# my-secret: arn:... + +# # Apply configuration to nitric APIs +# apis: +# # The nitric name of the API to configure +# my-api: +# # Array of domains to apply to the API +# # The domain or parent domain must have a hosted zone already in Route53 +# domains: +# - api.example.com + +# # Configure your deployed functions/services +# config: +# # How functions without a type will be deployed +# default: +# # configure a sample rate for telemetry (between 0 and 1) e.g. 0.5 is 50% +# telemetry: 0 +# # configure functions to deploy to AWS lambda +# lambda: # Available since v0.26.0 +# # set 128MB of RAM +# # See lambda configuration docs here: +# # https://docs.aws.amazon.com/lambda/latest/dg/configuration-function-common.html#configuration-memory-console +# memory: 128 +# # set a timeout of 15 seconds +# # See lambda timeout values here: +# # https://docs.aws.amazon.com/lambda/latest/dg/configuration-function-common.html#configuration-timeout-console +# timeout: 15 +# # set a provisioned concurrency value +# # For info on provisioned concurrency for AWS Lambda see: +# # https://docs.aws.amazon.com/lambda/latest/dg/configuration-concurrency.html +# provisioned-concurrency: 0 +# # Configure VPCs that the lambda can access +# vpc: +# # Array of existing security group ids to apply +# security-group-ids: +# - sg-xxx +# # Array of existing subnet ids to apply +# subnet-ids: +# - subnet-xxx +# # Additional deployment types +# # You can target these types by setting a `type` in your project configuration +# big-service: +# telemetry: 0 +# lambda: +# memory: 1024 +# timeout: 60 +# provisioned-concurrency: 1 diff --git a/v1/flutter/nitric.yaml b/v1/flutter/nitric.yaml new file mode 100644 index 0000000..c3f15fc --- /dev/null +++ b/v1/flutter/nitric.yaml @@ -0,0 +1,9 @@ +name: backend +services: + - match: lib/services/*.dart + runtime: flutter + start: dart run $SERVICE_PATH +runtimes: + flutter: + dockerfile: ./docker/flutter.dockerfile + args: {} diff --git a/v1/flutter/pubspec.yaml b/v1/flutter/pubspec.yaml new file mode 100644 index 0000000..7fde40a --- /dev/null +++ b/v1/flutter/pubspec.yaml @@ -0,0 +1,92 @@ +name: word_generator +description: "A new Flutter project." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: "none" # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: ">=3.4.4 <4.0.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.6 + english_words: ^4.0.0 + http: ^1.2.2 + provider: ^6.1.2 + nitric_sdk: ^1.4.0 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^3.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages