cloudflare / node-cloudflare Goto Github PK
View Code? Open in Web Editor NEWNode.js API for Client API
Home Page: https://cloudflare.github.io/node-cloudflare/
License: Other
Node.js API for Client API
Home Page: https://cloudflare.github.io/node-cloudflare/
License: Other
CloudFlare send response with error.
Example: {"success":false,"errors":[{"code":81053,"message":"An A, AAAA or CNAME record already exists with that host."}],"messages":[],"result":null}
Client should return this back with error somehow.
This script using 2.7.0, 2.8.0, and master
var cf = require('cloudflare')({
email: "xxx",
key: "xxx"
// this doesn't work either
// token: 'xxx'
});
cf.zoneSettings.edit("xxx", "always_use_https", "on")
.then((res) => {
console.log("res", res)
}).catch(e => {throw e})
Fails with
ErrorClass [HTTPError]: Response code 400 (Bad Request)
at //wwwman/node_modules/got/index.js:123:13
at processTicksAndRejections (node:internal/process/task_queues:93:5) {
host: 'api.cloudflare.com',
hostname: 'api.cloudflare.com',
method: 'PATCH',
path: '/client/v4/zones/xxx/settings/always_use_https',
statusCode: 400,
statusMessage: 'Bad Request'
}
Adding some logging to the Got library (since the error doesn't have the body) shows it just sends "on" to that URL, the API is returning a JSON parsing error.
req opts {
protocol: 'https:',
path: '/client/v4/zones/xxx/settings/always_use_https',
host: 'api.cloudflare.com',
hostname: 'api.cloudflare.com',
pathname: '/client/v4/zones/xxx/settings/always_use_https',
href: 'https://api.cloudflare.com/client/v4/zones/xxx/settings/always_use_https',
json: true,
method: 'PATCH',
headers: {
'user-agent': 'cloudflare/2.8.0 node/15.5.1',
'accept-encoding': 'gzip,deflate',
'content-type': 'application/json',
accept: 'application/json',
'x-cloudflare-client-user-agent': '{"bindings_version":"2.8.0","lang":"node","lang_version":"v15.5.1","platform":"linux","arch":"x64","publisher":"cloudflare"}',
'x-auth-key': 'xxx',
'x-auth-email': 'xxx',
'content-length': 3
},
body: 'off',
....
}
res body {
success: false,
errors: [ { code: 6007, message: 'Malformed JSON in request body' } ],
messages: [],
result: null
}
It would be cool to have support for pages using this api.
I was going through the docs and noticed that at ZoneWorkersScripts there are two read
methods, the first taking just a zone ID, and the second taking a zone ID and a script. And there seems to be some additional typos in that section as well.
According to the latest documentation of v4 Cloudflare Api
https://api.cloudflare.com/#worker-filters-deprecated
And has been shifted to
https://api.cloudflare.com/#worker-routes-properties
Due to this I am unable to connect my worker to the pattern.
Hello there ! Thanks for the creation of this api tool. I wan't to create a dnsRecord but I don't understand how to write the params. Can i eventually get an example ? Thanks for answer ! <3
We are using cloudflare-node version 2.7.0 and cannot use got due to the related issue.
cloudflare has a dependency that needs updating
[email protected]
└─┬ [email protected]
└── [email protected]
related issue:
sindresorhus/got#1039
Does this library not support the GraphQL Analytics API? I don't see it in the library code or the generated JSDoc.
You can fetch a cloudflare zone by calling:
GET zones/$zone_id/dns_records
This is possible with node-cloudflare. However, you can fetch it in bind configuration syntax if you append /export:
GET zones/$zone_id/dns_records/export
This does not appear to be possible with node-cloudflare. Please consider adding this functionality.
Any chance of the firewall access rules for a zone API being added to this library?
https://api.cloudflare.com/#firewall-access-rule-for-a-zone-list-access-rules
I'm getting the following error:
ErrorClass [HTTPError]: Response code 400 (Bad Request)
at ~/cloudflare/node_modules/got/index.js:123:13
at processTicksAndRejections (internal/process/task_queues.js:97:5) {
host: 'api.cloudflare.com',
hostname: 'api.cloudflare.com',
method: 'POST',
path: '/client/v4/zones',
statusCode: 400,
statusMessage: 'Bad Request'
}
while cf.zones.browse
works fine, the following code does not:
import CF from 'cloudflare'
// And in an async function
async function main() {
const cf = CF({
email: process.env.EMAIL,
key: process.env.KEY
})
try {
await cf.zones.add({
account: {
id: process.env.ACCOUNT_ID
},
name: 'example.com',
jump_start: true
})
} catch (error) {
console.error(error)
}
}
main()
I may be missing something obvious, so help would be appreciated. I tried CURL as well with no success (EDIT: CURL does indeed work). Thanks!
Again, this may not be a bug, but it sure looks like one. I also investigated this issue a bit and I think it's because the JSON stringify sets too many variables:
{"id":"","type":"A","name":"sub.domain.tld","content":"8.8.4.4","proxiable":false,"proxied":false,"locked":false,"zone_id":"censored-zone-id","zone_name":"domain.tld","created_on":"2016-05-23T23:19:39.092Z","modified_on":"2016-05-23T23:19:39.092Z"}
var CloudFlare = require('cloudflare');
var client = new CloudFlare({
email: 'some-email',
key: 'some-key'
});
client.browseZones().then((zones) => {
zones.result.forEach((zone) => {
if (zone.name === 'domain.tld') {
var record = CloudFlare.DNSRecord.create({
name: 'sub.domain.tld',
zone_id: zone.id,
type: 'A',
content: '8.8.4.4'
});
client.addDNS(record).then((data) => {
console.log(data);
}).catch((error) => {
console.error(error);
});
}
});
}).catch((error) => {
console.error(error);
});
{ HTTPError: Response code 400 (Bad Request)
at stream.catch.then.e (/home/dodekeract/code/dyndekeract/node_modules/cloudflare/node_modules/got/index.js:114:13)
at process._tickCallback (internal/process/next_tick.js:103:7)
message: 'Response code 400 (Bad Request)',
host: 'api.cloudflare.com',
hostname: 'api.cloudflare.com',
method: 'POST',
path: '/client/v4/zones/censored-zone-id/dns_records',
statusCode: 400,
statusMessage: 'Bad Request' }
➜ source git:(master) ✗ node -v && npm list | grep cloudflare && lsb_release -a
v6.2.0
├─┬ [email protected]
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 16.04 LTS
Release: 16.04
Codename: xenial
Some methods like .dnsRecords.browse()
return paginated results with a property result_info
that looks some like this.
{
page: 1,
per_page: 20,
total_pages: 2,
count: 20,
total_count: 24
}
Is it possible to paginate or return all results at once?
The current dependent of [email protected]
breaks runtime because it tries to require package.json in a extensionless mannar require('./package');
, more recent versions does not have this problem.
Would you please consider upgrading?
// right after initializing cf with my login and key
cf.zones.browse().then(function (resp) {
return resp.result.status;
});
/root/cf2/node_modules/cloudflare/index.js:48
this._got = function (endpoint, options) {
^
TypeError: Cannot set property '_got' of undefined
at constructor (/root/cf2/node_modules/cloudflare/index.js:48:15)
at Object. (/root/cf2/i2.js:2:31)
at Module._compile (module.js:569:30)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:503:32)
at tryModuleLoad (module.js:466:12)
at Function.Module._load (module.js:458:3)
at Function.Module.runMain (module.js:605:10)
at startup (bootstrap_node.js:158:16)
at bootstrap_node.js:575:3
The current version of this on NPM is 2.7.0, next is 2.8.0, which simply includes a bump to http-proxy-agent.
I spent days trying to figure out why some completely unrelated issue was having problems, and it turns out the mere existence of an older version of agent-base will break other libraries. I found that solution in this random unrelated comment over here, for example: https://github.com/AzureAD/passport-azure-ad/issues/522
Forcing the resolution of http-proxy-agent fixed my issue, but this was actually the only package that caused the issue, and 2.8.0 fixes it. Please publish it to avoid other devs having to go on this goose chase! haha :)
Took me quite some time to find the package on NPM, would help tons of users out if it was linked in README.md
I think that instead of returning Response code xxx (xxx)
errors when calls were not successful, JSON response should be parsed and error should be returned.
Example:
I wanted to add DNS record to my tk domain, I got this error Response code 403 (Forbidden)
. I thought that there is some problem with my API and I couldn't investigate the problem. Then I called the API endpoint directly with CURL and I got this response:
{
"result": null,
"success": false,
"errors": [{
"code": 1038,
"message": "You cannot use this API for domains with a .cf, .ga, .gq, .ml, or .tk TLD (top-level domain). To configure the DNS settings for this domain, use the Cloudflare Dashboard."
}],
"messages": []
}
The wrapper should display the error message instead of just request status.
Error:
Failure: Cloudflare: Unknown arguments ([object Object]). Did you mean to pass an options object? (on API request to PUT accounts/:accountId/storage/kv/namespaces/:namespaceId/bulk)
Code:
await cf.enterpriseZoneWorkersKV.addMulti(accountID, namespaceID, kv, {base64: true});
The KV is in the structure
{
key: 'key' (string)
value: 'value' (typeof Buffer)
}
Hi,
Any plans to add support for updating the zone settings?
https://api.cloudflare.com/#zone-settings-edit-zone-settings-info
Disclaimer: This may not be a bug, since this is my first attempt to use this API and I may have made some stupid mistake. (some simple examples on how to use this API would be really appreciated!)
var CloudFlare = require('cloudflare');
var client = new CloudFlare({
email: 'email-address-for-dodekeract.report',
key: 'somekey'
});
client.browseZones().then((response) => {
console.log(response);
}).catch((error) => {
console.log(error);
});
Error: Cannot set static values
at Object.id (/home/dodekeract/code/dyndekeract/node_modules/verymodel/lib/model.js:347:27)
at Object.<anonymous> (/home/dodekeract/code/dyndekeract/node_modules/verymodel/lib/model.js:450:43)
at Array.forEach (native)
at Object.model.loadData (/home/dodekeract/code/dyndekeract/node_modules/verymodel/lib/model.js:422:36)
at Object.<anonymous> (/home/dodekeract/code/dyndekeract/node_modules/verymodel/lib/model.js:446:43)
at Array.forEach (native)
at Object.model.loadData (/home/dodekeract/code/dyndekeract/node_modules/verymodel/lib/model.js:422:36)
at VeryModel.__dirname.create (/home/dodekeract/code/dyndekeract/node_modules/verymodel/lib/model.js:559:19)
at /home/dodekeract/code/dyndekeract/node_modules/cloudflare/lib/zones.js:62:19
at Array.map (native)
$ node -v && npm list | grep cloudflare && lsb_release -a
v6.2.0
├─┬ [email protected]
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 16.04 LTS
Release: 16.04
I originally opened #33 to enable users to specify an HTTPS proxy that should be used by the API client, but the PR was closed down in #33 (comment):
I'm probably going to implement this in a different way, since
got
is an implementation detail (and worse: likely to be removed). Can you open an issue instead?
I personally don't care about any of the other options offered by got
while using the Cloudflare API and don't think any other user would.
@terinjokes I'd be happy to open another PR that implements proxy support, like you would want it, since I'm interested in moving this forward quickly. ⚡ 😄
I think allowing users to pass an HTTPS agent offers the greatest flexibility to users, since some might need to tunnel through proprietary proxy protocols. The agent
option is understood universally by all underlying HTTP libs, like got
or request
.
caw
import Cloudflare from 'cloudflare':
import caw from 'caw';
const cf = new Cloudflare({
email: '[email protected]',
key: '<your API key here>',
agent: caw({ protocol: 'https' }) // Cloudflare API is HTTPS only
});
tunnel-agent
directlyimport Cloudflare from 'cloudflare':
import TunnelAgent from 'tunnel-agent';
const cf = new Cloudflare({
email: '[email protected]',
key: '<your API key here>',
agent: TunnelAgent.httpsOverHttp({ port: 80, host: 'proxy.example.com' })
});
Example: curl - XPOST - vs - H "X-Auth-Key: {key}" - H "X-Auth-Email: {email}" - H 'Content-Type: application/json' - d '{"hostname":"app.example2.com", "ssl":{"method":"cname", "type":"dv"}}' https : //api.cloudflare.com/client/v4/zones/{zone_id}/custom_hostnames
client.addDNS(CFClient.DNSRecord.create(
{ zone_id: 'ZONEID',
type: 'CNAME',
name: 'SUBDOMAIN',
ttl: 120,
proxiable: true,
proxied: true,
content: 'TARGET' }
);
Results in
{
protocol: 'https:',
path: '/client/v4/zones/ZONEID/dns_records',
retries: [Function: backoff],
slashes: true,
auth: null,
host: 'api.cloudflare.com',
port: null,
hostname: 'api.cloudflare.com',
hash: null,
search: null,
query: undefined,
pathname: '/client/v4/zones/ZONEID/dns_records',
href: 'https://api.cloudflare.com/client/v4/zones/ZONEID/dns_records',
json: true,
timeout: 10000,
method: 'POST',
body: '{"type":"CNAME","name":"SUBDOMAIN","content":"TARGET","ttl":120}',
headers:
{ 'user-agent': 'cloudflare/1.0.4 node/5.3.0',
'accept-encoding': 'gzip,deflate',
'x-auth-key': 'APIKEY',
'x-auth-email': 'EMAIL',
'content-type': 'application/json',
accept: 'application/json',
'content-length': 138 },
agent: undefined,
followRedirect: true
}
Proxied flag is not sent, therefore it doesn't set desired value. API seems to work on it's own.
Currently when you make the cf.zoneCustomHostNames.browse(<ZONE_ID>)
call it only returns page 1 of the custom hostnames (just the first 20 custom hostnames) and there is no option for pagination at all. It would be really helpful feature to have an option to return all custom hostnames from all the pages.
The only work around right now is to use the CloudFlare api (not the nodeJS wrapper) and deal with the pagination logic yourself.
Please don't take it personally :)
Probably you'd be better releasing cloudflare v3 as (modified) version of icodeforlove/node-cloudflare
instead of re-inventing the wheel. It's 1:1 with the API and has almost 100% coverage on functionality. This package has much different API (than official REST API) and supports very few methods.
Why is zones.browse missing all the api options?
Let me start off with the use case... Auto wildcard SSL certificates via Let's encrypt.
In the current API model, zones can only be accessed by their ID (not by the DNS name), which can only be obtained with a call to zones.browse().
At the same time, a token can be made to either have read access to all zone OR read/write access to all zones OR read access to a single zone OR read/write access to a single zone. In other words, you can't make a single token have both read access to all zones AND read/write access to a single zone. You can however make two tokens - one that can read all zones, and one that read/write a single zone.
The only way to address this scenario right now is to have two clients - one for zones.browse(), and other for the other calls needed in the SSL procedure. But it would be a lot easier if SOMETHING changes.
In this case, I'm proposing what I think is the simplest change, which would be to allow the token to be overriden per call.
Though in terms of what CloudFlare as a whole should do, I think a token's permissions should be more flexible, i.e. allow them to both read all zones and read/write a certain sinlgle zone, and also, maybe allow zones.browse() for all tokens, but only list zones that the token can read or read/write. Or better yet, add a call that can get the ID(s?) from a given DNS name directly (that is available to any token with read access to the zone), so that a call to zones.browse() isn't even needed.
The endpoint for SSL functionality should be added: https://api.cloudflare.com/#ssl-verification-properties
This enables you to use the new verification tools for the SSL for Saas V2 that rolling out soon.
Looking at
Line 52 in f3a9131
However, during times of API outages such as https://www.cloudflarestatus.com/incidents/g7nd3k80rxdb, requests seem to just hang forever, and never timeout.
Code to replicate:
const cloudflare = require('cloudflare')({
"email": "xxx",
"key": "xxxxx"
});
cloudflare.dns.add('xxxxxx', {
content: '1.2.3.4',
name: '_test.xxxx.xx',
type: 'A',
proxied: false
}, (err, results) => {
// this callback never fires with an error or success - the timeout never occurs and it just waits infinitely
console.log(err, results);
});
Hi there!
Are the any plans for CLI?
If there aren't, would you accept a PR?
Thank you
return an error saying record already exists with the same name
add the record anyway
Hi, we updated to the lastest 2.1.0 release, and had a server crash in one of our API calls to Cloudflare. Here is the stacktrace:
throw er; // Unhandled 'error' event Exception
^
Error: Can't send request after GOAWAY Exception
at Connection.reserveStream (/app/node_modules/spdy-transport/lib/spdy-transport/connection.js:614:15)
at Agent._createStream (/app/node_modules/spdy/lib/spdy/agent.js:242:27)
at Handle.<anonymous> (/app/node_modules/spdy/lib/spdy/agent.js:198:29)
at Object.onceWrapper (events.js:314:30)
at emitNone (events.js:105:13)
at Handle.emit (events.js:207:7)
at ClientRequest.send [as _send] (/app/node_modules/spdy/lib/spdy/handle.js:156:10)
at ClientRequest.end (_http_outgoing.js:780:16)
at EventEmitter.ee.on.req (/app/node_modules/got/index.js:100:8)
at emitOne (events.js:115:13)
at EventEmitter.emit (events.js:210:7)
at Immediate.setImmediate [as _onImmediate] (/app/node_modules/got/index.js:81:7)
at runCallback (timers.js:800:20)
at tryOnImmediate (timers.js:762:5)
at processImmediate [as _immediateCallback] (timers.js:733:5)
I've been looking for a way to catch that error on our side, but it looks like it is not possible. The options passed to got
and more specifically the spdy
agent are hard-coded in your library.
Any idea on that kind of error and how to properly handle it? It looks like some event listeners are missing somewhere in the dep chain (spdy-http2/node-spdy#282).
Thanks
Version: 2.9.1
zoneSettings.editAll(id, {
ssl: { value: 'flexible' },
always_use_https: { value: 'on' },
})
.then(resp => console.log(resp))
.catch(err => console.log(err))
Show Error
code: 1004, message: Missing 'items'
does this wrapper support pagination?
This bug just happened today and is one of those that make me give up my faith in humanity and computers. Just posting this here in case someone else has this problem.
This file will work just fine:
import got from 'got'
;(async() => {
console.dir(await got('https://icanhazip.com'))
})()
This won't:
import got from 'got'
import * as cloudflare from 'cloudflare'
;(async() => {
console.dir(await got('https://icanhazip.com'))
})()
(node:17224) UnhandledPromiseRejectionWarning: RequestError: connect ECONNREFUSED 127.0.0.1:443
at ClientRequest.<anonymous> (node_modules/got/dist/source/core/index.js:889:25)
at Object.onceWrapper (events.js:422:26)
at ClientRequest.emit (events.js:327:22)
at ClientRequest.EventEmitter.emit (domain.js:485:12)
at ClientRequest.origin.emit (node_modules/@szmarczak/http-timer/dist/source/index.js:39:20)
at TLSSocket.socketErrorListener (_http_client.js:432:9)
at TLSSocket.emit (events.js:315:20)
at TLSSocket.EventEmitter.emit (domain.js:485:12)
at emitErrorNT (internal/streams/destroy.js:84:8)
at processTicksAndRejections (internal/process/task_queues.js:84:21)
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1141:16)
Note that I'm not even using the imported package, the import itself is sufficient to break all usages of got
in the whole file.
The fix was to bump the version of cloudflare to 2.8.0 in the package.json
. SMH.
Occasionally we will get a bad request error from an api call to purge the cache. However upon retrying the same request immediately after, it succeeds.
Could a retry policy be implemented directly in the Cloudflare API?
For the zones.purgeCache
in particular.
I'm using the basic example you gave on this repo, using API token and it's returning the following error:
TypeError [ERR_HTTP_INVALID_HEADER_VALUE]: Invalid value "undefined" for header "x-auth-key"
at ClientRequest.setHeader (_http_outgoing.js:473:3)
at new ClientRequest (_http_client.js:194:14)
at Object.request (https.js:289:10)
at Object.request (/Users/wesleymilan/Documents/Servers/havasupai-docker/www/workers/firewall/node_modules/agent-base/patch-core.js:25:22)
at get (/Users/wesleymilan/Documents/Servers/havasupai-docker/www/workers/firewall/node_modules/got/index.js:33:18)
at requestAsEventEmitter (/Users/wesleymilan/Documents/Servers/havasupai-docker/www/workers/firewall/node_modules/got/index.js:85:2)
at Promise (/Users/wesleymilan/Documents/Servers/havasupai-docker/www/workers/firewall/node_modules/got/index.js:91:14)
at new Promise (<anonymous>)
at asPromise (/Users/wesleymilan/Documents/Servers/havasupai-docker/www/workers/firewall/node_modules/got/index.js:90:9)
at got (/Users/wesleymilan/Documents/Servers/havasupai-docker/www/workers/firewall/node_modules/got/index.js:302:10)
When using API key everything works fine
Node v10.21.0
node-cloudfare v2.7.0
I can browse zones and dns records just fine, but the moment I try to add a dns record I get an unhelpful error message:
{ HTTPError: Response code 400 (Bad Request)
at stream.catch.then.data (/Users/lfdp/workspace/deploy/node_modules/got/index.js:123:13)
at process._tickCallback (internal/process/next_tick.js:68:7)
message: 'Response code 400 (Bad Request)',
host: 'api.cloudflare.com',
hostname: 'api.cloudflare.com',
method: 'POST',
path:
'/client/v4/zones/:zoneId/dns_records',
statusCode: 400,
statusMessage: 'Bad Request' }
This is my code, what is supposed to be the schema for the object in this method?
return await cloudfare.dnsRecords.add(zoneId, {
name: 'test.test.com',
type: 'CNAME',
content: '127.0.0.1',
});
Why can't this run in a browser?
Is there any reason that the addDNS()
function only passes [type, name, content, ttl]
to the body? The following record is valid and avoids the extra step of editing the newly created record.
example DNSrecord
DNSRecord.create({
type: 'A',
name: 'test.website.com',
content: '1.2.3.4',
proxied: true,
zone_id: 'xxx'
})
Line 98 in 084ec15
var cf = require('cloudflare')({
email: 'xxxx',
key: 'xxxx',
});
when i check cf, the value is {}
i checked the email & api key, and they are not wrong.
Hi,
Is dependencies upgrade planned? I am getting following warning.
(node:3640) [DEP0066] DeprecationWarning: OutgoingMessage.prototype._headers is deprecated.
Apparently this is caused by very old version of "get" package.
Anyway, many other dependencies are quite outdated and npm install executed on this repo clone reports many vulnerabilities.
Right now the documentation site is generated from JSDoc comments often detached from the implementation, typos and easily result in incorrect documentation. (see #51)
Due to the API's heavy use of variadic arguments, this often results in incomplete definitions of parameters. (see #56, #28)
The documentation generation should be replaced with something that can do the same job but deriving more information from the types.
Noticed npm has v2.4.1 but I'm looking to upload worker files so would like it if v2.5.1 was pushed. Just using GitHub tag for now yarn add -D https://github.com/cloudflare/node-cloudflare.git#2.5.1
. Thanks!
To help aid in better documentation and autocomplete in editors (#59), I'd like to begin to using TypeScript. Packages pushed to npm would be compiled to JavaScript and include the "*.d.ts" files.
This is mostly a tracking ticket for this work.
what i tried:
cf.dnsRecords.edit('zone id', 'dns record id', {content: 'some ip', type: 'A', ttl: 120, proxied: false, name: 'bla.example.com'})
and lots of variations.
thats how far i come: https://github.com/cloudflare/node-cloudflare/blob/master/lib/method.js#L100
Is there argo tunnel support in this package? I can see it is available in the v4 api. Sorry if I missed it in the docs
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.