diff --git a/appengine/hello-world/flexible_nodejs16_and_earlier/README.md b/appengine/hello-world/flexible_nodejs16_and_earlier/README.md new file mode 100644 index 0000000000..62116429ef --- /dev/null +++ b/appengine/hello-world/flexible_nodejs16_and_earlier/README.md @@ -0,0 +1,38 @@ +# Quickstart for Node.js in the App Engine flexible environment + +This is the sample application for the +[Quickstart for Node.js in the App Engine flexible environment][tutorial] +tutorial found in the [Google App Engine Node.js flexible environment][appengine] +documentation. + +* [Setup](#setup) +* [Running locally](#running-locally) +* [Deploying to App Engine](#deploying-to-app-engine) +* [Running the tests](#running-the-tests) + +## Setup + +Before you can run or deploy the sample, you need to do the following: + +1. Refer to the [appengine/README.md][readme] file for instructions on + running and deploying. +1. Install dependencies: + + npm install + +## Running locally + + npm start + +## Deploying to App Engine + + gcloud app deploy + +## Running the tests + +See [Contributing][contributing]. + +[appengine]: https://cloud.google.com/appengine/docs/flexible/nodejs +[tutorial]: https://cloud.google.com/appengine/docs/flexible/nodejs/quickstart +[readme]: ../../README.md +[contributing]: https://github.com/GoogleCloudPlatform/nodejs-docs-samples/blob/main/CONTRIBUTING.md diff --git a/appengine/hello-world/flexible_nodejs16_and_earlier/app.js b/appengine/hello-world/flexible_nodejs16_and_earlier/app.js new file mode 100644 index 0000000000..d86bbd9860 --- /dev/null +++ b/appengine/hello-world/flexible_nodejs16_and_earlier/app.js @@ -0,0 +1,34 @@ +// Copyright 2017 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +// [START gae_flex_quickstart_v1] +const express = require('express'); + +const app = express(); + +app.get('/', (req, res) => { + res.status(200).send('Hello, world!').end(); +}); + +// Start the server +const PORT = parseInt(process.env.PORT) || 8080; +app.listen(PORT, () => { + console.log(`App listening on port ${PORT}`); + console.log('Press Ctrl+C to quit.'); +}); +// [END gae_flex_quickstart_v1] + +module.exports = app; diff --git a/appengine/hello-world/flexible_nodejs16_and_earlier/app.yaml b/appengine/hello-world/flexible_nodejs16_and_earlier/app.yaml new file mode 100644 index 0000000000..10c1a5072c --- /dev/null +++ b/appengine/hello-world/flexible_nodejs16_and_earlier/app.yaml @@ -0,0 +1,29 @@ +# Copyright 2017, Google, Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START gae_flex_quickstart_yaml_v1] +runtime: nodejs +env: flex + +# This sample incurs costs to run on the App Engine flexible environment. +# The settings below are to reduce costs during testing and are not appropriate +# for production use. For more information, see: +# https://cloud.google.com/appengine/docs/flexible/nodejs/configuring-your-app-with-app-yaml +manual_scaling: + instances: 1 +resources: + cpu: 1 + memory_gb: 0.5 + disk_size_gb: 10 + +# [END gae_flex_quickstart_yaml_v1] diff --git a/appengine/hello-world/flexible_nodejs16_and_earlier/package.json b/appengine/hello-world/flexible_nodejs16_and_earlier/package.json new file mode 100644 index 0000000000..cf34423120 --- /dev/null +++ b/appengine/hello-world/flexible_nodejs16_and_earlier/package.json @@ -0,0 +1,26 @@ +{ + "name": "appengine-hello-world", + "description": "Simple Hello World Node.js sample for Google App Engine Flexible Environment.", + "version": "0.0.2", + "private": true, + "license": "Apache-2.0", + "author": "Google Inc.", + "repository": { + "type": "git", + "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git" + }, + "engines": { + "node": "=v16.19.1" + }, + "scripts": { + "start": "node app.js", + "test": "mocha --exit test/*.test.js" + }, + "dependencies": { + "express": "^4.17.1" + }, + "devDependencies": { + "mocha": "^10.0.0", + "supertest": "^6.0.0" + } +} diff --git a/appengine/hello-world/flexible_nodejs16_and_earlier/test/app.test.js b/appengine/hello-world/flexible_nodejs16_and_earlier/test/app.test.js new file mode 100644 index 0000000000..727151de7d --- /dev/null +++ b/appengine/hello-world/flexible_nodejs16_and_earlier/test/app.test.js @@ -0,0 +1,14 @@ +const app = require('../app'); +const request = require('supertest'); + +describe('gae_flex_quickstart', () => { + describe('GET /', () => { + it('should get 200', done => { + request(app).get('/').expect(200, done); + }); + + it('should get Hello World', done => { + request(app).get('/').expect('Hello, world!', done); + }); + }); +}); diff --git a/appengine/metadata/flexible_nodejs16_and_earlier/app.yaml b/appengine/metadata/flexible_nodejs16_and_earlier/app.yaml new file mode 100644 index 0000000000..4dfe96aa23 --- /dev/null +++ b/appengine/metadata/flexible_nodejs16_and_earlier/app.yaml @@ -0,0 +1,15 @@ +# Copyright 2016, Google, Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +runtime: nodejs +env: flex diff --git a/appengine/metadata/flexible_nodejs16_and_earlier/package.json b/appengine/metadata/flexible_nodejs16_and_earlier/package.json new file mode 100644 index 0000000000..71f7b309ef --- /dev/null +++ b/appengine/metadata/flexible_nodejs16_and_earlier/package.json @@ -0,0 +1,27 @@ +{ + "name": "appengine-metadata", + "description": "Sample for accessing the Compute metadata server on GAE Flexible Environment.", + "version": "0.0.1", + "private": true, + "license": "Apache-2.0", + "author": "Google Inc.", + "repository": { + "type": "git", + "url": "https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git" + }, + "engines": { + "node": "16.x.x" + }, + "scripts": { + "system-test": "mocha --exit test/*.test.js", + "test": "npm run system-test" + }, + "dependencies": { + "express": "^4.17.1", + "node-fetch": "^2.6.1" + }, + "devDependencies": { + "mocha": "^10.0.0", + "supertest": "^6.0.0" + } +} diff --git a/appengine/metadata/flexible_nodejs16_and_earlier/server.js b/appengine/metadata/flexible_nodejs16_and_earlier/server.js new file mode 100644 index 0000000000..472ad82a9c --- /dev/null +++ b/appengine/metadata/flexible_nodejs16_and_earlier/server.js @@ -0,0 +1,61 @@ +// Copyright 2017 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +// [START gae_flex_metadata_v1] +const express = require('express'); +const fetch = require('node-fetch'); + +const app = express(); +app.enable('trust proxy'); + +const METADATA_NETWORK_INTERFACE_URL = + 'http://metadata/computeMetadata/v1/' + + '/instance/network-interfaces/0/access-configs/0/external-ip'; + +const getExternalIp = async () => { + const options = { + headers: { + 'Metadata-Flavor': 'Google', + }, + json: true, + }; + + try { + const response = await fetch(METADATA_NETWORK_INTERFACE_URL, options); + const ip = await response.json(); + return ip; + } catch (err) { + console.log('Error while talking to metadata server, assuming localhost'); + return 'localhost'; + } +}; + +app.get('/', async (req, res, next) => { + try { + const externalIp = await getExternalIp(); + res.status(200).send(`External IP: ${externalIp}`).end(); + } catch (err) { + next(err); + } +}); + +const PORT = parseInt(process.env.PORT) || 8080; +app.listen(PORT, () => { + console.log(`App listening on port ${PORT}`); + console.log('Press Ctrl+C to quit.'); +}); +// [END gae_flex_metadata_v1] +module.exports = app; diff --git a/appengine/metadata/flexible_nodejs16_and_earlier/test/server.test.js b/appengine/metadata/flexible_nodejs16_and_earlier/test/server.test.js new file mode 100644 index 0000000000..59f2197743 --- /dev/null +++ b/appengine/metadata/flexible_nodejs16_and_earlier/test/server.test.js @@ -0,0 +1,9 @@ +const path = require('path'); +const app = require(path.join(__dirname, '../', 'server.js')); +const supertest = require('supertest'); + +describe('gae_flex_metadata', () => { + it('should be listening', async () => { + await supertest(app).get('/').expect(200); + }); +}); diff --git a/appengine/storage/flexible_nodejs16_and_earlier/README.md b/appengine/storage/flexible_nodejs16_and_earlier/README.md new file mode 100644 index 0000000000..de65b02ee3 --- /dev/null +++ b/appengine/storage/flexible_nodejs16_and_earlier/README.md @@ -0,0 +1,40 @@ +# Node.js Google Cloud Storage sample for Google App Engine + +This sample demonstrates how to use [Google Cloud Storage](https://cloud.google.com/storage/) +on [Google App Engine flexible environment](https://cloud.google.com/appengine). + +## Setup + +Before you can run or deploy the sample, you will need to do the following: + +1. Enable the Cloud Storage API in the [Google Developers Console](https://console.developers.google.com/project/_/apiui/apiview/storage/overview). + +1. Create a Cloud Storage Bucket. You can do this with the [Google Cloud SDK](https://cloud.google.com/sdk) +with the following command: + + gsutil mb gs:// + +1. Set the default ACL on your bucket to public read in order to serve files +directly from Cloud Storage. You can do this with the [Google Cloud SDK](https://cloud.google.com/sdk) +with the following command: + + gsutil defacl set public-read gs:// + +1. Update the environment variables in `app.yaml`. + +## Running locally + +Refer to the [top-level README](../../README.md) for instructions on running and +deploying. + +When running locally, you can use the [Google Cloud SDK](https://cloud.google.com/sdk) +to provide authentication to use Google Cloud APIs: + + gcloud init + +Then set environment variables before starting your application: + + export GOOGLE_CLOUD_PROJECT= + export GCLOUD_STORAGE_BUCKET= + npm install + npm start diff --git a/appengine/storage/flexible_nodejs16_and_earlier/app.js b/appengine/storage/flexible_nodejs16_and_earlier/app.js new file mode 100644 index 0000000000..813a5d03cc --- /dev/null +++ b/appengine/storage/flexible_nodejs16_and_earlier/app.js @@ -0,0 +1,90 @@ +// Copyright 2016 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const process = require('process'); // Required to mock environment variables + +// [START gae_flex_storage_app_v1] +const {format} = require('util'); +const express = require('express'); +const Multer = require('multer'); + +// By default, the client will authenticate using the service account file +// specified by the GOOGLE_APPLICATION_CREDENTIALS environment variable and use +// the project specified by the GOOGLE_CLOUD_PROJECT environment variable. See +// https://github.com/GoogleCloudPlatform/google-cloud-node/blob/master/docs/authentication.md +// These environment variables are set automatically on Google App Engine +const {Storage} = require('@google-cloud/storage'); + +// Instantiate a storage client +const storage = new Storage(); + +const app = express(); +app.set('view engine', 'pug'); + +// This middleware is available in Express v4.16.0 onwards +app.use(express.json()); + +// Multer is required to process file uploads and make them available via +// req.files. +const multer = Multer({ + storage: Multer.memoryStorage(), + limits: { + fileSize: 5 * 1024 * 1024, // no larger than 5mb, you can change as needed. + }, +}); + +// A bucket is a container for objects (files). +const bucket = storage.bucket(process.env.GCLOUD_STORAGE_BUCKET); + +// Display a form for uploading files. +app.get('/', (req, res) => { + res.render('form.pug'); +}); + +// Process the file upload and upload to Google Cloud Storage. +app.post('/upload', multer.single('file'), (req, res, next) => { + if (!req.file) { + res.status(400).send('No file uploaded.'); + return; + } + + // Create a new blob in the bucket and upload the file data. + const blob = bucket.file(req.file.originalname); + const blobStream = blob.createWriteStream(); + + blobStream.on('error', err => { + next(err); + }); + + blobStream.on('finish', () => { + // The public URL can be used to directly access the file via HTTP. + const publicUrl = format( + `https://storage.googleapis.com/${bucket.name}/${blob.name}` + ); + res.status(200).send(publicUrl); + }); + + blobStream.end(req.file.buffer); +}); + +const PORT = parseInt(process.env.PORT) || 8080; +app.listen(PORT, () => { + console.log(`App listening on port ${PORT}`); + console.log('Press Ctrl+C to quit.'); +}); +// [END gae_flex_storage_app_v1] + +module.exports = app; diff --git a/appengine/storage/flexible_nodejs16_and_earlier/app.yaml b/appengine/storage/flexible_nodejs16_and_earlier/app.yaml new file mode 100644 index 0000000000..34140af01e --- /dev/null +++ b/appengine/storage/flexible_nodejs16_and_earlier/app.yaml @@ -0,0 +1,20 @@ +# Copyright 2015-2016, Google, Inc. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START gae_flex_storage_yaml_v1] +runtime: nodejs +env: flex + +env_variables: + GCLOUD_STORAGE_BUCKET: YOUR_BUCKET_NAME +# [END gae_flex_storage_yaml_v1] diff --git a/appengine/storage/flexible_nodejs16_and_earlier/package.json b/appengine/storage/flexible_nodejs16_and_earlier/package.json new file mode 100644 index 0000000000..5f3df34f4c --- /dev/null +++ b/appengine/storage/flexible_nodejs16_and_earlier/package.json @@ -0,0 +1,28 @@ +{ + "name": "appengine-storage", + "description": "Node.js Google Cloud Storage sample for Google App Engine", + "scripts": { + "start": "node app.js", + "test": "mocha system-test/*.test.js --exit --timeout=30000" + }, + "engines": { + "node": "16.x.x" + }, + "dependencies": { + "@google-cloud/storage": "^6.9.3", + "express": "^4.18.2", + "multer": "^1.4.5-lts.1", + "pug": "^3.0.2" + }, + "devDependencies": { + "@types/express": "^4.17.17", + "@types/multer": "^1.4.7", + "@types/proxyquire": "^1.3.28", + "@types/supertest": "^2.0.12", + "@types/uuid": "^9.0.1", + "mocha": "^10.2.0", + "proxyquire": "^2.1.3", + "supertest": "^6.3.3", + "uuid": "^9.0.0" + } +} diff --git a/appengine/storage/flexible_nodejs16_and_earlier/system-test/app.test.js b/appengine/storage/flexible_nodejs16_and_earlier/system-test/app.test.js new file mode 100644 index 0000000000..ef8b88919a --- /dev/null +++ b/appengine/storage/flexible_nodejs16_and_earlier/system-test/app.test.js @@ -0,0 +1,85 @@ +// Copyright 2017 Google, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const path = require('path'); +const {Storage} = require('@google-cloud/storage'); +const supertest = require('supertest'); +const storage = new Storage(); +const assert = require('assert'); +const proxyquire = require('proxyquire').noPreserveCache(); +const uuid = require('uuid'); + +const bucketName = + `nodejs-docs-samples-test-appengine-storage-flex-${uuid.v4()}`.slice(0, 63); +const bucket = storage.bucket(bucketName); +process.env.GCLOUD_STORAGE_BUCKET = bucketName; + +const cwd = path.join(__dirname, '../'); +const requestObj = supertest(proxyquire(path.join(cwd, 'app'), {process})); + +before(async () => { + try { + await bucket.create(bucket).then(() => { + return bucket.acl.add({ + entity: 'allUsers', + role: Storage.acl.READER_ROLE, + }); + }); + } catch (err) { + if ( + !err.message.match( + /Your previous request to create the named bucket succeeded and you already own it./ + ) + ) { + throw err; + } + } +}); +after(async () => { + try { + await bucket.deleteFiles(); + await bucket.delete(); + } catch (err) { + // ignore error + } +}); + +describe('gae_flex_storage_app', () => { + it('should load', async () => { + await requestObj + .get('/') + .expect(200) + .expect(response => { + assert.strictEqual( + new RegExp(//).test(response.text), + true + ); + }); + }); + + it('should upload a file', async () => { + await requestObj + .post('/upload') + .attach('file', path.join(__dirname, 'resources/test.txt')) + .expect(200) + .expect(response => { + assert.strictEqual( + response.text, + `https://storage.googleapis.com/${bucketName}/test.txt` + ); + }); + }); +}); diff --git a/appengine/storage/flexible_nodejs16_and_earlier/system-test/resources/test.txt b/appengine/storage/flexible_nodejs16_and_earlier/system-test/resources/test.txt new file mode 100644 index 0000000000..3dcff04919 --- /dev/null +++ b/appengine/storage/flexible_nodejs16_and_earlier/system-test/resources/test.txt @@ -0,0 +1 @@ +nodejs-docs-samples/appengine/storage test file \ No newline at end of file diff --git a/appengine/storage/flexible_nodejs16_and_earlier/views/form.pug b/appengine/storage/flexible_nodejs16_and_earlier/views/form.pug new file mode 100644 index 0000000000..1089129a82 --- /dev/null +++ b/appengine/storage/flexible_nodejs16_and_earlier/views/form.pug @@ -0,0 +1,10 @@ +doctype html +html(lang="en") + head + title Static Files + meta(charset='utf-8') + link(rel="stylesheet", href="/static/main.css") + body + form(method="POST", action="/upload", enctype="multipart/form-data") + input(type="file", name="file") + input(type="submit")