How to use Ionic proxy in conjunction with AWS SDK - ionic-framework

Using Ionic 4.4.0 and aws-sdk 2.157.0. I'm trying to create an S3 bucket from my local web browser, but am running into CORS problems when attempting to run the following code, method createBucketByCompanyKey():
import { Injectable } from '#angular/core';
import * as AWS from 'aws-sdk';
#Injectable()
export class AwsProvider {
private accessKeyId:string = 'myAccessKey';
private secretAccessKey:string = 'mySuperSecret';
private region:string = 'us-east-1';
constructor() {
AWS.config.update({accessKeyId: this.accessKeyId, secretAccessKey: this.secretAccessKey, region: this.region});
}
createBucketByCompanyKey(companyKey){
let s3 = new AWS.S3();
let params = {
Bucket: companyKey,
CreateBucketConfiguration: {
LocationConstraint: this.region
}
};
s3.createBucket(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
}
}
This gives me the error
Failed to load https://s3.amazonaws.com/-KwzdjmyrHiMBCqHH1ZC: Response
to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:8100' is therefore not allowed
access. The response had HTTP status code 403.
Which led me to this post here after several hours of googling. It appears I need to run ionic through a proxy. I've also tried changing my "path" to http://localhost:8100, but stuck I remain.
{
"name": "MyApp",
"app_id": "",
"type": "ionic-angular",
"integrations": {},
"proxies": [
{
"path": "/",
"proxyUrl": "https://s3.amazonaws.com/"
}
]
}
I've also come across posts telling my to download a Chrome extension that disables CORS, but that didn't work either.
Any ideas on how to setup this proxy to work with AWS' SDK?

Forget the proxies. For Mac, enter in the following in the terminal to open a Google Chrome browser with CORS disabled.
open -a Google\ Chrome --args --disable-web-security --user-data-dir
Compliments of this post.

Related

After successfully deploying Next.js app on AWS Amplify, https://www.example.com/api/any-route showing below error in console

The app is deployed successfully but the API routes (/pages/api) are not working as expected, showing below error in the console.
Build is successful and deployed on aws-amplify, I have added environment variables correctly, don't know why this is happening?
Does aws-amplify doesn't support serverless functions writer inside /api folder??
{
"error": {
"message": "connect ECONNREFUSED 127.0.0.1:80",
"name": "Error",
"stack": "Error: connect ECONNREFUSED 127.0.0.1:80\n at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1148:16)",
"config": {
"url": "undefined/campaigns",
"method": "get",
"headers": {
"Accept": "application/json, text/plain, */*",
"User-Agent": "axios/0.21.4"
},
"auth": {},
"transformRequest": [null],
"transformResponse": [null],
"timeout": 0,
"xsrfCookieName": "XSRF-TOKEN",
"xsrfHeaderName": "X-XSRF-TOKEN",
"maxContentLength": -1,
"maxBodyLength": -1,
"transitional": {
"silentJSONParsing": true,
"forcedJSONParsing": true,
"clarifyTimeoutError": false
}
},
"code": "ECONNREFUSED"
}
}
Here is the code
import axios from 'axios';
import Cors from 'cors';
import rateLimit from '../../../utils/rate-limit';
import initMiddleware from '../../../lib/init-middleware';
// Initialize the cors middleware
const cors = initMiddleware(
Cors({
methods: ['POST'],
origin: ['https://www.example.com', /\.example\.com$/],
})
);
const limiter = rateLimit({
interval: 60 * 1000, // 60 seconds
uniqueTokenPerInterval: 500, // Max 500 users per second
});
const handler = async (req, res) => {
await cors(req, res);
if (req.method === 'GET') {
try {
await limiter.check(res, 50, 'CACHE_TOKEN');
const { data } = await axios.get(`${process.env.BASE_URL}/campaigns`, {
auth: {
username: process.env.MAIL_SERVER_USERNAME,
password: process.env.MAIL_SERVER_PASSWORD,
},
});
return res.status(200).json(data);
} catch (error) {
return res.status(429).json({ error });
}
} else {
try {
await limiter.check(res, 10, 'CACHE_TOKEN'); // 10 requests per minute
return res.status(200).json('not allowed');
} catch (err) {
return res.status(429).json({ error: 'Rate limit exceeded' });
}
}
};
export default handler;
I figured out that environment variables are not getting reflected, so did some googling; found this solution and it worked for me.
Add your desired environment variable in the Amplify Console-like normal (steps)
Update (or create) your next.config.js file with the environment variable you added in the Amplify Console. E.g if you created an environment variable named MY_ENV_VAR in the console in step 1) above, then you would add the following:
module.exports = {
env: {
MY_ENV_VAR: process.env.MY_ENV_VAR
}
};
Now after your next build you will be able to reference your environment variable (process.env.MY_ENV_VAR) in your SSR lambdas!
Here is the link: Github
Ran into the same problem Amplify only supports NextJS 11 at the moment. If you go with the default settings it will use the latest NextJS 12 and /api routes wont work, they return a 503 with a cloudformation permission error.
Specify next 11.1.3 in your package.json
https://aws.amazon.com/about-aws/whats-new/2021/08/aws-amplify-hosting-support-next-js-version-11/
I also faced that problem. Amplify does not support v12 of next. Simply downgrade your next.js version in package.json to v1.1.3 and your routes will work as normal.
Best regrets.
Artem Meshkov

Openfire XMPP Client Connection via Ionic App

In order to test the client side connectivity with our Openfire XMPP server, the following test script (nodejs) was initially tested, which ran successfully.
async function connectX() {
try {
const keyPair = await XmppUser.generateKeys()
XmppUser.connect(username, password, keyPairs).then(
async user => {
user.getFriends().then(friends => {
console.log("List of Friends: ");
console.log(friends);
user.getChatHistory().then(history => {
console.log("Chat History: ")
console.log(history);
user.closeConnection();
}).catch(e => console.log(e));
}).catch(e => console.log(e));
}
).catch(e => console.log(e));
} catch(e) {
console.log(e)
}
}
connectX()
Output:
List of Friends:
[ 'test1.test#chat.company.io', 'test2.test#chat.company.io' ]
Chat History:
{ messages:
[ { date: 2020-08-11T12:18:34.314Z,
from: 'test1.test#chat.company.io',
to: 'test6.test#chat.company.io',
message: 'Hello! Received.',
id: 'D255Q-132' }
] }
lastId: '1',
firstId: '1',
count: '1' }
However, when I run the script in an ionic angular app using the same ChatService class it leads me to the following CORS error.
Access to fetch at 'https://chat.company.io/.well-known/host-meta' from origin 'http://localhost:8100' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
For information, https://chat.company.io/.well-known/host-meta exists and has the following content:
<?xml version='1.0' encoding='utf-8'?>
<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>
<Link rel="urn:xmpp:alt-connections:httppoll"
href="https://chat.company.io:443" />
</XRD>
The backend infrastructure includes an Nginx server and the chat server runs behind it with the following open ports: 443, 7070, 5222, 5223.
Please help in figuring out what the issue might be.
Possible CORS issue,
enable support for CORS in Openfire, server -> server settings -> http binding -> CORS section.

Set request header in Protractor

Is it possible to set request headers in Protractor?
In the following post (2014), Protractor confirmed that currently they don't have any way to do this (as protractor was not designed to interact with under-the-hood things.
here
Has anyone found out a way how to do this?
Thanks in advance :)
I had the same problem so I implemented a simple proxy to inject headers in the requests. I published it on npm repository. You can find it here https://www.npmjs.com/package/headers-injection-proxy
Very easy to use:
header-injection-proxy -p 3000 -t "https://www.google.com" -h "headers.json"
At that point you just send the requests to the proxy instead of the real server
You can use an external library like request. For me it worked as a charm with Protractor:
https://github.com/request/request#custom-http-headers
EDIT:
Here you have an example of implementation. It uses request-promise
import {browser} from 'protractor';
import {put} from 'request-promise';
import {ADMIN_URL} from '../data/definitions/urls';
export class CreateUser {
public setRoles(username: string, roles: string[]) {
return this.getUserId(username).then((userId) => {
const data = {
url: `${ADMIN_URL}/user/${userId}/role`,
headers: {
Authorization: browser.params.token
},
body: roles,
json: true,
rejectUnauthorized: false
};
return put(data);
});
}
}

Ionic CORS issue with JIRA API

Problem description : I have built a ionic app which uses JIRA rest api to fetch issue (GET data), create issue (POST data). I always get CORS error's like preflight request did not succeed or same origin policy which are expected when we use ionic serve but the same is not working when I build and release the signed apk.
My ionic server runs on localhost:8100 (ionic version -4) &
Jira server runs on localhost:8089 (JIRA version - core 7)
What I have done so far :
followed the proxy approach as mentioned in ionic blog --No success
enabled CORS filter plugin in JIRA server and whitelisted ionic server --No success
Added headers for Allow control origin -- No success
Build the apk file using --prod release signed and tried the same on device --No sucess
Here is my auth.ts file reference if any-one can help and advise here about what I am doing wrong here.
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { AuthProvider } from '../auth/auth';
import { Issue } from '../../models/issue'
#Injectable()
export class JiraProvider {
apiVersion: string = '2'; // The API version we want to use
jiraInstanceUrl: string = 'http://localhost:8089' // The Jira instance URL
urlString: string = `${this.jiraInstanceUrl}/rest/api/${this.apiVersion}`; // Concat those together
constructor(
public http: HttpClient,
public auth: AuthProvider
) {
}
// Authenticate the user against Jira's profile endpoint.
public authenticateUser(username: string, password: string): Observable<Object> {
return this.http.get(`${this.urlString}/myself`, {
headers: new HttpHeaders()
.append('Authorization', `Basic ${btoa(username + ':' + password)}`)
.append("Access-Control-Allow-Origin","*")
});
}
// Get issue details based on the provided key.
public getIssue(key: string): Observable<Issue> {
return this.http.get<Issue>(`${this.urlString}/issue/${key}`, {
headers: new HttpHeaders()
.append('Authorization', `Basic ${this.auth.getAuthString()}`)
.append("Access-Control-Allow-Origin","*")
});
}
public getAllIssue():Observable<any> {
return this.http.get(`${this.urlString}/search?jql=project=PM`,{
headers: new HttpHeaders()
.append('Authorization', `Basic ${this.auth.getAuthString()}`)
.append("Access-Control-Allow-Origin","*")
});
}
public postIssue(data):Observable<any>{
return this.http.post(`${this.urlString}/issue`,JSON.stringify(data),{
headers: new HttpHeaders()
.append('Authorization', `Basic ${this.auth.getAuthString()}`)
.append('Content-Type','application/json')
.append("X-Atlassian-Token", "no-check")
.append("User-Agent", "xx")
.append("Access-Control-Allow-Origin","*")
});
}
}
Added screenshot as per request(please not I changed the JIRA server intentionally to 8089 updated the question accordingly)
I soved this issue by using a CORS proxy in a container (Docker) for when you need to Access-Control-Allow-Origin: *`! this acts like a proxy https://github.com/imjacobclark/cors-container and now I am able to route the requests.
Hope it helps others !!

Using Grunt to Mock Endpoints

I'm using Yeoman, Grunt, and Bower, to construct a platform for building a frontend independently of a a backend. The idea would be that all of my (AngularJS) controller, services, factories, etc live in this project, and get injected afterwards into my serverside codebase based off the result of grunt build.
My question is:
How can I mock endpoints so that the Grunt server responds to the same endpoints as my (Rails) App will?
At the moment I am using:
angular.module('myApp', ['ngResource'])
.run(['$rootScope', function ($rootScope) {
$rootScope.testState = 'test';
}]);
And then in each of my individual services:
mockJSON = {'foo': 'myMockJSON'}
And on every method:
if($rootScope.testState == 'test'){
return mockJSON;
}
else {
real service logic with $q/$http goes here
}
Then after grunt build, testState = 'test' gets removed.
This is clearly a relatively janky architecture. How can I avoid it? How can I have Grunt respond to the same endpoints as my app (some of which have dynamic params) apply some logic (if necessary), and serve out a json file (possibly dependent on path params)?
I've fixed this issue by using express to write a server that responds with static json.
First I created a directory in my project called 'api'. Within that directory I have the following files:
package.json:
{
"name": "mockAPI",
"version": "0.0.0",
"dependencies": {
"express": "~3.3.4"
}
}
Then I run npm install in this directory.
index.js:
module.exports = require('./lib/server');
lib/server.js:
express = require('express');
var app = express();
app.get('/my/endpoint', function(req, res){
res.json({'foo': 'myMockJSON'});
});
module.exports = app
and finally in my global Gruntfile.js:
connect: {
options: {
port: 9000,
hostname: 'localhost',
},
livereload: {
options: {
middleware: function (connect, options) {
return [
lrSnippet,
mountFolder(connect, '.tmp'),
mountFolder(connect, yeomanConfig.app),
require('./api')
];
}
}
},
Then the services make the requests, and the express server serves the correct JSON.
After grunt build, the express server is simply replaced by a rails server.
As of grunt-contrib-connect v.0.7.0 you can also just add your custom middleware to the existing middleware stack without having to manually rebuild the existing middleware stack.
livereload: {
options: {
open: true,
base: [
'.tmp',
'<%= config.app %>'
],
middleware: function(connect, options, middlewares) {
// inject a custom middleware into the array of default middlewares
middlewares.push(function(req, res, next) {
if (req.url !== '/my/endpoint') {
return next();
}
res.writeHead(200, {'Content-Type': 'application/json' });
res.end("{'foo': 'myMockJSON'}");
});
return middlewares;
}
}
},
See https://github.com/gruntjs/grunt-contrib-connect#middleware for the official documentation.
Alternatively you can use the grunt-connect-proxy to proxy everything that is missing in your test server to an actual backend.
It's quite easy to install, just one thing to remember when adding proxy to your livereload connect middleware is to add it last, like this:
middleware: function (connect) {
return [
lrSnippet,
mountFolder(connect, '.tmp'),
mountFolder(connect, yeomanConfig.app),
proxySnippet
];
}
grunt-connect-prism is similar to the Ruby project VCR. It provides an easy way for front end developers to record HTTP responses returned by their API (or some other remote source) and replay them later. It's basically an HTTP cache, but for developers working on a Single Page Application (SPA). You can also generate stubs for API calls that don't exist, and populate them the way you want.
It's useful for mocking complex & high latency API calls during development. It's also useful when writing e2e tests for your SPA only, removing the server from the equation. This results in much faster execution of your e2e test suite.
Prism works by adding a custom connect middleware to the connect server provided by the grunt-contrib-connect plugin. While in 'record' mode it will generate a file per response on the filesystem with content like the following:
{
"requestUrl": "/api/ponies",
"contentType": "application/json",
"statusCode": 200,
"data": {
"text": "my little ponies"
}
}
DISCLAIMER: I'm the author of this project.
You can use Apache proxy and connect your REST server with gruntjs.
Apache would do this:
proxy / -> gruntjs
proxy /service -> REST server
you would use your application hitting Apache and angular.js application would think that is talking with itself so no cross domain problem.
Here is a great tutorial on how to set this up:
http://alfrescoblog.com/2014/06/14/angular-js-activiti-webapp-with-activiti-rest/
Just my alternative way that based on Abraham P's answer. It does not need to install express within 'api' folder. I can separate the mock services for certain files. For example, my 'api' folder contains 3 files:
api\
index.js // assign all the "modules" and then simply require that.
user.js // all mocking for user
product.js // all mocking for product
file user.js
var user = function(req, res, next) {
if (req.method === 'POST' && req.url.indexOf('/user') === 0) {
res.end(
JSON.stringify({
'id' : '5463c277-87c4-4f1d-8f95-7d895304de12',
'role' : 'admin'
})
);
}
else {
next();
}
}
module.exports = user;
file product.js
var product = function(req, res, next) {
if (req.method === 'POST' && req.url.indexOf('/product') === 0) {
res.end(
JSON.stringify({
'id' : '5463c277-87c4-4f1d-8f95-7d895304de12',
'name' : 'test',
'category': 'test'
})
);
}
else {
next();
}
}
module.exports = product;
index.js just assigns all the "modules" and we simply require that.
module.exports = {
product: require('./product.js'),
user: require('./user.js')
};
My Gruntfile.js file
connect: {
options: {
port: 9000,
// Change this to '0.0.0.0' to access the server from outside.
hostname: 'localhost',
livereload: 35729
},
livereload: {
options: {
open: true,
middleware: function (connect) {
return [
connect.static('.tmp'),
connect().use(
'/bower_components',
connect.static('./bower_components')
),
connect.static(appConfig.app),
require('./api').user,
require('./api').product,
];
}
}
}