I installed Hono+Ditto using helm-charts, as it is described in cloud2edge.
That means Hono+Ditto is running inside a minikube on my PC.
I also created a connection, policy, and a device. So far everything works fine.
In the next step, I just wrote a simple "frond-end" to fetch the thing state from Ditto-HTTP-API.
As long as I fetch the thing state manually by the mean of fetch-API everything is fine. But as soon as I try to use the SSE (Eventsource) I get the following CORS error:
index.html:1 Access to resource at 'http://192.168.99.100:32084/api/2/things/de.iot1:dev1' from
origin 'http://localhost:63342' has been blocked by CORS policy: The value of the
'Access-Control-Allow-Origin' header in the response must not be the wildcard '*'
when the request's credentials mode is 'include'.
I am just struggling with this error since yesterday and none of the answers regarding CORS-errors I found on the internet worked :(.
How can I communicate with Ditto from my PC using Eventsource without getting CORs-error?
Below is my simple front-end:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
{
box-sizing: border-box;
}
.container {
display: grid;
}
.row:after {
content: "";
display: table;
clear: both;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<label for="selector">Choose update strategy:
<select name="method" id="selector">
<option value="auto">Autorefresh</option>
<option value="SSE">SSE</option>
</select>
</label>
</div>
<div class="row">
<label for="dev"><h3>Device state:</h3></label>
</div>
<div class="row">
<textarea id="dev" name="dev-data" rows="20" cols="50"></textarea>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
crossorigin="anonymous"></script>
<script>
const baseUrl = "http://192.168.99.100:32084"; // Ditto IP:PORT
const username = "ditto";
const password = "ditto";
const interval = 1000;
const thingId = "de.iot1:dev1";
const thingUrl = `${baseUrl}/api/2/things/${thingId}`;
let intervalId;
let eventSource = null
function requestData(url) {
var headers;
headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Authorization', 'Basic ' + btoa(`${username}:${password}`));
init = {
method: 'GET',
headers: headers,
};
var request = new Request(url);
return fetch(request, init)
.then(function (response) {
if (response.ok) {
return response;
}
throw response;
})
}
function updateDeviceState(data) {
$('#dev').val(JSON.stringify(data, null, 2));
}
function onRefresh() {
requestData(thingUrl)
.then(response => {
for (var pair of response.headers.entries()) {
console.log(pair[0] + ': ' + pair[1]);
}
return response.json()
})
.then(data => { updateDeviceState(data) });
}
function enableAutoRefresh(enabled=true) {
if (enabled) {
intervalId = setInterval(() => { onRefresh() }, interval);
} else {
clearInterval(intervalId);
}
}
function enableEventSource(enabled=true) {
if (enabled) {
eventSource = new EventSource(thingUrl, {withCredentials: true})
eventSource.addEventListener('message', (e) => { console.log(e) })
} else if (eventSource != null) {
eventSource.removeEventListener('message', this.eventListener)
eventSource.close();
eventSource = null;
}
}
function applyUpdateStrategy() {
let val = $('#selector').val();
let autoRefreshEnabled = val.includes('auto');
enableAutoRefresh(autoRefreshEnabled);
enableEventSource(!autoRefreshEnabled);
}
$('#selector').on('change', () => { applyUpdateStrategy() })
applyUpdateStrategy()
</script>
</body>
</html>
Thanks!
Thank you for reaching out.
You found a bug which was already fixed the Ditto nginx configuration, however not yet applied to the "packages" project.
I created a PR to fix this, so this should be fixed in the next Helm version of the Ditto chart:
https://github.com/eclipse/packages/pull/193
This question would have been better placed on GitHub as issue - but you could of course not have known that this was a bug before.
Related
I have to add a list of data structures to a js object on an HTML page.
I have a list of data structures on the server side each identified by a data-key.
A js function loops the list of data-keys
the function gets each structure one at a time from a async_cache using the corresponding data-key.
There may be multiples of same data-keys in the list
if the async_cache doesn't have the data-structure by that data-key it async-fetches and adds it to the cache.
if the async_cache has the data-structure by that data-key it returns it right away.
if the async_cache has already requested a fetch for a data-key and before the data arrives has another request for the same data-key it must not duplicate the request but wait until the data arrives.
I have constructed the following code so far (as an example for discussion). The actual environment can't be shared.
<?php
switch($_GET['--action'] ?? false){
case 'data-structure':{
$data_group = [
'data-01' => ['a' => 'info-01', 'b' => 'extra-01'],
'data-02' => ['a' => 'info-02', 'b' => 'extra-02'],
'data-03' => ['a' => 'info-03', 'b' => 'extra-03'],
'data-04' => ['a' => 'info-04', 'b' => 'extra-04'],
'data-05' => ['a' => 'info-05', 'b' => 'extra-05'],
];
if($data_struct = ($data_group[$_GET['key'] ?? false] ?? false)){
\header('Content-Type: application/json');
echo json_encode(['status'=> 'ok', 'data' => $data_struct], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
exit();
} else {
http_response_code(404);
echo json_encode('Not Found', JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
exit();
}
} break;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta key="viewport" content="width=device-width, initial-scale=1.0">
<title>JS Async Data Buffer Experiment</title>
</head>
<body>
<div class="container">
</div>
<script>
xui = {};
xui.async_cache = {
async async_get_await(url = '', options = {}){
// Default options are marked with *
const response = await fetch(
url,
{
method: 'GET', // *GET, POST, PUT, DELETE, etc.
mode: options.mode || 'same-origin', // no-cors, *cors, same-origin
cache: options.cache || 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: options.credintials || 'same-origin', // include, *same-origin, omit
redirect: options.redirect || 'follow', // manual, *follow, error
referrerPolicy: options.referrerPolicy || 'no-referrer', // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
}
);
return response.json(); // parses JSON response into native JavaScript objects;
},
get(action, key){
if(key in this){
return new Promise((resolve, reject) => {
resolve(this[key]);
});
} else {
return this.async_get_await(
`?--action=${action}&key=${key}`,
).then((r) => {
this[key] = r.data;
return this[key];
});
}
},
};
window.addEventListener('DOMContentLoaded', function(){
var list = [
'data-01',
'data-01',
'data-02',
'data-01',
'data-03',
'data-02',
'data-04',
'data-02',
'data-01',
];
list.forEach((key) => {
console.log({key});
xui.async_cache.get('data-structure', key).then((data) => {
console.log(data);
});
});
});
</script>
</body>
</html>
Case 1 and 2 are taken care of but case 3 is where I am stuck. As you can see the code in the addEventListner executes in one shot before the data is received or event-loop kicks-in. So multiple requests go out for the same data-key.
I am looking for a minimalist approach. If there is a coding feature in JS library without using third party library - that will do as well.
I've also looked into the cache option on the Fetch-API but that is not a solution.
Thanks!
Thanks to #Bergi I finally figured it out!
I rewrote the async_cache and went on simplifying ...
Here is what I got (I've added a few test buttons as well):
<?php
switch($_GET['--action'] ?? false){
case 'data-structure':{
$data_group = [
'data-01' => ['a' => 'info-01', 'b' => 'extra-01'],
'data-02' => ['a' => 'info-02', 'b' => 'extra-02'],
'data-03' => ['a' => 'info-03', 'b' => 'extra-03'],
'data-04' => ['a' => 'info-04', 'b' => 'extra-04'],
'data-05' => ['a' => 'info-05', 'b' => 'extra-05'],
];
if($data_struct = ($data_group[$_GET['key'] ?? false] ?? false)){
\header('Content-Type: application/json');
echo json_encode(['status'=> 'ok', 'data' => $data_struct], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
exit();
} else {
http_response_code(404);
echo json_encode('Not Found', JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
exit();
}
} break;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta key="viewport" content="width=device-width, initial-scale=1.0">
<title>JS Async Data Cache Example</title>
</head>
<body>
<div class="container">
<button type="button" onclick="run()">Run</button> <br/>
<button type="button" onclick="delete xui.async_cache['data-01']">Delete 01</button> <br/>
<button type="button" onclick="delete xui.async_cache['data-02']">Delete 02</button> <br/>
<button type="button" onclick="delete xui.async_cache['data-03']">Delete 03</button> <br/>
<button type="button" onclick="delete xui.async_cache['data-04']">Delete 04</button> <br/>
Look at the console for responses and check the network tab as well!
</div>
<script>
xui = {};
xui.async_cache = {
get(key){
return this[key] ?? (this[key] = (fetch(`?--action=data-structure&key=${key}`).then((response) => {
return response.json();
})));
}
};
function run(){
var list = [
'data-01',
'data-01',
'data-02',
'data-01',
'data-03',
'data-02',
'data-04',
'data-02',
'data-01',
];
list.forEach((key) => {
console.log({key});
xui.async_cache.get(key).then((data) => {
console.log(data);
});
});
}
window.addEventListener('DOMContentLoaded', function(){
run();
});
</script>
</body>
</html>
Looks like we can take advantage of the closure's data storage (or caching) inside the promise. And we don't need await or async, atleast for this rudimentary stage.
Stripe Checkout in webView flutter app
Integrating Stripe checkout with Flutter mobile app. Worked with this in previous versions of Flutter & webview_flutter plugin.
But now it's showing this error in the console and stuck the webView at initial URL:
I/chromium(26312): [INFO:CONSOLE(1)] "Stripe.js requires 'allow-same-origin' if sandboxed.", source: https://js.stripe.com/v3/ (1)
I/chromium(26312): [INFO:CONSOLE(1)] "Uncaught SecurityError: Failed to read the 'cookie' property from 'Document': Cookies are disabled inside 'data:' URLs.", source: https://js.stripe.com/v3/ (1)
Flutter Code for Webview Stripe Checkout
class StripePaymentCheckout extends StatefulWidget {
final String sessionId;
const StripePaymentCheckout({Key key, this.sessionId}) : super(key: key);
#override
_StripePaymentCheckoutState createState() => _StripePaymentCheckoutState();
}
class _StripePaymentCheckoutState extends State<StripePaymentCheckout> {
WebViewController _webViewController;
String get initialUrl =>
"data:text/html;base64,${base64Encode(const Utf8Encoder().convert(kStripeHTMLPage))}";
static const String kStripeHTMLPage = '''
<!DOCTYPE html>
<html>
<script src="https://js.stripe.com/v3/"></script>
<head>
<title>Stripe Checkout</title>
</head>
<body>
<div style="position: absolute; text-align: center; width:100%; height:100%; top:50%;">
<h1>Loading Stripe...!</h1>
</div>
</body>
</html>
''';
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: SafeArea(
child: WebView(
initialCookies: [
WebViewCookie(
name: 'sessionid',
value: widget.sessionId,
domain: 'https://js.stripe.com/v3/', // <-- not sure what to do here.
),
],
initialUrl: initialUrl,
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (webViewController) {
_webViewController = webViewController;
},
onPageFinished: (String url) {
if (url == initialUrl) {
_redirectToStripe(widget.sessionId);
}
},
navigationDelegate: (NavigationRequest request) {
if (request.url.startsWith('https://success.com')) {
Navigator.of(context).pop("success");
} else if (request.url.startsWith('https://cancel.com')) {
Navigator.of(context).pop('cancel');
}
return NavigationDecision.navigate;
},
),
),
);
}
Future<void> _redirectToStripe(String sessionId) async {
final redirectToCheckoutJs = '''
var stripe = Stripe('${dotenv.env['apiKey']}');
stripe.redirectToCheckout({
sessionId: '$sessionId'
}).then(function (result) {
result.error.message = 'Error'
});
''';
return await _webViewController
.runJavascriptReturningResult(redirectToCheckoutJs);
}
}
the code:
<!DOCTYPE html>
<html>
<script src="https://js.stripe.com/v3/"></script>
<head>
<title>Stripe Checkout</title>
</head>
<body>
<div style="position: absolute; text-align: center; width:100%; height:100%; top:50%;">
<h1>Loading Stripe...!</h1>
</div>
</body>
</html>
need to be deployed on an https for your code to work.
there's a guy who presented the same problem and solved with code you can find here:
https://github.com/MarcinusX/flutter_stripe_demo/blob/master/lib/checkout/stripe_checkout_mobile.dart
and he already hosted the needed code on:
https://marcinusx.github.io/test1/index.html
I'm currently making a project to put up "Post" from mysql onto a website and then they can also be updated from the website into Mysql. My insert function works fine because I can add post like nothing. Whenever I try and delete a post it gives me a long CORS policy error. I've looked all over the internet for an answer, but haven't found a solution. I've tried installing the CORS extensions in chrome and to also change the header into no cors. I'm the owner of the API being used.
index.js
const baseUrl = "**redacted for security**"
//const baseUrl = "https://localhost:5001/api/post"
//update
function getPost(){
//const allPostsApi = "https://localhost:5001/api/post";
const allPostsApi = baseUrl;
fetch(allPostsApi).then(function(response){
console.log(response);
return response.json();
}).then(function(json){
let html = "<ul>";
json.forEach((post)=>{
html += "<li>" + post.text + " Posted by Big Al! </li>";
})
html += "</ul>";
document.getElementById("Post").innerHTML = html;
}).catch(function(error){
console.log(error);
});
}
function handleOnSubmit(){
console.log("We made it");
var postText = document.getElementById("text").value;
//const placeHolder = document.getElementById("Nothing").value;
//const addPostsApi = "https://localhost:5001/api/post";
console.log(postText);
const addPostsApi = baseUrl;
var text ={
Text: postText
}
PlacePost(text);
}
function handleOnEnter(){
console.log("We made it");
var postId = document.getElementById("id").value;
//const placeHolder = document.getElementById("Nothing").value;
//const addPostsApi = "https://localhost:5001/api/post";
console.log(postId);
const addPostsApi = baseUrl;
var id ={
Text: postId
}
RemovePost(postId);
}
function PlacePost(text){
const PlacePostUrl = baseUrl;
fetch(PlacePostUrl, {
method: "POST",
headers: {
"Accept": 'application/json',
"Content-Type": 'application/json'
},
body: JSON.stringify(text)
}).then(response=>{
getPost();
})
}
function RemovePost(id){
const RemovePostUrl = baseUrl;
fetch(RemovePostUrl, {
mode: 'cors',
method: "PUT",
headers: {
"Accept":'application/json',
"Content-Type": 'application/json; charset=UTF-8'
},
body: JSON.stringify(id)
}).then(response=>{
getPost();
})
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="resources/index.css" rel="stylesheet">
<title>Document</title>
</head>
<body onload = "getPost()">
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script type = "text/javascript" src = "scripts/index.js"></script>
<div id="Post">
</div>
<div class = "row">
<form id = "addPost" onsumbit ="return false;" method = "post">
<label for="title">Enter Post</label>
<input type ="text" name = "text" id ="text">
<input type ="button" value = "Submit" onclick="handleOnSubmit()">
</form>
<form id = "RemovePost" onsubmit ="return false;" method = "put">
<label for ="title">Enter Post Number you wish to delete</label>
<input type ="text" name = "text" id ="id">
<input type ="button" value = "Submit" onclick="handleOnEnter()">
</form>
</div>
</body>
</html>
I'm just very confused on how it does through on the PlacePost, but gets caught up during RemovePost. Any and all help is appreciated.
Remove mode: 'cors' from your RemovePostUrl fetch.
You could set it to same-origin if your js is running on the same domain as your API.
https://developer.mozilla.org/en-US/docs/Web/API/Request/mode
You 100% do not need a browser extension.
install 'cors' by using 'npm i cors'
const express = require("express");
const app = express();
const cors = require("cors");
app.use(
cors({
origin: "*"
}
));
cors does not care about requests between the same origin. If you want to allow request from an within the same origin then you can set the origin value to the URL you want to request. e.g
origin: 'http://127.0.0.1:8000'
And if you want to just allow all URLs to access your site you can instead use origin: '*' in the header and that will allow cross origin requests from any URL
Hope this helps :)
I have a custom form that makes a few requests to a database to verify the user. I noticed that if I have a single google account it works fine but it doesn't with multiple. The other thing I noticed is that the script doesn't throw any error it just doesn't communicate back the result from the custom form.
This is how my custom forms look like:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<div class="container select-client">
<div class="client">Client</div>
<select class="client-select">
<option>Select Client</option>
<!-- ...options -->
</select>
<div class="market">Market</div>
<select class="market-select">
<option>Select Market</option>
<!-- ...options -->
</select>
<div class="error-message"></div>
<button class="button" id="select-button" onclick="handleSelect()">Select</button>
</div>
<script>
// ...code to validate the user
function handleSelect() {
var _client = clients.find(
(client) => client.id === parseInt(selectedClient)
);
var _market = markets.find(
(market) => market.id === parseInt(selectedMarkets)
);
if (!_client && !_market) {
return;
}
if (!_client) {
errorMessageClientMarket.innerHTML = 'Please select client';
return;
}
if (!_market) {
errorMessageClientMarket.innerHTML = 'Please select market';
return;
}
google.script.run
.withSuccessHandler()
.loginData({ token, market: _market, client: _client, user: userInfo, platform });
google.script.host.close();
}
</script>
</body>
</html>
This is how I create the custom form using app script
const loginForm = () => {
const html = HtmlService.createHtmlOutputFromFile('loginFormHtml')
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL)
.setWidth(600)
.setHeight(600);
const ui = SpreadsheetApp.getUi();
ui.showModalDialog(html, `Login`);
};
This is the callback function:
const loginData = (data) => { // <--- this function is ignored when a the users has multiple google accounts
console.log('LOGIN FORM');
const { token, market, user, client, platform } = data;
UserProperties.setProperty('token', token);
UserProperties.setProperty('userId', user.id);
UserProperties.setProperty('clientId', client.id);
UserProperties.setProperty('clientName', client.name);
UserProperties.setProperty('marketId', market.id);
UserProperties.setProperty('marketName', market.code_name);
UserProperties.setProperty('username', `${user.first_name} ${user.last_name}`);
UserProperties.setProperty('userEmailAddress', user.email);
UserProperties.setProperty('platform', platform);
const info = UserProperties.getProperties();
console.log('info ---> ', info)
const ui = SpreadsheetApp.getUi();
getMenu(true);
ui.alert('Logged in Successfully');
};
Does anyone know if there's a away to fix this?
I have a problem with running VueJS on mobile devices. I created a weather prediction app on copepen.io
Here is the link for the project:
http://codepen.io/techcater/pen/xOZmgv
HTML code:
<div class="container-fluid text-center">
<h1>Your Local Weather</h1>
<p>
{{location}}
</p>
<p>
{{temperature}}
<a #click="changeDegree">{{degree}}</a>
</p>
<p>
{{weather | capitalize}}
</p>
<img :src="iconURL" alt="" />
<br>
by Dale Nguyen
<!-- <pre>{{$data | json}}</pre> -->
</div>
JS code:
new Vue({
el: '.container-fluid',
data: {
location: "",
temperature: "",
degree: "C",
weather: "",
iconURL: ""
},
created: function(){
this.getWeather();
},
methods: {
getWeather: function(){
var that = this;
this.$http.get("http://ipinfo.io").then((response) => {
console.log(response.data);
that.location = response.data.city + ", " + response.data.country;
// Get weather informaiton
var api = 'ebd4d312f85a230d5dc1db91e20c2ace';
var city = response.data.city;
var url = "http://api.openweathermap.org/data/2.5/weather?q={CITY}&APPID={APIKEY}&units=metric";
url = url.replace("{CITY}",city);
url = url.replace("{APIKEY}", api);
that.$http.post(url,{dataType: 'jsonp'},{
headers : {
'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8'
}}).then((response) => {
console.log(response.data);
that.temperature = response.data.main.temp;
that.weather = response.data.weather[0]['description'];
that.iconURL = "http://openweathermap.org/img/w/" + response.data.weather[0]['icon'] + ".png";
}, (response) => {
// error callback
});
}, (response) => {
console.log(response.data);
});
},
changeDegree: function() {
if(this.degree == "C"){
this.degree = "F";
this.temperature = Math.round((this.temperature*9/5 + 32)*100)/100;
}else {
this.degree = "C";
this.temperature = Math.round(((this.temperature - 32)*5 /9)* 100)/100;
}
}
}
})
It works well on my laptop but not on mobile. At first, I thought that it is because of Codepen. It may cause something when running through the site. However, when I created a project on my website, it also doesn't work.
Can you help to find the issue? Thanks,
Your code seems to be working well, except that on codepen it gives me error XMLHttpRequest cannot load http://ipinfo.io/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://s.codepen.io' is therefore not allowed access..
You can put your domain name on headers options to enable cross-origin, here is example:
this.$http.get('http://ipinfo.io', {
'headers': {
'Origin': 'http://yourdomain.com'
}
})
See example: http://bozue.com/weather.html
I also noticed you put vue.min.js and vue-resource.js scripts in wrong order that might trigger some error, vue.min.js should be on the first place.
I found a solution for this. I works on my mobile now. I believe that I will work on other browses too. The problem is that some browsers doesn't recognize the operation ">", so I changed it.
Here is the new code:
getWeather: function(){
var that = this;
this.$http.get('http://ipinfo.io', {'headers': {
'Origin': 'http://yourdomain.com'}
}).then(function(response) {
console.log(response.data);
that.location = response.data.city + ", " + response.data.country;
// Get weather informaiton
var api = 'ebd4d312f85a230d5dc1db91e20c2ace';
var city = response.data.city;
var url = "https://crossorigin.me/http://api.openweathermap.org/data/2.5/weather?q={CITY}&APPID={APIKEY}&units=metric";
url = url.replace("{CITY}",city);
url = url.replace("{APIKEY}", api);
that.$http.post(url,{dataType: 'jsonp'},{
headers : {
'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8'
}}).then(function(response) {
console.log(response.data);
that.temperature = response.data.main.temp;
that.weather = response.data.weather[0]['description'];
that.iconURL = "http://openweathermap.org/img/w/" + response.data.weather[0]['icon'] + ".png";
}).then(function(){
// error callback
});
}).then(function(){
console.log(response.data);
});
},