<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Irena Russeva - SashiDo.io | API Development, Deployment and Scaling made simple.]]></title><description><![CDATA[SashiDo.io is a serverless API development platform with scalable json rest and graphql apis, headless cms, built with nodejs, mongodb, parse server, kubernetes and docker.]]></description><link>https://blog.sashido.io/</link><image><url>https://blog.sashido.io/favicon.png</url><title>Irena Russeva - SashiDo.io | API Development, Deployment and Scaling made simple.</title><link>https://blog.sashido.io/</link></image><generator>Ghost 1.20</generator><lastBuildDate>Tue, 26 May 2026 05:00:49 GMT</lastBuildDate><atom:link href="https://blog.sashido.io/author/irena/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[iOS Push Notifications: How to start using APNS Token-Based Authentication.]]></title><description><![CDATA[As many of you know, Apple is moving from a certificate-based authentication with their push notifications service to a token-based one. SashiDo is fully ready with the implementation of any changes required from our end and here is a simple guide that will help complete the process seamlessly.]]></description><link>https://blog.sashido.io/ios-push-notifications-how-to-start-using-apns-token-based-authentication/</link><guid isPermaLink="false">6053779214887600170b6221</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[iOS]]></category><category><![CDATA[ios-push-notifications]]></category><category><![CDATA[Push Notifications]]></category><dc:creator><![CDATA[Irena Russeva]]></dc:creator><pubDate>Sun, 21 Mar 2021 07:19:03 GMT</pubDate><media:content url="https://media-blog.sashido.io/content/images/2021/03/cover.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://media-blog.sashido.io/content/images/2021/03/cover.png" alt="iOS Push Notifications: How to start using APNS Token-Based Authentication."><p>As many of you know, Apple is moving from a certificate-based authentication with their push notifications service to a token-based one. Token-based authentication is stateless communication and therefore faster than certificate-based one. Additional advantages of token-based authentication is that tokens do not expire and you use only one token to send notifications for all of your environments.</p>
<blockquote>
<p>The deadline to upgrade to the APNs provider API has been set to March 31, 2021 and after this date, APNs will no longer support the legacy binary protocol.</p>
</blockquote>
<p>We know that every change comes with its inconveniences and over the last couple of weeks we’ve had many clients asking: &quot;Should I modify something for my app, so I can have push notifications working?&quot; - No worries! We took the care to provide you with all the actions you need to perform in this simple guide.</p>
<p>Basically you’ll need to take two easy steps - <a href="#receiveauthenticationkeyfromapple">Receive Authentication Key from Apple</a> and <a href="#setthecredentialstoyoursashidoapp">Set the credentials to your SashiDo App</a></p>
<h1 id="receiveauthenticationkeyfromapple">Receive Authentication Key from Apple</h1>
<p>To generate  an APNs auth key, visit the <a href="https://developer.apple.com/account/">Apple Developer Member Center</a> and follow the steps below:</p>
<ol>
<li>Go to <strong><code>Certificates, Identifiers &amp; Profiles</code></strong>.</li>
<li>Click on <strong><code>Keys</code></strong> from the Menu on the left.</li>
<li>Add a new key - from the <strong><code>+</code></strong> button in the upper right corner.</li>
<li>Next, you’ll need to enter a <strong><code>Key Description Name</code></strong> and select the service you want enabled. In our case that’ll be APNS.</li>
<li>Next, press the <strong><code>Continue</code></strong> button.</li>
<li>You are automatically landed to a page with the option to <strong><code>Download</code></strong> the .p8 auth key file.</li>
</ol>
<h1 id="setthecredentialstoyoursashidoapp">Set the Credentials to your SashiDo App</h1>
<p>For sending push notifications, Apple requires the following information about your app - BundleId, KeyId and TeamId. So that’s the credentials you’ll need to add to your SashiDo app.</p>
<ol>
<li>Go to your Dashboard -&gt; App -&gt; App Settings -&gt; Push -&gt; Apple Push Tokens and click Upload a File.</li>
</ol>
<p><img src="https://media-blog.sashido.io/content/images/2021/03/add_key_0.png" alt="iOS Push Notifications: How to start using APNS Token-Based Authentication."></p>
<ol start="2">
<li>Upload the .p8 file and enter your credentials</li>
</ol>
<p><img src="https://media-blog.sashido.io/content/images/2021/03/add_key_2.1-1.jpg" alt="iOS Push Notifications: How to start using APNS Token-Based Authentication."></p>
<p><strong>BundleId</strong> - The BundleId of your project.<br>
<strong>KeyId</strong> - That’s the KeyId for the Authentication token.<br>
<strong>TeamId</strong> - That’s the ID for your Apple Account. You can find it from “View Account”.</p>
<ol start="3">
<li>Et Voila! Your APNS token is uploaded and you can start sending millions of push notifications per minute with <a href="https://blog.sashido.io/sending-milions-of-push-notifications-with-go-redis-and-nats/">SashiDo’s Push Notification Service</a>.</li>
</ol>
<p><img src="https://media-blog.sashido.io/content/images/2021/03/Screenshot-from-2021-03-17-22-15-53.png" alt="iOS Push Notifications: How to start using APNS Token-Based Authentication."></p>
<blockquote>
<p>We would recommend you to remove the old .p12 file from your SashiDo app, once you switch to the APNS token-based authentication.</p>
</blockquote>
<p>Well, that’s all folks! If any questions arise, our team is always happy to help! Reach us at <a href="mailto:support@sashido.io">support@sashido.io</a>.</p>
<p>Happy Coding!</p>
</div>]]></content:encoded></item><item><title><![CDATA[How to load a Teachable Machine image model in a Node.JS project]]></title><description><![CDATA[A short tutorial on how to easily train and load a Teachable Machine's Image classification model to your Node.js application in less than 10 minutes. Machine Learning is no longer a reserved filed only for experienced Data Scientist and ML engineers!]]></description><link>https://blog.sashido.io/how-to-load-a-teachable-machine-image-model-in-a-node-js-project/</link><guid isPermaLink="false">5f8e879cbab85f0015059beb</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[machine learning]]></category><category><![CDATA[nodejs]]></category><category><![CDATA[tensorflow]]></category><category><![CDATA[node]]></category><category><![CDATA[image classification]]></category><category><![CDATA[Teachable Machine]]></category><dc:creator><![CDATA[Irena Russeva]]></dc:creator><pubDate>Wed, 04 Nov 2020 07:06:01 GMT</pubDate><media:content url="https://media-blog.sashido.io/content/images/2020/11/cover-snt-1.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://media-blog.sashido.io/content/images/2020/11/cover-snt-1.png" alt="How to load a Teachable Machine image model in a Node.JS project"><p>It’s no secret Node.js is one of the most widespread frameworks for writing apps nowadays. Though with the rise of Cloud-based Machine learning popularity, finding ways to stay on top of the trends by adding machine learning-based functionalities is becoming vital for Node.js developers and the success of their apps.</p>
<p>If you’re one of those app creators banging their heads about how to introduce machine learning to your Node.js project, let me give you a break by saying that the opportunity to train Machine Learning models is not a reserved space only for experienced Data Scientist and ML engineers anymore. <a href="https://teachablemachine.withgoogle.com/">Google’s Teachable Machine</a> tool empowers you to create and export your own model in the browser without any prior machine learning experience needed.</p>
<p>In the following lines I’ll show you how to train and load an Image classification model to your Node.js application in less than 10 minutes.</p>
<p><strong>Content:</strong></p>
<ol>
<li><a href="#machinelearningfordummiesitsthatsimple">Machine learning for dummies - It's that simple!</a></li>
<li><a href="#trainamachinelearningmodelwithteachablemachine">Train a Machine Learning Model with Teachable Machine</a></li>
<li><a href="#exporttotensorflowjs">Export to Tensorflow.js</a></li>
<li><a href="#installsashidoteachablemachinenode">Install NPM @sashido/teachablemachine-node package</a></li>
<li><a href="#usageexamples">Usage examples</a><br>
5.1 <a href="#plainnodejs">Plain Node.Js</a><br>
5.2 <a href="#express">Express</a></li>
<li><a href="#conclusion">Conclusion</a></li>
</ol>
<h1 id="1machinelearningfordummiesitsthatsimple">1. Machine learning for dummies - It's that simple!</h1>
<p>To be absolutely sure this approach is fit for machine learning beginners, I’ve personally put the simplicity of Google's Teachable Machine to the ultimate test with my mom who has no idea what machine learning is. It was not only fun, but she didn’t have any issue building a powerful image classification model that recognizes her cat amongst a bunch of other photos with very little data. 🐈 So if you have any doubts about the complexity of training a model, have no fear - Teachable Machine makes this complex task a piece of cake!</p>
<p>So far, so good. Now you probably wonder how to load your model into your project and make it work for you. Well, our team decided to jump in and offer an easy and open-source solution for serving machine learning models in a live environment. Et, voila - <a href="https://www.npmjs.com/package/@sashido/teachablemachine-node">@sashido/teachablemachine-node</a> is here to save the day and help you utilize a Teachable Machine image  model into a Node.Js project out of the box. Simple, right?</p>
<h1 id="2trainamachinelearningmodelwithteachablemachine">2. Train a Machine Learning Model with Teachable Machine</h1>
<p>Currently, the <a href="https://www.npmjs.com/package/@sashido/teachablemachine-node">teachablemachine-node</a> recognizes only image models. After a thorough analysis of the field, we figured that the greatest demand is for training image models and decided that’s the place to start. Moreover, image models serve well in many cases, such as object detection(a trendy machine learning example in real life is this fun Mask detection project), object recognition, image moderation and so many more.</p>
<p>Gathering samples is with no doubt the fundamental first step from training a model. You can use some open datasets, free photo platforms, own data or simply use your PC's camera to collect data. Teachable Machine allows both ways - you can load preselected images or directly use your camera to collect data.</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/11/ezgif.com-gif-maker--1-.gif" alt="How to load a Teachable Machine image model in a Node.JS project"></p>
<p>Split the photos into as many classes, as you need. Once done, hit the ‘Train Model’ button and leave it to the Teachable Machine. Training might take a while, depending on the dataset volume. Have patience and do not switch the tab. 😊</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/10/train_model_tm.gif" alt="How to load a Teachable Machine image model in a Node.JS project"></p>
<p>For further insight on your model’s performance click the Advanced option</p>
<h1 id="3exporttotensorflowjs">3. Export to Tensorflow.js</h1>
<p>Once your model is trained and you’re satisfied with its accuracy, it’s time to export it.  Make sure that you select Tensorflow.js format when exporting. That way your model will be uploaded (for free) and you will receive an access URL.</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/11/export_tendorflow.js.png" alt="How to load a Teachable Machine image model in a Node.JS project"></p>
<p>Next you’ll use this shareable link to load the model into your Node.js project.</p>
<h1 id="4installsashidoteachablemachinenode">4. Install @sashido/teachablemachine-node</h1>
<p>Simple as it can be, just install using npm or yarn:</p>
<h3 id="installusingnpm">Install using npm</h3>
<p><code>npm install @sashido/teachablemachine-node</code></p>
<h3 id="installusingyarn">Install using yarn</h3>
<p><code>yarn add @sashido/teachablemachine-node</code></p>
<h1 id="5usageexamples">5. Usage examples</h1>
<p>Our <strong><a href="https://www.npmjs.com/package/@sashido/teachablemachine-node">@sashido/teachablemachine-node</a></strong> makes loading an image model trained with Teachable Machine in your Node.Js project a child’s play.</p>
<h3 id="51plainnodejs">5.1 Plain Node.Js</h3>
<p>Just with a few lines of code the Teachable Machine model is loaded into your project. Simply invoke classify and handle the predictions.</p>
<pre><code class="language-js">const TeachableMachine = require(&quot;@sashido/teachablemachine-node&quot;);

const model = new TeachableMachine({
  modelUrl: &quot;https://teachablemachine.withgoogle.com/models/r6BBk-hiN/&quot;
});

model.classify({
  imageUrl: &quot;https://media-blog.sashido.io/content/images/2020/09/SashiDo_Dog.jpg&quot;,
}).then((predictions) =&gt; {
  console.log(&quot;Predictions:&quot;, predictions);
}).catch((e) =&gt; {
  console.log(&quot;ERROR&quot;, e);
});
</code></pre>
<h3 id="52express">5.2 Express</h3>
<p>As easy as the example above, you can also load your model in an Express app.</p>
<pre><code class="language-js">const express = require(&quot;express&quot;);
const TeachableMachine = require(&quot;@sashido/teachablemachine-node&quot;);

const model = new TeachableMachine({
  modelUrl: &quot;https://teachablemachine.withgoogle.com/models/r6BBk-hiN/&quot;
});

const app = express();
const port = 3000;

app.get(&quot;/image/classify&quot;, async (req, res) =&gt; {
  const { url } = req.query;

  return model.classify({
    imageUrl: url,
  }).then((predictions) =&gt; {
    console.log(predictions);
    return res.json(predictions);
  }).catch((e) =&gt; {
    console.error(e);
    res.status(500).send(&quot;Something went wrong!&quot;)
  });
});

app.listen(port, () =&gt; {
  console.log(`Example app listening at http://localhost:${port}`);
});
</code></pre>
<h1 id="conclusion">Conclusion</h1>
<p>In the last decade, Machine Learning has matured from a research field to applied business solutions. Luckily there’s no need to wait for another decade to upgrade your project with some cool machine learning based features - Teachable Machine gives you an opportunity to flatten the learning curve and easily train Machine Learning models of your own.</p>
<p>And now with the <a href="https://www.npmjs.com/package/@sashido/teachablemachine-node">@sashido/teachablemachine-node</a> you also have a fast and simple way to load any image Teachable Machine model to your Node.js application. Yes, it is that simple!</p>
<blockquote>
<p>Check the <a href="https://github.com/SashiDo/awesome-teachable-machine">Awesome Teachable Machine</a> list, full of fantastic learning and inspiration resources, detailed tutorials and useful tools, that will help you bring your creative ideas to life!</p>
</blockquote>
<p>In the long run, our RoadMap includes support for Pose Models, Audio Models, Gifs and Videos, so you can train and load all kinds of Teachable Machine Models.</p>
<p>Don’t be shy and let us know which's the one you want to use first and we’ll take care of the rest. We’d love to chat, just write to us at <a href="mailto:hello@sashido.io">hello@sashido.io</a>.</p>
<p>Happy Coding!</p>
<div class="push-banner">Deploy ML models into production in 59 seconds & serve a million predictions for FREE with TeachableHub!
    <a class="btn green text" href="https://bit.ly/3oDIQrP">Get Early Access<span></span></a>
</div>
</div>]]></content:encoded></item><item><title><![CDATA[Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 3: Moderation Admin App.]]></title><description><![CDATA[In this final part we'll put the cherry on top of our open-source Content Moderation Service by showing you how we've built a Image Moderation Admin App with ReactJS, how to directly clone it and integrate it to your projects.]]></description><link>https://blog.sashido.io/content-moderation-service-with-nodejs-and-tensorflow-part-3-moderation-admin-app/</link><guid isPermaLink="false">5ee9e9195297da00155e67a9</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[machine learning]]></category><category><![CDATA[tensorflow]]></category><category><![CDATA[nsfw]]></category><category><![CDATA[nsfwjs]]></category><category><![CDATA[content moderation]]></category><category><![CDATA[nodejs]]></category><category><![CDATA[nsfw-classifier]]></category><category><![CDATA[nsfw-detection]]></category><category><![CDATA[parse-server]]></category><category><![CDATA[parse-platform]]></category><category><![CDATA[reactjs]]></category><category><![CDATA[Cloud Code]]></category><category><![CDATA[advanced cloud code]]></category><dc:creator><![CDATA[Irena Russeva]]></dc:creator><pubDate>Mon, 20 Jul 2020 13:08:23 GMT</pubDate><media:content url="https://media-blog.sashido.io/content/images/2020/06/nsfw-2.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://media-blog.sashido.io/content/images/2020/06/nsfw-2.png" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 3: Moderation Admin App."><p>With the approximately <strong>4.54 billion internet users</strong>(June 2020) worldwide, generating an unprecedented quantity of content, moderation is becoming a heavier task by the day! And the number of active users and apps on the market is constantly steeping. Left unsupervised, users can freely share any kind of content and create tons of digital garbage.</p>
<p>Naturally, content moderation is becoming a mandatory feature for each web or mobile application. But manual moderation is extremely time-consuming and surely stressful and Machine Learning solutions come quite pricey. So many drawbacks! Our team loves nothing more but a good challenge, so we rolled up our sleeves determined to aid developers in this heavy task and offer a solution.</p>
<p><strong>Here, we are presenting the final piece of our simple, affordable and most-importantly effective fully-functional Content Moderation Service</strong>. It is built entirely with open-source libraries and can be integrated into any Parse Node.JS project and hosted anywhere.</p>
<h1 id="agenda">Agenda</h1>
<p><strong><a href="#background">1. Background</a></strong><br>
<strong><a href="#theproblem">2. The Problem</a></strong><br>
<strong><a href="#thesolution">3. The Solution</a></strong><br>
<strong><a href="#moderationpreferences">4. Moderation Preferences</a></strong><br>
<strong><a href="#dbschema">5. DB Schema</a></strong><br>
<strong><a href="#adminpanel">6. Admin Panel</a></strong><br>
<strong><a href="#configurationanddeployment">7. Configuration and Deployment</a></strong><br>
<strong><a href="#fin">8. Fin</a></strong></p>
<h1 id="background">Background</h1>
<p>The project was decided to come in three parts, each more advanced than the previous. We committed this way to give you the opportunity to pick the piece you need and integrate it into your application, without any hassles. Nothing less, nothing more!🙂 So far we’ve released:</p>
<p><strong>1. Image Classification REST API</strong> - The first part of this service holds an Image Classification REST API that works with <a href="https://github.com/infinitered/nsfwjs">NSFW.JS</a>. NSFW.JS is an awesome library developed by <a href="https://gantlaborde.com/">Gant Laborde</a> that uses <a href="https://www.tensorflow.org/js">Tensorflow</a> pre-trained ML models. The API has quite plain logic - given an URL, it returns predictions how likely the image falls into each of the following classes - Drawing, Neutral, Sexy, Porn and Hentai.</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/api_example_428-1.gif" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 3: Moderation Admin App."></p>
<p>More details you can find at the following:</p>
<ul>
<li><a href="https://blog.sashido.io/content-moderation-service-with-nodejs-tensorflowjs-and-reactjs-part-1-restful-api-service/">Content Moderation Service with Node.JS and TensorFlow. Part 1: REST API</a>. A blog post that gives details on our team’s inspiration, how the API works and what’s the tech Stack used for all three project stages.</li>
<li><a href="https://github.com/SashiDo/content-moderation-image-api">Image Classification REST API GitHub</a> - In case your project needs only REST API image classification, clone the repo and follow the README.md.</li>
</ul>
<p><strong>2. REST API + Automation Engine</strong> - The Automation Engine is tightly coupled with the REST API. Basically, it’s work consists of checking how the classification of a certain image corresponds to the parameters you pre-define as safe for your project. The process is automated with a <a href="https://docs.parseplatform.org/cloudcode/guide/#aftersave-triggers">Parse Server afterSave trigger</a>.</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/automation_final-1.gif" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 3: Moderation Admin App."></p>
<p>For further insight check:</p>
<ul>
<li><a href="https://blog.sashido.io/content-moderation-service-with-nodejs-and-tensorflow-part-2-moderation-automation">Content Moderation Service with Node.JS and TensorFlow. Part2: Automation Engine</a> - An article explaining the purpose and settings of the Automation Engine.</li>
<li><a href="https://github.com/SashiDo/content-moderation-automations">Content Moderation Automation GitHub</a> - For all folks who want to build a moderation interface of their own, but could take advantage of the moderation automation.</li>
</ul>
<blockquote>
<p>Our blog offers a synthesized explanation of the logic behind the REST API and the Automation Engine, so <strong>we would strongly recommend you to have a look at the articles before continuing</strong>.</p>
</blockquote>
<h1 id="theproblem">The Problem</h1>
<p>With the REST API and AUTOMATION ENGINE already put forth, the problem is almost solved. After all, the decision-making process for a great number of user-generated content is already automated and the number of pictures that require manual moderation has drastically decreased.</p>
<p>Unfortunately, for manual moderation you surely need to go through photos one by one. Next, you need to decide for each of the images if it is safe or toxic for your specific audience and apply your decision. All that combined may once again become a tedious task.</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/people_collage.jpg" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 3: Moderation Admin App."></p>
<p>If you look at Instagram with its <a href="https://www.omnicoreagency.com/instagram-statistics/">100 million+ photos</a> uploaded per day or Facebook with more than <a href="https://www.omnicoreagency.com/facebook-statistics/">120 million fake profiles</a> you can imagine that the count of inappropriate photos daily can also reach 7 digit numbers. Even if your project can’t compare with these giants yet and your pile of images for moderation is significantly smaller it still remains a boring burden</p>
<p><strong>The final touch of our Content Moderation Service puts an end to this issue by providing a super user-friendly interface that makes manual moderation all fun and games.</strong></p>
<h1 id="thesolution">The Solution</h1>
<p>To cope with the situation and make the most of it, we'll take all photos flagged from the Automation Engine for moderation and display them in an eye-pleasing Admin Panel that empowers you to approve or reject a picture with just a click.</p>
<p><strong>We combine the REST API and Automation Engine with an exquisite Admin Application interface</strong>, where all users’ images that need manual moderation are stacked up for approval. It’s <strong>desktop and mobile-friendly, easy to operate</strong> and empowers you to go through a huge pile of photos in just a few brief moments.</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/nsfw--1-.png" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 3: Moderation Admin App."></p>
<blockquote>
<p>As in the previous releases, we’ll once again use a SashiDo app for all examples. Because it is easy, fun and all infra hassles are left behind. 😊</p>
</blockquote>
<h1 id="moderationpreferences">Moderation Preferences</h1>
<p>Hopefully, you’ve already gone through our <a href="https://blog.sashido.io/content-moderation-service-with-nodejs-and-tensorflow-part-2-moderation-automation/">Part 2: Moderation Automation</a> article and you can skip this section. However, if you didn’t have the chance to read the previous blog post, we’ll go over the Moderation Preferences briefly once more, as they are an essential component.</p>
<p>To put in a few words, <strong>the moderation preferences are the criteria specifically set for your app which images will be automatically approved from the automation engine considered safe and which will be rejected</strong>.</p>
<p>Therefore, you need to carefully review which classes are dangerous or unwanted to your users. The photos classified above the <code>max</code> values that you set will be automatically marked <code>deleted</code>, the ones lower than the <code>min</code> values -&gt; <code>auto-approved</code>, all in between go to our Admin App for manual moderation.</p>
<p>For example, if you’ve created an innovations app, where enthusiastic engineers present their AI robot ideas, you’ll most probably consider Sexy, Hentai and Porn classified images as inappropriate. Drawing seems a bit grey area, as you might want to allow robot sketches. Here is one example of how such moderation preferences can be adjusted:</p>
<pre><code class="language-js">{
  &quot;Sexy&quot;: { &quot;min&quot;: 0.4,  &quot;max&quot;: 0.8  },
  &quot;Drawing&quot;: { &quot;min&quot;: 0.4, &quot;max&quot;: 0.9 },
  &quot;Porn&quot;: { &quot;min&quot;: 0.4, &quot;max&quot;: 0.8 },
  &quot;Hentai&quot;: {  &quot;min&quot;: 0.2, &quot;max&quot;: 0.8 }
}
</code></pre>
<p>Let’s set these params and upload some suspicious pictures like the one below, which falls between the min and max values.</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/image--3-.png" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 3: Moderation Admin App."></p>
<p>See, it’s directly stacked up for approval into the Admin App. Cool, right?! Still, in the long run, it all depends on your particular use-case and judgment.</p>
<blockquote>
<p>No worries, we’ve made sure to provide you with an easy way of adjusting the moderation preferences on the fly.😉 We’ll save those as moderationScores config setting for the project, but more on that in the <a href="#configurationanddeployment">Configuration and Deployment Section</a>.</p>
</blockquote>
<p>The right moderation preferences settings leave more room for the automation to make the decisions and untie your hands for cooler stuff!</p>
<h1 id="dbschema">DB Schema</h1>
<p>The Database Schema is the other must-mention unit and it is also related to the Automation Engine and explained in detail within our previous blog post on Moderation Automation. The idea is that you need to keep the naming and add a few columns to your DB to store the Automation Engine’s results and NSFW predictions.</p>
<ol>
<li>The <strong>afterSave automation is hooked to a collection UserImage by default</strong>. To use straight away after deployment, you should either keep the same class name or change with the respective one <a href="https://github.com/SashiDo/content-moderation-application/blob/master/src/cloud/triggers.js#L3">here</a> into your production code.</li>
<li>To keep a neat record of the REST API predictions and Automation Engine results, you will need to <strong>add a few columns to your Database collection that holds user-generated content</strong>.</li>
</ol>
<ul>
<li><strong>isSafe(Boolean)</strong> - If an image is below the <code>min</code> value of your moderation preferences, it’s marked <code>isSafe - true</code>.</li>
<li><strong>deleted(Boolean)</strong> - Likewise, the Automation Engine will mark the inappropriate pictures, as <code>deleted - true</code>. Those pictures won’t be automatically deleted from the file storage.</li>
<li><strong>moderationRequired(Boolean)</strong> - Those are loaded in the Admin Panel for manual moderation.</li>
<li><strong>NSFWPredictions(Array)</strong> - Stores the NSFW predictions as json for this image.</li>
</ul>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/add_cols.gif" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 3: Moderation Admin App."></p>
<ol start="3">
<li>Last but not least you need to <strong>add an isModerator column to the predefined _User class.</strong> It’s again of type boolean and allows you to manage access to the Admin Panel for different users.</li>
</ol>
<p>Well, that’s about all preparations needed to kick it off! Next, we’ll check the rudiments of the last fragment - the Admin Application and move on to deploying in production.</p>
<h1 id="adminpanel">Admin Panel</h1>
<p>Our awesome engineering team built a plain and chic ReactJS-based Admin Application to put the final touch. Its primary mission is allowing you to go through all images that require manual moderation and make a decision in a blink of an eye. We’ve chosen ReactJS, as it is by far the most popular front-end framework loved by many developers.</p>
<iframe width="854" height="480" src="https://www.youtube.com/embed/1uQBzkIzqS4" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
<p>Let’s check briefly the features of the Admin panel and how it helps:</p>
<ul>
<li>First thing comes first, we have made a basic <strong>login/logout functionality</strong>.</li>
<li>At SashiDo we truly believe that security should be a top priority for every project, so we made sure to provide you with a way for restricting access to the Admin app. You can <strong>grant access to the app for your trusted partners by setting isModerator=true</strong> in the databas.</li>
</ul>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/isModerator.gif" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 3: Moderation Admin App."></p>
<ul>
<li>All pictures that are flagged by the Automation Engine as <strong>moderationRequired=true are displayed</strong> in a beautiful interface, which makes manual moderation a fun play.</li>
<li>Just below each picture you have a useful <strong>scale showing the classification result for most hazardous classes</strong> - Sexy, Porn and Hentai.</li>
<li><strong>You approve or reject a photo with a simple click or swipe</strong>. As easy as it can be!😊</li>
<li>And it gets even better! <strong>The Admin app is desktop and mobile-friendly</strong>, which makes it the ultimate time-saver. You can moderate your app’s content even from the subway, while on your way to work, the restaurant or the bar.</li>
</ul>
<blockquote>
<p>All parts of this project are open for collaboration. Fork the <a href="https://github.com/SashiDo/content-moderation-application#admin-panel">repo</a>, apply your awesome ideas and open a pull request!</p>
</blockquote>
<h1 id="configurationanddeployment">Configuration and Deployment</h1>
<h2 id="configuration">Configuration</h2>
<p>Parse Server offers two approaches for app config settings. <a href="https://blog.sashido.io/parse-config/">Parse.Config</a> is very simple, useful and empowers you to update the configuration of your app on the fly, without redeploying. The trouble is configs are public by design.  <a href="https://blog.sashido.io/announcing-environment-variables/">Environment variables</a> are the second, more secure option, as these settings are private, but each time you change those your app is automatically redeployed. We’ll move on wisely and use both. 😉</p>
<h3 id="parseconfigs">Parse.Configs</h3>
<ul>
<li><strong>moderationScores</strong> - save the moderation preferences for your app in a Parse.Config object. That will allow you to update and fine-tune criteria any time you need on the fly.</li>
<li><strong>moderationAutomation</strong> - add this boolean option that makes enabling/disabling content moderation automation an effortless process finished with just a click when needed. For example, when you want to test the new code version without automation.</li>
</ul>
<p>To set the configs for your SashiDo application go to the App’s Dashboard -&gt; Core -&gt; Config. The final result looks like:</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/config.png" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 3: Moderation Admin App."></p>
<h3 id="environmentvariables">Environment Variables</h3>
<p>For production, we'll set the NSFW model URL, NSFW Model Shape size and Config caching as environment variables.</p>
<ul>
<li><strong>TF_MODEL_URL</strong> - SashiDo stores three pre-trained ready for integration <a href="https://github.com/GantMan/nsfw_model">nsfw models</a> that you can choose from.</li>
</ul>
<script class="ct" src="https://gist.github.com/mignev/f6c08ff0c73055deb1ea0c16edc30560.js"></script>
<ul>
<li>
<p><strong>TF_MODEL_SHAPE_SIZE</strong> - Еach model comes with its shape size</p>
</li>
<li>
<p><strong>CONFIG_CACHE_MS</strong> variable. It will serve us for cashing the Parse.Configs and the value you pass is in milliseconds.</p>
</li>
</ul>
<p>In SashiDo set the environment variables for your projects from the App's Dashboard -&gt; Runtime -&gt; Environment variables. Have a look at all you need:</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/env.var.png" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 3: Moderation Admin App."></p>
<blockquote>
<p>For this project, we have preset the Mobilenet 93 model. However, you can always choose a different one. Want to test out different options and evaluate each model’s performance? Simply check the <a href="https://nsfwjs.com/">NSFWJS Demo page</a>. 🙂</p>
</blockquote>
<h2 id="deployment">Deployment</h2>
<p>SashiDo has implemented an effortless automatic git deployment process, so simply make sure your <a href="https://blog.sashido.io/how-to-start-using-github-with-sashido-for-beginners/">SashiDo and Github accounts are connected</a>. Next, follow these simple steps:</p>
<ol>
<li>Clone the repo from GitHub.</li>
</ol>
<pre><code class="language-sh">git clone https://github.com/SashiDo/content-moderation-application.git
</code></pre>
<ol start="2">
<li>Set the configs and env vars in production.</li>
</ol>
<ul>
<li>checked ✔</li>
</ul>
<ol start="3">
<li>Add your SashiDo app as a remote branch and push changes.</li>
</ol>
<pre><code class="language-sh">git remote add production git@github.com:parsegroundapps/&lt;your-pg-app-your-app-repo&gt;.git
git push -f production master
</code></pre>
<p>Voila!!! You can leave content moderation to the newly integrated service and go grab a beer. Cheers 🍻!</p>
<h1 id="fin">FIN!</h1>
<p>All chunks of the Moderation Services are already assembled. We would love to have your feedback on what other ready-to-use Machine Learning services can help your projects grow. Don’t be shy and <strong>share your thoughts at <a href="mailto:hello@sashido.io">hello@sashido.io</a>.</strong></p>
<p>And if you’re still wondering about where to host such a project, don’t forget that SashiDo offers a <a href="https://dashboard.sashido.io/register">Free trial</a>, no credit card required as well as exclusive <a href="https://form.typeform.com/to/x1zJtH">Free consultation by SashiDo's experts</a> for projects involving Machine Learning.</p>
<p>Happy Coding!</p>
</div>]]></content:encoded></item><item><title><![CDATA[Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 2: Moderation Automation.]]></title><description><![CDATA[In Part 2 of our series related to the open-source Content Moderation Service built with Node.js, TensorFlowJS, and ReactJS, we'll show you how we've created a Moderation Automation Engine, how to directly clone it, and integrate it to your projects.]]></description><link>https://blog.sashido.io/content-moderation-service-with-nodejs-and-tensorflow-part-2-moderation-automation/</link><guid isPermaLink="false">5ee9e87f5297da00155e67a8</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[machine learning]]></category><category><![CDATA[tensorflow]]></category><category><![CDATA[nsfw]]></category><category><![CDATA[nsfwjs]]></category><category><![CDATA[content moderation]]></category><category><![CDATA[nodejs]]></category><category><![CDATA[automation]]></category><category><![CDATA[nsfw-classifier]]></category><category><![CDATA[nsfw-detection]]></category><category><![CDATA[parse-server]]></category><category><![CDATA[parse-platform]]></category><category><![CDATA[Cloud Code]]></category><dc:creator><![CDATA[Irena Russeva]]></dc:creator><pubDate>Mon, 13 Jul 2020 08:58:40 GMT</pubDate><media:content url="https://media-blog.sashido.io/content/images/2020/06/nsfw-1.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://media-blog.sashido.io/content/images/2020/06/nsfw-1.png" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 2: Moderation Automation."><p>Over the last weeks, our team successfully has taken on the challenge to develop a simple and yet effective Content Moderation Service. <strong>The end goal is to help app creators keep their projects safe for work and free from abusive images, without spending too much time and effort in content moderation.</strong></p>
<p>The solution comes in a brief series of just three tutorials that present a <strong>fully-functional Open-Source Content Moderation service with ReactJS based Admin Panel</strong> that can be easily integrated into every project even if this is your first encounter with Machine Learning.</p>
<h1 id="agenda">Agenda</h1>
<p><strong><a href="#background">1. Background</a></strong></p>
<p><strong><a href="#theproblem">2. The Problem</a></strong></p>
<p><strong><a href="#thesolution">3. The Solution</a></strong></p>
<p><strong><a href="#automationengine">4. Automation Engine</a></strong></p>
<ul>
<li><a href="#moderationpreferences">Moderation Preferences</a></li>
<li><a href="#thepureautomationprocess">The Pure Automation Process</a></li>
<li><a href="#databaseschema">Database Schema</a></li>
<li><a href="#moderationpreferencestuning">Moderation Preferences Tuning</a></li>
</ul>
<p><strong><a href="#manualmoderation">5. Manual Moderation</a></strong></p>
<p><strong><a href="#configurationanddeployment">6. Configuration and Deployment</a></strong></p>
<p><strong><a href="#whatisnext">7. What is next</a></strong></p>
<h1 id="background">Background</h1>
<p>We have already released the first piece of our moderation system just last week. It offers a ready-to-integrate image <a href="https://github.com/SashiDo/content-moderation-image-api">classification REST API</a>, which uses <a href="https://github.com/infinitered/nsfwjs">NSFW.JS</a> to return predictions how likely a certain image is categorized as Porn, Sexy, Hentai, Drawing or Neutral.</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/api_example_428.gif" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 2: Moderation Automation."></p>
<blockquote>
<p><strong>In case you missed the previous article about <a href="https://blog.sashido.io/content-moderation-service-with-nodejs-tensorflowjs-and-reactjs-part-1-restful-api-service/">building the REST API</a>, we would strongly recommend you to have a look now, as it gives further insights on the motivation behind this service, stack used, API setup and configuration</strong>.</p>
</blockquote>
<h1 id="theproblem">The Problem</h1>
<p><strong>Solely classification of images, still leaves us with the task to manually moderate them one by one...</strong> and if you’re lucky enough to have many active users, the total number of pics for approval may easily get your head spinning.</p>
<p>Let’s say we have a growing self-training fitness application with a social element to it. Everybody wants a beach body...and they want to show it off! 😄 To stay on track and motivate each other, our users upload photos of their progress.</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/collageFitness.jpg" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 2: Moderation Automation."></p>
<p>Now, imagine it’s a relatively medium-sized app with 5 000 daily users and 5 000 photos are updated every 24 hours. That makes 35 000 per week and 150 000 per month. WOW! Do we need to set aside a budget for content moderators? <strong>Or go through 150.000 pictures manually each month? No, thanks! There’s gotta be an easier way.</strong></p>
<p>Instead of examining a huge pile of photos, we will put Machine Learning into action and upgrade the Image Classification REST API with automation logic that will significantly cut down the number of photos for manual moderation.</p>
<h1 id="thesolution">The Solution</h1>
<p>We already have an easy way of classifying images, but going through all the results still is quite time-consuming...and to be honest - not really fun. <strong>So to optimize the process - we will add automation</strong>.</p>
<p>As in the <a href="http://blog.sashido.io/content-moderation-service-with-nodejs-tensorflowjs-and-reactjs-part-1-restful-api-service/">first tutorial</a>, we will use a SashiDo hosted app for the examples, so we can spare any infrastructure hassles. Anyhow, the code is <strong>open-source</strong> and can be integrated into projects hosted on any other provider that supports fully featured NodeJS + MongoDB -  Parse Server or even cloud-hosting solutions like Digital Ocean and AWS.</p>
<p><strong>This second part of our moderation service holds altogether the Image Classification API and Moderation Automation Engine</strong>. You can simply clone the project from SashiDo’s <a href="https://github.com/SashiDo/content-moderation-automations">GitHub repo</a>.</p>
<pre><code class="language-bash">git clone https://github.com/SashiDo/content-moderation-automations
</code></pre>
<p>Next, <a href="#configurationanddeployment">deploy it to production</a> and set the parameters defining which photos will be considered as safe or toxic. Simple as that! 😊 But wouldn’t be nice to have some context what’s all about?</p>
<img src="https://media-blog.sashido.io/content/images/2020/07/nsfw-3.png" width="800" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 2: Moderation Automation.">
<h1 id="automationengine">Automation Engine</h1>
<p>The entire service is built on top of Parse Server (NodeJS Backend framework) and uses Cloud Code for all server-side logic. To solve the task at hand and automate the decision making process we use <a href="https://docs.parseplatform.org/cloudcode/guide/#aftersave-triggers">Simple Cloud Code Triggers</a> and build the process around a few easy steps. Basically, <strong>the fundament of the Automation Engine is to determine whether an image is considered safe or harmful for your users based on your predefined ranges</strong>.</p>
<h3 id="moderationpreferences">Moderation Preferences</h3>
<p>In the beginning, you need to define which of the five <a href="#background">NSFW classes</a> and values can contain disturbing images and require moderation, i.e. which prognoses are considered safe and which toxic.</p>
<p>For our fitness application, for example, images with classification <code>Hentai &gt; 0.8%</code> are absolutely intolerable and we’ll want to directly flag those for deletion. Briefly, the idea is to <strong>define all classes and value ranges that will require moderation</strong>, e.g:</p>
<pre><code class="language-Js">{
  &quot;Sexy&quot;:   { &quot;min&quot;: 0.5, &quot;max&quot;: 0.8 },
  &quot;Porn&quot;:   { &quot;min&quot;: 0.4, &quot;max&quot;: 0.8 },
  &quot;Hentai&quot;: { &quot;min&quot;: 0.4, &quot;max&quot;: 0.8 }
}

</code></pre>
<blockquote>
<p>As defining your moderation preferences is of great importance, we’ll pay some special attention to its <a href="#moderationpreferencestuning">tuning</a> later on.</p>
</blockquote>
<h3 id="thepureautomationprocess">The Pure Automation Process</h3>
<p>The automation process is integrated with  <a href="https://docs.parseplatform.org/cloudcode/guide/#aftersave-triggers">Parse Server afterSave trigger</a>, which self-executes once a change to the class holding user-generated content is made.</p>
<p>The logic inside is simply <strong>to check if the newly uploaded photo is safe, toxic or requires manual moderation</strong> and save the result to the database.</p>
<p>For the above-mentioned parameters, once we integrate the Automation Engine, it will identify <strong>all pictures that classify below the <code>min</code> values as safe and the ones above the <code>max</code> values - toxic. The in-between results require manual moderation.</strong></p>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/manual_moderation_rules_example.png" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 2: Moderation Automation."></p>
<blockquote>
<p>In this project the afterSave is hooked to a collection named <code>UserImage</code>. To use straight away after deployment, you should either keep the same class name or change with the respective one <a href="https://github.com/SashiDo/content-moderation-application/blob/master/src/cloud/triggers.js#L3">here</a> into your production code.</p>
</blockquote>
<p>Of course, it is up to you how these images will be handled on the client-side. You can also extend the Engine logic and customize server-side in a way corresponding to the specific use case, applying auto-deletion of toxic images for example and so on.</p>
<p>More information about the implementation of the Automation Engine and the Trigger you can find in the repo - <a href="https://github.com/SashiDo/content-moderation-application/blob/master/src/cloud/ml_models/nsfw_model.js#L61">NSFW Image Valuation</a> and <a href="https://github.com/SashiDo/content-moderation-application/blob/master/src/cloud/triggers.js#L3">Cloud Trigger Automation</a></p>
<h3 id="databaseschema">Database Schema</h3>
<p>Keeping a neat record of classification and moderation actions also falls into the best practices that save time and effort. Storing information for each image gives us a clear history of why for instance it has been flagged as rejected or approved. However, you need to make some adjustments to the DB Schema and <strong>add the following columns to the DB collection that stores users’ images</strong>.</p>
<ul>
<li><strong>isSafe(Boolean)</strong> - If an image is safe according to the moderation preferences, the Automation Engine will mark <code>isSafe - true</code>.</li>
<li><strong>deleted(Boolean)</strong> - Likewise, the Automation Engine will mark the inappropriate images, as <code>deleted - true</code>. Those pictures won’t be automatically deleted from the file storage. Good practice suggests toxic images should not be erased, as they might help you detect abusive users. For example, we can easily check how many indecent photos are uploaded from a specific user and ban him from the app after a certain count that makes clear this is not an involuntary mistake but intended action.</li>
<li><strong>moderationRequired(Boolean)</strong> - All images that lay in between the isSafe and the deleted mark. Manual moderation is required for those images!</li>
<li><strong>NSFWPredictions(Array)</strong> - Stores the NSFW predictions as json for this image.</li>
</ul>
<p>The automation engine will take care to fill all data inside these columns  respectively, once a photo is uploaded.</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/automation_final.gif" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 2: Moderation Automation."></p>
<blockquote>
<p>To learn more on how to upload your files with a Parse SDK you can take a look here: <a href="https://docs.parseplatform.org/js/guide/#files">Uploading file with Parse JS SDK</a></p>
</blockquote>
<h3 id="moderationpreferencestuning">Moderation Preferences Tuning</h3>
<p><strong>Evaluating which images are safe and which inacceptable is crucial and individual for every app or business.</strong> There are as many different preferences, like the apps on the market and no clear-cut, recognized standards.</p>
<p>For example, in our fitness application, uploading sexy images of well-shaped bodies after a completed program makes perfect sense. However, nudes are highly undesirable. And on the other hand, if we have a children’s app, where pre-school kids upload drawings - sexy photos are also out of boundaries.</p>
<p>You have to take into account which parameters match your needs best and set the ranges respectively. From then on our content moderation service provides a flawless way to adjust these settings. We <strong>save the Moderation Preferences into a moderationScores config parameter</strong> prior to <a href="#configuration">deployment</a>. Such an approach makes changing the criteria in times of need or for different projects as easy as pie! 😉</p>
<h4 id="examples">Examples</h4>
<p>Let’s see how the automation will behave with some examples of suitable moderation parameters. We’ll set different moderationScores that match the respective audience for our fitness application and the one for children's art and check the results.</p>
<blockquote>
<p>Quick throwback -  the REST API classifies image probabilities 5 classes: <em>Drawing, Hentai, Neutral, Porn, Sexy</em>. If any of these classes is disturbing for your users, include it to the moderationScores.</p>
</blockquote>
<h5 id="1fitnessapplication"><strong>1. Fitness Application</strong></h5>
<p>Let’s take the fitness Application we already mentioned for our first example. We decided that sexy photos are perfectly okay and do not need moderation, hence this class has no place in the moderationScores config. We’ll just add Porn and Hentai, as this is undesirable content for our users. Next, let's also include the Drawing class since we want fitness photos only. Here’s one recommendation for moderation preferences:</p>
<pre><code class="language-JS">{  
  &quot;Drawing&quot;: { &quot;min&quot;: 0.4, &quot;max&quot;: 0.8 },
  &quot;Porn&quot;: { &quot;min&quot;: 0.4, &quot;max&quot;: 0.8 },
  &quot;Hentai&quot;: { &quot;min&quot;: 0.2, &quot;max&quot;: 0.8 }
}
</code></pre>
<p>Let’s upload this sexy pic of woman training and see what happens. 🙂</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/fitness.png" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 2: Moderation Automation."></p>
<p>The prediction we get from the API is:</p>
<pre><code class="language-JS">{&quot;className&quot;: &quot;Sexy&quot;,&quot;probability&quot;: 0.9727559089660645},
{&quot;className&quot;: &quot;Neutral&quot;,&quot;probability&quot;: 0.01768375374376774},
{&quot;className&quot;: &quot;Porn&quot;,&quot;probability&quot;: 0.009044868871569633},
{&quot;className&quot;: &quot;Drawing&quot;,&quot;probability&quot;: 0.0004224120930302888},
{&quot;className&quot;: &quot;Hentai&quot;,&quot;probability&quot;: 0.00009305890125688165}
</code></pre>
<p>As the picture doesn’t fall in any of the classes that require moderation, the upload is automatically approved and we get the following result in the database:</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/result_fitness_app_f.png" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 2: Moderation Automation."></p>
<h5 id="2childrensdrawingsplatform"><strong>2. Children’s drawings platform.</strong></h5>
<p>While the photo above is perfectly fine for our fitness app example, it can’t be defined as a kid’s drawing for sure. Therefore it has no place on our app for preschoolers’ art. Also images that have even the slightest chance to be categorized as Hentai or Porn are definitely off the table. If only children’s drawings should be uploaded, we can include the Neutral class for moderation as well, but select higher values, as there might be a child holding a drawing for example.</p>
<p>Take a look at the parameters we’ve set to protect children and how the Automation Engine handles the same photo once we change the moderationScores.</p>
<pre><code class="language-JS">{ 
  &quot;Porn&quot;: { &quot;min&quot;: 0.1, &quot;max&quot;: 0.4 },
  &quot;Sexy&quot;:  { &quot;min&quot;: 0.1, &quot;max&quot;: 0.6 },
  &quot;Hentai&quot;: { &quot;min&quot;: 0.1, &quot;max&quot;: 0.4 } ,
  &quot;Neutral&quot;: { &quot;min&quot;: 0.4, &quot;max&quot;: 0.9}
}
</code></pre>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/result_child_app_f-1.png" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 2: Moderation Automation."></p>
<p>As already established - <strong>moderationScores values are strictly specific</strong>, so even though we’ve shared some examples, our recommendation is to put some serious thought on fine-tuning the parameters to match your needs best!</p>
<blockquote>
<p>Best thing to do is to set some default parameters, like the ones we suggest above and test with some real pictures from your app. Take 50-60 images that you consider inappropriate and monitor the NSFW predictions. This is a safe and effective way to calibrate the settings according to the beat of your project.</p>
</blockquote>
<h1 id="manualmoderation">Manual Moderation</h1>
<p>Next week we will bundlе the Image Classification REST API and Automation Engine with an impeccable React-based UI. This will allow you to rapidly make a decision for all photos that require manual moderation and apply action with just one click.</p>
<p>Still, if you can’t wait to add an interface to the moderation service, you can build an Admin panel of your own. On SashiDo, you can easily build an Admin panel with the JS technologies that please you most - Angular, React, Vue… just take your pick. 🙂</p>
<p>Here’s an example of how to get all images which require moderation from the most-used Parse SDKs and from Parse REST API.</p>
<h3 id="jssdk">JS SDK</h3>
<pre><code class="language-js">const query = new Parse.Query(&quot;UserPicture&quot;);
query.equalTo(&quot;moderationRequired&quot;, true);
query.find().then((results) =&gt; {
 console.log(results);
});
</code></pre>
<p>More information about how to work with the Parse JS SDK can be found in the <a href="https://docs.parseplatform.org/js/guide/#queries">official docs</a></p>
<h3 id="androidsdk">Android SDK</h3>
<pre><code class="language-java">ParseQuery&lt;ParseObject&gt; query = ParseQuery.getQuery(&quot;UserPicture&quot;);
query.whereEqualTo(&quot;moderationRequired&quot;, true);
query.findInBackground(new FindCallback&lt;ParseObject&gt;() {
    public void done(List&lt;ParseObject&gt; UserPicture, ParseException e) {
        if (e == null) {
            Log.d(&quot;isSafe&quot;, &quot;Safe images retrieved&quot;);
        } else {
            Log.d(&quot;isSafe&quot;, &quot;Error: &quot; + e.getMessage());
        }
    }
});
</code></pre>
<p>More information about how to work with the Android SDK can be found in the <a href="https://docs.parseplatform.org/android/guide/#queries">official docs</a>.</p>
<h3 id="iossdk">iOS SDK</h3>
<pre><code class="language-c">let query = PFQuery(className:&quot;UserImage&quot;)
query.whereKey(&quot;moderationRequired&quot;, equalTo:true)
query.findObjectsInBackground { (objects: [PFObject]?, error: Error?) in
    if let error = error {
        // Log details of the failure
        print(error.localizedDescription)
    } else if let objects = objects {
        // The find succeeded.
        print(&quot;Successfully retrieved images for moderation&quot;)
        }
    }
}
</code></pre>
<p>More information about how to work with the Parse iOS SDK can be found in the <a href="https://docs.parseplatform.org/ios/guide/#queries">official docs</a>.</p>
<h3 id="restapi">REST API</h3>
<pre><code class="language-sh">curl -X GET \
  -H &quot;X-Parse-Application-Id: ${APPLICATION_ID}&quot; \
  -H &quot;X-Parse-REST-API-Key: ${REST_API_KEY}&quot; \
  -G \
  --data-urlencode 'where={&quot;moderationRequired&quot;: true}' \
  http://localhost:1337/1/classes/UserImage
});
</code></pre>
<p>More details on REST Queries you can find in the official <a href="https://docs.parseplatform.org/rest/guide/#queries">Parse REST API Guide</a>. And SashiDo users can test REST requests from a super-friendly <a href="https://blog.sashido.io/introducing-the-api-console/">API Console</a> that’s built in the Dashboard.</p>
<h1 id="configurationanddeployment">Configuration and Deployment</h1>
<p>Hopefully, you now have a clear image of how the Classification REST API and Automation Engine work together. All that’s left is to set the configs. Apart from the <code>moderationScores</code>, we’ll include an option to <code>enable/disable</code> the automation and config caching.</p>
<h2 id="configuration"><a href="#configuration">Configuration</a></h2>
<p>Parse Server offers two approaches for app config settings <a href="https://blog.sashido.io/parse-config/">Parse.Config</a> and <a href="https://blog.sashido.io/announcing-environment-variables/">Environment Variables</a>. What’s the difference? Parse.Config is a very simple and useful feature that empowers you to update the configuration of your app on the fly, without redeploying. However, the downside is that these settings are public, so it’s not recommended for sensitive data. Environment variables, on the other hand, are private but will trigger a redeployment of your project each time you change something. As always, truth is somewhere in between and we’ll use both!</p>
<h3 id="parseconfigs">Parse.Configs</h3>
<p>We’ve chosen to keep the <a href="#moderationpreferences"><strong>moderationScores</strong></a> as a Parse.Config, <strong>so we can update and fine-tune criteria on the fly</strong>.</p>
<p>Also, we’ll add <strong>moderationAutomation option</strong> of type boolean. It gives us a way for enabling/disabling content moderation automation with just a click when needed. For example, when you want to test the new code version without automation.</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/config.png" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 2: Moderation Automation."></p>
<h4 id="environmentvariables">Environment Variables</h4>
<p>If you have <a href="https://blog.sashido.io/content-moderation-service-with-nodejs-tensorflowjs-and-reactjs-part-1-restful-api-service/#deploytoproduction">already integrated the first piece of our service</a> - the image classification API, then <code>TF_MODEL_URL</code> and <code>TF_MODEL_SHAPE_SIZE</code> are already set to your project. As these are a must, let me refresh your memory for the available options.</p>
<script class="ct" src="https://gist.github.com/mignev/f6c08ff0c73055deb1ea0c16edc30560.js"></script>
<p>All that’s left to add is the  <code>CONFIG_CACHE_MS</code> variable. It will serve us for cashing the Parse.Configs and the value you pass is in milliseconds.</p>
<p>Here are all the environment variables you need.</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/07/env.var.png" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. Part 2: Moderation Automation."></p>
<h2 id="deployment">Deployment</h2>
<p>SashiDo has implemented an automatic git deployment process following The Twelve-Factor App principle. To deploy the  Automation Engine, first <a href="https://blog.sashido.io/how-to-start-using-github-with-sashido-for-beginners/">connect your SashiDo account to Github</a>.</p>
<p>Once done, continue with the following simple steps:</p>
<h5 id="1clonetherepofromgithub">1. Clone the repo from GitHub</h5>
<pre><code class="language-bash">git clone https://github.com/SashiDo/content-moderation-automations.git
</code></pre>
<h5 id="2settheconfigsandenvvarsinproduction">2. Set the configs and env vars in production</h5>
<ul>
<li><code>checked ✔️</code></li>
</ul>
<h5 id="3addyoursashidoappasaremotebranchandpushchanges">3. Add your SashiDo app as a remote branch and push changes</h5>
<pre><code class="language-bash">git remote add production git@github.com:parsegroundapps/&lt;your-pg-app-your-app-repo&gt;.git
git push -f production master
</code></pre>
<blockquote>
<p>Missing something? Check the <a href="https://github.com/SashiDo/content-moderation-automations/blob/master/README.md">README.md</a> for details. And don’t forget to drop us a line of what you would like to see also included in this article at <a href="mailto:hello@sashido.io">hello@sashido.io</a> 🙂</p>
</blockquote>
<p>TA-DA!🎉 You’re now equipped with fully functional Content moderation logic that will surely spare you a lot of time.</p>
<h1 id="whatisnext">What is Next</h1>
<p>The first two chunks of the Moderation Services are already assembled. The icing on the cake comes with the third part -  a beautiful <a href="https://blog.sashido.io/content-moderation-service-with-nodejs-and-tensorflow-part-3-moderation-admin-app/">ReactJS Admin Panel</a> that turns even the most boring tasks into a game. 😄 Check the <a href="https://nsfw-demo.sashido.io/moderator">demo</a>!</p>
<p>And if you’re still wondering about where to host such a project, don’t forget that SashiDo offers a <a href="https://dashboard.sashido.io/register">Free trial</a>, no credit card required as well as exclusive <a href="https://form.typeform.com/to/x1zJtH">Free consultation by SashiDo's experts</a> for projects involving Machine Learning.</p>
<p>What is your specific use case and what features would you like added to our moderation service? You're more than welcome to <strong>share your thoughts at <a href="mailto:hello@sashido.io">hello@sashido.io</a>.</strong></p>
<p>Happy Coding!</p>
</div>]]></content:encoded></item><item><title><![CDATA[Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. 
Part 1: Restful API Service.]]></title><description><![CDATA[In Part 1 of our series related to the open-source Content Moderation Service built with Node.js, TensorFlowJS, and ReactJS, we'll show you how we've created a an NSFW Image Classification REST API, how to directly clone it, and integrate it to your projects.]]></description><link>https://blog.sashido.io/content-moderation-service-with-nodejs-tensorflowjs-and-reactjs-part-1-restful-api-service/</link><guid isPermaLink="false">5ee26dbc5297da00155e6765</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[machine learning]]></category><category><![CDATA[content moderation]]></category><category><![CDATA[rest api]]></category><category><![CDATA[nodejs]]></category><category><![CDATA[node]]></category><category><![CDATA[Parse Server]]></category><category><![CDATA[API]]></category><category><![CDATA[tensorflow]]></category><category><![CDATA[tensorflow-js]]></category><category><![CDATA[nsfw]]></category><category><![CDATA[nsfwjs]]></category><category><![CDATA[image classifier]]></category><category><![CDATA[Cloud Code]]></category><category><![CDATA[nsfw-classifier]]></category><dc:creator><![CDATA[Irena Russeva]]></dc:creator><pubDate>Wed, 01 Jul 2020 12:53:54 GMT</pubDate><media:content url="https://media-blog.sashido.io/content/images/2020/06/nsfw.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://media-blog.sashido.io/content/images/2020/06/nsfw.png" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. 
Part 1: Restful API Service."><p>The Open-Source Content Moderation Service we have prepared for you is so simple and elegant to use, that even if you do not have any knowledge about algorithms, it won’t matter! You just need to know, it’s using Machine Learning … it’s trendy! ;)</p>
<h1 id="agenda">Agenda:</h1>
<p><a href="#theproblem"><strong>1. The Problem</strong></a><br>
<a href="#thesolution"><strong>2. The Solution</strong></a><br>
<a href="#thestack"><strong>3. The Stack</strong></a><br>
<a href="#developmentenvironment"><strong>4. Development Environment</strong></a><br>
<a href="#apiusageexamples"><strong>5. API Usage Examples</strong></a><br>
<a href="#filestructure"><strong>6. File Structure</strong></a><br>
<a href="#nsfwmodels"><strong>7. NSFW Models</strong></a><br>
<a href="#deploytoproduction"><strong>8. Deploying on Production</strong></a><br>
<a href="#whatcomesnext"><strong>9. What comes Next</strong></a></p>
<h1 id="theproblem">The Problem</h1>
<p>Publishing user-generated content always comes with a risk and moderation is a must! Going through piles of disturbing content, however, is time-consuming and <a href="https://www.youtube.com/watch?v=DlcAjc0ka10">seriously stressful</a>. Big companies like Google and Facebook have entire teams dedicated to removing offensive content, but that’s a luxury we can’t all share. Not to mention that automating this process can come <strong>quite pricey &amp; require a high level of expertise</strong>.</p>
<p>However, advances in computer processing power, storage, data tools, web, etc made machine learning technologies more and more affordable. Machine Learning has already matured to the point where it can be a vital part of various projects of all sizes. All this inspired our team to roll up our sleeves and create a fully-functional Content Moderation service built entirely with Open-Source tools and libraries, which is easily maintained and can be integrated into any <a href="https://www.g2i.co/blog/react-separation-of-concerns">Node.js project effortlessly</a>.</p>
<h1 id="thesolution">The Solution!</h1>
<p>In a brief series of three tutorials, we will build with you a <strong>fully functional Open-Source Content Moderation service with ReactJS based Admin Panel</strong>. It is designed to be easily integrated into every project even if this is your first encounter with Machine Learning. ;)</p>
<p>Here’s what you’ll get by the end of this hattrick:</p>
<ul>
<li><strong>Restful APIs</strong>, so you can flawlessly integrate content moderation logic in every project with user generated content.</li>
<li><strong>Content Moderation Automation Engine</strong> - A straightforward and effortless way to remove all unseemly images. Set the params which decisions should be taken by the ML and for which manual moderation is needed, with no hassle.</li>
<li><strong>Ready-to-use Mobile-first React JS Moderation application</strong> - A beautiful admin panel, so your team can conveniently moderate and manage all images that require manual assessment from an impeccable UI, no additional coding required. Check out <a href="https://bit.ly/3gbSkWP">our demo</a> to have a sneak peek at the Admin App and feel the power in your hands!</li>
<li><strong>A cost-efficient solution</strong> - Our mission is to bring Machine Learning closer to single developers and teams of all sizes by making it affordable. SashiDo managed to drop all costs to the bare minimum of $4.95 for 1 000 000 predictions.</li>
<li><strong>No need for Machine Learning knowledge or skills to get this working!!!</strong><br>
<br></li>
</ul>
<h3 id="examplesanddemos">Examples and Demos</h3>
<table align="center" class="demos">
  <tbody>
    <tr>
      <th align="center">Image Source</th>
      <th align="center">Image Source</th>
      <th align="center">Image Source</th>
    </tr>
    <tr>
      <td align="center">
        <a href="https://nsfw-demo.sashido.io/api/image/classify?url=https://mqfy379g6xncpc3va4epdzkfsxf0vs.files-sashido.cloud/41a516cc1141cdaf775a5cfa083dcb26_thumbnail.png">
          <image src="https://mqfy379g6xncpc3va4epdzkfsxf0vs.files-sashido.cloud/41a516cc1141cdaf775a5cfa083dcb26_thumbnail.png">
        </image></a>
      </td>
      <td align="center">
        <a href="https://nsfw-demo.sashido.io/api/image/classify?url=https://mqfy379g6xncpc3va4epdzkfsxf0vs.files-sashido.cloud/a3115074f603c99770d376f3e6d0f72e_thumbnail.png">
          <image src="https://mqfy379g6xncpc3va4epdzkfsxf0vs.files-sashido.cloud/a3115074f603c99770d376f3e6d0f72e_thumbnail.png">
        </image></a>
      </td>
      <td align="center">
        <a href="https://nsfw-demo.sashido.io/api/image/classify?url=https://mqfy379g6xncpc3va4epdzkfsxf0vs.files-sashido.cloud/355a4b37b8a657dd09f3f50816481cca_thumbnail.png">
          <image src="https://mqfy379g6xncpc3va4epdzkfsxf0vs.files-sashido.cloud/355a4b37b8a657dd09f3f50816481cca_thumbnail.png">
        </image></a>
      </td>
    </tr>
    <tr>
      <th align="center">Classification Result</th>
      <th align="center">Classification Result</th>
      <th align="center">Classification Result</th>
    </tr>
    <tr>
      <td>
<pre>[{
  "className": "Neutral",
  "probability": 0.93821
}, {
  "className": "Drawing",
  "probability": 0.05473
}, {
  "className": "Sexy",
  "probability": 0.00532
}, {
  "className": "Hentai",
  "probability": 0.00087
}, {
  "className": "Porn",
  "probability": 0.00085
}]</pre>
      </td>
      <td>
<pre>[{
  "className": "Sexy",
  "probability": 0.99394
}, {
  "className": "Neutral",
  "probability": 0.00432
}, {
  "className": "Porn",
  "probability": 0.00164
}, {
  "className": "Drawing",
  "probability": 0.00006
}, {
  "className": "Hentai",
  "probability": 0.00001
}]</pre>
      </td>
      <td>
<pre>[{
  "className": "Drawing",
  "probability": 0.96063
}, {
  "className": "Neutral",
  "probability": 0.03902
}, {
  "className": "Hentai",
  "probability": 0.00032
}, {
  "className": "Sexy",
  "probability": 0.00001
}, {
  "className": "Porn",
  "probability": 0.00005
}]</pre>
      </td>
    </tr>
    <tr>
      <td align="center">
          <a href="https://nsfw-demo.sashido.io/api/image/classify?url=https://mqfy379g6xncpc3va4epdzkfsxf0vs.files-sashido.cloud/41a516cc1141cdaf775a5cfa083dcb26_thumbnail.png" class="btn green" target="_blank">Neutral Demo</>
      </a></td>
      <td align="center"><a href="https://nsfw-demo.sashido.io/api/image/classify?url=https://mqfy379g6xncpc3va4epdzkfsxf0vs.files-sashido.cloud/a3115074f603c99770d376f3e6d0f72e_thumbnail.png" class="btn green" target="_blank">Sexy Demo</>
      </a></td>
      <td align="center"><a href="https://nsfw-demo.sashido.io/api/image/classify?url=https://mqfy379g6xncpc3va4epdzkfsxf0vs.files-sashido.cloud/355a4b37b8a657dd09f3f50816481cca_thumbnail.png" class="btn green" target="_blank">Drawing Demo</>
      </a></td>
    </tr>
  </tbody>
</table>
<br>
<h1 id="thestack">The Stack</h1>
<p>For the purpose of this article, we will implement image classification into a <a href="https://www.sashido.io/en/">SashiDo</a> application, as such an approach will allow us to complete the task at hand masterly in no time 😊. If you have your project elsewhere, no worries - our content moderation service is open-source and can be hosted on any other platform that supports fully featured <strong>Node.js</strong> + <strong>MongoDB</strong> or even cloud hosting providers as AWS VMs and Digital Ocean.</p>
<p>However, as we want to focus on the content moderation and not waste time on the infrastructure side, we decided to go serverless and use SashiDo. Have a look at how you can <a href="https://dashboard.sashido.io/register">register</a> and <a href="https://blog.sashido.io/sashidos-getting-started-guide/#createyourfirstapp">create an application</a> in SashiDo with all Parse Server functionalities built-in.</p>
<p>When it comes to the tools and libraries, I’m sure you’ve asked yourself more than once - “Which one is the perfect tool to start off?”. Following the good old <strong>‘Keep it simple’</strong> rule we have preselected this awesome bundle for you.</p>
<ul>
<li><a href="https://nodejs.org/en/">Node.js</a></li>
<li><a href="https://www.mongodb.com/">Mongo DB</a> - a document database that’s used directly by the Parse Server.</li>
<li><a href="https://parseplatform.org/">Parse Server</a> - an open-source Backend-as-a-Service(BaaS) framework initially developed by Facebook. It comes with a bunch of built-in functionalities and can be deployed to any infrastructure that can run Node.js. To implement server-side logic, we'll use <a href="https://docs.parseplatform.org/cloudcode/guide/">Parse Server Cloud Code</a>. It is built directly into the Parse Server and we hope you’ll find it quite straightforward and easy to use.</li>
<li><a href="https://www.tensorflow.org/js">TensorFlow</a> - an open-source software library by Google, designed specifically for deep machine learning applications.</li>
<li><a href="https://github.com/infinitered/nsfwjs">nsfw.js</a> - an amazing JavaScript library by <a href="https://gantlaborde.com/">Gant Laborde</a> for identifying unseemly images. Check out their cool demo at <a href="https://nsfwjs.com">https://nsfwjs.com</a>.</li>
<li><a href="https://github.com/axios/axios">axios</a> - our preferred HTTP client. Of course, you can easily replace it with your favorite lib.</li>
<li><a href="https://parceljs.org/">Parcel.js</a>- Parcel deserved its place here, as it’s a blazing fast, zero-configuration web application bundler.</li>
</ul>
<p>Since <strong>the goal is to have a ready-to-use product</strong>, not only classification logic, for the upcoming tutorials we will add <a href="https://reactjs.org/">React</a> to our stack. It will help us build the admin panel in Part 3 of the series.</p>
<p>Anyhow, today we’ll begin with building the REST API for image classification, so let’s kick it off by starting the project in a development environment!</p>
<h1 id="developmentenvironment">Development Environment</h1>
<p>Implementing new features can always be challenging and has its risks, but setting up a local environment for testing makes life easier for you. 🙂 So in case you want to apply some customizations or run a few tests before deploying in production, that’s the safe way to go.</p>
<p>We have developed and tested with <strong>Node.js 10.2.1 and MongoDB 3.4.X</strong>, so these are the minimum requirements.</p>
<p>You can find our plug-and-play service on <a href="https://github.com/SashiDo/content-moderation-image-api">GitHub</a>. Then simply open the terminal into the directory you’ll use for storing and take the plunge by cloning the project.</p>
<p>Detailed instructions on how to proceed are available in the <a href="https://github.com/SashiDo/content-moderation-image-api/blob/master/README.md">README file</a>, but let me summarize the setup here as well:</p>
<p><strong>1. Clone the project</strong>.</p>
<pre><code class="language-bash">    git clone https://github.com/SashiDo/content-moderation-image-api.git
</code></pre>
<br>
<p><strong>2. Copy the env.example file to a .env</strong> and set your database URL.</p>
<pre><code class="language-bash">   cd content-moderation-image-api
   cp env.example .env
</code></pre>
<p>Since we are testing with a SashiDo application, there is no need to run a local instance of MongoDB. Simply get your application's Database URL from the Dashboard -&gt; App -&gt; App Settings -&gt; Security &amp; Keys.</p>
<p>Once you set the DATABASE_URI variable into the newly created <code>.env</code> file, continue with the next step.</p>
<p><strong>3. Install dependencies</strong> The project holds a complete example, all you need to do to is execute:</p>
<pre><code class="language-bash">    npm install
</code></pre>
<br>
<p><strong>4. Start the local server</strong>.</p>
<pre><code class="language-bash">    npm run dev
</code></pre>
<br>
<p>Et voila! Running on <a href="http://localhost:1337">http://localhost:1337</a> displays on your terminal.</p>
<pre><code class="language-bash">[nodemon] 2.0.4
...
[nodemon] starting `node index index.js`
✨  Built in 2.55s.
node-pre-gyp ...
...
Running on http://localhost:1337
⠙ Building index.js...The NSFW Model was loaded successfuly!
✨  Built in 16.41s.
</code></pre>
<br>
<p>Now that we have the classification service running locally, let’s put the focus on the usage, file structure and models.</p>
<h1 id="apiusageexamples">API Usage Examples</h1>
<p>To make you feel as comfortable as possible we have added two approaches for classifying images - from an Express route, and for all Parse Server fans - from a Parse Cloud Code Function. 🙂</p>
<h2 id="curl">cURL</h2>
<p>The <strong>Express Router</strong> is a complete middleware and routing system. You can easily classify images in the browser or with a simple cURL request.</p>
<pre><code class="language-bash">curl http://localhost:1337/api/image/classify?url=https://nsfw-demo.sashido.io/sexy.png
</code></pre>
<p><img src="https://media-blog.sashido.io/content/images/2020/06/express_crop.gif" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. 
Part 1: Restful API Service."></p>
<p>This is for (future)Parse Server fans - <strong>Simple Cloud Code Function</strong>  - <a href="https://docs.parseplatform.org/cloudcode/guide/#what-is-cloud-code">Cloud Code</a> runs in the Parse Server rather than running on the user’s device. You can get predictions calling the  <code>nsfwImageClassify</code> function from REST API or from your Android, iOS, etc app. Check the example below on how to invoke from the REST API:</p>
<pre><code class="language-bash">curl -X POST \
  -H &quot;X-Parse-Application-Id: myAppId&quot; \
  -H &quot;X-Parse-REST-API-Key: myRestKey&quot; \
  -d '{ &quot;url&quot;: &quot;https://nsfw-demo.sashido.io/sexy.png&quot; }' \
  http://localhost:1337/functions/nsfwImageClassify
</code></pre>
<br>
<h2 id="parsesdks">Parse SDKs</h2>
<p>You can invoke the <code>nsfwImageClassify</code> function from the client-side or using the Parse SDKs as well:</p>
<h3 id="androidsdk">Android SDK</h3>
<pre><code class="language-Java">HashMap&lt;String, String&gt; params = new HashMap&lt;String, String&gt;();
params.put(&quot;url&quot;, &quot;https://nsfw-demo.sashido.io/sexy.png&quot;);
ParseCloud.callFunctionInBackground(&quot;nsfwImageClassify&quot;, params, new FunctionCallback&lt;Object&gt;() {
  void done(Object predictions, ParseException e) {
    if (e == null) {
      // prediction
    }
  }
});
</code></pre>
<p>More information about how to work with the Android SDK can be found in the <a href="https://docs.parseplatform.org/android/guide/#use-cloud-code">official docs</a>.</p>
<h3 id="iossdk">iOS SDK</h3>
<pre><code class="language-c">PFCloud.callFunctionInBackground(&quot;nsfwImageClassify&quot;, withParameters: [&quot;url&quot;:&quot;https://nsfw-demo.sashido.io/sexy.png&quot;]) {
  (predictions, error) in
  if !error {
    // prediction
  }
}
</code></pre>
<p>More information about how to work with the Parse iOS SDK can be found in the <a href="https://docs.parseplatform.org/ios/guide/#use-cloud-code">official docs</a>.</p>
<h3 id="apiconsole">API Console</h3>
<p>SashiDo app owners can <strong>test all Cloud Code functions from the Dashboard</strong> with just a click. Check our super-friendly <a href="https://blog.sashido.io/introducing-the-api-console/">API Console</a> that also gives you the option to export any request to a cURL.<br>
<br><br>
<img src="https://media-blog.sashido.io/content/images/2020/06/cloud_code_crop.gif" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. 
Part 1: Restful API Service."></p>
<h1 id="filestructure">File Structure</h1>
<p>Being well informed about the file structure is of great importance, as it puts you on solid grounds. To avoid any misunderstanding, we will give the architecture a bit of special attention.</p>
<p>Following the best practices, we keep all the source code in the <code>src</code> folder, with two subfolders - for the Cloud Code and visualization. Let’s focus on the <code>cloud</code> folder that holds all the image classification logic.</p>
<pre><code class="language-bash">src/cloud/
├── ml_models/
│   ├── nsfw_model.js
├── routes/
│   ├── api.js
│   └── index.js
├── app.js
├── config.js
├── functions.js
└── main.js
</code></pre>
<br>
<p>Our recommendation for a structure is decoupling the API, NSFW models, and image classification logic to different files. Let me go brief over the architecture:</p>
<ul>
<li>
<p><code>nsfw_model.js</code> - contains logic for loading the NSFW model. The Classification method also belongs here.</p>
</li>
<li>
<p><code>config.js</code> - instead of loading the model and shape from process.env directly each time, we’ve saved them here.</p>
</li>
<li>
<p><code>api.js</code> - as you’ve already guessed, here’s the heart of this project - the API. To keep things simple and move one step at a time, we have implemented just one handler. Its only job is invoking the classify method for an image and returning a prediction.</p>
</li>
</ul>
<pre><code class="language-js">import express from &quot;express&quot;;
const router = express.Router();

router.get(&quot;/image/classify&quot;, async (req, res) =&gt; {
 try {
   const { url } = req.query;
   const result = await nsfwModel.classify(url);
   res.json(result)
 } catch (err) {
   console.error(err);
   res.status(500).json({
      error: &quot;Ups... Something went wrong!&quot;
    });
 }
});
</code></pre>
<br>
<p>That’s it! Our API is ready to go through a pile of photos in no time helping you protect your project’s users from inappropriate content.</p>
<p><strong>NB!</strong> <code>main.js</code>, <code>app.js</code> and <code>functions.js</code> are part of SashiDo’s predefined cloud code and the structure is Parse Server specific. Let me give a piece of information about each file’s function as such insights could help you hand over effortlessly to your project if hosted elsewhere.</p>
<ul>
<li>
<p><code>main.js</code> -  is the entry point for each SashiDo application.</p>
</li>
<li>
<p><code>app.js</code> - holds your Express logic, as we mount it automatically to the Parse Server Deployment.</p>
</li>
<li>
<p><code>functions.js</code> - is where Simple Cloud Code lives. 🙂</p>
</li>
</ul>
<h1 id="nsfwmodels">NSFW Models</h1>
<p>To keep it simple, in the context of this tutorial, you can think of the Machine Learning model as a file that has been trained to recognize certain types of patterns. For our specific case, we pass the model an image and it returns a JSON of predictions how likely this image falls into each of the following 5 classes:</p>
<ul>
<li><em>Drawing</em> — Harmless art, or picture of art</li>
<li><em>Hentai</em> — Pornographic art, unsuitable for most work environments</li>
<li><em>Neutral</em> — General, inoffensive content</li>
<li><em>Porn</em> — Indecent content and actions, often involving genitalia</li>
<li><em>Sexy</em> — Unseemly provocative content, can include nipples</li>
</ul>
<p>SashiDo stores three pre-trained ready for integration <a href="https://github.com/GantMan/nsfw_model">nsfw models</a> that you can choose from. Each of these models has different accuracy, shape size and resource consumption.</p>
<script class="ct" src="https://gist.github.com/mignev/f6c08ff0c73055deb1ea0c16edc30560.js"></script>
<p>You've probably noticed that mobilenet models are far more lightweight than the inception one, which has a high <strong>RAM and CPU consumption</strong>. That's why for the development environment we have preset the Mobilenet 93. However, you can always choose a different model. To evaluate each model’s performance and check which matches your use case best, check the <a href="https://nsfwjs.com">NSFWJS Demo page</a>.</p>
<p>For our examples and demos, we’re using Inception_v3, as that’s our favorite choice. If you also choose using Inception_v3, keep in mind that your project will need more computing power. For the occasion in which SashiDo is your mBaaS provider - the minimum <a href="https://blog.sashido.io/power-up-with-sashidos-brand-new-engine-feature/">Engine</a> that can handle this task is Production 2X.</p>
<h1 id="deploytoproduction">Deploy to Production</h1>
<h3 id="1configuration">1. Configuration</h3>
<br>
<p>The configs we need to set up in production are <strong>TF_MODEL_URL</strong> for the TensorFlow Model URL and <strong>TF_MODEL_INPUT_SHAPE_SIZE</strong> for the Input Shape Size. Check the <a href="#nsfwmodels">previous section</a> to make your pick.</p>
<p>If you are a proud <a href="https://dashboard.sashido.io">SashiDo</a> app owner or collaborator, jump to <code>Runtime -&gt; Env Variables</code> section to set the values. Please, note that any change of environment variables from the Dashboard automatically triggers a <a href="https://blog.sashido.io/manage-and-keep-track-of-your-builds-and-deployments/">Deployment</a> of your project. When you’re all set, the final result should be:</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/06/Screenshot-2020-06-25-at-11.01.50.png" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. 
Part 1: Restful API Service."></p>
<h3 id="2pushtheprojectcodetoyoursashidoapp">2. Push the project code to your SashiDo App</h3>
<br>
<p>Having a local environment for experimenting surely pays off! You already went through the code and verified it’s behaving properly. Make sure that you've <a href="https://blog.sashido.io/how-to-start-using-github-with-sashido-for-beginners/">connected your SashiDo account to GitHub</a> and safely push the changes to your production app’s master branch.</p>
<p>An elegant approach to go with is adding a remote:</p>
<pre><code class="language-bash">git remote add production git@github.com:parsegroundapps/&lt;your-pg-app-your-app-repo&gt;.git
</code></pre>
<br>
<p>... and applying to the live app seamlessly.</p>
<pre><code class="language-bash">git push -f production master
</code></pre>
<br>
<p>If you're keeping up with the good practice and have a testing/staging application apart from your live one, we would always advise pushing changes there first.</p>
<p>Naturally, pushing new code to GitHub automatically triggers a <a href="https://blog.sashido.io/manage-and-keep-track-of-your-builds-and-deployments/">Build and Deployment</a> of your SashiDo App.</p>
<h2 id="3getlivepredictions">3. Get Live Predictions</h2>
<br>
<p>Same as with testing locally, try out calling the classify method to validate the smooth operation of the service applied. In case you forgot what are the commands, refresh your memory <a href="#apiusageexamples">here</a>.</p>
<p><img src="https://media-blog.sashido.io/content/images/2020/06/sexy.jpg" alt="Content Moderation Service with Node.js, TensorFlowJS, and ReactJS. 
Part 1: Restful API Service."></p>
<p><strong>Well, that’s it! A piece of cake, right. 🙂</strong></p>
<h1 id="whatcomesnext">What comes Next</h1>
<ul>
<li>
<p><a href="https://blog.sashido.io/content-moderation-service-with-nodejs-and-tensorflow-part-2-moderation-automation/"><strong>Content Moderation Service with Node.js, TensorFlowJS and ReactJS. Part 2: Moderation Automation</strong></a> - The next part describes in details how to add an <strong>Automation Engine</strong> and implement the logic for the automatic deletion of most hazardous images. It will be quite useful, I promise!</p>
</li>
<li>
<p><a href="https://blog.sashido.io/content-moderation-service-with-nodejs-and-tensorflow-part-3-moderation-admin-app/"><strong>Content Moderation Service with Node.js, TensorFlowJS and ReactJS. Part 3: Moderation Admin App</strong></a> - To put the cherry on top, we add a beautiful <strong>Admin Panel</strong> and prove that even most boring tasks may be completed with finesse.</p>
</li>
</ul>
<p>And if you’re still wondering about where to host such a project, don’t forget that SashiDo offers a <a href="https://dashboard.sashido.io/register">Free trial</a>, as well as exclusive <a href="https://form.typeform.com/to/x1zJtH">Free consultation by SashiDo's experts</a> for projects involving Machine Learning.</p>
<p>Our team will be happy to hear about your specific use case and what problems our moderation service helped you solve. You're more than welcome to drop us a line at <a href="mailto:hello@sashido.io">hello@sashido.io</a>!</p>
<p>Happy Coding!</p>
</div>]]></content:encoded></item><item><title><![CDATA[How to migrate Database and Collections Efficiently with Studio 3T]]></title><description><![CDATA[One of the great benefits that SashiDo gives you is full access to your database. You can not only explore your records from our super-user-friendly DB Browser but go further and connect directly to your MongoDB with a tool of your choice. 

We already have introduced you to Studio 3T - an easily operated, quite fast and very well documented MongoDB client...
We already have introduced you to Studio 3T ]]></description><link>https://blog.sashido.io/migrate-database-and-collections-efficiently-with-studio-3t/</link><guid isPermaLink="false">5d235594290cea0015212b41</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[export database]]></category><category><![CDATA[import database]]></category><category><![CDATA[mongodump]]></category><category><![CDATA[mongorestore]]></category><category><![CDATA[connect to database]]></category><category><![CDATA[Studio 3T]]></category><dc:creator><![CDATA[Irena Russeva]]></dc:creator><pubDate>Thu, 25 Jul 2019 12:00:35 GMT</pubDate><media:content url="https://media-blog.sashido.io/content/images/2019/07/Migrate-Database.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://media-blog.sashido.io/content/images/2019/07/Migrate-Database.png" alt="How to migrate Database and Collections Efficiently with Studio 3T"><p>One of the great benefits that SashiDo gives you is full access to your database. You can not only explore your records from our super-user-friendly DB Browser but go further and connect directly to your MongoDB with a tool of your choice.</p>
<p>We already have introduced you to Studio 3T - an easily operated, quite fast and very well documented MongoDB client, in our <a href="https://www.youtube.com/channel/UCaoY71FbidHgmclsOBNplyg/videos">YouTube video tutorial series</a>. There we presented a demo on how to import and export collections to JSON.</p>
<p>Nevertheless, we often get many questions on how to migrate to SashiDo and today, I will shed more light on how you can easily migrate your data with Studio 3T. The best solution for export/import data when it comes to migration will be in BSON, as MongoDB represents JSON documents in binary-encoded format behind the scenes.</p>
<p>I will guide you step by step through the process, but first, let me share why Studio 3T:</p>
<p><strong>Studio 3T</strong> is a fully featured IDE for MongoDB that allows users to explore their local database or to work with shards and replica sets. It is fully compatible with current and legacy releases of MongoDB and has a set of useful features for interacting with the database, including SQL queries. Customers are granted a free trial period of 30 days and discounts are available for Students, Teachers, Nonprofit Organizations and Startups. :)</p>
<blockquote>
<p>On top of that, <strong>we have arranged a discount for all SashiDo customers</strong>, as we want you to have the most convenient and easy way of interacting with your database. <strong>Just contact Angelo from Studio 3T and pass on SD0619 promo code and you’ll get 20% off</strong>. How cool is that?! You can download Studio 3T from <a href="https://studio3t.com/download/">here</a>.</p>
</blockquote>
<p>Now, when we know our tool, let’s start off by connecting to your SashiDo MongoDB!</p>
<h2 id="connecttoyoursashidodb">Connect to your SashiDo DB</h2>
<p>The Studio 3T Connection Manager enables you to link to your MongoDB almost instantly. There are several options that you can use to start a connection, but for the purposes of this tutorial, I'll focus on using the MongoDB URI. The URL for your SashiDo Database can be found on the App's Dashboard -&gt; App Settings -&gt; Security&amp;Key. Just click on Show Database URL and copy the address.</p>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/DB_URI.png" alt="How to migrate Database and Collections Efficiently with Studio 3T"></p>
<p>Next, open Studio 3T  and press the <code>Connect</code> button in the global toolbar. This will open the Connection Manager. Click on <code>New Connection</code> to launch the New Connection Window. It would display several options for you to choose from. Select from URI and paste the already copied connection string. Confirm with the <code>OK</code> button.</p>
<p>Last but not least, give the new Connection an easily recognizable name and hit <code>Save</code>. The DB is now displayed in the Connection Manager and all that’s left is to press <code>Connect</code></p>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/connectDbFinal.gif" alt="How to migrate Database and Collections Efficiently with Studio 3T"></p>
<p>The cool thing is that Studio 3T will remember that connection and automatically save it, so next time you will connect with just one click!</p>
<p>Once you are connected, you can start exploring all great features that Studio 3T offers - Visual Query Builder, SQL Queries, In-place Data Editing, Tasks, and many more neat options. In this tutorial, we will use the Import and Export properties, which enable you to migrate data in a specified format. All JSON, CSV, BSON, and SQL are supported, together with import/export to Another Collection.</p>
<h2 id="exporttobsonmongodump">Export to BSON(mongodump)</h2>
<ol>
<li><strong>First open the <strong>Export  Wizard</strong>!</strong> There are two ways to do that:</li>
</ol>
<ul>
<li>
<p>Press on Export in the Global Toolbar.</p>
</li>
<li>
<p>Right-click on any server, database, or collection and choose Export Data.</p>
</li>
</ul>
<ol start="2">
<li><strong>Select Export Format</strong>. We will choose <strong>mongodump</strong> for our migration example, as this is the binary format to export a database/collection together with the indexing rules, hence is most suitable for migrations.</li>
</ol>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/dumpFinal.png" alt="How to migrate Database and Collections Efficiently with Studio 3T"></p>
<p>On the next screen select <strong>BSON - mongodump folder</strong>.</p>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/dump2.png" alt="How to migrate Database and Collections Efficiently with Studio 3T"></p>
<ol start="3">
<li>
<p><strong>Pick out the folder to which the data will be exported</strong>. Move on by selecting the folder where you will export the DB. Have in mind that mongodump folders have a specific structure. This means that Studio 3T will automatically create a subfolder with the dumped database within the chosen folder, which will have the existing DB name. If you want to import this database at a later stage, you will need to rename the subfolder with the new database name.</p>
</li>
<li>
<p><strong>Smash the <code>Start Export</code> button</strong> and wait a few seconds or minutes, depending on the database size. See how straightforward the whole process is:</p>
</li>
</ol>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/exportFinale.gif" alt="How to migrate Database and Collections Efficiently with Studio 3T"></p>
<p>Let’s move on and see how easy is to import data. :)</p>
<h2 id="importfrombsonmongorestore">Import from BSON(mongorestore)</h2>
<p>Well, the process here is quite similar to the one we have already gone through.</p>
<ol>
<li><strong>Connect to the new Database</strong>, where you will import the previously exported data and get its name. You will need the name to rename the subfolder of the already dumped Database, remember?</li>
</ol>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/copyName.gif" alt="How to migrate Database and Collections Efficiently with Studio 3T"></p>
<ol start="2">
<li>
<p><strong>Rename the already existing mongodump subfolder</strong> respectively.</p>
</li>
<li>
<p><strong>Open the Import Wizard</strong> - using the below approaches, analogous to the Export one:</p>
</li>
</ol>
<ul>
<li>Right Click on the specific collection/database and Choose Import Collections</li>
<li>From the Global Toolbar -&gt; Import</li>
</ul>
<ol start="4">
<li><strong>Choose BSON - mongodump folder</strong> for Import Option and hit OK.</li>
</ol>
<p>Then from the <strong>Select mongodump folder to import</strong> field choose the root folder of the dumped database. All collections that are about to be imported will display on the right side of the screen.</p>
<ol start="5">
<li><strong>Start the import</strong> of collections by simply clicking the <code>Execute</code> button.</li>
</ol>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/ImportGoodQlty.png" alt="How to migrate Database and Collections Efficiently with Studio 3T"></p>
<p>It’s simple as that! See for yourself how easy and flawless you can import a Database with Studio 3T:</p>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/importFinale.gif" alt="How to migrate Database and Collections Efficiently with Studio 3T"></p>
<p><strong>Voilà!</strong> The Import is completed and you can play around with your Database directly from your super-user-friendly SashiDo DB Browser, which makes every task a piece of cake.</p>
<blockquote>
<p>N.B! If you are a Windows user, note that Windows has a specific general naming convention and character limitations, which in some cases can be in controversy with the MongoDB naming system. In that line of thinking, pay attention to databases that have columns of type Pointer or Relation as these may not be properly exported/imported. Another tricky case while using Studio 3T is that mongodump creates BSON archives in .gzip, which is incompatible with Windows.</p>
</blockquote>
<p>Happy coding!</p>
</div>]]></content:encoded></item><item><title><![CDATA[How to integrate Push Notifications in Android Step by Step]]></title><description><![CDATA[We all know that communication with app users is essential for app publishers. And push notifications are probably the most effective way to speak directly to your audience. They don't get caught in spam filters, or forgotten in an inbox.]]></description><link>https://blog.sashido.io/android-push-notifications-with-sashido-step-by-step/</link><guid isPermaLink="false">5d233772290cea0015212b39</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Getting Started]]></category><category><![CDATA[android]]></category><category><![CDATA[Push Notifications]]></category><category><![CDATA[android-push-notifications]]></category><dc:creator><![CDATA[Irena Russeva]]></dc:creator><pubDate>Tue, 23 Jul 2019 08:29:01 GMT</pubDate><media:content url="https://media-blog.sashido.io/content/images/2019/07/Push-Notifications-Android.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://media-blog.sashido.io/content/images/2019/07/Push-Notifications-Android.png" alt="How to integrate Push Notifications in Android Step by Step"><p>We all know that communication with app users is essential for app publishers. And push notifications are probably the most effective way to speak directly to your audience. They don't get caught in spam filters, or forgotten in an inbox — furthermore, they usually provide a quite high number of click-through rates. Another strong benefit of Push notifications is that they can be a delicate reminder to use an app, regardless if it is open or not.</p>
<p>Exactly these great advantages are the reason for every developer to want to have a quick and easy way of <a href="https://blog.sashido.io/sending-milions-of-push-notifications-with-go-redis-and-nats/">Sending Millions of Push Notifications per Minute</a>.</p>
<p>In this tutorial, we will walk through all mandatory steps for setting Android Push Notifications using Firebase Cloud Messaging and SashiDo Dashboard, as follows:</p>
<ol>
<li><a href="#1connectyourandroidapptoyoursashidoparseserver">Connect your Android App to your SashiDo Parse Server</a></li>
<li><a href="#2createafirebaseprojectandconnectittoyourandroidapp">Create a Firebase Project and connect it to your Android App</a></li>
<li><a href="#3addthefirebasecredentialstosashido">Add the Firebase Credentials to SashiDo</a></li>
<li><a href="#4setuptheandroidmanifest">Include the mandatory FCM Parse services to the Android.Manifest file</a></li>
<li><a href="#5createyourfirstinstallation">Create Installation and send your first Push Notification</a></li>
</ol>
<p>Let’s go together through each step one by one :)</p>
<h3 id="1connectyourandroidapptoyoursashidoparseserver">1. Connect your Android App to your SashiDo Parse Server</h3>
<p>1.1 <strong>Add the Parse Android SDK to your Android project</strong></p>
<p>First, include the <code>JitPack</code> repository to the <code>build.gradle(Project:ProjectName)</code> file of your project. Keep in mind there is one more <code>build.gradle</code> file and pay attention to make this change to the one that is in charge of the Project, not the Modules.</p>
<pre><code class="language-java">// File: build.gradle(Project:ProjectName)

allprojects {
   repositories {
       ....
       maven { url &quot;https://jitpack.io&quot; }
}
</code></pre>
<p>Then add the library to the dependencies, this will be in the different build.gradle(Module:app) folder.</p>
<pre><code class="language-java">// File: build.gradle(Module:app)

dependencies {
…
implementation &quot;com.github.parse-community.Parse-SDK-Android:parse:latest_Jitpack_Version_Here&quot;

implementation &quot;com.github.parse-community.Parse-SDK-Android:fcm:latest_Jitpack_Version_Here&quot;

}
</code></pre>
<p>Wondering how would you know which is the latest version? Simply check at <a href="https://jitpack.io/#parse-community/Parse-SDK-Android">https://jitpack.io/</a>. :)</p>
<p>1.2 <strong>Connect to your SashiDo App</strong></p>
<p>Create a new Java Class, named App.</p>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/CreateAppClass.jpg" alt="How to integrate Push Notifications in Android Step by Step"></p>
<p>Make sure to import Parse and  Initialize your SashiDo Parse Server Application like this:</p>
<pre><code class="language-java">//File: App.java

import com.parse.Parse;

//   Initialize your SashiDo Parse Server:public class App extends Application

public class App extends Application {
   @Override
   public void onCreate() {
     super.onCreate();

Parse.initialize(new Parse.Configuration.Builder(context: this) 
.applicationId(&quot;YOUR_SASHIDO_APPLICATIONID_HERE&quot;)
             .clientKey(&quot;YOUR_SASHIDO_CLIENTKEY_HERE&quot;)
             .server(&quot;YOUR_SASHIDO_API_URL_ADDRESS_HERE&quot;)
             .build()
       );
   }
}
</code></pre>
<p><strong>All credentials for your SashiDo app can be found at the app’s Dashboard -&gt; App Settings -&gt; Security &amp; Keys section.</strong></p>
<p>Continue with defining the App.java class to the <code>AndroidManifest.xml</code> like that:</p>
<pre><code class="language-java">// File: AndroidManifest.xml 

&lt;application
   android:name=&quot;.App&quot;
    ...
&lt;/application&gt;
</code></pre>
<h3 id="2createafirebaseprojectandconnectittoyourandroidapp">2. Create a Firebase Project and connect it to your Android App</h3>
<p>2.1 <strong>Create a Project In Firebase</strong></p>
<p>Go to the <a href="https://console.firebase.google.com/u/0/">Firebase Console</a> and click on <code>+Add Project</code>. Then choose an appropriate name for your project in Firebase:</p>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/Create_Firebase1-1.png" alt="How to integrate Push Notifications in Android Step by Step"></p>
<p>Once the project is ready click on the Android logo in the Get started section by adding Firebase to your app field that is displayed. Once you select it this screen will appear:</p>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/AddFirebasetoApp.png" alt="How to integrate Push Notifications in Android Step by Step"></p>
<p>Get the project package name from your Android App’s <code>AndroidManifest.xml</code>:</p>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/ProjectPackageName.png" alt="How to integrate Push Notifications in Android Step by Step"></p>
<p>2.2 <strong>Add the google.json file to your Android Project</strong></p>
<p>The second step requires you to download the config file. Once you download the google-services.json, make sure to include it to the <em>app folder</em> of your Android Studio project.</p>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/AddFirebaseToApp_2.png" alt="How to integrate Push Notifications in Android Step by Step"></p>
<p>2.3 <strong>Add the Firebase SDK</strong></p>
<p>Basically, you need to modify the <code>build.gradle</code> files, so they can use the <code>google-services.json</code>.</p>
<p>Proceed by following the steps in the Firebase Console and include the respective line to the dependencies in your <code>build.gradle(Project:ProjectName)</code>.</p>
<pre><code class="language-java">//File: build.gradle(Project:ProjectName)

buildscript {
  dependencies {
	....
    classpath 'com.google.gms:google-services:latest_Version_Here'
  }
}

</code></pre>
<p>Then move to the <code>build.gradle(Module:app)</code> and include the following line to the bottom of the file:</p>
<pre><code class="language-java">// File: build.gradle(Module:app)

apply plugin: 'com.google.gms.google-services'
</code></pre>
<p>The latest versions will be embedded in the Firebase console guidance code. Still, it’s always good to know from where you can check them. <a href="https://developers.google.com/android/guides/google-services-plugin">Here</a> you may find up-to-date info for the Google Services Gradle Plugin.</p>
<p>If you stumble upon some bumps while creating the Firebase project, check out the <a href="https://firebase.google.com/docs/android/setup?authuser=0">official Firebase docs</a> for some hints. ;)</p>
<h3 id="3addthefirebasecredentialstosashido">3. Add the Firebase Credentials to SashiDo</h3>
<p>Maybe the easiest part from the setup ;)</p>
<p>Go to your Firebase <strong>Project Settings</strong>…</p>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/Project_Settings.png" alt="How to integrate Push Notifications in Android Step by Step"></p>
<p>… <strong>Cloud Messaging tab</strong></p>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/Cloud_messaging.png" alt="How to integrate Push Notifications in Android Step by Step"></p>
<p>Copy the <code>Sender Id</code> and the <code>Server Key</code> credentials and add them to your SashiDo App from App Settings -&gt; Push</p>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/Add_to_SashiDo.png" alt="How to integrate Push Notifications in Android Step by Step"></p>
<p>Simple as that ;)</p>
<p>Hit the <code>Save Changes</code> button and continue with setting up the <code>Android.Manifest</code> file.</p>
<h3 id="4setuptheandroidmanifest">4. Set up the Android Manifest</h3>
<p>Next, you will need to add the following services to the manifest:</p>
<p>4.1. <strong>The FCM ParseFirebaseMessagingService</strong>:</p>
<pre><code class="language-java">// File: Android.Manifest

&lt;service
    android:name=&quot;com.parse.fcm.ParseFirebaseMessagingService&quot;&gt;
    &lt;intent-filter&gt;
        &lt;action android:name=&quot;com.google.firebase.MESSAGING_EVENT&quot;/&gt;
    &lt;/intent-filter&gt;
&lt;/service&gt;
</code></pre>
<p>4.2. <strong>ParsePushBroadcastReceiver</strong>:</p>
<pre><code class="language-java">// File: Android.Manifest

&lt;receiver
    android:name=&quot;com.parse.ParsePushBroadcastReceiver&quot;
    android:exported=&quot;false&quot;&gt;
    &lt;intent-filter&gt;
        &lt;action android:name=&quot;com.parse.push.intent.RECEIVE&quot; /&gt;
        &lt;action android:name=&quot;com.parse.push.intent.DELETE&quot; /&gt;
        &lt;action android:name=&quot;com.parse.push.intent.OPEN&quot; /&gt;
    &lt;/intent-filter&gt;
&lt;/receiver&gt;
</code></pre>
<p>After finishing with the <code>Android.Manifest</code>, you are almost ready to start sending Push Notifications to your users. Just one last step left and you are good to go!</p>
<p>To be able to send Push Notifications, you need to have installations, right?! Let’s see how that would work!</p>
<h3 id="5createyourfirstinstallation">5. Create your first Installation</h3>
<p>Move on to the final step - create and save the device <code>Installation</code>.</p>
<pre><code class="language-java">//File: App.java

 ParseInstallation.getCurrentInstallation().save(); 
</code></pre>
<p>The installation object that you save in the database contains the token of the device, which is actually used by SashiDo's Push Notification Service to send notifications.</p>
<p>Voilà! Now just go to your awesome project’s Dashboard and send your first Android Push Notification.</p>
<p><img src="https://media-blog.sashido.io/content/images/2019/07/SendPushFinal.gif" alt="How to integrate Push Notifications in Android Step by Step"></p>
<p>Happy Coding!</p>
<div class="push-banner">Keep your users engaged with SashiDo's fully integrated Push Notifications service!
    <a class="btn green text" href="https://bit.ly/3nG83Ch">Try For Free<span>no vendor lock-in. no credit card</span></a>
</div>
<p>Useful docs related to this tutorial:</p>
<p><a href="https://docs.parseplatform.org/parse-server/guide/#android-apps">Oficial Parse docs on how to Configure your Clients to Receive Push Notifications<br>
</a></p>
<p><a href="https://firebase.google.com/docs/android/setup">Oficial Firebase docs on how to Add Firebase to your Android Project<br>
</a></p>
</div>]]></content:encoded></item><item><title><![CDATA[What’s new in the Era of Parse Server v3]]></title><description><![CDATA[SashiDo's Team is happy to announce Parse Server v3.1.3 is finally here and it brings a whole new brave world with it! Beneficial upgrades, long-awaited bug fixes, and cool improvements - get a quick glimpse at the highlights here.]]></description><link>https://blog.sashido.io/whats-new-in-the-era-of-parse-server-v3/</link><guid isPermaLink="false">5c98e299c67ad20016e0eb75</guid><category><![CDATA[Parse Server]]></category><category><![CDATA[Cloud Code]]></category><category><![CDATA[announcements]]></category><dc:creator><![CDATA[Irena Russeva]]></dc:creator><pubDate>Tue, 02 Apr 2019 14:01:31 GMT</pubDate><media:content url="https://media-blog.sashido.io/content/images/2019/03/parse-server-v3.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://media-blog.sashido.io/content/images/2019/03/parse-server-v3.png" alt="What’s new in the Era of Parse Server v3"><p>Our team is happy to announce that once and for all you have the opportunity to replace the little bit rusty Parse Server v.2.3.3. It is time to a leap ahead into the up-to-date versions! And we're not simply giving you one new version, but whole 3 of them to choose from - v2.8.4, v.3.0.0, and the latest v.3.1.3.</p>
<p>Let’s see some of the fresh advantages they are bringing :)</p>
<h3 id="cleanercloudcode">Cleaner Cloud Code</h3>
<p>Improved lightweight Cloud Code syntax is a major advantage that comes with Parse Server v3. Handlers have a new interface based on promises, which achieves a more clear and readable code with the es6 async/await constructs. No need to rely on heavy then/catch blocks and respectively response.success / response.error are to be removed from Cloud Code. Follow our tutorial on <a href="https://blog.sashido.io/fearlessly-upgrading-your-sashido-app-from-parse-server-v2-to-v3">how to upgrade your SashiDo app from Parse Server v2 to v3</a> and you will flawlessly switch to the new syntax.</p>
<h3 id="parseserversdk20">Parse Server SDK 2.0</h3>
<p>Another significant innovation present from v3 is that Cloud Code runs with <a href="https://github.com/parse-community/Parse-SDK-JS/blob/master/2.0.0.md">Parse-SDK 2.0</a>. Many bug fixes are implemented to the 2.0 SDK. Furthermore, <a href="https://parseplatform.org/Parse-SDK-JS/api/2.1.0/Parse.Query.html#containedBy">containedBy</a> and <a href="https://parseplatform.org/Parse-SDK-JS/api/2.1.0/Parse.Query.html#includeAll">includeAll</a> query methods are added, together with the option to fetch objects with includes.</p>
<h3 id="fulltextqueries">Full-text Queries</h3>
<p>Up to date, Parse Server has been enriched to add automatic indexing on $text indexes. That makes the search capabilities on text fields much more efficient and allows you to perform <a href="https://docs.parseplatform.org/js/guide/#queries-on-string-values">Full-Text Search</a>  without any hassle or need of workaround solutions.</p>
<h3 id="requestoriginalip">Request original IP</h3>
<p>One more well-known challenge has been resolved in the most recent Parse versions, as presently the request original IP is added to cloud code hooks, which facilitates developers that need to access the user IP easily and utilize it to fetch geolocation for example.</p>
<h3 id="livequeryclps">Live Query CLPs</h3>
<p>This is definitely an amendment that stands out and it is available in the new Parse versions that SashiDo supports. Its core idea is that CLPs are now passed alongside the object when notifying the LiveQuery server so they are handled without having to access the Database.</p>
<h3 id="sharedataobjectbetweenbeforesaveandaftersave">Share data object between <code>beforeSave</code> and <code>afterSave</code></h3>
<p>With version 3.0.0 another cool improvement is added to the deck - the ability to pass a context object between beforeSave and afterSave on the same object. Here is how to easily add data between the Triggers:</p>
<pre><code class="language-js">
Parse.Cloud.beforeSave('Review', async request =&gt; {
  request.context = {
    comment: 'Great'
  };
});

Parse.Cloud.afterSave('Review', async request =&gt; {
  console.info(request.context.comment); //Great
});

</code></pre>
<p>Luckily, all these are integrated into the new Parse Versions that SashiDo supports and are now available to you! There are much more changes, which you may check-out in the <a href="https://github.com/parse-community/parse-server/blob/master/CHANGELOG.md">parse-community GitHub</a>.</p>
<p>Happy Coding with Parse Server v3! :)</p>
</div>]]></content:encoded></item><item><title><![CDATA[Manage and Keep track of your Builds and Deployments]]></title><description><![CDATA[Let me present you the Builds and Deployments -  awaited features by many of you that are now a fact! ]]></description><link>https://blog.sashido.io/manage-and-keep-track-of-your-builds-and-deployments/</link><guid isPermaLink="false">5c1a0439ff04020015d194c2</guid><category><![CDATA[announcements]]></category><category><![CDATA[Builds]]></category><category><![CDATA[Deployments]]></category><dc:creator><![CDATA[Irena Russeva]]></dc:creator><pubDate>Fri, 04 Jan 2019 08:15:17 GMT</pubDate><media:content url="https://media-blog.sashido.io/content/images/2019/01/builds_and_deployments.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://media-blog.sashido.io/content/images/2019/01/builds_and_deployments.png" alt="Manage and Keep track of your Builds and Deployments"><p>I am sure most of you are already aware of the new Engines feature SashiDo released shortly. However, in case you missed our Announcement on how to <a href="https://blog.sashido.io/power-up-with-sashidos-brand-new-engine-feature/">Power up with SashiDo’s brand new Engine feature</a>, I suggest for you to check it out now, as it is closely related to the main topic here.</p>
<p>After this short intro, let me get back on track and present you Builds and Deployments -  awaited features by many of you that are now a fact!</p>
<h3 id="builds">Builds</h3>
<p><img src="https://media-blog.sashido.io/content/images/2018/12/Builds.gif" alt="Manage and Keep track of your Builds and Deployments"></p>
<p><strong>Builds</strong> are generated only by specific actions you take as the following events:</p>
<ul>
<li>Change of the Parse Server version from the Dashboard.</li>
<li>After git push to the master branch of your application repo, usually when you push changes to your cloud code.</li>
<li>Manually from the Dashboard.</li>
</ul>
<p>Once a Build is triggered, what happens is that the code from your private SashiDo GitHub repo is bundled together with the Parse Server version you chose for your app from the SashiDo Dashboard. Then a new <a href="https://docs.docker.com/engine/reference/commandline/images/">Docker Image</a> is created of this bundle, which contains multiple layers, used to execute code by the Docker engine on our Kubernetes clusters.</p>
<p>Probably it is not a surprise for any of you, that some minor syntax error present in the package.json file may cause the Build to fail. Actually, that is the most common reason, others may include a wrong NPM package name, failing of the unit tests, etc. Luckily, Build Logs are here! Now you can effortlessly follow the progress of your Build and if it fails, you will be perfectly able to localize and fix the error.</p>
<p>We laid out the Builds Logs with a coloured bar chart for your newest 15 Builds together with a simple List of Builds below, holding the recent 100. Last, but not least the <code>Start Build</code> button is in the upper right corner.</p>
<h3 id="deployment">Deployment</h3>
<p><img src="https://media-blog.sashido.io/content/images/2018/12/Deploy.png" alt="Manage and Keep track of your Builds and Deployments"></p>
<p><strong>Deployments</strong> are triggered by everything that changes the runtime configuration of your app. Here are the examples:</p>
<ul>
<li>Every time after Build, no argue there.</li>
<li>Scaling vertically, in other words changing the type of <a href="https://blog.sashido.io/power-up-with-sashidos-brand-new-engine-feature/">Engines</a> your app runs on</li>
<li>Any update to your <a href="http://blog.sashido.io/tag/environment-variables/">Environment Variables</a>.</li>
<li>Any change in the App Settings.</li>
<li>Manual Start Deployment from the Dashboard.</li>
</ul>
<p>To put it in a few words, the Deployment takes all of your application's artifacts and runs them to the real servers.</p>
<p>Deployments usually do not fail over a mistake you may have made. However, an Engine being stuck while deploying may cause the operation to fail.</p>
<p>We chose to display your app’s last 100 Deployments structured in a simple and neat Deployment List, holding details like creation, completion time, current status etc.</p>
<p>Have in mind that the last Deployment will always be with <em>ACTIVE</em> status. You need to have one running version of your app, right? :)</p>
<p>Same as with Builds, the <code>Start Deployment</code> button is present in the upper right corner to manually trigger the process if needed.</p>
<h3 id="fin">Fin</h3>
<p>Well, that’s it! An awaited feature is finally present on your Dashboard. Not to mention, it is one more debugging tool in your sleeve. Take your time and explore Builds and Deployments! We’ll be looking forward to your feedback as usual!</p>
<p>Those of you who know us can easily guess that in the future, we plan to further upgrade Builds and Deployments and customize them with some graphs and configurations. But I will leave the details for this improvement to be a surprise :)</p>
<p>Happy Coding!</p>
</div>]]></content:encoded></item><item><title><![CDATA[How to properly delete a file, without leaving an orphan]]></title><description><![CDATA[How to proceed If you wish to delete a simple file from the file storage ]]></description><link>https://blog.sashido.io/how-to-properly-delete-a-file-without-leaving-an-orphan/</link><guid isPermaLink="false">5c13fc8fff04020015d1948d</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Getting Started]]></category><category><![CDATA[Delete Files]]></category><category><![CDATA[Cloud Code]]></category><category><![CDATA[Files]]></category><dc:creator><![CDATA[Irena Russeva]]></dc:creator><pubDate>Thu, 03 Jan 2019 12:38:33 GMT</pubDate><media:content url="https://media-blog.sashido.io/content/images/2019/01/delete_orphan_files.png" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://media-blog.sashido.io/content/images/2019/01/delete_orphan_files.png" alt="How to properly delete a file, without leaving an orphan"><p>Here at SashiDo, we know that owning an app requires keeping neat and up to date database. Therefore you certainly will need to add, update or delete records from time to time. In this tutorial, we will focus on erasing objects and files.</p>
<p>For starters, we would like to share more details on SashiDo’s database and file storage model. The first thing to know is that we do not record the files themselves in your database directly.  When an entry, holding a file is created, this file is immediately saved to your S3 bucket. This action will return the file URL, which is actually what is stored in your database. We've chosen this particular model, to give our clients the opportunity to have a lightweight database, with faster performance and lower costs.</p>
<p>In relation to our storage design, If you wish to delete an entry or a simple file from the DB Browser, you may think that it will be removed from the file storage as well. Still, this is not the case. When you delete an entry from the Database through the Dashboard or any of the Parse SDKs, the respective object still remains on the cloud storage. And if you want to remove the particular entry or file from the S3 bucket as well, a different approach will be needed. Don’t you worry, we will guide you through the process.</p>
<p><img src="https://media-blog.sashido.io/content/images/2018/12/2018-12-17_2107.png" alt="How to properly delete a file, without leaving an orphan"></p>
<p>To illustrate better the situation, imagine you have a class “Products” in your DB Browser, which contains product characteristics and a field called “Image”, which holds a picture of the respective product. Now, let’s go through the different scenarios of erasing data.</p>
<h2 id="deletinganobject">Deleting an object</h2>
<p><img src="https://media-blog.sashido.io/content/images/2018/12/deleteObj--2-.gif" alt="How to properly delete a file, without leaving an orphan"><br>
The proper way to delete an object from your file storage when erasing it from the database is by registering a handler in your cloud code with the <code>beforeDelete</code> or <code>afterDelete</code> Triggers. I would recommend you to use the beforeDelete method, as this way you can easily catch if an error appears while deleting the object from your Database and handle it accordingly.</p>
<p>Assuming the entry is successfully deleted from the database and no error occurs, continue with these two simple steps:</p>
<ul>
<li>
<p>Get the name of the removed entry, as you will need it to erase the object from the storage as well.</p>
</li>
<li>
<p>Finish with a simple rest API call to your Parse Server.</p>
</li>
</ul>
<p>You can see how to easily do that in the sample code below:</p>
<pre><code class="language-js">Parse.Cloud.beforeDelete('Products', async (request, response) =&gt; {
   const file = request.object.get('Image'); 
   if (file) {
       const split_url = file.url().split('/');
       const filename = split_url[split_url.length - 1];
       try {
           await Parse.Cloud.httpRequest({
               url: `${process.env.PARSE_SERVER_URL}files/${filename}`,
               method: 'DELETE',
               headers: {
                   'X-Parse-Master-Key': process.env.PARSE_SERVER_MASTER_KEY, 
                   'X-Parse-Application-Id': process.env.PARSE_SERVER_APPLICATION_ID
               }
           });
       } catch (e) {
           const error = `error deleting file ${filename} - ${e.message ||
           e.stack ||
           e.text ||
           JSON.stringify(e)}`;
           console.error(error);
           return response.error(error);
       }
   }

   response.success();
});

</code></pre>
<blockquote>
<p>Still, if by any chance you have ended up with some orphaned files, but you’re unsure of their names to be able to delete them, don’t worry - we can help! For such situations you can take advantage of our Custom Development Work service and our team will provide you with a list of all your files names. The service is paid by the hour, takes 1-2 hours tops and is executed within 7 biz days, but for an exact quote and pricing contact our team at <a href="mailto:support@sashido.io">support@sashido.io</a>.</p>
</blockquote>
<h2 id="deletingasinglefile">Deleting a single file</h2>
<p><img src="https://media-blog.sashido.io/content/images/2018/12/outputSingle-1.gif" alt="How to properly delete a file, without leaving an orphan"><br>
However, deleting an entire object from your database is not the case every time. In different situations, you may need to wipe out just a single file. Well, if removing just the ‘Image’ file from our class ‘Products’ is the desired action, then the approach will be slightly different. Basically, it will involve a simple before/after object comparison, past the deletion of the file from the DB Browser. Let’s dive straight into more details and some code :)</p>
<p>For the situations you want to perform any actions after an object is saved, the <code>afterSave</code> Trigger takes places. You need to register a handler with the <code>afterSave</code> method and compare <code>request.object</code> and <code>request.original</code>. If in the <code>request.object</code> the file is missing, you simply delete it from the file storage via the REST API.</p>
<p>You may get a clear idea on how to proceed from the few lines of code below:</p>
<pre><code class="language-js">Parse.Cloud.afterSave('Products', async request =&gt; {
   const image = request.object.get('Image');
   const imageOriginal = request.original.get('Image');
   if (imageOriginal &amp;&amp; image === imageOriginal) {
       return;
   }

   const split_url = imageOriginal.url().split('/');
   const filename = split_url[split_url.length - 1];
   try {
       await Parse.Cloud.httpRequest({
           url: `${process.env.PARSE_SERVER_URL}files/${filename}`,
           method: 'DELETE',
           headers: {
               'X-Parse-Master-Key': process.env.PARSE_SERVER_MASTER_KEY,
               'X-Parse-Application-Id':
               process.env.PARSE_SERVER_APPLICATION_ID
           }
       });
       console.info('File Deleted');
   } catch (e) {
       const error = `error deleting file ${filename} - ${e.message ||
       e.stack ||
       e.text ||
       JSON.stringify(e)}`;
       console.error(error);
   }
});

</code></pre>
<p>That’s it! You are all set up now to delete files from the database and the file storage :)</p>
<p>More detailed information can be found in the official Parse documentation, respectively for:</p>
<ul>
<li>
<p><a href="https://parseplatform.org/Parse-SDK-JS/api/v1.11.1/Parse.Cloud.html#.TriggerRequest">TriggerRequests</a></p>
</li>
<li>
<p><a href="https://docs.parseplatform.org/cloudcode/guide/#afterdelete-triggers">AfterDelete Triggers</a></p>
</li>
<li>
<p><a href="https://docs.parseplatform.org/cloudcode/guide/#aftersave-triggers">afterSave Trigger</a></p>
</li>
<li>
<p><a href="https://docs.parseplatform.org/rest/guide/#deleting-files">The Rest requests to delete a file</a>.</p>
</li>
</ul>
<p>Happy coding!</p>
</div>]]></content:encoded></item><item><title><![CDATA[Setup your Multilingual Email templates and User-Facing pages]]></title><description><![CDATA[<div class="kg-card-markdown"><p>Multilingual communication is a powerful tool, whether you want to become an insider in another culture market or create higher value and be more efficient in your business. Therefore in order to successfully speak to the world without losing the true meaning behind your message, it is important to do</p></div>]]></description><link>https://blog.sashido.io/multilingual-email-templates-and-user-facing-pages/</link><guid isPermaLink="false">5b8e6389212a6000158b0285</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[Web Hosting]]></category><category><![CDATA[Getting Started]]></category><category><![CDATA[Email Templates]]></category><category><![CDATA[User-Facing Pages]]></category><dc:creator><![CDATA[Irena Russeva]]></dc:creator><pubDate>Mon, 08 Oct 2018 11:50:00 GMT</pubDate><media:content url="https://media-blog.sashido.io/content/images/2018/09/multilingual-email-and-user-facing-pages-cover-2.jpg" medium="image"/><content:encoded><![CDATA[<div class="kg-card-markdown"><img src="https://media-blog.sashido.io/content/images/2018/09/multilingual-email-and-user-facing-pages-cover-2.jpg" alt="Setup your Multilingual Email templates and User-Facing pages"><p>Multilingual communication is a powerful tool, whether you want to become an insider in another culture market or create higher value and be more efficient in your business. Therefore in order to successfully speak to the world without losing the true meaning behind your message, it is important to do it in the languages most familiar to your audience. For that reason, we decided to expand your options and get you to the next level. Now with SashiDo, you have the opportunity to send multilingual emails and multilingual user-facing pages to your users.</p>
<h2 id="setupthemultilingualemailtemplates">Setup the Multilingual Email Templates</h2>
<h3 id="1configureuserslocale">1. Configure Users Locale</h3>
<p>If you wish to address your customers in their own language, first you need to add a column named <strong>localeIdentifier</strong> to your application’s <strong>User</strong> class in the DB Browser. The <strong>localeIdentifier</strong> field should be of type <strong>String</strong>. In this column, you will add the respective language for communication to the current user. To minimize the chance of mistakes we decided to use a primary sub-code that identifies the language (e.g., &quot;en&quot;), and a sub-code in capital letters that specifies the national variety (e.g., &quot;GB&quot; or &quot;US&quot; according to <a href="https://en.wikipedia.org/wiki/ISO_3166-2">ISO 3166-2</a>). The sub-codes should be linked with a hyphen(eg. <a href="https://en.wikipedia.org/wiki/Language_localisation">en-GB</a>). Implementing the logic how you will collect this data is up to you :) If the field is empty or contains invalid language, the default templates will be sent to this user.</p>
<p><img src="https://media-blog.sashido.io/content/images/2018/09/add-localeIdentifier--2-.gif" alt="Setup your Multilingual Email templates and User-Facing pages"></p>
<h3 id="2managemultilingualemailtemplates">2. Manage multilingual Email Templates</h3>
<p>To manage your multilingual email templates, you should go to the Dashboard -&gt; Your App -&gt; App Settings -&gt; Emails &amp; Hosting. At the Email Templates section, you now have the option to choose Country and Language and <code>Add</code> template. Hereafter choosing Country and language, please pay attention to the code displayed in the <strong>Locale</strong> field, as this is how the selected language should be entered in the <strong>localeIdentifier</strong> field. Later on, the same code should be used again when you set the user-facing pages.</p>
<p><img src="https://media-blog.sashido.io/content/images/2018/09/MMT_locale-1.png" alt="Setup your Multilingual Email templates and User-Facing pages"></p>
<p>As soon as you press the add button Email templates for verification and password reset are created and opened for you to customize. Just change the text by your preferences, to give your users the best experience and press <code>Save changes</code> button in the lower right corner.</p>
<p><img src="https://media-blog.sashido.io/content/images/2018/09/MM-Add-final-1.gif" alt="Setup your Multilingual Email templates and User-Facing pages"></p>
<p>You can edit each template's text whenever you wish, from the <code>Manage</code> button. Just remember to press the <code>Save changes</code> after you finish editing.</p>
<p>To delete a certain template, also first you need to go to <code>Manage</code> -&gt; <code>Delete these templates</code> -&gt; <code>Save changes</code>.</p>
<p><img src="https://media-blog.sashido.io/content/images/2018/09/Delete-final-1.gif" alt="Setup your Multilingual Email templates and User-Facing pages"></p>
<p>Another great feature we find of use is the ability to choose the default language for your customers. Deciding which will be the default language is of great importance since if there is no valid entry in the <strong>localeIdentifier</strong> field, the default email template will be sent. Therefore you need to choose wisely :) The default language is of note regarding the User-Facing pages too, but we will get to that later.</p>
<p>You can simply select the default language from the slide bar in the <code>Default</code>  field before each language. Again press <code>Save changes</code> and it is set!</p>
<p><img src="https://media-blog.sashido.io/content/images/2018/09/default-1.gif" alt="Setup your Multilingual Email templates and User-Facing pages"></p>
<h2 id="setupthemultilingualuserfacingpages">Setup the Multilingual User-Facing Pages</h2>
<p>Well, multilingual email templates are a great feature, but probably you want the page that displays next to be also in your customer's language. Don't worry, we now support Multilingual User-Facing Pages too!</p>
<p>We know dealing with new features may be at times challenging and I will try to simplify the process for you.</p>
<h3 id="1startwithcloudcode">1. Start with Cloud Code</h3>
<p>As you know, the multilingual email templates that you configured earlier for your users contain some link to a respective page. Basically, there are four essential pages, that the emailed link can lead to: <strong>Successful verification</strong>, <strong>Choose password</strong>, <strong>Successfully changed password</strong>, and of course <strong>Invalid Verification link</strong>.</p>
<p>Each of these pages can be presented in your customers' language. All you have to do is write the desired HTML and CSS files in your GitHub repo. And of course, few lines of cloud code logic should be implemented in the <a href="https://blog.sashido.io/github-integration-of-advanced-cloud-code-part-1/">Advanced Cloud Code</a>.</p>
<p>Here I am offering a sample code, which you can put in the <code>app.js</code> file of your cloud code.</p>
<pre><code class="language-js">const path = require('path');

/**
 * Folder structure
 * public/
 * ├── pages/
 * │   ├── choose_password.html // default
 * │   └── en-US/
 * │       └── choose_password.html // en-US template
 */

function getUserFacingTemplate(templateName) {
  return async (req, res) =&gt; {
    let templatePath = path.join(__dirname, '../public/pages/');
    const { username } = req.query;    
    if (username) {
      

      const user = await new Parse.Query(Parse.User)
        .equalTo('username', username)
        .first({ useMasterKey: true });

      if (!user) {
        return res.end(JSON.stringify({
          error: 'unauthorized'
        }));
      }

      const locale = user.get('localeIdentifier');
      if (locale) {
        templatePath += `${locale}/`
      }
    }

    templatePath += templateName;
    return res.sendFile(templatePath);
  };
}

app.get(
  '/localized_choose_password',
  getUserFacingTemplate('choose_password.html')
);
app.get(
  '/localized_password_changed',
  getUserFacingTemplate('password_reset_success.html')
);
app.get(
  '/localized_email_verified',
  getUserFacingTemplate('verification_successful.html')
);
app.get(
  '/localized_invalid_verification_link',
  getUserFacingTemplate('invalid_verification_link.html')
);
</code></pre>
<h3 id="2continuewithuserfacingtemplates">2. Continue with User-Facing Templates</h3>
<p><strong>Have in mind that the folder structure is of great importance!</strong></p>
<p><img src="https://media-blog.sashido.io/content/images/2019/01/MM-verification_successful.gif" alt="Setup your Multilingual Email templates and User-Facing pages"></p>
<p>Here I can offer some guidance on how to add your User Facing Templates to your cloud code:</p>
<p>In the public directory in your cloud code repo, you will need to create a new folder, named <code>pages</code>.<br>
Then in <code>pages</code> create a separate folder for each different language you would like to use. Every folder should be named as the value of the <code>locale</code> field in the multilingual email section in your application’s Dashboard. These folders will hold all the HTML and CSS files for the respective language. You can even have different CSS for each language if you wish.<br>
With this folder structure, the user-facing pages for the default language should be placed right into the <code>pages</code> folder. Therefore if the content of the <code>localeIdentifier</code> field is unidentified or different from the languages you chose for communication, the default templates will be sent.</p>
<p>If you haven't had the chance to look at our previous tutorial on the topic, you can check it out <a href="https://blog.sashido.io/emails-and-custom-user-facing-pages/">here</a>.</p>
<h3 id="3finalizebyconfiguringlocalizeduserfacingpagesinsashidosdashboard">3. Finalize by configuring Localized User-Facing pages in SashiDo’s Dashboard</h3>
<p>Last, but not least is telling SashiDo you want to render the localized User-facing pages. For that just go one more time to the App Settings -&gt; Emails &amp; Hosting. There in the User-Facing Pages section add the path for the localized templates as shown below. Actually, these will be the routes you defined earlier in the cloud code, with Express.js.</p>
<p><img src="https://media-blog.sashido.io/content/images/2021/06/localized_custom_pages.png" alt="Setup your Multilingual Email templates and User-Facing pages"></p>
<p>I believe that's all you need to know to get started with multilingual emails and user-facing pages. And I am sure that the users will appreciate such distinctive communication.</p>
<p>That’s it, you are all set now :)</p>
<p>Happy coding!</p>
</div>]]></content:encoded></item></channel></rss>