-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CORS error when using this library in web app #9
Comments
For anyone coming around the issue, the following assumes you've already created an account on Tuya IoT Platform as also a Cloud Project in order to get the required credentials, aka I was facing the same issue regarding CORS policy, but I've solved that by abstracting tuya-connector-nodejs in order to create an ordinary nodejs / express server. Also I've used On Tuya Iot Platform -> Cloud -> Development: I've left the Nodejs / Express:
TUYA_DATA_CENTER_URL=https://openapi.tuyaus.com
TUYA_CLIENT_ID=
TUYA_CLIENT_SECRET=
TUYA_IMAGE_URL=https://images.tuyacn.com/
import dotenv from 'dotenv';
dotenv.config()
// Tuya Cloud - IoT Platform
const config = {
baseUrl: `${process.env.TUYA_DATA_CENTER_URL}`, // Western America Data Center
clientId: `${process.env.TUYA_CLIENT_ID}`, // Access ID / Client ID
clientSecret: `${process.env.TUYA_CLIENT_SECRET}`, // Access Secret / Client Secret:
apiImageUrl: `${process.env.TUYA_IMAGE_URL}` // https://images.tuyacn.com/
};
if (!Object.values(config).every(Boolean)) {
throw new Error(
'Please create .env from .env.example and specify all values',
);
}
export default config;
import dotenv from 'dotenv'
import express from 'express'
import axios from 'axios'
import cors from 'cors'
// Custom Tuya Api Routes
import { routes } from './routes/index.js'
const router = express.Router()
dotenv.config()
const port = 3399
const app = express()
app.use(express.urlencoded({ extended: true }))
app.use(express.json({ limit: '32mb' }))
app.use(cors({ origin: '*' })) // This alone didn't work!
app.use(
cors({
origin: [
'https://openapi.tuyeu.com',
'https://openapi.tuyaus.com',
'http://localhost:3000',
'http://localhost:3399',
],
methods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE', 'OPTIONS'],
allowedHeaders: [
'Origin',
'X-Requested-With',
'Content-Type',
'Accept',
'Signature-Headers',
'client_id',
'access_token',
'sign',
'sign_method',
'stringToSign',
't',
'nonce',
],
})
)
app.use(routes)
app.listen(port, () => {
console.log(
`TuyaApi Started ✓ - Express listening on port : ${port} - press Ctrl-C to terminate.`
)
})
import express from 'express'
const router = express.Router()
// Main Api Routes
import { apiRoutes } from './api/index.js' // src/routes/api/index.js
const api = '/api/v1'
router.use(api, apiRoutes)
router.use(api, (req, res) => res.status(404).json('No API route found'))
export { router as routes }
import express from 'express'
const router = express.Router()
import { deviceRoutes } from './devices.js' // src/routes/api/device.js
import { userRoutes } from './user.js' // src/routes/api/user.js
// Device Routes
router.use('/device', deviceRoutes)
// User Routes
router.use('/users', userRoutes)
// Other Routes...
export { router as apiRoutes }
// Redis (ioRedis) connection
import dotenv from 'dotenv'
dotenv.config()
export const redisConnect = {
port: `${process.env.REDIS_PORT}`,
host: `${process.env.REDIS_HOST}`,
username: 'default',
password: `${process.env.REDIS_PASS}`,
db: 0
}
//
// redis store
// @see: https://github.com/tuya/tuya-connector-nodejs
//
import Redis from 'ioredis'
import { redisConnect } from '../utils/redisConnect.js'
export class RedisTokenStore {
client = new Redis(redisConnect)
constructor (client, key = 'tuya::token') {
this.client = client
this.key = key
}
async setTokens (tokens) {
const res = await this.client.set(this.key, JSON.stringify(tokens))
return !!res
}
async getAccessToken () {
const jsonStr = (await this.client.get(this.key)) || '{}'
const tokens = JSON.parse(jsonStr)
return tokens && tokens.access_token
}
async getRefreshToken () {
const jsonStr = (await this.client.get(this.key)) || '{}'
const tokens = JSON.parse(jsonStr)
return tokens.refresh_token
}
}
Here tuya-connector-nodejs functions are wrapped within ordinary express router logic... import express from 'express'
import axios from 'axios'
import Redis from 'ioredis'
import { TuyaContext } from '@tuya/tuya-connector-nodejs'
import { RedisTokenStore } from '../../utils/tokenStore.js'
import { redisConnect } from '../../utils/redisConnect.js'
import config from '../../config.js'
const { baseUrl, clientId, clientSecret, deviceId, uid } = config
const router = express.Router()
const redis = new Redis(redisConnect)
const tuya = new TuyaContext({
baseUrl: baseUrl, // https://openapi.tuyaus.com',
accessKey: clientId,
secretKey: clientSecret,
store: new RedisTokenStore(redis),
rpc: axios
})
//
// Devices API Routes
// Tuya Cloud API Reference: https://developer.tuya.com/en/docs/iot/api-reference?id=Ka7qb7vhber64
// @see: https://github.com/tuya/tuya-connector-nodejs
//
//
// Post commands to device
//
router.post('/:id/commands', async (req, res) => {
try {
const deviceId = req.params.id
const { commands } = req.body
const result = await tuya.request({
method: 'POST',
path: `/v1.0/devices/${deviceId}/commands`,
body: { commands }
})
res.status(200).json({
...result,
message: 'Device updated successfully!'
})
} catch (error) {
console.log('ERROR', error)
}
})
//
//
// Update (modify) device details, such as name etc..
//
router.put('/:id/update', async (req, res) => {
try {
const deviceId = req.params.id
const update = req.body
if (Object.keys(update).length === 0) {
return res.status(400).json({ error: 'Missing body params' })
}
const result = await tuya.request({
method: 'PUT',
path: `/v1.0/iot-03/devices/${deviceId}`,
body: update
})
res.status(200).json({
...result,
message: 'Device updated successfully!'
})
} catch (error) {
console.log('ERROR', error)
}
})
//
// Get all devices under a user's account by user ID
//
router.get('/', async (req, res) => {
try {
const response = await tuya.request({
method: 'GET',
path: `/v1.0/users/${uid}/devices`
// body: {},
})
res.status(200).json({
...response.result
})
} catch (error) {
console.log('ERROR', error)
}
})
//
// Get device details by id
//
router.get('/:id', async (req, res) => {
try {
const deviceId = req.params.id
const response = tuya.request({
method: 'GET',
path: `/v1.0/devices/${deviceId}`
// body: {},
})
await response.then(result => {
res.status(200).send(result)
})
} catch (error) {
console.log('ERROR', error)
}
})
//
// Get device available data points
//
router.get('/:id/status', async (req, res) => {
try {
const deviceId = req.params.id
const response = tuya.request({
method: 'GET',
path: `/v1.0/devices/${deviceId}/status`
// body: {},
})
await response.then(result => {
res.status(200).json(result)
})
} catch (error) {
console.log('ERROR', error)
}
})
//
// Get device available functions
//
router.get('/:id/functions', async (req, res) => {
try {
const deviceId = req.params.id
const response = tuya.request({
method: 'GET',
path: `/v1.0/iot-03/devices/${deviceId}/functions`
// body: {},
})
await response.then(result => {
res.status(200).send(result)
})
} catch (error) {
console.log('ERROR', error)
}
})
export { router as deviceRoutes } After starting the server, if no errors, the API is ready to be consumed. So in the Frontend as usual, by making a GET request to I had no issues with CORS with this set up! Please leave your enhancement ideas, such as a tuya middleware function or any other type of set up that works for you! |
Hi did you run the tuya connector node sdk and this node server together,or you just took the functions from their code and that redis token they have in their github,I was facing sign invalid issue. |
@monecchi Can you share your tokenStore.ts file code |
Hey @JimRh! You're right, I use their suggested tokenStore solution based on redis. The project I've set up does not use typescript ... I've updated the answer code blocks to include an abstraction of the way I'm connectng to the redis server (remote or local). |
I have a nuxt app which throws an error when trying to make requests through the TuyaContext.
Access to XMLHttpRequest at 'https://openapi.tuyaeu.com/v1.0/token?grant_type=1' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header has a value 'https://iot.tuya.com' that is not equal to the supplied origin.
The text was updated successfully, but these errors were encountered: