diff --git a/dart/file_backup_v2/README.md b/dart/file_backup_v2/README.md new file mode 100644 index 00000000..aabffb99 --- /dev/null +++ b/dart/file_backup_v2/README.md @@ -0,0 +1,47 @@ +# 🗂 File Backup using the Dropbox API +A sample Dart Cloud Function that leverages Dropbox to create backups of important files uploaded to Appwrite. + +## 📝 Environment Variables +Add the following environment variables in your Cloud Function settings. + +* **APPWRITE_API_KEY** - Create a key from the Appwrite console with the following scope (`files.read`) +* **APPWRITE_ENDPOINT** - Your Appwrite Endpoint +* **DROPBOX_KEY** - OAuth token from [Dropbox](https://blogs.dropbox.com/developers/2014/05/generate-an-access-token-for-your-own-account) + +## 🚀 Building and Packaging + +To package this example as a cloud function, follow these steps. + +```bash +$ cd demos-for-functions/dart/file_backup + +$ PUB_CACHE=.appwrite/ dart pub get +``` + +* Ensure that your folder structure looks like this +``` +. +├── .appwrite/ +├── main.dart +├── pubspec.lock +└── pubspec.yaml +``` + +* Create a tarfile + +```bash +$ cd .. +$ tar -zcvf code.tar.gz file_backup +``` + +* Upload the tarfile to your Appwrite Console and use the following entrypoint command + +```bash +dart main.dart +``` + +## 🎯 Trigger + +Head over to your function in the Appwrite console and under the Settings Tab, enable the `storage.files.create` event. + +If your function errors out with code 124, increase the timeout under the Settings Tab. \ No newline at end of file diff --git a/dart/file_backup_v2/main.dart b/dart/file_backup_v2/main.dart new file mode 100644 index 00000000..3235f55b --- /dev/null +++ b/dart/file_backup_v2/main.dart @@ -0,0 +1,62 @@ +import 'dart:convert'; +import 'dart:typed_data'; +import 'package:dart_appwrite/dart_appwrite.dart'; +import 'package:http/http.dart' as http; + +Future backup( + Uint8List buffer, + String originalFileName, + String dropboxAuthToken, +) async { + final dropboxEndpoint = + Uri.parse('https://content.dropboxapi.com/2/files/upload'); + + final headers = { + 'Authorization': 'Bearer $dropboxAuthToken', + 'Content-Type': 'application/octet-stream', + 'Dropbox-API-Arg': '{"path": "/$originalFileName", "mode": "add" }' + }; + + final dropboxResponse = + await http.post(dropboxEndpoint, body: buffer, headers: headers); + + if (dropboxResponse.statusCode != 200) { + throw Exception( + 'Could not backup file $originalFileName! Dropbox response was: ${dropboxResponse.body}'); + } +} + +Future start(final request, final response) async { + final env = request.env; + final endpoint = env['APPWRITE_ENDPOINT']; + if (endpoint == null) { + throw ('Appwrite endpoint not specified. Please set an environment variable "APPWRITE_ENDPOINT" with your endpoint to the function'); + } + + final client = Client() + ..setEndpoint(endpoint) + ..setProject(env['APPWRITE_FUNCTION_PROJECT_ID']) + ..setKey(env['APPWRITE_API_KEY']); + + final payload = jsonDecode(env['APPWRITE_FUNCTION_EVENT_DATA']!); + final bucketId = payload['bucketId']; + final fileId = payload['\$id']!; + final originalName = payload[r'name']!; + + final storage = Storage(client); + + final dropboxKey = env['DROPBOX_KEY']; + if (dropboxKey == null) { + throw( + 'Dropbox key not specified. Please set an environment variable "DROPBOX_KEY" containing your dropbox key'); + } + + final response = await storage.getFileDownload(bucketId: bucketId, fileId: fileId); + + try { + await backup(response.data!, originalName, dropboxKey!); + response.send('Successfully backed up $originalName'); + } catch (e) { + response.send('Failed backing up the file: $e'); + } +} diff --git a/dart/file_backup_v2/pubspec.yaml b/dart/file_backup_v2/pubspec.yaml new file mode 100644 index 00000000..38cf4def --- /dev/null +++ b/dart/file_backup_v2/pubspec.yaml @@ -0,0 +1,10 @@ +name: file_backup +description: Demo for backing up files to Dropbox in an Appwrite function. + +environment: + sdk: '>=2.12.0 <3.0.0' + +dependencies: + dart_appwrite: ^4.0.1 + http: ^0.13.4 + \ No newline at end of file diff --git a/dart/generate-giphy-gif_v2/.gitignore b/dart/generate-giphy-gif_v2/.gitignore new file mode 100644 index 00000000..2fd0be1c --- /dev/null +++ b/dart/generate-giphy-gif_v2/.gitignore @@ -0,0 +1,9 @@ +# Files and directories created by pub. +.dart_tool/ +.packages + +# Conventional directory for build output. +build/ + +# Some Constants. +constants.dart \ No newline at end of file diff --git a/dart/generate-giphy-gif_v2/README.md b/dart/generate-giphy-gif_v2/README.md new file mode 100644 index 00000000..b1db8f8c --- /dev/null +++ b/dart/generate-giphy-gif_v2/README.md @@ -0,0 +1,61 @@ +# GIPHYGif Generator + +A sample Dart Cloud Function which takes a keyword and returns the first result with giphy gif url + +## Getting Started + +Head over to the [Giphy Developers](https://developers.giphy.com/docs/api) to read their quick start guide and get your API key. Make sure to use the **GIPHY API** and not the **GIPHY SDK**. + + +Since we are retrieving an url from the API, the GIPHY API is more than enough for our needs. + +## ☁️ Create a new Function + +**NOTE:** If your project is not created yet, you will be prompted to create a new project. + +1. Go to your `appwrite console` +2. Select your project and go to `Functions` + +3. Under `Functions` click on `Add Function` + + +**NOTE:** If you don't see the Dart option in the runtimes. Follow [`troubleshooting.md`](troubleshoot.md) guide to learn how to add the Dart runtime. + +## 📝 Environment Variables + +Go to Settings tab of your Cloud Function. Add the following environment variable. + +* **GIPHY_API_KEY** - API Key of your GIPHY account + +## 🚀 Building and Packaging + +To package this example as a cloud function, follow these steps. + +* Ensure that your folder structure looks like this + +``` +. +├── main.dart +└── pubspec.yaml +``` + +* Create a tarfile + +```bash +cd .. +tar -zcvf code.tar.gz generate-giphy-gif +``` + +* Navigate to the Overview Tab of your Cloud Function > Deploy Tag +* Input the command that will run your function (in this case "main.dart") as your entrypoint +* Upload your tarfile +* Click 'Activate' + +## 🎯 Trigger + +Head over to your function in the Appwrite console and just press **Execute**. You will notice the output in your logs + + +## 👨‍💻 Live Working + + \ No newline at end of file diff --git a/dart/generate-giphy-gif_v2/main.dart b/dart/generate-giphy-gif_v2/main.dart new file mode 100644 index 00000000..9cf3b493 --- /dev/null +++ b/dart/generate-giphy-gif_v2/main.dart @@ -0,0 +1,29 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; + +Future start(final request, final response) async { + Uri url = Uri(); + if(request.env['GIPHY_API_KEY'] == null) { + throw Exception("GIPHY_API_KEY is required"); + } + + url = url.replace( + scheme: 'https', + host: 'api.giphy.com', + path: 'v1/gifs/search', + queryParameters: { + 'api_key': request.env['GIPHY_API_KEY'], // The Api key which we will store in Appwrite Console. + // Note you can change the var name to something else too. + + 'q': env['APPWRITE_FUNCTION_DATA'], // The search query which we will pass during execution + 'limit': '1', // Since we want the first top result we set the limit to 1. + // This parameter is optional. To read about what parameters you can pass . + // https://developers.giphy.com/docs/api/endpoint#search -> Refer to this + }, + ); + + http.Response response = await http.get(url); + + var data = json.decode(response.body); + response.send(data['data'][0]['url']); +} diff --git a/dart/generate-giphy-gif_v2/pubspec.yaml b/dart/generate-giphy-gif_v2/pubspec.yaml new file mode 100644 index 00000000..c212e452 --- /dev/null +++ b/dart/generate-giphy-gif_v2/pubspec.yaml @@ -0,0 +1,10 @@ +name: giphy +description: A simple command-line application. +version: 1.0.0 + +environment: + sdk: ">=2.12.0 <3.0.0" + + +dependencies: + http: ^0.13.4 diff --git a/dart/generate-giphy-gif_v2/screenshots/SDKvsAPI.png b/dart/generate-giphy-gif_v2/screenshots/SDKvsAPI.png new file mode 100644 index 00000000..805d0644 Binary files /dev/null and b/dart/generate-giphy-gif_v2/screenshots/SDKvsAPI.png differ diff --git a/dart/generate-giphy-gif_v2/screenshots/ans.gif b/dart/generate-giphy-gif_v2/screenshots/ans.gif new file mode 100644 index 00000000..356502cc Binary files /dev/null and b/dart/generate-giphy-gif_v2/screenshots/ans.gif differ diff --git a/dart/generate-giphy-gif_v2/screenshots/function2.png b/dart/generate-giphy-gif_v2/screenshots/function2.png new file mode 100644 index 00000000..095b875e Binary files /dev/null and b/dart/generate-giphy-gif_v2/screenshots/function2.png differ diff --git a/dart/generate-giphy-gif_v2/screenshots/functions.png b/dart/generate-giphy-gif_v2/screenshots/functions.png new file mode 100644 index 00000000..c9987d9f Binary files /dev/null and b/dart/generate-giphy-gif_v2/screenshots/functions.png differ diff --git a/dart/generate-giphy-gif_v2/screenshots/ss4.png b/dart/generate-giphy-gif_v2/screenshots/ss4.png new file mode 100644 index 00000000..5cc0baeb Binary files /dev/null and b/dart/generate-giphy-gif_v2/screenshots/ss4.png differ diff --git a/dart/generate-giphy-gif_v2/troubleshoot.md b/dart/generate-giphy-gif_v2/troubleshoot.md new file mode 100644 index 00000000..5db69f31 --- /dev/null +++ b/dart/generate-giphy-gif_v2/troubleshoot.md @@ -0,0 +1,27 @@ +
+ +# TroubleShoot Guide ⚒️ + +
+ +## Add Dart Runtime to your Appwrite Console + +* Go to your appwrite directory + +If you are using WSL it should be located in `\home\username\appwrite\` + +* Open up the `.env` file + +* Keep the `_APP_FUNCTIONS_ENVS` empty and add the `dart-2.17` alongside the existing ones in the `_APP_FUNCTIONS_RUNTIMES` + + + +* Save the file + +* Then run the following command in the WSL terminal + +```bash +docker compose up -d +``` + +This command will restart all the existing appwrite containers along with the latest changes. Now hopover to appwrite console and you can see the `Dart` Runtime option in the functions. \ No newline at end of file diff --git a/dart/generate_open_street_map_v2/README.md b/dart/generate_open_street_map_v2/README.md new file mode 100644 index 00000000..96b44378 --- /dev/null +++ b/dart/generate_open_street_map_v2/README.md @@ -0,0 +1,47 @@ +# 🗂 Generate Open Street Maps +A sample Dart Cloud Function that saves an open street maps tile for given latitude and longitude to Appwrite storage. + +## 📝 Environment Variables +Add the following environment variables in your Cloud Function settings. + +* **APPWRITE_API_KEY** - Create a key from the Appwrite console with the following scope (`files.write`) +* **APPWRITE_ENDPOINT** - Your Appwrite Endpoint +* **APPWRITE_BUCKET_ID** - ID of the bucket that you want to upload the map image to + +## 🚀 Building and Packaging + +To package this example as a cloud function, follow these steps. + +* Ensure that your folder structure looks like this +``` +. +├── main.dart +├── pubspec.lock +└── pubspec.yaml +``` + +* Create a tarfile + +```bash +$ cd .. +$ tar -zcvf code.tar.gz generate_open_street_map +``` + +* Upload the tarfile to your Appwrite Console and use the following entrypoint + +```bash +main.dart +``` + +## 🎯 Trigger + +Head over to your function in the Appwrite console and after clicking the `Execute Now` button, enter a JSON object in the form of +```json +{ + "latitude": 37.7822403, + "longitude": -122.3910414 +} +``` +with your respective coordinates. + +If your function errors out with code 124, increase the timeout under the Settings Tab. \ No newline at end of file diff --git a/dart/generate_open_street_map_v2/main.dart b/dart/generate_open_street_map_v2/main.dart new file mode 100644 index 00000000..8f00f835 --- /dev/null +++ b/dart/generate_open_street_map_v2/main.dart @@ -0,0 +1,72 @@ +import 'dart:convert'; +import 'dart:math'; +import 'dart:io'; + +import 'package:http/http.dart' as http; +import 'package:dart_appwrite/dart_appwrite.dart'; +import 'package:vector_math/vector_math.dart'; + +const zoomLevel = 15; + +String ensureEnvVariable(String key, final request) { + final value = request.env[key]; + if (value == null) { + print( + 'Could not find environment variable $key. Please set it following the instructions in the readme file.'); + exit(1); + } + + return value; +} + +// Adapted from https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Mathematics +Point coordinatesToTilePoint(double latitude, double longitude) { + final x = (pow(2, zoomLevel) * ((longitude + 180) / 360)).floor(); + + final radLat = radians(latitude); + final y = (pow(2, zoomLevel - 1) * + (1 - (log(tan(radLat) + (1 / cos(radLat))) / pi))) + .floor(); + + return Point(x, y); +} + +Future> fetchTile(double latitude, double longitude) async { + final point = coordinatesToTilePoint(latitude, longitude); + final tileUrl = + 'https://tile.openstreetmap.org/${zoomLevel}/${point.x}/${point.y}.png'; + + print(tileUrl); + + final response = await http.get(Uri.parse(tileUrl)); + if (response.statusCode != 200) { + print('There was an error loading the tile for $point: ${response.body}'); + exit(1); + } + + return response.bodyBytes; +} + +Future start(final request, final response) async { + final client = Client(); + client + .setKey(ensureEnvVariable('APPWRITE_API_KEY', request)) + .setProject(ensureEnvVariable('APPWRITE_FUNCTION_PROJECT_ID', request)) + .setEndpoint(ensureEnvVariable('APPWRITE_ENDPOINT', request)); + + final data = jsonDecode(ensureEnvVariable('APPWRITE_FUNCTION_DATA', request)); + final bucketId = ensureEnvVariable('APPWRITE_BUCKET_ID'); + + final storage = Storage(client); + final latitude = data['latitude']; + final longitude = data['longitude']; + + final imageBytes = await fetchTile(latitude, longitude); + final filename = 'osm_tile_${latitude}_${longitude}_z$zoomLevel.png'; + + final file = await storage.createFile( + bucketId: bucketId, + file: InputFile.fromBytes('file', imageBytes, filename: filename, contentType: 'image/png'), + ); + response.json({"message": "Map created", "fileId": file.$id, "bucketId": bucketId }); +} diff --git a/dart/generate_open_street_map_v2/pubspec.yaml b/dart/generate_open_street_map_v2/pubspec.yaml new file mode 100644 index 00000000..15a6f1fe --- /dev/null +++ b/dart/generate_open_street_map_v2/pubspec.yaml @@ -0,0 +1,10 @@ +name: generate_open_street_map +description: An example implementation of saving an OpenStreetMaps tile to Storage given latitude and longitude +version: 1.0.0 + +environment: + sdk: '>=2.12.0 <2.13.0' + +dependencies: + dart_appwrite: ^5.0.1 + vector_math: ^2.1.2 diff --git a/dart/storage_cleaner_v2/README.md b/dart/storage_cleaner_v2/README.md new file mode 100644 index 00000000..35effe3b --- /dev/null +++ b/dart/storage_cleaner_v2/README.md @@ -0,0 +1,51 @@ +# 🚮 Clean up files in your storage older than XX days +A sample Dart Cloud Function for deleting files that are older than XX days on a schedule. + +## 📝 Environment Variables +Go to Settings tab of your Cloud Function. Add the following environment variables. + +* **APPWRITE_ENDPOINT** - Your Appwrite Endpoint +* **APPWRITE_API_KEY** - Your Appwrite API key with `files.read` and `files.write` permissions +* **DAYS_TO_EXPIRE** - Days for files to expire + +## 🚀 Building and Packaging + +To package this example as a cloud function, follow these steps. + +```bash +$ cd demos-for-functions/dart/storage_cleaner + +$ export PUB_CACHE=.appwrite/ +$ dart pub get + +``` +* Ensure that your folder structure looks like this +``` +. +├── main.dart +├── .appwrite/ +├── pubspec.lock +└── pubspec.yaml +``` + +* Create a tarfile + +```bash +$ cd .. +$ tar -zcvf code.tar.gz storage_cleaner +``` + +* Navigate to the Overview Tab of your Cloud Function > Deploy Tag +* Input the command that will run your function (in this case `dart main.dart`) as your entrypoint command +* Upload your tarfile +* Click 'Activate' + +## ⏰ Schedule + +Head over to your function in the Appwrite console and under the Settings Tab, enter a reasonable schedule time (cron syntax). + +For example: + +- `*/30 * * * *` every 30 minutes +- `0 * * * *` every hour +- `0 0 * * *` every day diff --git a/dart/storage_cleaner_v2/main.dart b/dart/storage_cleaner_v2/main.dart new file mode 100644 index 00000000..0cc0f780 --- /dev/null +++ b/dart/storage_cleaner_v2/main.dart @@ -0,0 +1,41 @@ +import 'package:dart_appwrite/dart_appwrite.dart' hide Response; + +Future start(final request, final response) async { + // Initialise the client SDK + Map envVars = request.env; + final Client client = Client(); + if( + envVars['APPWRITE_ENDPOINT'] == null + || envVars['APPWRITE_API_KEY'] == null + || envVars['APPWRITE_BUCKET'] == null + || envVars['DAYS_TO_EXPIRE'] == null + ) { + throw("Environment variables not found: " + envVars.toString()); + } + client + .setEndpoint(envVars['APPWRITE_ENDPOINT'] ?? + 'http://192.168.10.4/v1') // This is manually set + .setProject(envVars[ + 'APPWRITE_FUNCTION_PROJECT_ID']) // this is available by default + .setKey(envVars['APPWRITE_API_KEY']); + + // Initialise the storage SDK + final storage = new Storage(client); + String bucketId = envVars['APPWRITE_BUCKET'].toString(); + int daysToExpire = int.parse(envVars['DAYS_TO_EXPIRE'].toString()); + + final res = await storage.listFiles(bucketId: bucketId, orderType: 'DESC', limit: 100); + final files = res.files; + var timestamp = DateTime.now() + .subtract(Duration(days: daysToExpire)) + .millisecondsSinceEpoch; + var deletedFiles = 0; + for (final file in files) { + if (file.dateCreated * 1000 < timestamp) { + await storage.deleteFile(bucketId: bucketId, fileId: file.$id); + print("Deleted ${file.$id}"); + deletedFiles++; + } + } + response.send("Total files deleted: $deletedFiles"); +} diff --git a/dart/storage_cleaner_v2/pubspec.yaml b/dart/storage_cleaner_v2/pubspec.yaml new file mode 100644 index 00000000..bf05bbf4 --- /dev/null +++ b/dart/storage_cleaner_v2/pubspec.yaml @@ -0,0 +1,9 @@ +name: storage_cleaner +version: 1.0.0 +description: "" + +environment: + sdk: ">=2.12.0 <3.0.0" + +dependencies: + dart_appwrite: ^4.0.1 diff --git a/dart/welcome_email_v2/README.md b/dart/welcome_email_v2/README.md new file mode 100644 index 00000000..b4b462fd --- /dev/null +++ b/dart/welcome_email_v2/README.md @@ -0,0 +1,44 @@ +# 📧 Sending Welcome Emails using Mailgun's Email API +A sample Dart Cloud Function for sending a welcome email to a newly registered user. + +## 📝 Environment Variables +Go to Settings tab of your Cloud Function. Add the following environment variables. + +* **MAILGUN_API_KEY** - API Key for Mailgun +* **MAILGUN_DOMAIN** - Domain Name from Mailgun + +## 🚀 Building and Packaging + +To package this example as a cloud function, follow these steps. + +```bash +$ cd demos-for-functions/dart/welcome_email + +$ export PUB_CACHE=.appwrite/ +$ dart pub get +``` + +* Ensure that your folder structure looks like this +``` +. +├── main.dart +├── .appwrite +├── pubspec.lock +└── pubspec.yaml +``` + +* Create a tarfile + +```bash +$ cd .. +$ tar -zcvf code.tar.gz welcome_email +``` + +* Navigate to the Overview Tab of your Cloud Function > Deploy Tag +* Input the command that will run your function (in this case "dart main.dart") as your entrypoint command +* Upload your tarfile +* Click 'Activate' + +## 🎯 Trigger + +Head over to your function in the Appwrite console and under the Settings Tab, enable the `users.create` and `account.create` event. diff --git a/dart/welcome_email_v2/main.dart b/dart/welcome_email_v2/main.dart new file mode 100644 index 00000000..900f1b56 --- /dev/null +++ b/dart/welcome_email_v2/main.dart @@ -0,0 +1,114 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:http/http.dart' as http; + +Future start(final request, final response) async { + Map envVars = request.env; + + if (envVars['MAILGUN_DOMAIN'] == null || envVars['MAILGUN_API_KEY'] == null) { + throw ("Envorinment variables not found: " + envVars.toString()); + } + + final domain = envVars['MAILGUN_DOMAIN']!; + final mailgun = MailgunMailer( + apiKey: envVars['MAILGUN_API_KEY']!, + domain: domain, + ); + + // Get the name and email of the newly created user from Appwrite's environment variable + final payload = jsonDecode(envVars['APPWRITE_FUNCTION_EVENT_DATA']!); + final name = payload['name']; + final email = payload['email']; + +// Create your email + final sent = await mailgun.send( + from: 'Welcome to My Awesome App ', + to: [email], + subject: 'Welcome on board ${name}!', + text: 'Hi ${name}\nGreat to have you with us. ! 😍'); + if (sent) { + response.send("email sent successfully."); + } else { + response.send("failed to send message"); + } +} + +class MailgunMailer { + final String domain; + final String apiKey; + + MailgunMailer({required this.domain, required this.apiKey}); + + Future send( + {String? from, + List to = const [], + List cc = const [], + List bcc = const [], + List attachments = const [], + String? subject, + String? html, + String? text, + String? template, + Map? options}) async { + var client = http.Client(); + try { + var request = http.MultipartRequest( + 'POST', + Uri( + userInfo: 'api:$apiKey', + scheme: 'https', + host: 'api.mailgun.net', + path: '/v3/$domain/messages')); + if (subject != null) { + request.fields['subject'] = subject; + } + if (html != null) { + request.fields['html'] = html; + } + if (text != null) { + request.fields['text'] = text; + } + if (from != null) { + request.fields['from'] = from; + } + if (to.length > 0) { + request.fields['to'] = to.join(", "); + } + if (cc.length > 0) { + request.fields['cc'] = cc.join(", "); + } + if (bcc.length > 0) { + request.fields['bcc'] = bcc.join(", "); + } + if (template != null) { + request.fields['template'] = template; + } + if (options != null) { + if (options.containsKey('template_variables')) { + request.fields['h:X-Mailgun-Variables'] = + jsonEncode(options['template_variables']); + } + } + if (attachments.length > 0) { + request.headers["Content-Type"] = "multipart/form-data"; + for (var i = 0; i < attachments.length; i++) { + var attachment = attachments[i]; + if (attachment is File) { + request.files.add(await http.MultipartFile.fromPath( + 'attachment', attachment.path)); + } + } + } + var response = await client.send(request); + if (response.statusCode != HttpStatus.ok) { + return false; + } + + return true; + } catch (e) { + return false; + } finally { + client.close(); + } + } +} diff --git a/dart/welcome_email_v2/pubspec.yaml b/dart/welcome_email_v2/pubspec.yaml new file mode 100644 index 00000000..832158bd --- /dev/null +++ b/dart/welcome_email_v2/pubspec.yaml @@ -0,0 +1,9 @@ +name: functions_demo_dart_welcome_email +description: "" +version: 1.0.0 + +environment: + sdk: '>=2.12.0 <3.0.0' + +dependencies: + http: ^0.13.4