Top 4 PHP Libraries for Parsing URLs

In today's post we will learn about Top 4 PHP Libraries for parsing URLs.

What is Parsing URLs?

URL parsing is a function of traffic management and load-balancing products that scan URLs to determine how to forward traffic across different links or into different servers. A URL includes a protocol identifier (http, for Web traffic) and a resource name, such as www.microsoft.com.

Table of contents:

  • PHP Domain Parser - A domain suffix parser library.
  • Purl - A URL manipulation library.
  • Sabre/uri - A functional URI manipulation library.
  • Uri - Another URL manipulation library.

1 - PHP Domain Parser:

A domain suffix parser library.

PHP Domain Parser is a resource based domain parser implemented in PHP.

Installation

Composer

$ composer require jeremykendall/php-domain-parser

System Requirements

You need:

  • PHP >= 7.4 but the latest stable version of PHP is recommended
  • the intl extension

Usage

If you are upgrading from version 5 please check the upgrading guide for known issues.

Resolving Domains

This library can resolve a domain against:

In both cases this is done using the resolve method implemented on the resource instance. The method returns a Pdp\ResolvedDomain object which represents the result of that process.

For the Public Suffix List you need to use the Pdp\Rules class as shown below:

<?php 
use Pdp\Rules;
use Pdp\Domain;

$publicSuffixList = Rules::fromPath('/path/to/cache/public-suffix-list.dat');
$domain = Domain::fromIDNA2008('www.PreF.OkiNawA.jP');

$result = $publicSuffixList->resolve($domain);
echo $result->domain()->toString();            //display 'www.pref.okinawa.jp';
echo $result->subDomain()->toString();         //display 'www';
echo $result->secondLevelDomain()->toString(); //display 'pref';
echo $result->registrableDomain()->toString(); //display 'pref.okinawa.jp';
echo $result->suffix()->toString();            //display 'okinawa.jp';
$result->suffix()->isICANN();                  //return true;

For the IANA Top Level Domain List, the Pdp\TopLevelDomains class is use instead:

<?php

use Pdp\Domain;
use Pdp\TopLevelDomains;

$topLevelDomains = TopLevelDomains::fromPath('/path/to/cache/tlds-alpha-by-domain.txt');
$domain = Domain::fromIDNA2008('www.PreF.OkiNawA.jP');

$result = $topLevelDomains->resolve($domain);
echo $result->domain()->toString();            //display 'www.pref.okinawa.jp';
echo $result->suffix()->toString();            //display 'jp';
echo $result->secondLevelDomain()->toString(); //display 'okinawa';
echo $result->registrableDomain()->toString(); //display 'okinawa.jp';
echo $result->subDomain()->toString();         //display 'www.pref';
echo $result->suffix()->isIANA();              //return true

In case of an error an exception which extends Pdp\CannotProcessHost is thrown.

The resolve method will always return a ResolvedDomain even if the domain syntax is invalid or if there is no match found in the resource data. To work around this limitation, the library exposes more strict methods, namely:

  • Rules::getCookieDomain
  • Rules::getICANNDomain
  • Rules::getPrivateDomain

for the Public Suffix List and the following method for the Top Level Domain List:

  • TopLevelDomains::getIANADomain

These methods resolve the domain against their respective data source using the same rules as the resolve method but will instead throw an exception if no valid effective TLD is found or if the submitted domain is invalid.

All these methods expect as their sole argument a Pdp\Host implementing object, but other types (ie: string, null and stringable objects) are supported with predefined conditions as explained in the remaining document.

<?php

use Pdp\Domain;
use Pdp\Rules;
use Pdp\TopLevelDomains;

$publicSuffixList = Rules::fromPath('/path/to/cache/public-suffix-list.dat');
$domain = Domain::fromIDNA2008('qfdsf.unknownTLD');

$publicSuffixList->getICANNDomain($domain);
// will throw because `.unknownTLD` is not part of the ICANN section

$result = $publicSuffixList->getCookieDomain($domain);
$result->suffix()->value();   // returns 'unknownTLD'
$result->suffix()->isKnown(); // returns false
// will not throw because the domain syntax is correct.

$publicSuffixList->getCookieDomain(Domain::fromIDNA2008('com'));
// will not throw because the domain syntax is invalid (ie: does not support public suffix)

$result = $publicSuffixList->resolve(Domain::fromIDNA2008('com'));
$result->suffix()->value();   // returns null
$result->suffix()->isKnown(); // returns false
// will not throw but its public suffix value equal to NULL

$topLevelDomains = TopLevelDomains::fromPath('/path/to/cache/public-suffix-list.dat');
$topLevelDomains->getIANADomain(Domain::fromIDNA2008('com'));
// will not throw because the domain syntax is invalid (ie: does not support public suffix)

To instantiate each domain resolver you can use the following named constructor:

  • fromString: instantiate the resolver from a inline string representing the data source;
  • fromPath: instantiate the resolver from a local path or online URL by relying on fopen;

If the instantiation does not work an exception will be thrown.

View on Github

2 - Purl:

A URL manipulation library.

Purl is a simple Object Oriented URL manipulation library for PHP 7.2+

Installation

The suggested installation method is via composer:

composer require jwage/purl

Using Purl

Creating Url instances is easy. You can specify the URL you want, or just use the current URL:

use Purl\Url;

$url = new Url('http://jwage.com');
$currentUrl = Url::fromCurrent();

You can chain methods together after creating the Url like this:

$url = (new Url('http://jwage.com'))
    ->set('scheme', 'https')
    ->set('port', '443')
    ->set('user', 'jwage')
    ->set('pass', 'password')
    ->set('path', 'about/me')
    ->set('query', 'param1=value1&param2=value2')
    ->set('fragment', 'about/me?param1=value1&param2=value2');

echo $url->getUrl(); // https://jwage:password@jwage.com:443/about/me?param1=value1&param2=value2#about/me?param1=value1&param2=value2

// $url->path becomes instanceof Purl\Path
// $url->query becomes instanceof Purl\Query
// $url->fragment becomes instanceof Purl\Fragment

Path Manipulation

$url = new Url('http://jwage.com');

// add path segments one at a time
$url->path->add('about')->add('me');

// set the path data from a string
$url->path = 'about/me/another_segment'; // $url->path becomes instanceof Purl\Path

// get the path segments
print_r($url->path->getData()); // array('about', 'me', 'another_segment')

View on Github

3 - Sabre/uri:

A functional URI manipulation library.

sabre/uri is a lightweight library that provides several functions for working with URIs, staying true to the rules of RFC3986.

Partially inspired by Node.js URL library, and created to solve real problems in PHP applications. 100% unit tested and many tests are based on examples from RFC3986.

The library provides the following functions:

  1. resolve to resolve relative urls.
  2. normalize to aid in comparing urls.
  3. parse, which works like PHP's parse_url.
  4. build to do the exact opposite of parse.
  5. split to easily get the 'dirname' and 'basename' of a URL without all the problems those two functions have.

Build status

branchstatusminimum PHP version
masterBuild StatusPHP 7.0
1.xBuild StatusPHP 5.4.7

Further reading

Questions?

Head over to the sabre/dav mailinglist, or you can also just open a ticket on GitHub.

Made at fruux

This library is being developed by fruux. Drop us a line for commercial services or enterprise support.

View on Github

4 - Uri:

Another URL manipulation library.

The Uri package provides simple and intuitive classes to manage URIs in PHP. You will be able to

  • parse, build and resolve URIs
  • create URIs from different sources (string, PHP environment, base URI, URI template, ...);
  • handle internalisation;
  • infer properties and features from URIs;
<?php

use League\Uri\UriTemplate;

$template = 'https://api.twitter.com:443/{version}/search/{term:1}/{term}/{?q*,limit}#title';
$defaultVariables = ['version' => '1.1'];
$params = [
    'term' => 'john',
    'q' => ['a', 'b'],
    'limit' => '10',
];

$uriTemplate = new UriTemplate($template, $defaultVariables);
$uri = $uriTemplate->expand($params);
// $uri is a League\Uri\Uri object

echo $uri->getScheme();    //displays "https"
echo $uri->getAuthority(); //displays "api.twitter.com:443"
echo $uri->getPath();      //displays "/1.1/search/j/john/"
echo $uri->getQuery();     //displays "q=a&q=b&limit=10"
echo $uri->getFragment();  //displays "title"
echo $uri;
//displays "https://api.twitter.com:443/1.1/search/j/john/?q=a&q=b&limit=10#title"
echo json_encode($uri);
//displays "https:\/\/api.twitter.com:443\/1.1\/search\/j\/john\/?q=a&q=b&limit=10#title"

Highlights

System Requirements

  • You require PHP >= 7.3 but the latest stable version of PHP is recommended
  • You will need the ext-intl to handle i18n URI.
  • Since version 6.2.0 you will need the ext-fileinfo to handle Data URI creation from a filepath.

Dependencies

In order to handle IDN host you are required to also install the intl extension otherwise an exception will be thrown when attempting to validate such host.

In order to create Data URI from a filepath, since version 6.2, you are required to also install the fileinfo extension otherwise an exception will be thrown.

Installation

$ composer require league/uri

View on Github

Thank you for following this article.

Related videos:

Parse URL to extract parameters - Part 9 | PHP MVC Framework from scratch

#php #url 

Top 4 PHP Libraries for Parsing URLs
Reid  Rohan

Reid Rohan

1662993360

6 Best Libraries for URL & Network Address Manipulation in JavaScript

In today's post we will learn about 6 Best Libraries for URL & Network Address Manipulation in JavaScript. 

URL manipulation or URL rewriting is a technique to modify URLs for various reasons. While Amazon SEO experts practice it to get higher rankings on search engines, hackers attempt it for nefarious purposes. They use URL manipulation to access unauthorized elements of a website and alter or steal private data, including users’ personal data they can exploit for financial gain.

Table of contents:

  • Query-string - Parse and stringify URL query strings.
  • URI.js - Javascript URL mutation library.
  • JSurl - Lightweight URL manipulation with JavaScript.
  • Arg.js - Lightweight URL argument and parameter parser
  • Node-ip - IP address tools for node.js
  • IP-address - A library for parsing and manipulating IPv6 (and v4) addresses in JavaScript

1 - Query-string: Parse and stringify URL query strings.

Install

$ npm install query-string

Not npm install querystring!!!!!

This module targets Node.js 6 or later and the latest version of Chrome, Firefox, and Safari.

Usage

const queryString = require('query-string');

console.log(location.search);
//=> '?foo=bar'

const parsed = queryString.parse(location.search);
console.log(parsed);
//=> {foo: 'bar'}

console.log(location.hash);
//=> '#token=bada55cafe'

const parsedHash = queryString.parse(location.hash);
console.log(parsedHash);
//=> {token: 'bada55cafe'}

parsed.foo = 'unicorn';
parsed.ilike = 'pizza';

const stringified = queryString.stringify(parsed);
//=> 'foo=unicorn&ilike=pizza'

location.search = stringified;
// note that `location.search` automatically prepends a question mark
console.log(location.search);
//=> '?foo=unicorn&ilike=pizza'

View on Github

2 - URI.js: Javascript URL mutation library.

I always want to shoot myself in the head when looking at code like the following:

var url = "http://example.org/foo?bar=baz";
var separator = url.indexOf('?') > -1 ? '&' : '?';

url += separator + encodeURIComponent("foo") + "=" + encodeURIComponent("bar");

Things are looking up with URL and the URL spec but until we can safely rely on that API, have a look at URI.js for a clean and simple API for mutating URIs:

var url = new URI("http://example.org/foo?bar=baz");
url.addQuery("foo", "bar");

URI.js is here to help with that.

API Example

// mutating URLs
URI("http://example.org/foo.html?hello=world")
  .username("rodneyrehm")
    // -> http://rodneyrehm@example.org/foo.html?hello=world
  .username("")
    // -> http://example.org/foo.html?hello=world
  .directory("bar")
    // -> http://example.org/bar/foo.html?hello=world
  .suffix("xml")
    // -> http://example.org/bar/foo.xml?hello=world
  .query("")
    // -> http://example.org/bar/foo.xml
  .tld("com")
    // -> http://example.com/bar/foo.xml
  .query({ foo: "bar", hello: ["world", "mars"] });
    // -> http://example.com/bar/foo.xml?foo=bar&hello=world&hello=mars

// cleaning things up
URI("?&foo=bar&&foo=bar&foo=baz&")
  .normalizeQuery();
    // -> ?foo=bar&foo=baz

// working with relative paths
URI("/foo/bar/baz.html")
  .relativeTo("/foo/bar/world.html");
    // -> ./baz.html

URI("/foo/bar/baz.html")
  .relativeTo("/foo/bar/sub/world.html")
    // -> ../baz.html
  .absoluteTo("/foo/bar/sub/world.html");
    // -> /foo/bar/baz.html

// URI Templates
URI.expand("/foo/{dir}/{file}", {
  dir: "bar",
  file: "world.html"
});
// -> /foo/bar/world.html

See the About Page and API Docs for more stuff.

View on Github

3 - JSurl: Lightweight URL manipulation with JavaScript.

How To Use

First of all it is required to include Url class on the page. It can be simply done as

<script src="url.min.js"></script>

Then any time it's required to do some work over the URL string, it's just required to instantiate the Url object and work with that object instead of initial string. See API description below to get a clue.

Install with JAM

It is possible also to install domurl via JAM repository (http://jamjs.org/). Could be simply done as:

$ jam install domurl

Install with Bower

It is also possible now to install domurl using Bower package repository. Could be done simply as:

$ bower install domurl

Install with NPM

Domurl is available on NPM and is now works well for both server and browser:

$ npm install domurl

View on Github

4 - Arg.js: Lightweight URL argument and parameter parser

Installing

Changes

v1.3

  • BUG: Empty arrays result in extra &&&&

v1.2

  • Simplified project structure and file names
  • Minified version included in /dist
  • Resolves decoding URL issues #17
  • Fix for Arg.query() in IE 8 #19
  • Use hasOwnProperty when looping though arguments

v1.1

  • Added Arg(key) shorter interface as well as Arg.get(key).
  • Ignores undefined/empty keys and values.
  • Cleans up edge cases (i.e. where paths are present in parse() calls etc).
  • Will now optionally coerce a native type out of value if possible (i.e. Number, Boolean, undefined, etc). To not coerce, set Arg.coerceMode = false
  • Better handling of complex objects that have mixed nested objects/arrays. See new test case added to test/spec/arg.js for an example object that was failing and is no longer failing.
  • Added support for anchors in Arg.url(path, params, anchorString) (i.e. no longer assumes they're variables if it's a string)

v1

  • Launch

People who like arg.js, also like:

View on Github

5 - Node-ip: IP address tools for node.js

Installation

npm

npm install ip

git

git clone https://github.com/indutny/node-ip.git

Usage

Get your ip address, compare ip addresses, validate ip addresses, etc.

var ip = require('ip');

ip.address() // my ip address
ip.isEqual('::1', '::0:1'); // true
ip.toBuffer('127.0.0.1') // Buffer([127, 0, 0, 1])
ip.toString(new Buffer([127, 0, 0, 1])) // 127.0.0.1
ip.fromPrefixLen(24) // 255.255.255.0
ip.mask('192.168.1.134', '255.255.255.0') // 192.168.1.0
ip.cidr('192.168.1.134/26') // 192.168.1.128
ip.not('255.255.255.0') // 0.0.0.255
ip.or('192.168.1.134', '0.0.0.255') // 192.168.1.255
ip.isPrivate('127.0.0.1') // true
ip.isV4Format('127.0.0.1'); // true
ip.isV6Format('::ffff:127.0.0.1'); // true

// operate on buffers in-place
var buf = new Buffer(128);
var offset = 64;
ip.toBuffer('127.0.0.1', buf, offset);  // [127, 0, 0, 1] at offset 64
ip.toString(buf, offset, 4);            // '127.0.0.1'

// subnet information
ip.subnet('192.168.1.134', '255.255.255.192')
// { networkAddress: '192.168.1.128',
//   firstAddress: '192.168.1.129',
//   lastAddress: '192.168.1.190',
//   broadcastAddress: '192.168.1.191',
//   subnetMask: '255.255.255.192',
//   subnetMaskLength: 26,
//   numHosts: 62,
//   length: 64,
//   contains: function(addr){...} }
ip.cidrSubnet('192.168.1.134/26')
// Same as previous.

// range checking
ip.cidrSubnet('192.168.1.134/26').contains('192.168.1.190') // true


// ipv4 long conversion
ip.toLong('127.0.0.1'); // 2130706433
ip.fromLong(2130706433); // '127.0.0.1'

View on Github

6 - IP-address: A library for parsing and manipulating IPv6 (and v4) addresses in JavaScriptParse and stringify URL query strings.

Migrating from 6.x to 7.x

ip-address was rewritten in TypeScript for version 7. If you were using version 6 you'll need to make these changes to upgrade:

  • Instead of checking isValid(), which has been removed, you'll need to use a try/catch if you're accepting unknown input. This made the TypeScript types substantially easier as well as allowed the use of an AddressError class which will contain a parseMessage if an error occurred in the parsing step.
  • Instead of using the error, parseError, and valid attributes you'll need to use the message and parseMessage of the thrown AddressError.

Examples

var Address6 = require('ip-address').Address6;

var address = new Address6('2001:0:ce49:7601:e866:efff:62c3:fffe');

var teredo = address.inspectTeredo();

teredo.client4;    // '157.60.0.1'

View on Github

Thank you for following this article.

Related videos:

JavaScript DOM Manipulation

#javascript #url #network #ip 

6 Best Libraries for URL & Network Address Manipulation in JavaScript
Hunter  Krajcik

Hunter Krajcik

1662060780

URL_launcher_android: The android Implementation Of Url_launcher

url_launcher_android

The Android implementation of url_launcher.

Usage

This package is endorsed, which means you can simply use url_launcher normally. This package will be automatically included in your app when you do.

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add url_launcher_android

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  url_launcher_android: ^6.0.17

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:url_launcher_android/url_launcher_android.dart';

example/lib/main.dart

// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// ignore_for_file: public_member_api_docs

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'URL Launcher',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'URL Launcher'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final UrlLauncherPlatform launcher = UrlLauncherPlatform.instance;
  bool _hasCallSupport = false;
  Future<void>? _launched;
  String _phone = '';

  @override
  void initState() {
    super.initState();
    // Check for phone call support.
    launcher.canLaunch('tel:123').then((bool result) {
      setState(() {
        _hasCallSupport = result;
      });
    });
  }

  Future<void> _launchInBrowser(String url) async {
    if (!await launcher.launch(
      url,
      useSafariVC: false,
      useWebView: false,
      enableJavaScript: false,
      enableDomStorage: false,
      universalLinksOnly: false,
      headers: <String, String>{},
    )) {
      throw 'Could not launch $url';
    }
  }

  Future<void> _launchInWebView(String url) async {
    if (!await launcher.launch(
      url,
      useSafariVC: true,
      useWebView: true,
      enableJavaScript: false,
      enableDomStorage: false,
      universalLinksOnly: false,
      headers: <String, String>{'my_header_key': 'my_header_value'},
    )) {
      throw 'Could not launch $url';
    }
  }

  Future<void> _launchInWebViewWithJavaScript(String url) async {
    if (!await launcher.launch(
      url,
      useSafariVC: true,
      useWebView: true,
      enableJavaScript: true,
      enableDomStorage: false,
      universalLinksOnly: false,
      headers: <String, String>{},
    )) {
      throw 'Could not launch $url';
    }
  }

  Future<void> _launchInWebViewWithDomStorage(String url) async {
    if (!await launcher.launch(
      url,
      useSafariVC: true,
      useWebView: true,
      enableJavaScript: false,
      enableDomStorage: true,
      universalLinksOnly: false,
      headers: <String, String>{},
    )) {
      throw 'Could not launch $url';
    }
  }

  Widget _launchStatus(BuildContext context, AsyncSnapshot<void> snapshot) {
    if (snapshot.hasError) {
      return Text('Error: ${snapshot.error}');
    } else {
      return const Text('');
    }
  }

  Future<void> _makePhoneCall(String phoneNumber) async {
    // Use `Uri` to ensure that `phoneNumber` is properly URL-encoded.
    // Just using 'tel:$phoneNumber' would create invalid URLs in some cases,
    // such as spaces in the input, which would cause `launch` to fail on some
    // platforms.
    final Uri launchUri = Uri(
      scheme: 'tel',
      path: phoneNumber,
    );
    await launcher.launch(
      launchUri.toString(),
      useSafariVC: false,
      useWebView: false,
      enableJavaScript: false,
      enableDomStorage: false,
      universalLinksOnly: true,
      headers: <String, String>{},
    );
  }

  @override
  Widget build(BuildContext context) {
    // onPressed calls using this URL are not gated on a 'canLaunch' check
    // because the assumption is that every device can launch a web URL.
    const String toLaunch = 'https://www.cylog.org/headers/';
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: ListView(
        children: <Widget>[
          Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.all(16.0),
                child: TextField(
                    onChanged: (String text) => _phone = text,
                    decoration: const InputDecoration(
                        hintText: 'Input the phone number to launch')),
              ),
              ElevatedButton(
                onPressed: _hasCallSupport
                    ? () => setState(() {
                          _launched = _makePhoneCall(_phone);
                        })
                    : null,
                child: _hasCallSupport
                    ? const Text('Make phone call')
                    : const Text('Calling not supported'),
              ),
              const Padding(
                padding: EdgeInsets.all(16.0),
                child: Text(toLaunch),
              ),
              ElevatedButton(
                onPressed: () => setState(() {
                  _launched = _launchInBrowser(toLaunch);
                }),
                child: const Text('Launch in browser'),
              ),
              const Padding(padding: EdgeInsets.all(16.0)),
              ElevatedButton(
                onPressed: () => setState(() {
                  _launched = _launchInWebView(toLaunch);
                }),
                child: const Text('Launch in app'),
              ),
              ElevatedButton(
                onPressed: () => setState(() {
                  _launched = _launchInWebViewWithJavaScript(toLaunch);
                }),
                child: const Text('Launch in app (JavaScript ON)'),
              ),
              ElevatedButton(
                onPressed: () => setState(() {
                  _launched = _launchInWebViewWithDomStorage(toLaunch);
                }),
                child: const Text('Launch in app (DOM storage ON)'),
              ),
              const Padding(padding: EdgeInsets.all(16.0)),
              ElevatedButton(
                onPressed: () => setState(() {
                  _launched = _launchInWebView(toLaunch);
                  Timer(const Duration(seconds: 5), () {
                    print('Closing WebView after 5 seconds...');
                    launcher.closeWebView();
                  });
                }),
                child: const Text('Launch in app + close after 5 seconds'),
              ),
              const Padding(padding: EdgeInsets.all(16.0)),
              FutureBuilder<void>(future: _launched, builder: _launchStatus),
            ],
          ),
        ],
      ),
    );
  }
}

Download Details:

Author: flutter
Source Code: https://github.com/flutter/plugins/ 
License: BSD-3-Clause license

#flutter #dart #url #android 

URL_launcher_android: The android Implementation Of Url_launcher
Rui  Silva

Rui Silva

1661799600

Crie Um Encurtador De URL Com Cloudflare Workers

Você já usou ferramentas como Bitly ou TinyURL para encurtar links longos? Ou você já se perguntou como esses serviços funcionam? Talvez você quisesse criar um encurtador de URL, mas nunca encontrou tempo ou as ferramentas adequadas para fazê-lo. De qualquer forma, se você está interessado neste tema, este artigo é perfeito para você.

Neste post, demonstraremos como construir um serviço básico de encurtador de URL usando Cloudflare Workers . Forneceremos informações detalhadas sobre como os serviços de encurtador de URL funcionam, apresentaremos vários recursos do Cloudflare Workers e forneceremos instruções passo a passo sobre como começar com o Cloudflare Workers.

Vamos começar!

O que são os Trabalhadores da Cloudflare?

O Cloudflare Workers é um serviço que permite implantar código sem servidor na rede Cloudflare. A rede Cloudflare, ou Edge, é uma rede de servidores web espalhados pelo mundo. Uma grande vantagem dos Cloudflare Workers é que você não precisa se preocupar em dimensionar seu código. Além disso, você não precisa se preocupar com os fusos horários em que seu código reside; seu código no Workers está espalhado pelo mundo segundos depois de implantado.

Além disso, os Cloudflare Workers vêm com um armazenamento de dados de chave-valor simples, chamado KV. Neste tutorial, usaremos uma combinação de Cloudflare Workers e armazenamento KV para criar nossa URL mais curta.

Visão geral do projeto: serviço de encurtador de URL

Começaremos criando um encurtador de URL simples e não dinâmico, onde você codifica os sites para os quais deseja redirecionar. Isso servirá como uma introdução para aprender a usar o Wrangler (ferramenta CLI oficial da Cloudflare) e demonstrará os conceitos básicos do domínio Workers.

Em seguida, vamos apimentar um pouco as coisas e adicionar suporte para URLs dinâmicos. Basicamente, vamos interagir com a loja Cloudflare Workers KV e inserir versões curtas da URL e a URL real para a qual queremos redirecionar. Os dados no armazenamento KV serão semelhantes à seguinte estrutura:

'short-url': 'https://my-cool-website.com'
'submit': 'https://my-cool-site.org/blog/ideas/submit'

Por fim, implantaremos nosso código em produção e o veremos funcionar ao vivo em todo o mundo.

Já está animado? Ótimo, vamos pular!

Configurando o ambiente

Para acompanhar este artigo, você precisará do seguinte:

  • Node.js e npm
  • Peão
  • curl (ou o navegador de sua escolha) para testar o encurtador de URL

Eu uso a ferramenta asdf para gerenciar minhas dependências locais, mas você pode usar qualquer gerenciador de versão que preferir. No momento em que escrevo, aqui está minha versão do Node e do npm:

$ node --version
v18.5.0
$ npm --version
8.12.1

Wrangler é uma ferramenta de linha de comando para construção e, recentemente, ganhou sua versão 2.0. Para os propósitos deste post, o Wrangler atenderá a todas as nossas necessidades. No futuro, podemos usar o Miniflare, um irmão mais robusto e rico em recursos do Wrangler . Mas, por enquanto, vamos instalar o Wrangler globalmente via npm:

$ npm install -g wrangler@2.0.21

No momento em que escrevo, a versão mais recente do Wrangler é 2.0.21, então vamos usar essa.

Legal. Agora que temos todas as dependências em vigor, podemos usar a CLI do Wrangler para gerar nosso Cloudflare Worker inicial.

Gerando o projeto

A ferramenta Wrangler CLI será muito útil aqui.

Para começar, vamos executar um comando para iniciar e configurar nosso projeto corretamente:

$ wrangler init short-it

Este comando fará algumas perguntas. Por enquanto, vamos responder sim (digitando y ) para todos eles:

$ wrangler init short-it
  wrangler 2.0.21
--------------------
Using npm as package manager.
 Created short-it/wrangler.toml
Would you like to use git to manage this Worker? (y/n)
 Initialized git repository at short-it
No package.json found. Would you like to create one? (y/n)
 Created short-it/package.json
Would you like to use TypeScript? (y/n)
 Created short-it/tsconfig.json
Would you like to create a Worker at short-it/src/index.ts?
  None
❯ Fetch handler
  Scheduled handler
 Created short-it/src/index.ts

added 62 packages, and audited 63 packages in 1s

1 package is looking for funding
  run `npm fund` for details

found 0 vulnerabilities
 Installed @cloudflare/workers-types and typescript into devDependencies

To start developing your Worker, run `cd short-it && npm start`
To publish your Worker to the Internet, run `npm run deploy`

Se você respondeu positivamente a todas as perguntas do Wrangler, você terá um nome de projeto short-it, com o seguinte dentro:

  • .gitdiretório em seu projeto, o que significa que você está pronto para enviá-lo ao seu provedor Git
  • package.jsonArquivo
  • tsconfig.jsonarquivo com toda a configuração do TypeScript
  • src/index.tsarquivo com alguma lógica direta para obter uma resposta do nosso Worker

Incrível. Vamos ver se essa coisa funciona!

Vamos cdentrar no short-itdiretório e iniciar o Wrangler no modo de desenvolvimento local:

$ cd short-it
$ wrangler dev --local

Isso deve executar nosso Worker em http://localhost:8787/ . Se visitarmos o localhost, devemos ver um simples “Hello World!” mensagem:

Mensagem Olá Mundo

O trabalhador gerado está exibindo um "Hello World!" mensagem.

Yay! Nós fizemos isso funcionar. Mas como? Vamos olhar mais de perto.

Como funcionam os Cloudflare Workers?

Recebemos nossa primeira mensagem localmente do Worker gerado, mas como exatamente isso funcionou?

Vamos analisar o src/index.tsarquivo gerado para entender melhor o que está acontecendo lá.

// src/index.ts

/**
 * Welcome to Cloudflare Workers! This is your first worker.
 *
 * - Run `wrangler dev src/index.ts` in your terminal to start a development server
 * - Open a browser tab at http://localhost:8787/ to see your worker in action
 * - Run `wrangler publish src/index.ts --name my-worker` to publish your worker
 *
 * Learn more at https://developers.cloudflare.com/workers/
 */

export interface Env {
  // Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
  // MY_KV_NAMESPACE: KVNamespace;
  //
  // Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/
  // MY_DURABLE_OBJECT: DurableObjectNamespace;
  //
  // Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
  // MY_BUCKET: R2Bucket;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    return new Response("Hello World!");
  },
};

O código acima inclui uma definição para nosso ambiente (a Envinterface) e alguns comentários relacionados à ENVinterface.

Como a interface está fora do escopo deste artigo, vamos ignorar essa parte do código e focar apenas na lógica principal:

// src/index.ts

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    return new Response("Hello World!");
  },
};

O que acontece aqui é que nosso index.tsexport uma fetchfunção. Esta é uma interface semelhante ao Web Workers . Na verdade, é dessa interface que se origina o nome “Cloudflare Workers”. O Cloudflare Workers é semelhante ao Web Workers, exceto que é executado na infraestrutura da Cloudflare em vez de em um navegador.

No código acima, a fetchfunção retorna um novo Responseobjeto com a mensagem “Hello World!” texto. Então, quando executamos nosso Worker, essa fetchfunção é invocada. Em seguida, a função invocada fetchretorna o “Hello World!” resposta, e é isso que pegamos no navegador (ou por meio de qualquer ferramenta usada para invocar o Worker).

OK, esclarecemos o básico dos Trabalhadores da Cloudflare. Podemos seguir em frente com confiança. Se você é novo no TypeScript, não se preocupe; usaremos seus recursos apenas levemente. Imagine isso como uma integração leve ao mundo do TypeScript.

Ótimo, vamos em frente!

Adicionando um primeiro redirecionamento

Começaremos a trabalhar em nossa lógica com um início suave. Primeiro, faremos com que nosso encurtador de URL redirecione um usuário para um site diferente. Esta será a base para alterações posteriores.

Por enquanto, faremos com que o usuário acesse uma página no site https://http.cat/ quando visitar nosso Worker local.

Se você não estiver familiarizado com https://http.cat/ , é um site divertido que exibe várias imagens de gatos para diferentes status HTTP. Por exemplo, se um usuário fizer uma solicitação ao nosso Trabalhador para http://localhost:8787/404 , ele será direcionado para https://http.cat/404 .

Para conseguir esse redirecionamento, editaremos o src/index.ts, assim:

// src/index.ts
// ...

const basePath = "https://http.cat";

export default {
  async fetch(
    request: Request,
    _env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = basePath + pathname;

    if (pathname === "/") {
      return new Response("Hello World from our awesome Worker!");
    }

    return Response.redirect(redirectURL, 301);
  },
};

Agora, se visitarmos http://localhost:8787 , receberemos uma mensagem atualizada: “Hello World from our awesome Worker!”, conforme mostrado abaixo:

Olá mundo do trabalhador incrível

Trabalhador exibindo uma mensagem “Hello world” atualizada.

Mas, se tentarmos ir para http://localhost:8787/404 , seremos redirecionados para https://http.cat/404 .

Usuário redirecionado

O usuário é redirecionado para o site http.cat/404.

Ótimo, iniciamos nosso primeiro redirecionamento. Agora, vamos fazer nosso encurtador de URL realmente encurtar alguns URLs.

Encurtando o URL

Por enquanto, adicionaremos uma pequena estrutura de dados para armazenar nossos URLs encurtados. Podemos fazer assim:

const shortURLs = {
  "/blog": "https://pragmaticpineapple.com/",
  "/twitter": "https://twitter.com/nikolalsvk",
  "/github": "https://github.com/nikolalsvk",
} as Record<any, string>;

export default {
  async fetch(
    request: Request,
    _env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = shortURLs[pathname];

    if (!redirectURL) {
      return new Response(
        `There is no defined URL for the path: '${pathname}', sorry :(`
      );
    }

    return Response.redirect(redirectURL, 301);
  },
};

Aqui, adicionamos alguns URLs abreviados:

Você pode alterá-lo para o que quiser apenas para vê-lo funcionando. Agora, quando visito http://localhost:8787/blog , sou redirecionado para uma URL mais longa onde meu blog está localizado. Aqui está o resultado:

Redirecionamentos para o blog

Visitar /blog redireciona para a página real do blog.

Mas, se solicitarmos algum caminho, como http://localhost:8787/missing , recebemos a seguinte mensagem de erro: “Não há URL definida para o caminho: '/missing', desculpe :(“.

Mensagem de erro ausente

Visitar/ausente exibe uma mensagem de erro.

Incrível, agora estamos prontos para mover nossos URLs codificados e suas versões abreviadas para um armazenamento em algum lugar. Felizmente, estamos usando Cloudflare Workers e ele oferece um armazenamento simples de valor-chave chamado KV.

Adicionando armazenamento

Antes de realmente criarmos o KV para nosso projeto, primeiro precisamos fazer login no Cloudflare Workers via Wrangler. Isso é necessário porque o Wrangler posteriormente precisará entrar em contato com a Cloudflare para criar uma instância KV para nós.

Fazendo login na Cloudflare

Para fazer login na Cloudflare, use o seguinte comando:

$ wrangler login

Um navegador será aberto, solicitando que você faça login na Cloudflare. Não se preocupe; o plano gratuito cobre tudo o que precisamos para este tutorial, e você não precisará pagar. Vá em frente e registre-se ou faça login se você já tiver uma conta.

Em seguida, a Cloudflare perguntará se você deseja conceder autorização ao Wrangler. Após concordar, você deverá ver a seguinte tela:

Ferramenta CLI do Wrangler

A ferramenta Wrangler CLI agora está conectada corretamente.

Não deve haver nenhum soluço durante o processo de inscrição. Mas, se você ficar preso em algum ponto, você pode seguir o guia da Cloudflare sobre como criar uma conta .

Incrível! Agora que você está cadastrado e logado, vamos verificar se tudo está conectado corretamente.

Use o seguinte comando:

$ wrangler whoami
  wrangler 2.0.21
--------------------
Getting User settings...
 You are logged in with an OAuth Token, associated with the email 'nikolaseap@gmail.com'!
┌──────────────────────┬──────────────────────────────────┐
│ Account Name │ Account ID │
├──────────────────────┼──────────────────────────────────┤
│ Nikola Đuza Personal │ 98a16dfefca0e2ee27e1e79ba590d973 │
└──────────────────────┴──────────────────────────────────┘

Ótimo, estamos prontos para criar um namespace KV.

Criando um namespace KV

Um namespace KV pode ser considerado como uma instância de KV na rede Cloudflare. Criaremos dois namespaces KV: um para produção onde nosso aplicativo ficará e funcionará e outro para o ambiente de visualização. Usaremos o namespace de visualização enquanto testamos e desenvolvemos nosso encurtador de URL.

Criaremos nossos namespaces KV via Wrangler com os seguintes comandos:

$ wrangler kv:namespace create SHORT_URLS
 Creating namespace with title "short-it-SHORT_URLS"
 Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "SHORT_URLS", id = "029d374ebd984e19b0bb98e37ab1a95e" }

$ wrangler kv:namespace create SHORT_URLS --preview
  wrangler 2.0.21
--------------------
 Creating namespace with title "short-it-SHORT_URLS_preview"
 Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "SHORT_URLS", preview_id = "99a72876e5f84cf58de722b1c2080604" }

Depois que esses dois comandos são executados e ambos os namespaces são criados, precisamos dizer ao Wrangler para usar esses namespaces quando executarmos wrangler dev.

Adicionaremos informações sobre namespaces KV ao wrangler.tomlarquivo na raiz do nosso projeto. Deve ser algo assim:

name = "short-it"
main = "src/index.ts"
compatibility_date = "2022-07-15"

kv_namespaces = [
  { binding = "SHORT_URLS", id = "029d374ebd984e19b0bb98e37ab1a95e", preview_id = "99a72876e5f84cf58de722b1c2080604" }
]

O wrangler.tomlarquivo é um arquivo de configuração que informa wranglercertas informações sobre nosso projeto. Agora, estamos amarrados e prontos para adicionar alguns dados ao nosso KV.

Adicionando dados ao KV

Nosso próximo passo é semear os dados para o KV. Lembre-se, temos dois namespaces, então teremos que executar dois comandos para ter os dados em ambos os lugares. Vamos adicionar a /blogentrada ao KV:

$ wrangler kv:key put --binding SHORT_URLS "/blog" "https://pragmaticpineapple.com/" --preview false
  wrangler 2.0.21
--------------------
Writing the value "https://pragmaticpineapple.com/" to key "/blog" on namespace 029d374ebd984e19b0bb98e37ab1a95e.

$ wrangler kv:key put --binding SHORT_URLS "/blog" "https://pragmaticpineapple.com/" --preview
  wrangler 2.0.21
--------------------
Writing the value "https://pragmaticpineapple.com/" to key "/blog" on namespace 99a72876e5f84cf58de722b1c2080604.

Incrível. Agora temos uma entrada no KV. Em seguida, vamos adicionar a lógica que lê a partir do KV e redireciona o usuário.

Leitura do KV

Removeremos rapidamente nossos URLs curtos codificados antigos e adicionaremos uma chamada ao KV, assim:

// src/index.ts
export interface Env {
  SHORT_URLS: KVNamespace;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = await env.SHORT_URLS.get(pathname);

    if (!redirectURL) {
      return new Response(
        `There is no defined URL for the path: '${pathname}', sorry :(`
      );
    }

    return Response.redirect(redirectURL, 301);
  },
};

Aqui, adicionamos SHORT_URLScomo um KVNamespacetipo. Isso nos permitirá chamar métodos KV para obter os dados adequados. Em vez do objeto codificado com URLs, desta vez usamos await env.SHORT_URLS.get(pathname).

A chamada para env.SHORT_URLS.get(pathname)tenta obter a chave do KV. Se ele retornar uma promessa, devemos await. Mas, se houver um valor para determinado pathname, o usuário será redirecionado para esse URL.

Agora, quando visitarmos http://localhost:8787/blog , seremos redirecionados para a URL real do blog que colocamos no KV. Isso parecerá assim:

Ainda redireciona o blog

Visitar /blog ainda nos redireciona para a página real do blog.

Mas, se agora tentarmos visitar qualquer um dos outros URLs que codificamos, receberemos uma mensagem informando que esses URLs não têm um redirecionamento:

Redirecionamento de URL ausente

Visitar /twitter resulta em uma mensagem indicando que o URL não tem um redirecionamento.

Vamos adicionar rapidamente o URL encurtado do Twitter ao KV usando estes comandos:

$ wrangler kv:key put --binding SHORT_URLS "/twitter" "https://twitter.com/nikolalsvk" --preview false
 wrangler 2.0.21
--------------------
Writing the value "https://twitter.com/nikolalsvk" to key "/twitter" on namespace 029d374ebd984e19b0bb98e37ab1a95e.

$ wrangler kv:key put --binding SHORT_URLS "/twitter" "https://twitter.com/nikolalsvk" --preview
  wrangler 2.0.21
--------------------
Writing the value "https://twitter.com/nikolalsvk" to key "/twitter" on namespace 99a72876e5f84cf58de722b1c2080604.

Agora, quando atualizarmos o http://localhost:8787/twitter , devemos ser redirecionados para a conta do Twitter.

Carregando Twitter

O Twitter carrega depois que adicionamos o URL encurtado ao KV.

Incrível, agora temos dois URLs curtos: /bloge /twitter. Vamos tentar implantar nosso serviço e vê-lo em produção.

Como implantar trabalhadores do Cloudflare

A etapa de implantação do Cloudflare Workers é bastante fácil. Vamos utilizar wrangler publish, assim:

$ wrangler publish
  wrangler 2.0.21
--------------------
Retrieving cached values for userId from node_modules/.cache/wrangler
Your worker has access to the following bindings:
- KV Namespaces:
  - SHORT_URLS: 029d374ebd984e19b0bb98e37ab1a95e
Total Upload: 0.45 KiB / gzip: 0.29 KiB
Worker ID: short-it
Worker ETag: f8395cab29edf297137631b803b14c32daaae982758c23e3019b700e2468c277
Uploaded short-it (2.14 sec)
Published short-it (6.33 sec)
  short-it.nikolalsvk.workers.dev

Agora, os serviços estão disponíveis em https://short-it.nikolalsvk.workers.dev . Yay!

Se você estiver seguindo este tutorial, seus serviços devem estar em algum lugar ao longo da URL https://short-it.YOUR_SUBDOMAIN.workers.dev , dependendo do que você selecionou para YOUR_SUBDOMAIN.

Neste ponto, nosso script Worker é implantado em todo o mundo na rede Cloudflare Edge. Isso significa que amigos e estranhos em todo o mundo podem ser redirecionados incrivelmente rápido para nossa conta do Twitter se visitarem https://short-it.nikolalsvk.workers.dev/twitter .

Empacotando

Obrigado por acompanhar a jornada de criação de um serviço simples de encurtador de URL usando Cloudflare Workers. Neste artigo, apresentamos os conceitos de um Worker dentro do contexto Cloudflare. Também demonstramos como criar e gerenciar dados no armazenamento KV da Cloudflare.

Conseguimos executar tudo isso sem problemas usando o Wrangler, que oferece uma ótima experiência ao desenvolvedor. Mas, o mais importante, conseguimos criar, testar e implantar nosso pequeno serviço que roda rápido em todos os cantos do mundo.

Conseguir isso em uma tecnologia ou serviço semelhante pode exigir muito dinheiro e esforço. No entanto, a Cloudflare oferece suporte a um generoso nível gratuito de 100.000 solicitações por dia. Assim, você pode encurtar muitos URLs e ter muitas visitas antes de entrar em um plano pago.

Todo o código deste artigo está disponível no repositório do GitHub (por favor, marque-o com uma estrela, se você gostar). O serviço de encurtador está disponível em https://short-it.nikolalsvk.workers.dev .

Se você gostou do post, considere compartilhá-lo com seus amigos e colegas de trabalho.

Até a próxima, abração!

Fonte: https://blog.logrocket.com/creating-url-shortener-cloudflare-workers/

#cloudflare #url 

Crie Um Encurtador De URL Com Cloudflare Workers
曾 俊

曾 俊

1661799600

使用 Cloudflare Workers 创建 URL 缩短器

您是否曾经使用过BitlyTinyURL等工具来缩短长链接?或者,您想知道这些服务是如何工作的吗?也许您想构建一个 URL 缩短器,但从未找到时间或合适的工具来做这件事。无论如何,如果您对此主题感兴趣,那么本文非常适合您。

在这篇文章中,我们将演示如何使用Cloudflare Workers构建基本的 URL 缩短服务。我们将提供有关 URL 缩短服务如何工作的详细信息,介绍 Cloudflare Workers 的几个功能,并提供有关如何开始使用 Cloudflare Workers 的分步说明。

让我们开始吧!

什么是 Cloudflare Workers?

Cloudflare Workers 是一项服务,可让您将无服务器代码部署到 Cloudflare 网络。Cloudflare 网络或 Edge 是遍布全球的 Web 服务器网络。Cloudflare Workers 的一大优点是您不必担心扩展代码。此外,您不必担心代码所在的时区;您在 Workers 中的代码在部署后的几秒钟内就会在全球范围内传播。

最重要的是,Cloudflare Workers 带有一个简单的键值对数据存储,称为 KV。在本教程中,我们将结合使用 Cloudflare Workers 和 KV 存储来构建更短的 URL。

项目概述:URL 缩短服务

我们将首先构建一个简单的非动态 URL 缩短器,您可以在其中硬编码要重定向到的网站。这将作为学习如何使用Wrangler(Cloudflare 的官方 CLI 工具)的介绍,并将演示 Workers 领域的基本概念。

接下来,我们将增加一些趣味并添加对动态 URL 的支持。基本上,我们将与 Cloudflare Workers KV 存储进行交互,并输入 URL 的简短版本和我们想要重定向到的实际 URL。KV 存储中的数据将类似于以下结构:

'short-url': 'https://my-cool-website.com'
'submit': 'https://my-cool-site.org/blog/ideas/submit'

最后,我们会将我们的代码部署到生产环境中,并在全球范围内看到它的运行情况。

你已经兴奋了吗?太好了,让我们跳进去!

设置环境

要继续阅读本文,您需要以下内容:

  • Node.js 和 npm
  • 牧马人
  • curl(或您选择的浏览器)来测试 URL 缩短器

我使用asdf 工具来管理我的本地依赖项,但您可以使用任何您喜欢的版本管理器。在撰写本文时,这是我的 Node 和 npm 版本:

$ node --version
v18.5.0
$ npm --version
8.12.1

Wrangler 是一个用于构建的命令行工具,最近它有了 2.0 版本。就本文而言,牧马人将满足我们的所有需求。将来,我们可能会使用Miniflare,它是 Wrangler 的一个更强大、功能更丰富的兄弟。但是,现在,让我们通过 npm 全局安装 Wrangler:

$ npm install -g wrangler@2.0.21

在撰写本文时,最新的 Wrangler 版本是 2.0.21,所以我们将使用那个版本。

凉爽的。现在我们已经有了所有的依赖项,我们可以使用 Wrangler CLI 来生成我们的入门 Cloudflare Worker。

生成项目

Wrangler CLI 工具在这里将非常有用。

首先,让我们运行一个命令来正确启动和设置我们的项目:

$ wrangler init short-it

这个命令会问几个问题。现在,我们将为所有这些回答是(通过输入y):

$ wrangler init short-it
  wrangler 2.0.21
--------------------
Using npm as package manager.
 Created short-it/wrangler.toml
Would you like to use git to manage this Worker? (y/n)
 Initialized git repository at short-it
No package.json found. Would you like to create one? (y/n)
 Created short-it/package.json
Would you like to use TypeScript? (y/n)
 Created short-it/tsconfig.json
Would you like to create a Worker at short-it/src/index.ts?
  None
❯ Fetch handler
  Scheduled handler
 Created short-it/src/index.ts

added 62 packages, and audited 63 packages in 1s

1 package is looking for funding
  run `npm fund` for details

found 0 vulnerabilities
 Installed @cloudflare/workers-types and typescript into devDependencies

To start developing your Worker, run `cd short-it && npm start`
To publish your Worker to the Internet, run `npm run deploy`

如果您对 Wrangler 提出的所有问题的回答都是肯定的,那么您将拥有一个项目名称short-it,其中包含以下内容:

  • .git项目中的目录,这意味着您已准备好将其推送到您的 Git 提供程序
  • package.json文件
  • tsconfig.json包含所有 TypeScript 配置的文件
  • src/index.ts带有一些简单逻辑的文件以从我们的 Worker 中获得响应

惊人的。让我们看看这个东西是否有效!

让我们cd进入short-it目录并以本地开发模式启动 Wrangler:

$ cd short-it
$ wrangler dev --local

这应该在http://localhost:8787/上运行我们的 Worker 。如果我们访问 localhost,我们应该会看到一个简单的“Hello World!” 信息:

你好世界消息

Generated Worker 显示“Hello World!” 信息。

耶!我们让它工作。但是怎么做?让我们仔细看看。

Cloudflare Workers 如何工作?

我们从生成的 Worker 在本地获得了第一条消息,但它究竟是如何工作的呢?

让我们浏览生成的src/index.ts文件,以更好地了解那里发生的事情。

// src/index.ts

/**
 * Welcome to Cloudflare Workers! This is your first worker.
 *
 * - Run `wrangler dev src/index.ts` in your terminal to start a development server
 * - Open a browser tab at http://localhost:8787/ to see your worker in action
 * - Run `wrangler publish src/index.ts --name my-worker` to publish your worker
 *
 * Learn more at https://developers.cloudflare.com/workers/
 */

export interface Env {
  // Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
  // MY_KV_NAMESPACE: KVNamespace;
  //
  // Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/
  // MY_DURABLE_OBJECT: DurableObjectNamespace;
  //
  // Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
  // MY_BUCKET: R2Bucket;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    return new Response("Hello World!");
  },
};

上面的代码包括我们的环境(Env接口)的定义和一些与接口相关的注释ENV

由于接口超出了本文的范围,我们将忽略这部分代码,只关注主要逻辑:

// src/index.ts

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    return new Response("Hello World!");
  },
};

这里发生的是我们index.ts导出了一个fetch函数。这是一个类似于Web Workers的界面。事实上,“Cloudflare Workers”这个名字就是从这个界面来的。Cloudflare Workers 类似于 Web Workers,不同之处在于它运行在 Cloudflare 基础架构而不是浏览器上。

在上面的代码中,fetch函数返回一个Response带有“Hello World!”的新对象。文本。所以当我们运行我们的 Worker 时,fetch会调用这个函数。然后,被调用的fetch函数返回“Hello World!” 响应,这就是我们在浏览器中获取的(或通过任何用于调用 Worker 的工具)。

好的,我们已经了解了 Cloudflare Workers 的基础知识。我们可以充满信心地继续前进。如果您是 TypeScript 新手,请不要担心;我们只会轻轻地使用它的功能。将其想象为 TypeScript 世界的轻量级入门。

太好了,让我们继续前进!

添加第一个重定向

我们将从一个温和的开始着手我们的逻辑。首先,我们将让我们的 URL 缩短器将用户重定向到不同的网站。这将是以后更改的基础。

现在,当用户访问我们的本地 Worker 时,我们将让用户访问https://http.cat/网站上的一个页面。

如果你不熟悉https://http.cat/,它是一个有趣的网站,显示不同 HTTP 状态的各种猫图片。例如,如果用户向我们的 Worker 请求http://localhost:8787/404,他们将被定向到https://http.cat/404

要实现此重定向,我们将编辑src/index.ts,如下所示:

// src/index.ts
// ...

const basePath = "https://http.cat";

export default {
  async fetch(
    request: Request,
    _env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = basePath + pathname;

    if (pathname === "/") {
      return new Response("Hello World from our awesome Worker!");
    }

    return Response.redirect(redirectURL, 301);
  },
};

现在,如果我们访问http://localhost:8787,我们将收到一条更新的消息:“Hello World from our awesome Worker!”,如下所示:

来自真棒工人的你好世界

显示更新的“Hello world”消息的工作人员。

但是,如果我们尝试访问http://localhost:8787/404,我们将被重定向到https://http.cat/404

用户重定向

用户被重定向到 http.cat/404 网站。

太好了,我们的第一个重定向开始了。现在,让我们的 URL 缩短器实际上缩短了一些 URL。

缩短网址

现在,我们将添加一个小数据结构来存储我们缩短的 URL。我们可以这样做:

const shortURLs = {
  "/blog": "https://pragmaticpineapple.com/",
  "/twitter": "https://twitter.com/nikolalsvk",
  "/github": "https://github.com/nikolalsvk",
} as Record<any, string>;

export default {
  async fetch(
    request: Request,
    _env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = shortURLs[pathname];

    if (!redirectURL) {
      return new Response(
        `There is no defined URL for the path: '${pathname}', sorry :(`
      );
    }

    return Response.redirect(redirectURL, 301);
  },
};

在这里,我们添加了几个缩短的 URL:

您可以将其更改为您喜欢的任何内容,以使其正常工作。现在,当我访问http://localhost:8787/blog时,我被重定向到我的博客所在的更长的 URL。结果如下:

重定向到博客

访问 /blog 会重定向到实际的博客页面。

但是,如果我们请求一些路径,例如http://localhost:8787/missing,我们会收到以下错误消息:“路径没有定义的 URL:'/missing',对不起 :(”。

错误消息丢失

访问 /missing 会显示错误消息。

太棒了,现在我们已经准备好将硬编码的 URL 及其缩短的版本移动到某个地方的存储中。幸运的是,我们正在使用 Cloudflare Workers,它提供了一种称为 KV 的简单键值存储。

添加存储

在我们为项目实际创建 KV 之前,我们首先需要通过 Wrangler 登录 Cloudflare Workers。这是必要的,因为 Wrangler 稍后需要联系 Cloudflare 以便为我们创建 KV 实例。

登录 Cloudflare

要登录 Cloudflare,请使用以下命令:

$ wrangler login

将打开一个浏览器,要求您登录 Cloudflare。不用担心; 免费计划涵盖了本教程所需的一切,并且不会要求您付款。继续注册,如果您已经有帐户,请登录。

接下来,Cloudflare 将询问您是否要授予 Wrangler 授权。同意后,您应该会看到以下屏幕:

牧马人 CLI 工具

Wrangler CLI 工具现在已正确连接。

在注册过程中不应该有任何问题。但是,如果您在任何时候遇到困难,可以按照Cloudflare 的创建帐户指南进行操作

惊人的!现在您已注册并登录,让我们检查一切是否正确连接。

使用以下命令:

$ wrangler whoami
  wrangler 2.0.21
--------------------
Getting User settings...
 You are logged in with an OAuth Token, associated with the email 'nikolaseap@gmail.com'!
┌──────────────────────┬──────────────────────────────────┐
│ Account Name │ Account ID │
├──────────────────────┼──────────────────────────────────┤
│ Nikola Đuza Personal │ 98a16dfefca0e2ee27e1e79ba590d973 │
└──────────────────────┴──────────────────────────────────┘

太好了,我们已经准备好创建 KV 命名空间了。

创建 KV 命名空间

可以将 KV 命名空间视为 Cloudflare 网络上的 KV up 实例。我们将创建两个 KV 命名空间:一个用于我们的应用程序将生活和工作的生产环境,另一个用于预览环境。我们将在测试和开发 URL 缩短器时使用 preview 命名空间。

我们将使用以下命令通过 Wrangler 创建 KV 命名空间:

$ wrangler kv:namespace create SHORT_URLS
 Creating namespace with title "short-it-SHORT_URLS"
 Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "SHORT_URLS", id = "029d374ebd984e19b0bb98e37ab1a95e" }

$ wrangler kv:namespace create SHORT_URLS --preview
  wrangler 2.0.21
--------------------
 Creating namespace with title "short-it-SHORT_URLS_preview"
 Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "SHORT_URLS", preview_id = "99a72876e5f84cf58de722b1c2080604" }

在这两个命令运行并创建了两个命名空间之后,我们需要告诉 Wrangler 在运行时使用这些命名空间wrangler dev

wrangler.toml我们将在项目根目录的文件中添加有关 KV 命名空间的信息。它应该看起来像这样:

name = "short-it"
main = "src/index.ts"
compatibility_date = "2022-07-15"

kv_namespaces = [
  { binding = "SHORT_URLS", id = "029d374ebd984e19b0bb98e37ab1a95e", preview_id = "99a72876e5f84cf58de722b1c2080604" }
]

wrangler.toml文件是一个配置文件,它告诉wrangler我们项目的某些信息。现在,我们已经准备好将一些数据添加到我们的 KV 中。

向 KV 添加数据

我们的下一步是将数据播种到 KV。请记住,我们有两个命名空间,所以我们必须运行两个命令才能在两个地方都有数据。让我们将/blog条目添加到 KV:

$ wrangler kv:key put --binding SHORT_URLS "/blog" "https://pragmaticpineapple.com/" --preview false
  wrangler 2.0.21
--------------------
Writing the value "https://pragmaticpineapple.com/" to key "/blog" on namespace 029d374ebd984e19b0bb98e37ab1a95e.

$ wrangler kv:key put --binding SHORT_URLS "/blog" "https://pragmaticpineapple.com/" --preview
  wrangler 2.0.21
--------------------
Writing the value "https://pragmaticpineapple.com/" to key "/blog" on namespace 99a72876e5f84cf58de722b1c2080604.

惊人的。现在我们在 KV 中有一个条目。接下来,让我们添加从 KV 读取并重定向用户的逻辑。

从 KV 读取

我们将快速删除旧的硬编码短 URL,并添加对 KV 的调用,如下所示:

// src/index.ts
export interface Env {
  SHORT_URLS: KVNamespace;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = await env.SHORT_URLS.get(pathname);

    if (!redirectURL) {
      return new Response(
        `There is no defined URL for the path: '${pathname}', sorry :(`
      );
    }

    return Response.redirect(redirectURL, 301);
  },
};

在这里,我们添加SHORT_URLSKVNamespace类型。这将允许我们调用 KV 方法来获取正确的数据。这次我们使用await env.SHORT_URLS.get(pathname).

调用env.SHORT_URLS.get(pathname)尝试从 KV 获取密钥。如果它返回一个承诺,我们必须await. 但是,如果给定的 有一个值pathname,那么用户将被重定向到该 URL。

现在,当我们访问http://localhost:8787/blog时,我们将被重定向到我们放入 KV 中的实际博客 URL。它看起来像这样:

仍然重定向博客

访问 /blog 仍然会将我们重定向到实际的博客页面。

但是,如果我们现在尝试访问我们硬编码的任何其他 URL,我们将收到一条消息,指出这些 URL 缺少重定向:

URL 缺少重定向

访问 /twitter 会产生一条消息,指示该 URL 缺少重定向。

让我们使用以下命令快速将 Twitter 缩短的 URL 添加到 KV:

$ wrangler kv:key put --binding SHORT_URLS "/twitter" "https://twitter.com/nikolalsvk" --preview false
 wrangler 2.0.21
--------------------
Writing the value "https://twitter.com/nikolalsvk" to key "/twitter" on namespace 029d374ebd984e19b0bb98e37ab1a95e.

$ wrangler kv:key put --binding SHORT_URLS "/twitter" "https://twitter.com/nikolalsvk" --preview
  wrangler 2.0.21
--------------------
Writing the value "https://twitter.com/nikolalsvk" to key "/twitter" on namespace 99a72876e5f84cf58de722b1c2080604.

现在,当我们刷新http://localhost:8787/twitter时,我们应该会被重定向到 Twitter 帐户。

推特加载

在我们将缩短的 URL 添加到 KV 后加载 Twitter。

太棒了,现在我们有两个短 URL:/blog/twitter. 让我们尝试部署我们的服务并在生产中查看它。

部署 Cloudflare Worker

Cloudflare Workers 部署步骤相当简单。我们将使用wrangler publish,如下所示:

$ wrangler publish
  wrangler 2.0.21
--------------------
Retrieving cached values for userId from node_modules/.cache/wrangler
Your worker has access to the following bindings:
- KV Namespaces:
  - SHORT_URLS: 029d374ebd984e19b0bb98e37ab1a95e
Total Upload: 0.45 KiB / gzip: 0.29 KiB
Worker ID: short-it
Worker ETag: f8395cab29edf297137631b803b14c32daaae982758c23e3019b700e2468c277
Uploaded short-it (2.14 sec)
Published short-it (6.33 sec)
  short-it.nikolalsvk.workers.dev

现在,这些服务在https://short-it.nikolalsvk.workers.dev上线。耶!

如果您按照本教程进行操作,您的服务应该位于 URL https://short-it.YOUR_SUBDOMAIN.workers.dev的某个位置,具体取决于您选择的内容YOUR_SUBDOMAIN

此时,我们的 Worker 脚本已部署在全球各地的 Cloudflare Edge 网络上。这意味着全球各地的朋友和陌生人访问https://short-it.nikolalsvk.workers.dev/twitter时,可以极快地重定向到我们的 Twitter 帐户。

包起来

感谢您继续使用 Cloudflare Workers 创建简单的 URL 缩短服务。在本文中,我们介绍了 Cloudflare 上下文中 Worker 的概念。我们还演示了如何在 Cloudflare 的 KV 存储中创建和管理数据。

我们能够使用 Wrangler 顺利执行所有这些工作,这提供了出色的开发人员体验。但是,最重要的是,我们设法创建、测试和部署了在世界各个角落快速运行的小型服务。

在类似的技术或服务中实现这一目标可能需要大量的金钱和努力。但是,Cloudflare 支持每天 100,000 个请求的免费套餐。因此,您可以在违反付费计划之前缩短许多 URL 并对其进行多次访问。

本文中的所有代码都可以在GitHub 存储库中找到(如果喜欢,请给它加星标)。缩短服务在https://short-it.nikolalsvk.workers.dev上线。

如果您喜欢这篇文章,请考虑与您的朋友和同事分享。

直到下一次,干杯!

来源:https ://blog.logrocket.com/creating-url-shortener-cloudflare-workers/

#cloudflare #url 

使用 Cloudflare Workers 创建 URL 缩短器
Thai  Son

Thai Son

1661797800

Tạo Trình Rút Ngắn URL Với Cloudflare Worker

Bạn đã bao giờ sử dụng các công cụ như Bitly hoặc TinyURL để rút ngắn các liên kết dài chưa? Hoặc, bạn đã tự hỏi làm thế nào các dịch vụ này hoạt động? Có thể bạn muốn xây dựng một công cụ rút ngắn URL nhưng chưa bao giờ tìm thấy thời gian hoặc công cụ thích hợp để làm điều đó. Trong mọi trường hợp, nếu bạn quan tâm đến chủ đề này, bài viết này là hoàn hảo cho bạn.

Trong bài đăng này, chúng tôi sẽ trình bày cách tạo dịch vụ rút gọn URL cơ bản bằng Cloudflare worker . Chúng tôi sẽ cung cấp thông tin chi tiết về cách hoạt động của các dịch vụ rút gọn URL, giới thiệu một số tính năng của Cloudflare worker và hướng dẫn từng bước về cách bắt đầu với Cloudflare worker.

Bắt đầu nào!

Công nhân Cloudflare là gì?

Cloudflare worker là một dịch vụ cho phép bạn triển khai mã không máy chủ vào mạng Cloudflare. Mạng Cloudflare, hay Edge, là một mạng lưới các máy chủ web trải rộng trên toàn cầu. Một điều tuyệt vời về Cloudflare worker là bạn không phải lo lắng về việc mở rộng mã của mình. Ngoài ra, bạn không phải lo lắng về múi giờ mà mã của bạn đang sống; mã của bạn trong Công nhân được lan truyền trên toàn cầu vài giây sau khi được triển khai.

Trên hết, Cloudflare worker đi kèm với một kho dữ liệu giá trị-khóa đơn giản, được gọi là KV. Trong hướng dẫn này, chúng tôi sẽ sử dụng kết hợp Cloudflare worker và KV lưu trữ để xây dựng URL của chúng tôi ngắn hơn.

Tổng quan về dự án: Dịch vụ rút gọn URL

Chúng tôi sẽ bắt đầu bằng cách xây dựng một trình rút ngắn URL đơn giản, không động, nơi bạn mã hóa cứng các trang web mà bạn muốn chuyển hướng đến. Đây sẽ là phần giới thiệu để học cách sử dụng Wrangler (công cụ CLI chính thức của Cloudflare) và sẽ trình bày các khái niệm cơ bản trong lĩnh vực Công nhân.

Tiếp theo, chúng tôi sẽ thêm gia vị cho mọi thứ và thêm hỗ trợ cho các URL động. Về cơ bản, chúng tôi sẽ tương tác với cửa hàng Cloudflare worker KV và nhập các phiên bản ngắn của URL và URL thực mà chúng tôi muốn chuyển hướng đến. Dữ liệu trong KV store sẽ tương tự như cấu trúc sau:

'short-url': 'https://my-cool-website.com'
'submit': 'https://my-cool-site.org/blog/ideas/submit'

Cuối cùng, chúng tôi sẽ triển khai mã của mình để sản xuất và xem nó hoạt động trực tiếp trên toàn cầu.

Bạn đã hào hứng chưa? Tuyệt vời, chúng ta hãy nhảy vào!

Thiết lập môi trường

Để làm theo bài viết này, bạn sẽ cần những thứ sau:

  • Node.js và npm
  • Wrangler
  • curl (hoặc trình duyệt bạn chọn) để kiểm tra trình rút gọn URL

Tôi sử dụng công cụ asdf để quản lý các phụ thuộc cục bộ của mình, nhưng bạn có thể sử dụng bất kỳ trình quản lý phiên bản nào bạn muốn. Tại thời điểm viết bài, đây là phiên bản Node và npm của tôi:

$ node --version
v18.5.0
$ npm --version
8.12.1

Wrangler là một công cụ dòng lệnh để xây dựng và gần đây, nó đã có phiên bản 2.0. Với mục đích của bài đăng này, Wrangler sẽ đáp ứng mọi nhu cầu của chúng tôi. Trong tương lai, chúng tôi có thể sử dụng Miniflare, một người anh em mạnh mẽ hơn và giàu tính năng hơn của Wrangler . Tuy nhiên, hiện tại, hãy cài đặt Wrangler trên toàn cầu thông qua npm:

$ npm install -g wrangler@2.0.21

Tại thời điểm viết bài, phiên bản Wrangler mới nhất là 2.0.21, vì vậy chúng tôi sẽ tiếp tục với phiên bản đó.

Mát mẻ. Bây giờ chúng ta đã có tất cả các phụ thuộc, chúng ta có thể sử dụng Wrangler CLI để tạo Cloudflare Worker khởi đầu của chúng ta.

Tạo dự án

Công cụ Wrangler CLI sẽ tỏ ra rất hữu ích ở đây.

Để bắt đầu, hãy chạy một lệnh để bắt đầu và thiết lập dự án của chúng tôi đúng cách:

$ wrangler init short-it

Lệnh này sẽ hỏi một số câu hỏi. Hiện tại, chúng tôi sẽ trả lời có (bằng cách nhập y ) cho tất cả chúng:

$ wrangler init short-it
  wrangler 2.0.21
--------------------
Using npm as package manager.
 Created short-it/wrangler.toml
Would you like to use git to manage this Worker? (y/n)
 Initialized git repository at short-it
No package.json found. Would you like to create one? (y/n)
 Created short-it/package.json
Would you like to use TypeScript? (y/n)
 Created short-it/tsconfig.json
Would you like to create a Worker at short-it/src/index.ts?
  None
❯ Fetch handler
  Scheduled handler
 Created short-it/src/index.ts

added 62 packages, and audited 63 packages in 1s

1 package is looking for funding
  run `npm fund` for details

found 0 vulnerabilities
 Installed @cloudflare/workers-types and typescript into devDependencies

To start developing your Worker, run `cd short-it && npm start`
To publish your Worker to the Internet, run `npm run deploy`

Nếu bạn trả lời tích cực cho tất cả các câu hỏi từ Wrangler, thì bạn sẽ có tên dự án short-it, với nội dung sau:

  • .gitthư mục trong dự án của bạn, nghĩa là bạn đã sẵn sàng đẩy nó đến nhà cung cấp Git của mình
  • package.jsontập tin
  • tsconfig.jsontệp với tất cả cấu hình TypeScript
  • src/index.tstệp với một số logic đơn giản để nhận được phản hồi từ Nhân viên của chúng tôi

Đáng kinh ngạc. Hãy xem điều này có hiệu quả không nhé!

Hãy cdvào thư mục short-itvà khởi động Wrangler ở chế độ phát triển cục bộ:

$ cd short-it
$ wrangler dev --local

Điều này sẽ chạy Worker của chúng tôi trên http: // localhost: 8787 / . Nếu chúng ta truy cập localhost, chúng ta sẽ thấy một thông báo đơn giản "Hello World!" thông điệp:

Hello World Message

Generated Worker đang hiển thị thông báo “Hello World!” thông điệp.

Yay! Chúng tôi đã làm cho nó hoạt động. Nhưng bằng cách nào? Chúng ta hãy xem xét kỹ hơn.

Công nhân Cloudflare hoạt động như thế nào?

Chúng tôi đã nhận được thông báo cục bộ đầu tiên từ Worker được tạo, nhưng chính xác thì thông báo đó hoạt động như thế nào?

Hãy xem qua src/index.tstệp đã tạo để hiểu rõ hơn về những gì đang xảy ra ở đó.

// src/index.ts

/**
 * Welcome to Cloudflare Workers! This is your first worker.
 *
 * - Run `wrangler dev src/index.ts` in your terminal to start a development server
 * - Open a browser tab at http://localhost:8787/ to see your worker in action
 * - Run `wrangler publish src/index.ts --name my-worker` to publish your worker
 *
 * Learn more at https://developers.cloudflare.com/workers/
 */

export interface Env {
  // Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
  // MY_KV_NAMESPACE: KVNamespace;
  //
  // Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/
  // MY_DURABLE_OBJECT: DurableObjectNamespace;
  //
  // Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
  // MY_BUCKET: R2Bucket;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    return new Response("Hello World!");
  },
};

Đoạn mã trên bao gồm định nghĩa cho môi trường của chúng ta ( Envgiao diện) và một số nhận xét liên quan đến ENVgiao diện.

Vì giao diện nằm ngoài phạm vi của bài viết này, chúng tôi sẽ bỏ qua phần đó của mã và chỉ tập trung vào logic chính:

// src/index.ts

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    return new Response("Hello World!");
  },
};

Điều xảy ra ở đây là chúng ta index.tsxuất một fetchhàm. Đây là một giao diện tương tự như Web Worker . Trên thực tế, chính từ giao diện này mà tên gọi “Cloudflare worker” bắt nguồn. Cloudflare worker tương tự như Web worker, ngoại trừ nó chạy trên cơ sở hạ tầng Cloudflare chứ không phải trình duyệt.

Trong đoạn mã trên, fetchhàm trả về một Responseđối tượng mới với "Hello World!" chữ. Vì vậy, khi chúng tôi chạy Worker của mình, fetchhàm này sẽ được gọi. Sau đó, hàm được gọi fetchtrả về "Hello World!" và đây là những gì chúng tôi nhận được trong trình duyệt (hoặc thông qua bất kỳ công cụ nào được sử dụng để gọi Worker).

OK, chúng tôi đã làm rõ những điều cơ bản về Cloudflare worker. Chúng ta có thể tự tin bước tiếp. Nếu bạn chưa quen với TypeScript, đừng lo lắng; chúng tôi sẽ chỉ sử dụng các tính năng của nó một cách nhẹ nhàng. Hãy tưởng tượng điều này giống như một sự giới thiệu nhẹ đến thế giới của TypeScript.

Tuyệt vời, chúng ta hãy tiếp tục!

Thêm chuyển hướng đầu tiên

Chúng tôi sẽ bắt đầu làm việc trên logic của chúng tôi với một khởi đầu nhẹ nhàng. Đầu tiên, chúng tôi sẽ yêu cầu trình rút gọn URL của chúng tôi chuyển hướng người dùng đến một trang web khác. Đây sẽ là nền tảng cho những thay đổi sau này.

Hiện tại, chúng tôi sẽ yêu cầu người dùng truy cập một trang trên trang web https://http.cat/ khi họ truy cập Nhân viên địa phương của chúng tôi.

Nếu bạn chưa quen với https://http.cat/ , đây là một trang web thú vị hiển thị nhiều hình ảnh mèo khác nhau cho các trạng thái HTTP khác nhau. Ví dụ: nếu người dùng yêu cầu Nhân viên của chúng tôi đến http: // localhost: 8787/404 , họ sẽ được chuyển hướng đến https://http.cat/404 .

Để đạt được chuyển hướng này, chúng tôi sẽ chỉnh sửa src/index.ts, như sau:

// src/index.ts
// ...

const basePath = "https://http.cat";

export default {
  async fetch(
    request: Request,
    _env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = basePath + pathname;

    if (pathname === "/") {
      return new Response("Hello World from our awesome Worker!");
    }

    return Response.redirect(redirectURL, 301);
  },
};

Bây giờ, nếu chúng tôi truy cập http: // localhost: 8787 , chúng tôi sẽ nhận được một thông báo cập nhật: “Xin chào Thế giới từ Người lao động tuyệt vời của chúng tôi!”, Như được hiển thị bên dưới:

Xin chào thế giới từ người lao động tuyệt vời

Nhân viên hiển thị thông báo “Hello world” được cập nhật.

Tuy nhiên, nếu chúng tôi cố gắng truy cập http: // localhost: 8787/404 , chúng tôi sẽ được chuyển hướng đến https://http.cat/404 .

Người dùng được chuyển hướng

Người dùng được chuyển hướng đến trang web http.cat/404.

Great, we got our first redirect going. Now, let’s make our URL shortener actually shorten some URLs.

Shortening the URL

For now, we’ll add a small data structure to store our shortened URLs. We can do it like this:

const shortURLs = {
  "/blog": "https://pragmaticpineapple.com/",
  "/twitter": "https://twitter.com/nikolalsvk",
  "/github": "https://github.com/nikolalsvk",
} as Record<any, string>;

export default {
  async fetch(
    request: Request,
    _env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = shortURLs[pathname];

    if (!redirectURL) {
      return new Response(
        `There is no defined URL for the path: '${pathname}', sorry :(`
      );
    }

    return Response.redirect(redirectURL, 301);
  },
};

Here, we added a couple of shortened URLs:

You can change it to whatever you like just to see it working. Now, when I visit http://localhost:8787/blog, I get redirected to a longer URL where my blog is located. Here’s the result:

Chuyển hướng đến Blog

Visiting /blog redirects to the actual blog page.

But, if we request some path, like http://localhost:8787/missing, we get the following error message: “There is no defined URL for the path: ‘/missing’, sorry :(“.

Thiếu thông báo lỗi

Visiting /missing displays an error message.

Awesome, now we are ready to move our hardcoded URLs and their shortened versions to a storage somewhere. Fortunately, we’re using Cloudflare Workers, and it offers a simple key-value storage called KV.

Adding storage

Before we actually create the KV for our project, we first need to log into Cloudflare Workers via Wrangler. This is necessary because Wrangler will later need to contact Cloudflare in order to create a KV instance for us.

Logging into Cloudflare

To log into Cloudflare, use the following command:

$ wrangler login

A browser will open, asking you to log in to Cloudflare. Don’t worry; the free plan covers everything we’ll need for this tutorial, and you will not be asked for payment. Go ahead and register, or log in if you already have an account.

Next, Cloudflare will ask if you want to grant authorization to Wrangler. After you agree, you should see the following screen:

Công cụ Wrangler CLI

The Wrangler CLI tool is now properly connected.

There shouldn’t be any hiccups during the signup process. But, if you got stuck at any point, you can follow Cloudflare’s guide on creating an account.

Awesome! Now that you are signed up and logged in, let’s check whether everything is connected properly.

Use the following command:

$ wrangler whoami
  wrangler 2.0.21
--------------------
Getting User settings...
 You are logged in with an OAuth Token, associated with the email 'nikolaseap@gmail.com'!
┌──────────────────────┬──────────────────────────────────┐
│ Account Name │ Account ID │
├──────────────────────┼──────────────────────────────────┤
│ Nikola Đuza Personal │ 98a16dfefca0e2ee27e1e79ba590d973 │
└──────────────────────┴──────────────────────────────────┘

Great, we’re ready to make a KV namespace.

Creating a KV namespace

A KV namespace can be thought of it as an instance of KV up on the Cloudflare network. We’ll create two KV namespaces: one for production where our app will live and work and another for the preview environment. We’ll use the preview namespace while we test and develop our URL shortener.

We’ll create our KV namespaces via Wrangler with the following commands:

$ wrangler kv:namespace create SHORT_URLS
 Creating namespace with title "short-it-SHORT_URLS"
 Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "SHORT_URLS", id = "029d374ebd984e19b0bb98e37ab1a95e" }

$ wrangler kv:namespace create SHORT_URLS --preview
  wrangler 2.0.21
--------------------
 Creating namespace with title "short-it-SHORT_URLS_preview"
 Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "SHORT_URLS", preview_id = "99a72876e5f84cf58de722b1c2080604" }

After these two commands run and both namespaces are created, we need to tell Wrangler to use these namespaces when we run wrangler dev.

We’ll add information about KV namespaces to the wrangler.toml file at the root of our project. It should look something like this:

name = "short-it"
main = "src/index.ts"
compatibility_date = "2022-07-15"

kv_namespaces = [
  { binding = "SHORT_URLS", id = "029d374ebd984e19b0bb98e37ab1a95e", preview_id = "99a72876e5f84cf58de722b1c2080604" }
]

The wrangler.toml file is a configuration file that tells wrangler certain information about our project. Now, we’re strapped up and ready to add some data to our KV.

Adding data to the KV

Our next step is to seed the data to the KV. Remember, we have two namespaces so we’ll have to run two commands to have the data in both places. Let’s add the /blog entry to the KV:

$ wrangler kv:key put --binding SHORT_URLS "/blog" "https://pragmaticpineapple.com/" --preview false
  wrangler 2.0.21
--------------------
Writing the value "https://pragmaticpineapple.com/" to key "/blog" on namespace 029d374ebd984e19b0bb98e37ab1a95e.

$ wrangler kv:key put --binding SHORT_URLS "/blog" "https://pragmaticpineapple.com/" --preview
  wrangler 2.0.21
--------------------
Writing the value "https://pragmaticpineapple.com/" to key "/blog" on namespace 99a72876e5f84cf58de722b1c2080604.

Awesome. Now we have one entry in the KV. Next, let’s add logic that reads from the KV and redirects the user.

Reading from the KV

We’ll quickly remove our old hardcoded short URLs and add a call to the KV, like so:

// src/index.ts
export interface Env {
  SHORT_URLS: KVNamespace;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = await env.SHORT_URLS.get(pathname);

    if (!redirectURL) {
      return new Response(
        `There is no defined URL for the path: '${pathname}', sorry :(`
      );
    }

    return Response.redirect(redirectURL, 301);
  },
};

Here, we add SHORT_URLS as a KVNamespace type. This will allow us to call KV methods to get the proper data. Instead of the hardcoded object with URLs, this time we use await env.SHORT_URLS.get(pathname).

The call to env.SHORT_URLS.get(pathname) tries to get the key from the KV. If it returns a promise, we must await. But, if there’s a value for the given pathname, then the user is redirected to that URL.

Now, when we visit http://localhost:8787/blog, we will be redirected to the actual blog URL we put in the KV. It will look like this:

Blog Vẫn chuyển hướng

Visiting /blog still redirects us to the actual blog page.

But, if we now try to visit any of the other URLs we hardcoded, we’ll get a message saying that those URLs are missing a redirect:

URL thiếu chuyển hướng

Visiting /twitter results in a message indicating the URL is missing a redirect.

Let’s quickly add the Twitter shortened URL to the KV using these commands:

$ wrangler kv:key put --binding SHORT_URLS "/twitter" "https://twitter.com/nikolalsvk" --preview false
 wrangler 2.0.21
--------------------
Writing the value "https://twitter.com/nikolalsvk" to key "/twitter" on namespace 029d374ebd984e19b0bb98e37ab1a95e.

$ wrangler kv:key put --binding SHORT_URLS "/twitter" "https://twitter.com/nikolalsvk" --preview
  wrangler 2.0.21
--------------------
Writing the value "https://twitter.com/nikolalsvk" to key "/twitter" on namespace 99a72876e5f84cf58de722b1c2080604.

Now, when we refresh the http://localhost:8787/twitter, we should get redirected to the Twitter account.

Đang tải trên Twitter

Twitter loads after we added the shortened URL to the KV.

Awesome, now we have two short URLs: /blog and /twitter. Let’s try to deploy our service and see it in production.

Deploying Cloudflare Workers

The Cloudflare Workers deployment step is fairly easy. We’ll utilize wrangler publish, like so:

$ wrangler publish
  wrangler 2.0.21
--------------------
Retrieving cached values for userId from node_modules/.cache/wrangler
Your worker has access to the following bindings:
- KV Namespaces:
  - SHORT_URLS: 029d374ebd984e19b0bb98e37ab1a95e
Total Upload: 0.45 KiB / gzip: 0.29 KiB
Worker ID: short-it
Worker ETag: f8395cab29edf297137631b803b14c32daaae982758c23e3019b700e2468c277
Uploaded short-it (2.14 sec)
Published short-it (6.33 sec)
  short-it.nikolalsvk.workers.dev

Now, the services are live at https://short-it.nikolalsvk.workers.dev. Yay!

If you’re following along with this tutorial, your services should live somewhere along the URL https://short-it.YOUR_SUBDOMAIN.workers.dev, depending on what you selected for YOUR_SUBDOMAIN.

At this point, our Worker script is deployed across the globe on the Cloudflare Edge network. This means that friends and strangers across the globe can get redirected blazingly fast to our Twitter account if they visit https://short-it.nikolalsvk.workers.dev/twitter.

Wrapping up

Thanks for following along on the journey of creating a simple URL shortener service using Cloudflare Workers. In this article, we introduced the concepts of a Worker inside the Cloudflare context. We also demonstrated how to create and manage data in Cloudflare’s KV storage.

We were able to execute all of this smoothly using Wrangler, which provides a great developer experience. But, most importantly, we managed to create, test, and deploy our small service that runs fast in all corners of the world.

Achieving this in a similar technology or service might require a lot of money and effort. However, Cloudflare supports a generous free tier of 100,000 requests per day. So you can shorten many URLs and have many visits on them before breaching into a paid plan.

Tất cả mã trong bài viết này đều có trong repo GitHub (vui lòng gắn dấu sao nếu bạn thích). Dịch vụ rút gọn có trực tuyến tại https://short-it.nikolalsvk.workers.dev .

Nếu bạn thích bài đăng, vui lòng xem xét chia sẻ nó với bạn bè và đồng nghiệp của bạn.

Cho đến lần sau, chúc mừng!

Nguồn: https://blog.logrocket.com/creating-url-shortener-cloudflare-workers/

 #cloudflare #url 

Tạo Trình Rút Ngắn URL Với Cloudflare Worker
高橋  花子

高橋 花子

1661796000

Cloudflare Workers で URL 短縮サービスを作成する

BitlyTinyURLなどのツールを使用して長いリンクを短縮したことがありますか? または、これらのサービスがどのように機能するのか疑問に思ったことはありませんか? おそらく、URL 短縮サービスを構築したいと思っていましたが、それを行うための時間や適切なツールが見つかりませんでした。いずれにせよ、このトピックに興味がある場合は、この記事が最適です。

この投稿では、Cloudflare Workersを使用して基本的な URL 短縮サービスを構築する方法を紹介します。URL短縮サービスがどのように機能するかについての詳細な情報を提供し、Cloudflare Workersのいくつかの機能を紹介し、Cloudflare Workersを使い始める方法について順を追って説明します.

始めましょう!

Cloudflare ワーカーとは何ですか?

Cloudflare Workers は、サーバーレス コードを Cloudflare ネットワークにデプロイできるサービスです。Cloudflare ネットワークまたは Edge は、世界中に広がる Web サーバーのネットワークです。Cloudflare Workers の素晴らしい点の 1 つは、コードのスケーリングについて心配する必要がないことです。また、コードが存在するタイム ゾーンについて心配する必要はありません。Workers のコードは、デプロイされてから数秒後に世界中に広がります。

その上、Cloudflare ワーカーには、KV と呼ばれる単純なキーと値のデータ ストアが付属しています。このチュートリアルでは、Cloudflare ワーカーと KV ストレージを組み合わせて、URL を短くします。

事業概要:URL短縮サービス

まず、リダイレクト先の Web サイトをハードコーディングする、単純で非動的な URL 短縮サービスを作成します。これは、 Wrangler (Cloudflare の公式 CLI ツール) の使用方法を学習するための入門として機能し、Workers 領域の基本的な概念を示します。

次に、少しスパイスを加えて、動的 URL のサポートを追加します。基本的に、Cloudflare Workers KV ストアと対話し、短いバージョンの URL とリダイレクト先の実際の URL を入力します。KV ストアのデータは、次のような構造になります。

'short-url': 'https://my-cool-website.com'
'submit': 'https://my-cool-site.org/blog/ideas/submit'

最後に、コードを本番環境にデプロイし、世界中で実際に動作することを確認します。

あなたはすでに興奮していますか?よし、飛び込もう!

環境のセットアップ

この記事を進めるには、次のものが必要です。

  • Node.js と npm
  • ラングラー
  • URL 短縮サービスをテストするための curl (または選択したブラウザー)

私はasdf ツールを使用してローカルの依存関係を管理していますが、任意のバージョン マネージャーを使用できます。執筆時点では、これが私の Node と npm のバージョンです。

$ node --version
v18.5.0
$ npm --version
8.12.1

Wrangler はビルド用のコマンドライン ツールで、最近 2.0 バージョンがリリースされました。この投稿では、Wrangler がすべてのニーズを満たします。将来的には、より堅牢で機能豊富な Wrangler の兄弟である Miniflareを使用する可能性があります。しかし、ここでは、npm を介して Wrangler をグローバルにインストールしましょう。

$ npm install -g wrangler@2.0.21

執筆時点で最新の Wrangler バージョンは 2.0.21 であるため、それを使用します。

涼しい。すべての依存関係が整ったので、Wrangler CLI を使用してスターター Cloudflare Worker を生成できます。

プロジェクトの生成

ここでは、Wrangler CLI ツールが非常に役立ちます。

まず、コマンドを実行して、プロジェクトを適切に開始および設定しましょう。

$ wrangler init short-it

このコマンドは、いくつかの質問をします。ここでは、それらすべてに対して( yと入力して) yes と答えます。

$ wrangler init short-it
  wrangler 2.0.21
--------------------
Using npm as package manager.
 Created short-it/wrangler.toml
Would you like to use git to manage this Worker? (y/n)
 Initialized git repository at short-it
No package.json found. Would you like to create one? (y/n)
 Created short-it/package.json
Would you like to use TypeScript? (y/n)
 Created short-it/tsconfig.json
Would you like to create a Worker at short-it/src/index.ts?
  None
❯ Fetch handler
  Scheduled handler
 Created short-it/src/index.ts

added 62 packages, and audited 63 packages in 1s

1 package is looking for funding
  run `npm fund` for details

found 0 vulnerabilities
 Installed @cloudflare/workers-types and typescript into devDependencies

To start developing your Worker, run `cd short-it && npm start`
To publish your Worker to the Internet, run `npm run deploy`

Wrangler からのすべての質問に肯定的に答えた場合はshort-it、次のようなプロジェクト名が表示されます。

  • .gitプロジェクトのディレクトリ。つまり、Git プロバイダーにプッシュする準備ができています。
  • package.jsonファイル
  • tsconfig.jsonすべての TypeScript 構成を含むファイル
  • src/index.tsワーカーから応答を取得するための簡単なロジックを含むファイル

素晴らしい。これが機能するかどうか見てみましょう!

cdディレクトリに入り、Wranglershort-itをローカル開発モードで起動します。

$ cd short-it
$ wrangler dev --local

これにより、ワーカーがhttp://localhost:8787/で実行されます。localhost にアクセスすると、単純な「Hello World!」が表示されるはずです。メッセージ:

ハローワールドメッセージ

生成されたワーカーは「Hello World!」を表示しています。メッセージ。

わーい!私たちはそれを機能させました。しかし、どのように?詳しく見てみましょう。

Cloudflare ワーカーはどのように機能しますか?

生成されたワーカーからローカルで最初のメッセージを取得しましたが、それはどのように機能したのでしょうか?

src/index.ts何が起こっているのかをよりよく理解するために、生成されたファイルを調べてみましょう。

// src/index.ts

/**
 * Welcome to Cloudflare Workers! This is your first worker.
 *
 * - Run `wrangler dev src/index.ts` in your terminal to start a development server
 * - Open a browser tab at http://localhost:8787/ to see your worker in action
 * - Run `wrangler publish src/index.ts --name my-worker` to publish your worker
 *
 * Learn more at https://developers.cloudflare.com/workers/
 */

export interface Env {
  // Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
  // MY_KV_NAMESPACE: KVNamespace;
  //
  // Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/
  // MY_DURABLE_OBJECT: DurableObjectNamespace;
  //
  // Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
  // MY_BUCKET: R2Bucket;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    return new Response("Hello World!");
  },
};

上記のコードには、環境 (Envインターフェース) の定義と、インターフェースに関連するいくつかのコメントが含まれていENVます。

インターフェイスはこの記事の範囲外であるため、コードのその部分は無視して、メイン ロジックのみに焦点を当てます。

// src/index.ts

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    return new Response("Hello World!");
  },
};

ここで何が起こるかというと、関数をindex.tsエクスポートするというfetchことです。これはWeb Workersに似たインターフェースです。実際、「Cloudflare Workers」という名前が由来するのは、このインターフェースからです。Cloudflare Workers は Web Workers と似ていますが、ブラウザーではなく Cloudflare インフラストラクチャで実行される点が異なります。

上記のコードでは、関数は「Hello World!」をfetch含む新しいオブジェクトを返します。Response文章。そのため、Worker を実行すると、このfetch関数が呼び出されます。次に、呼び出されたfetch関数は「Hello World!」を返します。これは、ブラウザーで (またはワーカーの呼び出しに使用される任意のツールを介して) 取得したものです。

OK、Cloudflare Workers の基本を片付けました。自信を持って進むことができます。TypeScript を初めて使用する場合でも、心配する必要はありません。その機能を軽く使用します。これを、TypeScript の世界への軽量のオンボーディングと想像してください。

よし、先に進もう!

最初のリダイレクトの追加

穏やかなスタートでロジックの作業を開始します。まず、URL 短縮サービスでユーザーを別の Web サイトにリダイレクトします。これは、後の変更の基礎となります。

ここでは、ユーザーがローカル ワーカーにアクセスしたときに、 https://http.cat/ Web サイトのページにアクセスするようにします。

https://http.cat/に慣れていない場合は、HTTP ステータスごとにさまざまな猫の写真が表示される楽しいサイトです。たとえば、ユーザーがワーカーにhttp://localhost:8787/404へのリクエストを行うと、リクエストはhttps://http.cat/404に転送されます。

このリダイレクトを実現するには、次のsrc/index.tsようにを編集します。

// src/index.ts
// ...

const basePath = "https://http.cat";

export default {
  async fetch(
    request: Request,
    _env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = basePath + pathname;

    if (pathname === "/") {
      return new Response("Hello World from our awesome Worker!");
    }

    return Response.redirect(redirectURL, 301);
  },
};

ここで、http://localhost:8787にアクセスすると、以下に示すように、「Hello World from our awesome Worker!」という更新されたメッセージが表示されます。

Hello World From Awesome Worker

更新された「Hello world」メッセージを表示するワーカー。

ただし、http://localhost:8787/404にアクセスしようとすると、 https://http.cat/404にリダイレクトされます。

ユーザーがリダイレクトされました

ユーザーは http.cat/404 Web サイトにリダイレクトされます。

よし、最初のリダイレクトを開始しました。では、URL 短縮機能で実際にいくつかの URL を短縮してみましょう。

URL の短縮

ここでは、短縮 URL を保存するための小さなデータ構造を追加します。次のように実行できます。

const shortURLs = {
  "/blog": "https://pragmaticpineapple.com/",
  "/twitter": "https://twitter.com/nikolalsvk",
  "/github": "https://github.com/nikolalsvk",
} as Record<any, string>;

export default {
  async fetch(
    request: Request,
    _env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = shortURLs[pathname];

    if (!redirectURL) {
      return new Response(
        `There is no defined URL for the path: '${pathname}', sorry :(`
      );
    }

    return Response.redirect(redirectURL, 301);
  },
};

ここでは、いくつかの短縮 URL を追加しました。

動作を確認するために、好きなように変更できます。今、http://localhost:8787/blogにアクセスすると、ブログのある長い URL にリダイレクトされます。結果は次のとおりです。

ブログにリダイレクト

/blog にアクセスすると、実際のブログ ページにリダイレクトされます。

しかし、 http://localhost:8787/missingのようなパスを要求すると、次のエラー メッセージが表示されます。

エラーメッセージがありません

/missing にアクセスすると、エラー メッセージが表示されます。

これで、ハードコードされた URL とその短縮バージョンをどこかのストレージに移動する準備が整いました。幸いなことに、私たちは Cloudflare Workers を使用しており、KV と呼ばれるシンプルなキーと値のストレージを提供しています。

ストレージの追加

プロジェクトの KV を実際に作成する前に、Wrangler 経由で Cloudflare Workers にログインする必要があります。KV インスタンスを作成するために、後で Wrangler が Cloudflare に連絡する必要があるため、これが必要です。

Cloudflareへのログイン

Cloudflare にログインするには、次のコマンドを使用します。

$ wrangler login

ブラウザーが開き、Cloudflare へのログインを求められます。心配しないで; 無料プランには、このチュートリアルに必要なものがすべて含まれており、支払いを求められることはありません。先に進んで登録するか、すでにアカウントをお持ちの場合はログインしてください。

次に、Cloudflare は、Wrangler に承認を付与するかどうかを尋ねます。同意すると、次の画面が表示されます。

ラングラー CLI ツール

Wrangler CLI ツールが正しく接続されました。

サインアップ プロセス中に問題が発生することはありません。ただし、どこかで行き詰まった場合は、アカウントの作成に関する Cloudflare のガイドに従うことができます。

素晴らしい!サインアップしてログインしたので、すべてが正しく接続されているかどうかを確認しましょう。

次のコマンドを使用します。

$ wrangler whoami
  wrangler 2.0.21
--------------------
Getting User settings...
 You are logged in with an OAuth Token, associated with the email 'nikolaseap@gmail.com'!
┌──────────────────────┬──────────────────────────────────┐
│ Account Name │ Account ID │
├──────────────────────┼──────────────────────────────────┤
│ Nikola Đuza Personal │ 98a16dfefca0e2ee27e1e79ba590d973 │
└──────────────────────┴──────────────────────────────────┘

これで、KV 名前空間を作成する準備が整いました。

KV 名前空間の作成

KV 名前空間は、Cloudflare ネットワーク上の KV のインスタンスと考えることができます。2 つの KV 名前空間を作成します。1 つはアプリが存在して動作する本番用で、もう 1 つはプレビュー環境用です。URL 短縮サービスをテストおよび開発する間は、プレビューの名前空間を使用します。

次のコマンドを使用して、Wrangler 経由で KV 名前空間を作成します。

$ wrangler kv:namespace create SHORT_URLS
 Creating namespace with title "short-it-SHORT_URLS"
 Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "SHORT_URLS", id = "029d374ebd984e19b0bb98e37ab1a95e" }

$ wrangler kv:namespace create SHORT_URLS --preview
  wrangler 2.0.21
--------------------
 Creating namespace with title "short-it-SHORT_URLS_preview"
 Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "SHORT_URLS", preview_id = "99a72876e5f84cf58de722b1c2080604" }

これら 2 つのコマンドが実行され、両方の名前空間が作成されたら、実行時にこれらの名前空間を使用するように Wrangler に指示する必要がありますwrangler dev

wrangler.tomlプロジェクトのルートにあるファイルに KV 名前空間に関する情報を追加します。次のようになります。

name = "short-it"
main = "src/index.ts"
compatibility_date = "2022-07-15"

kv_namespaces = [
  { binding = "SHORT_URLS", id = "029d374ebd984e19b0bb98e37ab1a95e", preview_id = "99a72876e5f84cf58de722b1c2080604" }
]

このファイルは、プロジェクトに関する特定の情報wrangler.tomlを伝える構成ファイルです。wranglerこれで、KV にデータを追加する準備が整いました。

KV へのデータの追加

次のステップは、データを KV にシードすることです。2 つの名前空間があるため、2 つのコマンドを実行して両方の場所にデータを配置する必要があることを思い出してください。/blogKV にエントリを追加しましょう。

$ wrangler kv:key put --binding SHORT_URLS "/blog" "https://pragmaticpineapple.com/" --preview false
  wrangler 2.0.21
--------------------
Writing the value "https://pragmaticpineapple.com/" to key "/blog" on namespace 029d374ebd984e19b0bb98e37ab1a95e.

$ wrangler kv:key put --binding SHORT_URLS "/blog" "https://pragmaticpineapple.com/" --preview
  wrangler 2.0.21
--------------------
Writing the value "https://pragmaticpineapple.com/" to key "/blog" on namespace 99a72876e5f84cf58de722b1c2080604.

素晴らしい。これで、KV に 1 つのエントリができました。次に、KV から読み取ってユーザーをリダイレクトするロジックを追加しましょう。

KV からの読み取り

古いハードコードされた短い URL をすばやく削除し、次のように KV への呼び出しを追加します。

// src/index.ts
export interface Env {
  SHORT_URLS: KVNamespace;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = await env.SHORT_URLS.get(pathname);

    if (!redirectURL) {
      return new Response(
        `There is no defined URL for the path: '${pathname}', sorry :(`
      );
    }

    return Response.redirect(redirectURL, 301);
  },
};

ここではSHORT_URLS、タイプとして追加しKVNamespaceます。これにより、KV メソッドを呼び出して適切なデータを取得できます。URL でハードコーディングされたオブジェクトの代わりに、今回はawait env.SHORT_URLS.get(pathname).

への呼び出しenv.SHORT_URLS.get(pathname)は、KV からキーを取得しようとします。promise を返す場合は、 する必要がありawaitます。ただし、指定された の値がある場合pathname、ユーザーはその URL にリダイレクトされます。

ここで、http://localhost:8787/blogにアクセスすると、KV に入力した実際のブログ URL にリダイレクトされます。次のようになります。

それでもブログをリダイレクト

/blog にアクセスすると、実際のブログ ページにリダイレクトされます。

しかし、ハードコーディングした他の URL にアクセスしようとすると、それらの URL にリダイレクトがないというメッセージが表示されます。

URL のリダイレクトがありません

/twitter にアクセスすると、URL にリダイレクトがないことを示すメッセージが表示されます。

次のコマンドを使用して、Twitter の短縮 URL を KV にすばやく追加しましょう。

$ wrangler kv:key put --binding SHORT_URLS "/twitter" "https://twitter.com/nikolalsvk" --preview false
 wrangler 2.0.21
--------------------
Writing the value "https://twitter.com/nikolalsvk" to key "/twitter" on namespace 029d374ebd984e19b0bb98e37ab1a95e.

$ wrangler kv:key put --binding SHORT_URLS "/twitter" "https://twitter.com/nikolalsvk" --preview
  wrangler 2.0.21
--------------------
Writing the value "https://twitter.com/nikolalsvk" to key "/twitter" on namespace 99a72876e5f84cf58de722b1c2080604.

ここで、http://localhost:8787/twitterを更新すると、Twitter アカウントにリダイレクトされるはずです。

読み込み中

短縮 URL を KV に追加すると、Twitter が読み込まれます。

/blogこれでとの 2 つの短い URL ができ/twitterました。サービスをデプロイして、本番環境で確認してみましょう。

Cloudflare ワーカーのデプロイ

Cloudflare Workers の展開手順はかなり簡単です。を次wrangler publishのように利用します。

$ wrangler publish
  wrangler 2.0.21
--------------------
Retrieving cached values for userId from node_modules/.cache/wrangler
Your worker has access to the following bindings:
- KV Namespaces:
  - SHORT_URLS: 029d374ebd984e19b0bb98e37ab1a95e
Total Upload: 0.45 KiB / gzip: 0.29 KiB
Worker ID: short-it
Worker ETag: f8395cab29edf297137631b803b14c32daaae982758c23e3019b700e2468c277
Uploaded short-it (2.14 sec)
Published short-it (6.33 sec)
  short-it.nikolalsvk.workers.dev

現在、サービスはhttps://short-it.nikolalsvk.workers.devで公開されています。わーい!

このチュートリアルに従っている場合、サービスは、選択した内容に応じて、URL https://short-it.YOUR_SUBDOMAIN.workers.devYOUR_SUBDOMAINのどこかに存在するはずです。

この時点で、Worker スクリプトは Cloudflare Edge ネットワーク上で世界中に展開されています。これは、世界中の友人や見知らぬ人がhttps://short-it.nikolalsvk.workers.dev/twitterにアクセスすると、非常に高速に Twitter アカウントにリダイレクトされることを意味します。

まとめ

Cloudflare Workers を使用してシンプルな URL 短縮サービスを作成する旅を続けてくれてありがとう。この記事では、Cloudflare コンテキスト内のワーカーの概念を紹介しました。また、Cloudflare の KV ストレージでデータを作成および管理する方法も示しました。

優れた開発者エクスペリエンスを提供する Wrangler を使用して、これらすべてをスムーズに実行することができました。しかし、最も重要なことは、世界の隅々で高速に動作する小さなサービスを作成、テスト、デプロイすることができたことです。

同様の技術やサービスでこれを実現するには、多額の費用と労力が必要になる場合があります。ただし、Cloudflare は、1 日あたり 100,000 リクエストの寛大な無料利用枠をサポートしています。したがって、有料プランに違反する前に、多くの URL を短縮し、それらに多くの訪問をすることができます.

この記事のすべてのコードは、GitHub リポジトリで入手できます(気に入った場合はスターを付けてください)。短縮サービスはhttps://short-it.nikolalsvk.workers.devで公開されています。

投稿が気に入ったら、友人や同僚と共有することを検討してください。

次回まで、乾杯!

ソース: https://blog.logrocket.com/creating-url-shortener-cloudflare-workers/

BitlyTinyURLなどのツールを使用して長いリンクを短縮したことがありますか? または、これらのサービスがどのように機能するのか疑問に思ったことはありませんか? おそらく、URL 短縮サービスを構築したいと思っていましたが、それを行うための時間や適切なツールが見つかりませんでした。いずれにせよ、このトピックに興味がある場合は、この記事が最適です。

この投稿では、Cloudflare Workersを使用して基本的な URL 短縮サービスを構築する方法を紹介します。URL短縮サービスがどのように機能するかについての詳細な情報を提供し、Cloudflare Workersのいくつかの機能を紹介し、Cloudflare Workersを使い始める方法について順を追って説明します.

始めましょう!

Cloudflare ワーカーとは何ですか?

Cloudflare Workers は、サーバーレス コードを Cloudflare ネットワークにデプロイできるサービスです。Cloudflare ネットワークまたは Edge は、世界中に広がる Web サーバーのネットワークです。Cloudflare Workers の素晴らしい点の 1 つは、コードのスケーリングについて心配する必要がないことです。また、コードが存在するタイム ゾーンについて心配する必要はありません。Workers のコードは、デプロイされてから数秒後に世界中に広がります。

その上、Cloudflare ワーカーには、KV と呼ばれる単純なキーと値のデータ ストアが付属しています。このチュートリアルでは、Cloudflare ワーカーと KV ストレージを組み合わせて、URL を短くします。

事業概要:URL短縮サービス

まず、リダイレクト先の Web サイトをハードコーディングする、単純で非動的な URL 短縮サービスを作成します。これは、 Wrangler (Cloudflare の公式 CLI ツール) の使用方法を学習するための入門として機能し、Workers 領域の基本的な概念を示します。

次に、少しスパイスを加えて、動的 URL のサポートを追加します。基本的に、Cloudflare Workers KV ストアと対話し、短いバージョンの URL とリダイレクト先の実際の URL を入力します。KV ストアのデータは、次のような構造になります。

'short-url': 'https://my-cool-website.com'
'submit': 'https://my-cool-site.org/blog/ideas/submit'

最後に、コードを本番環境にデプロイし、世界中で実際に動作することを確認します。

あなたはすでに興奮していますか?よし、飛び込もう!

環境のセットアップ

この記事を進めるには、次のものが必要です。

  • Node.js と npm
  • ラングラー
  • URL 短縮サービスをテストするための curl (または選択したブラウザー)

私はasdf ツールを使用してローカルの依存関係を管理していますが、任意のバージョン マネージャーを使用できます。執筆時点では、これが私の Node と npm のバージョンです。

$ node --version
v18.5.0
$ npm --version
8.12.1

Wrangler はビルド用のコマンドライン ツールで、最近 2.0 バージョンがリリースされました。この投稿では、Wrangler がすべてのニーズを満たします。将来的には、より堅牢で機能豊富な Wrangler の兄弟である Miniflareを使用する可能性があります。しかし、ここでは、npm を介して Wrangler をグローバルにインストールしましょう。

$ npm install -g wrangler@2.0.21

執筆時点で最新の Wrangler バージョンは 2.0.21 であるため、それを使用します。

涼しい。すべての依存関係が整ったので、Wrangler CLI を使用してスターター Cloudflare Worker を生成できます。

プロジェクトの生成

ここでは、Wrangler CLI ツールが非常に役立ちます。

まず、コマンドを実行して、プロジェクトを適切に開始および設定しましょう。

$ wrangler init short-it

このコマンドは、いくつかの質問をします。ここでは、それらすべてに対して( yと入力して) yes と答えます。

$ wrangler init short-it
  wrangler 2.0.21
--------------------
Using npm as package manager.
 Created short-it/wrangler.toml
Would you like to use git to manage this Worker? (y/n)
 Initialized git repository at short-it
No package.json found. Would you like to create one? (y/n)
 Created short-it/package.json
Would you like to use TypeScript? (y/n)
 Created short-it/tsconfig.json
Would you like to create a Worker at short-it/src/index.ts?
  None
❯ Fetch handler
  Scheduled handler
 Created short-it/src/index.ts

added 62 packages, and audited 63 packages in 1s

1 package is looking for funding
  run `npm fund` for details

found 0 vulnerabilities
 Installed @cloudflare/workers-types and typescript into devDependencies

To start developing your Worker, run `cd short-it && npm start`
To publish your Worker to the Internet, run `npm run deploy`

Wrangler からのすべての質問に肯定的に答えた場合はshort-it、次のようなプロジェクト名が表示されます。

  • .gitプロジェクトのディレクトリ。つまり、Git プロバイダーにプッシュする準備ができています。
  • package.jsonファイル
  • tsconfig.jsonすべての TypeScript 構成を含むファイル
  • src/index.tsワーカーから応答を取得するための簡単なロジックを含むファイル

素晴らしい。これが機能するかどうか見てみましょう!

cdディレクトリに入り、Wranglershort-itをローカル開発モードで起動します。

$ cd short-it
$ wrangler dev --local

これにより、ワーカーがhttp://localhost:8787/で実行されます。localhost にアクセスすると、単純な「Hello World!」が表示されるはずです。メッセージ:

ハローワールドメッセージ

生成されたワーカーは「Hello World!」を表示しています。メッセージ。

わーい!私たちはそれを機能させました。しかし、どのように?詳しく見てみましょう。

Cloudflare ワーカーはどのように機能しますか?

生成されたワーカーからローカルで最初のメッセージを取得しましたが、それはどのように機能したのでしょうか?

src/index.ts何が起こっているのかをよりよく理解するために、生成されたファイルを調べてみましょう。

// src/index.ts

/**
 * Welcome to Cloudflare Workers! This is your first worker.
 *
 * - Run `wrangler dev src/index.ts` in your terminal to start a development server
 * - Open a browser tab at http://localhost:8787/ to see your worker in action
 * - Run `wrangler publish src/index.ts --name my-worker` to publish your worker
 *
 * Learn more at https://developers.cloudflare.com/workers/
 */

export interface Env {
  // Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
  // MY_KV_NAMESPACE: KVNamespace;
  //
  // Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/
  // MY_DURABLE_OBJECT: DurableObjectNamespace;
  //
  // Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
  // MY_BUCKET: R2Bucket;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    return new Response("Hello World!");
  },
};

上記のコードには、環境 (Envインターフェース) の定義と、インターフェースに関連するいくつかのコメントが含まれていENVます。

インターフェイスはこの記事の範囲外であるため、コードのその部分は無視して、メイン ロジックのみに焦点を当てます。

// src/index.ts

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    return new Response("Hello World!");
  },
};

ここで何が起こるかというと、関数をindex.tsエクスポートするというfetchことです。これはWeb Workersに似たインターフェースです。実際、「Cloudflare Workers」という名前が由来するのは、このインターフェースからです。Cloudflare Workers は Web Workers と似ていますが、ブラウザーではなく Cloudflare インフラストラクチャで実行される点が異なります。

上記のコードでは、関数は「Hello World!」をfetch含む新しいオブジェクトを返します。Response文章。そのため、Worker を実行すると、このfetch関数が呼び出されます。次に、呼び出されたfetch関数は「Hello World!」を返します。これは、ブラウザーで (またはワーカーの呼び出しに使用される任意のツールを介して) 取得したものです。

OK、Cloudflare Workers の基本を片付けました。自信を持って進むことができます。TypeScript を初めて使用する場合でも、心配する必要はありません。その機能を軽く使用します。これを、TypeScript の世界への軽量のオンボーディングと想像してください。

よし、先に進もう!

最初のリダイレクトの追加

穏やかなスタートでロジックの作業を開始します。まず、URL 短縮サービスでユーザーを別の Web サイトにリダイレクトします。これは、後の変更の基礎となります。

ここでは、ユーザーがローカル ワーカーにアクセスしたときに、 https://http.cat/ Web サイトのページにアクセスするようにします。

https://http.cat/に慣れていない場合は、HTTP ステータスごとにさまざまな猫の写真が表示される楽しいサイトです。たとえば、ユーザーがワーカーにhttp://localhost:8787/404へのリクエストを行うと、リクエストはhttps://http.cat/404に転送されます。

このリダイレクトを実現するには、次のsrc/index.tsようにを編集します。

// src/index.ts
// ...

const basePath = "https://http.cat";

export default {
  async fetch(
    request: Request,
    _env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = basePath + pathname;

    if (pathname === "/") {
      return new Response("Hello World from our awesome Worker!");
    }

    return Response.redirect(redirectURL, 301);
  },
};

ここで、http://localhost:8787にアクセスすると、以下に示すように、「Hello World from our awesome Worker!」という更新されたメッセージが表示されます。

Hello World From Awesome Worker

更新された「Hello world」メッセージを表示するワーカー。

ただし、http://localhost:8787/404にアクセスしようとすると、 https://http.cat/404にリダイレクトされます。

ユーザーがリダイレクトされました

ユーザーは http.cat/404 Web サイトにリダイレクトされます。

よし、最初のリダイレクトを開始しました。では、URL 短縮機能で実際にいくつかの URL を短縮してみましょう。

URL の短縮

ここでは、短縮 URL を保存するための小さなデータ構造を追加します。次のように実行できます。

const shortURLs = {
  "/blog": "https://pragmaticpineapple.com/",
  "/twitter": "https://twitter.com/nikolalsvk",
  "/github": "https://github.com/nikolalsvk",
} as Record<any, string>;

export default {
  async fetch(
    request: Request,
    _env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = shortURLs[pathname];

    if (!redirectURL) {
      return new Response(
        `There is no defined URL for the path: '${pathname}', sorry :(`
      );
    }

    return Response.redirect(redirectURL, 301);
  },
};

ここでは、いくつかの短縮 URL を追加しました。

動作を確認するために、好きなように変更できます。今、http://localhost:8787/blogにアクセスすると、ブログのある長い URL にリダイレクトされます。結果は次のとおりです。

ブログにリダイレクト

/blog にアクセスすると、実際のブログ ページにリダイレクトされます。

しかし、 http://localhost:8787/missingのようなパスを要求すると、次のエラー メッセージが表示されます。

エラーメッセージがありません

/missing にアクセスすると、エラー メッセージが表示されます。

これで、ハードコードされた URL とその短縮バージョンをどこかのストレージに移動する準備が整いました。幸いなことに、私たちは Cloudflare Workers を使用しており、KV と呼ばれるシンプルなキーと値のストレージを提供しています。

ストレージの追加

プロジェクトの KV を実際に作成する前に、Wrangler 経由で Cloudflare Workers にログインする必要があります。KV インスタンスを作成するために、後で Wrangler が Cloudflare に連絡する必要があるため、これが必要です。

Cloudflareへのログイン

Cloudflare にログインするには、次のコマンドを使用します。

$ wrangler login

ブラウザーが開き、Cloudflare へのログインを求められます。心配しないで; 無料プランには、このチュートリアルに必要なものがすべて含まれており、支払いを求められることはありません。先に進んで登録するか、すでにアカウントをお持ちの場合はログインしてください。

次に、Cloudflare は、Wrangler に承認を付与するかどうかを尋ねます。同意すると、次の画面が表示されます。

ラングラー CLI ツール

Wrangler CLI ツールが正しく接続されました。

サインアップ プロセス中に問題が発生することはありません。ただし、どこかで行き詰まった場合は、アカウントの作成に関する Cloudflare のガイドに従うことができます。

素晴らしい!サインアップしてログインしたので、すべてが正しく接続されているかどうかを確認しましょう。

次のコマンドを使用します。

$ wrangler whoami
  wrangler 2.0.21
--------------------
Getting User settings...
 You are logged in with an OAuth Token, associated with the email 'nikolaseap@gmail.com'!
┌──────────────────────┬──────────────────────────────────┐
│ Account Name │ Account ID │
├──────────────────────┼──────────────────────────────────┤
│ Nikola Đuza Personal │ 98a16dfefca0e2ee27e1e79ba590d973 │
└──────────────────────┴──────────────────────────────────┘

これで、KV 名前空間を作成する準備が整いました。

KV 名前空間の作成

KV 名前空間は、Cloudflare ネットワーク上の KV のインスタンスと考えることができます。2 つの KV 名前空間を作成します。1 つはアプリが存在して動作する本番用で、もう 1 つはプレビュー環境用です。URL 短縮サービスをテストおよび開発する間は、プレビューの名前空間を使用します。

次のコマンドを使用して、Wrangler 経由で KV 名前空間を作成します。

$ wrangler kv:namespace create SHORT_URLS
 Creating namespace with title "short-it-SHORT_URLS"
 Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "SHORT_URLS", id = "029d374ebd984e19b0bb98e37ab1a95e" }

$ wrangler kv:namespace create SHORT_URLS --preview
  wrangler 2.0.21
--------------------
 Creating namespace with title "short-it-SHORT_URLS_preview"
 Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "SHORT_URLS", preview_id = "99a72876e5f84cf58de722b1c2080604" }

これら 2 つのコマンドが実行され、両方の名前空間が作成されたら、実行時にこれらの名前空間を使用するように Wrangler に指示する必要がありますwrangler dev

wrangler.tomlプロジェクトのルートにあるファイルに KV 名前空間に関する情報を追加します。次のようになります。

name = "short-it"
main = "src/index.ts"
compatibility_date = "2022-07-15"

kv_namespaces = [
  { binding = "SHORT_URLS", id = "029d374ebd984e19b0bb98e37ab1a95e", preview_id = "99a72876e5f84cf58de722b1c2080604" }
]

このファイルは、プロジェクトに関する特定の情報wrangler.tomlを伝える構成ファイルです。wranglerこれで、KV にデータを追加する準備が整いました。

KV へのデータの追加

次のステップは、データを KV にシードすることです。2 つの名前空間があるため、2 つのコマンドを実行して両方の場所にデータを配置する必要があることを思い出してください。/blogKV にエントリを追加しましょう。

$ wrangler kv:key put --binding SHORT_URLS "/blog" "https://pragmaticpineapple.com/" --preview false
  wrangler 2.0.21
--------------------
Writing the value "https://pragmaticpineapple.com/" to key "/blog" on namespace 029d374ebd984e19b0bb98e37ab1a95e.

$ wrangler kv:key put --binding SHORT_URLS "/blog" "https://pragmaticpineapple.com/" --preview
  wrangler 2.0.21
--------------------
Writing the value "https://pragmaticpineapple.com/" to key "/blog" on namespace 99a72876e5f84cf58de722b1c2080604.

素晴らしい。これで、KV に 1 つのエントリができました。次に、KV から読み取ってユーザーをリダイレクトするロジックを追加しましょう。

KV からの読み取り

古いハードコードされた短い URL をすばやく削除し、次のように KV への呼び出しを追加します。

// src/index.ts
export interface Env {
  SHORT_URLS: KVNamespace;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = await env.SHORT_URLS.get(pathname);

    if (!redirectURL) {
      return new Response(
        `There is no defined URL for the path: '${pathname}', sorry :(`
      );
    }

    return Response.redirect(redirectURL, 301);
  },
};

ここではSHORT_URLS、タイプとして追加しKVNamespaceます。これにより、KV メソッドを呼び出して適切なデータを取得できます。URL でハードコーディングされたオブジェクトの代わりに、今回はawait env.SHORT_URLS.get(pathname).

への呼び出しenv.SHORT_URLS.get(pathname)は、KV からキーを取得しようとします。promise を返す場合は、 する必要がありawaitます。ただし、指定された の値がある場合pathname、ユーザーはその URL にリダイレクトされます。

ここで、http://localhost:8787/blogにアクセスすると、KV に入力した実際のブログ URL にリダイレクトされます。次のようになります。

それでもブログをリダイレクト

/blog にアクセスすると、実際のブログ ページにリダイレクトされます。

しかし、ハードコーディングした他の URL にアクセスしようとすると、それらの URL にリダイレクトがないというメッセージが表示されます。

URL のリダイレクトがありません

/twitter にアクセスすると、URL にリダイレクトがないことを示すメッセージが表示されます。

次のコマンドを使用して、Twitter の短縮 URL を KV にすばやく追加しましょう。

$ wrangler kv:key put --binding SHORT_URLS "/twitter" "https://twitter.com/nikolalsvk" --preview false
 wrangler 2.0.21
--------------------
Writing the value "https://twitter.com/nikolalsvk" to key "/twitter" on namespace 029d374ebd984e19b0bb98e37ab1a95e.

$ wrangler kv:key put --binding SHORT_URLS "/twitter" "https://twitter.com/nikolalsvk" --preview
  wrangler 2.0.21
--------------------
Writing the value "https://twitter.com/nikolalsvk" to key "/twitter" on namespace 99a72876e5f84cf58de722b1c2080604.

ここで、http://localhost:8787/twitterを更新すると、Twitter アカウントにリダイレクトされるはずです。

読み込み中

短縮 URL を KV に追加すると、Twitter が読み込まれます。

/blogこれでとの 2 つの短い URL ができ/twitterました。サービスをデプロイして、本番環境で確認してみましょう。

Cloudflare ワーカーのデプロイ

Cloudflare Workers の展開手順はかなり簡単です。を次wrangler publishのように利用します。

$ wrangler publish
  wrangler 2.0.21
--------------------
Retrieving cached values for userId from node_modules/.cache/wrangler
Your worker has access to the following bindings:
- KV Namespaces:
  - SHORT_URLS: 029d374ebd984e19b0bb98e37ab1a95e
Total Upload: 0.45 KiB / gzip: 0.29 KiB
Worker ID: short-it
Worker ETag: f8395cab29edf297137631b803b14c32daaae982758c23e3019b700e2468c277
Uploaded short-it (2.14 sec)
Published short-it (6.33 sec)
  short-it.nikolalsvk.workers.dev

現在、サービスはhttps://short-it.nikolalsvk.workers.devで公開されています。わーい!

このチュートリアルに従っている場合、サービスは、選択した内容に応じて、URL https://short-it.YOUR_SUBDOMAIN.workers.devYOUR_SUBDOMAINのどこかに存在するはずです。

この時点で、Worker スクリプトは Cloudflare Edge ネットワーク上で世界中に展開されています。これは、世界中の友人や見知らぬ人がhttps://short-it.nikolalsvk.workers.dev/twitterにアクセスすると、非常に高速に Twitter アカウントにリダイレクトされることを意味します。

まとめ

Cloudflare Workers を使用してシンプルな URL 短縮サービスを作成する旅を続けてくれてありがとう。この記事では、Cloudflare コンテキスト内のワーカーの概念を紹介しました。また、Cloudflare の KV ストレージでデータを作成および管理する方法も示しました。

優れた開発者エクスペリエンスを提供する Wrangler を使用して、これらすべてをスムーズに実行することができました。しかし、最も重要なことは、世界の隅々で高速に動作する小さなサービスを作成、テスト、デプロイすることができたことです。

同様の技術やサービスでこれを実現するには、多額の費用と労力が必要になる場合があります。ただし、Cloudflare は、1 日あたり 100,000 リクエストの寛大な無料利用枠をサポートしています。したがって、有料プランに違反する前に、多くの URL を短縮し、それらに多くの訪問をすることができます.

この記事のすべてのコードは、GitHub リポジトリで入手できます(気に入った場合はスターを付けてください)。短縮サービスはhttps://short-it.nikolalsvk.workers.devで公開されています。

投稿が気に入ったら、友人や同僚と共有することを検討してください。

次回まで、乾杯!

ソース: https://blog.logrocket.com/creating-url-shortener-cloudflare-workers/

  #cloudflare #url 

Cloudflare Workers で URL 短縮サービスを作成する
Léon  Peltier

Léon Peltier

1661794740

Créer Un Raccourcisseur D'URL Avec Cloudflare Workers

Avez-vous déjà utilisé des outils comme Bitly ou TinyURL pour raccourcir les liens longs ? Ou vous êtes-vous demandé comment ces services fonctionnent ? Peut-être avez-vous voulu créer un raccourcisseur d'URL mais n'avez jamais trouvé le temps ni les outils appropriés pour le faire. En tout cas, si ce sujet vous intéresse, cet article est parfait pour vous.

Dans cet article, nous montrerons comment créer un service de raccourcissement d'URL de base à l'aide de Cloudflare Workers . Nous fournirons des informations détaillées sur le fonctionnement des services de raccourcissement d'URL, présenterons plusieurs fonctionnalités de Cloudflare Workers et donnerons des instructions étape par étape sur la façon de démarrer avec Cloudflare Workers.

Commençons!

Qu'est-ce que Cloudflare Workers ?

Cloudflare Workers est un service qui vous permet de déployer du code sans serveur sur le réseau Cloudflare. Le réseau Cloudflare, ou Edge, est un réseau de serveurs Web répartis dans le monde entier. L'un des avantages de Cloudflare Workers est que vous n'avez pas à vous soucier de la mise à l'échelle de votre code. De plus, vous n'avez pas à vous soucier des fuseaux horaires dans lesquels vit votre code ; votre code dans Workers est réparti dans le monde entier quelques secondes après son déploiement.

En plus de cela, Cloudflare Workers est livré avec un simple magasin de données clé-valeur, appelé KV. Dans ce didacticiel, nous utiliserons une combinaison de Cloudflare Workers et de stockage KV pour raccourcir notre URL.

Présentation du projet : service de raccourcissement d'URL

Nous allons commencer par créer un raccourcisseur d'URL simple et non dynamique dans lequel vous codez en dur les sites Web vers lesquels vous souhaitez rediriger. Cela servira d'introduction à l'apprentissage de l'utilisation de Wrangler (l'outil CLI officiel de Cloudflare) et démontrera les concepts de base dans le domaine des travailleurs.

Ensuite, nous allons pimenter un peu les choses et ajouter la prise en charge des URL dynamiques. Fondamentalement, nous allons interagir avec le magasin Cloudflare Workers KV et saisir des versions courtes de l'URL et de l'URL réelle vers laquelle nous voulons rediriger. Les données du magasin KV seront similaires à la structure suivante :

'short-url': 'https://my-cool-website.com'
'submit': 'https://my-cool-site.org/blog/ideas/submit'

Enfin, nous déploierons notre code en production et le verrons fonctionner en direct dans le monde entier.

Êtes-vous déjà excité? Super, allons-y !

Mise en place de l'environnement

Pour suivre cet article, vous aurez besoin des éléments suivants :

  • Node.js et npm
  • Cow-boy
  • curl (ou le navigateur de votre choix) pour tester le raccourcisseur d'URL

J'utilise l' outil asdf pour gérer mes dépendances locales, mais vous pouvez utiliser n'importe quel gestionnaire de version que vous préférez. Au moment d'écrire ces lignes, voici ma version de Node et npm :

$ node --version
v18.5.0
$ npm --version
8.12.1

Wrangler est un outil de construction en ligne de commande, et récemment, il a obtenu sa version 2.0. Aux fins de cet article, Wrangler répondra à tous nos besoins. À l'avenir, nous pourrions utiliser Miniflare, un frère plus robuste et riche en fonctionnalités de Wrangler . Mais, pour l'instant, installons Wrangler globalement via npm :

$ npm install -g wrangler@2.0.21

Au moment d'écrire ces lignes, la dernière version de Wrangler est la 2.0.21, nous allons donc choisir celle-là.

Cool. Maintenant que nous avons toutes les dépendances en place, nous pouvons utiliser la CLI Wrangler pour générer notre Cloudflare Worker de démarrage.

Génération du projet

L'outil Wrangler CLI s'avérera très utile ici.

Pour commencer, exécutons une commande pour lancer et configurer correctement notre projet :

$ wrangler init short-it

Cette commande posera quelques questions. Pour l'instant, nous allons répondre oui (en tapant y ) pour chacun d'eux :

$ wrangler init short-it
  wrangler 2.0.21
--------------------
Using npm as package manager.
 Created short-it/wrangler.toml
Would you like to use git to manage this Worker? (y/n)
 Initialized git repository at short-it
No package.json found. Would you like to create one? (y/n)
 Created short-it/package.json
Would you like to use TypeScript? (y/n)
 Created short-it/tsconfig.json
Would you like to create a Worker at short-it/src/index.ts?
  None
❯ Fetch handler
  Scheduled handler
 Created short-it/src/index.ts

added 62 packages, and audited 63 packages in 1s

1 package is looking for funding
  run `npm fund` for details

found 0 vulnerabilities
 Installed @cloudflare/workers-types and typescript into devDependencies

To start developing your Worker, run `cd short-it && npm start`
To publish your Worker to the Internet, run `npm run deploy`

Si vous avez répondu positivement à toutes les questions de Wrangler, alors vous aurez un nom de projet short-it, avec ce qui suit à l'intérieur :

  • .gitrépertoire dans votre projet, ce qui signifie que vous êtes prêt à le pousser vers votre fournisseur Git
  • package.jsondossier
  • tsconfig.jsonfichier avec toute la configuration TypeScript
  • src/index.tsfichier avec une logique simple pour obtenir une réponse de notre travailleur

Impressionnant. Voyons si cette chose fonctionne !

Passons cdau short-itrépertoire et démarrons Wrangler en mode développement local :

$ cd short-it
$ wrangler dev --local

Cela devrait exécuter notre Worker sur http://localhost:8787/ . Si nous visitons localhost, nous devrions voir un simple "Hello World!" message:

Message Bonjour le monde

Generated Worker affiche un "Hello World!" message.

Yay! Nous l'avons fait fonctionner. Mais comment? Regardons de plus près.

Comment fonctionne Cloudflare Workers ?

Nous avons reçu notre premier message localement du Worker généré, mais comment cela a-t-il fonctionné exactement ?

Passons en revue le src/index.tsfichier généré pour mieux comprendre ce qui s'y passe.

// src/index.ts

/**
 * Welcome to Cloudflare Workers! This is your first worker.
 *
 * - Run `wrangler dev src/index.ts` in your terminal to start a development server
 * - Open a browser tab at http://localhost:8787/ to see your worker in action
 * - Run `wrangler publish src/index.ts --name my-worker` to publish your worker
 *
 * Learn more at https://developers.cloudflare.com/workers/
 */

export interface Env {
  // Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
  // MY_KV_NAMESPACE: KVNamespace;
  //
  // Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/
  // MY_DURABLE_OBJECT: DurableObjectNamespace;
  //
  // Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
  // MY_BUCKET: R2Bucket;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    return new Response("Hello World!");
  },
};

Le code ci-dessus comprend une définition de notre environnement (l' Envinterface) et quelques commentaires relatifs à l' ENVinterface.

Étant donné que l'interface n'entre pas dans le cadre de cet article, nous allons ignorer cette partie du code et nous concentrer uniquement sur la logique principale :

// src/index.ts

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    return new Response("Hello World!");
  },
};

Ce qui se passe ici, c'est que notre index.tsexporte une fetchfonction. Il s'agit d'une interface similaire aux Web Workers . En fait, c'est de cette interface que provient le nom "Cloudflare Workers". Cloudflare Workers est similaire à Web Workers, sauf qu'il s'exécute sur l'infrastructure Cloudflare plutôt que sur un navigateur.

Dans le code ci-dessus, la fetchfonction renvoie un nouvel Responseobjet avec le "Hello World!" texte. Ainsi, lorsque nous exécutons notre Worker, cette fetchfonction est invoquée. Ensuite, la fetchfonction invoquée renvoie le "Hello World!" réponse, et c'est ce que nous récupérons dans le navigateur (ou via tout outil utilisé pour invoquer le Worker).

OK, nous avons clarifié les bases de Cloudflare Workers. Nous pouvons avancer en toute confiance. Si vous débutez avec TypeScript, ne vous inquiétez pas ; nous n'utiliserons ses fonctionnalités qu'à la légère. Imaginez cela comme une intégration légère dans le monde de TypeScript.

Super, allons de l'avant !

Ajouter une première redirection

Nous allons commencer à travailler sur notre logique avec un démarrage en douceur. Tout d'abord, notre raccourcisseur d'URL redirigera un utilisateur vers un autre site Web. Ce sera la base des changements ultérieurs.

Pour l'instant, nous demanderons à l'utilisateur d'accéder à une page du site Web https://http.cat/ lorsqu'il visitera notre Worker local.

Si vous n'êtes pas familier avec https://http.cat/ , c'est un site amusant qui affiche diverses images de chat pour différents statuts HTTP. Par exemple, si un utilisateur fait une demande à notre Worker sur http://localhost:8787/404 , il sera dirigé vers https://http.cat/404 .

Pour réaliser cette redirection, nous allons modifier le src/index.ts, comme ceci :

// src/index.ts
// ...

const basePath = "https://http.cat";

export default {
  async fetch(
    request: Request,
    _env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = basePath + pathname;

    if (pathname === "/") {
      return new Response("Hello World from our awesome Worker!");
    }

    return Response.redirect(redirectURL, 301);
  },
};

Maintenant, si nous visitons http://localhost:8787 , nous recevrons un message mis à jour : « Hello World from our awesome Worker ! », comme indiqué ci-dessous :

Bonjour le monde de Awesome Worker

Travailleur affichant un message "Hello world" mis à jour.

Mais si nous essayons d'accéder à http://localhost:8787/404 , nous serons redirigés vers https://http.cat/404 .

Utilisateur redirigé

L'utilisateur est redirigé vers le site Web http.cat/404.

Génial, nous avons lancé notre première redirection. Maintenant, faisons en sorte que notre raccourcisseur d'URL raccourcisse certaines URL.

Raccourcir l'URL

Pour l'instant, nous allons ajouter une petite structure de données pour stocker nos URL raccourcies. Nous pouvons le faire comme ceci :

const shortURLs = {
  "/blog": "https://pragmaticpineapple.com/",
  "/twitter": "https://twitter.com/nikolalsvk",
  "/github": "https://github.com/nikolalsvk",
} as Record<any, string>;

export default {
  async fetch(
    request: Request,
    _env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = shortURLs[pathname];

    if (!redirectURL) {
      return new Response(
        `There is no defined URL for the path: '${pathname}', sorry :(`
      );
    }

    return Response.redirect(redirectURL, 301);
  },
};

Ici, nous avons ajouté quelques URL raccourcies :

Vous pouvez le changer en ce que vous voulez juste pour le voir fonctionner. Maintenant, lorsque je visite http://localhost:8787/blog , je suis redirigé vers une URL plus longue où se trouve mon blog. Voici le résultat :

Redirection vers le blog

La visite de /blog redirige vers la page de blog actuelle.

Mais, si nous demandons un chemin, comme http://localhost:8787/missing , nous obtenons le message d'erreur suivant : « Il n'y a pas d'URL définie pour le chemin : '/missing', désolé :(“.

Message d'erreur manquant

Visiter / manquant affiche un message d'erreur.

Génial, nous sommes maintenant prêts à déplacer nos URL codées en dur et leurs versions raccourcies vers un stockage quelque part. Heureusement, nous utilisons Cloudflare Workers, et il offre un simple stockage clé-valeur appelé KV.

Ajouter du stockage

Avant de créer réellement le KV pour notre projet, nous devons d'abord nous connecter à Cloudflare Workers via Wrangler. Cela est nécessaire car Wrangler devra ultérieurement contacter Cloudflare afin de créer une instance KV pour nous.

Se connecter à Cloudflare

Pour vous connecter à Cloudflare, utilisez la commande suivante :

$ wrangler login

Un navigateur s'ouvrira, vous demandant de vous connecter à Cloudflare. Ne vous inquiétez pas; le plan gratuit couvre tout ce dont nous aurons besoin pour ce didacticiel, et aucun paiement ne vous sera demandé. Allez-y et inscrivez-vous, ou connectez-vous si vous avez déjà un compte.

Ensuite, Cloudflare vous demandera si vous souhaitez accorder une autorisation à Wrangler. Après avoir accepté, vous devriez voir l'écran suivant :

Outil CLI Wrangler

L'outil Wrangler CLI est maintenant correctement connecté.

Il ne devrait pas y avoir de problème pendant le processus d'inscription. Mais, si vous êtes bloqué à un moment donné, vous pouvez suivre le guide de Cloudflare sur la création d'un compte .

Impressionnant! Maintenant que vous êtes inscrit et connecté, vérifions si tout est correctement connecté.

Utilisez la commande suivante :

$ wrangler whoami
  wrangler 2.0.21
--------------------
Getting User settings...
 You are logged in with an OAuth Token, associated with the email 'nikolaseap@gmail.com'!
┌──────────────────────┬──────────────────────────────────┐
│ Account Name │ Account ID │
├──────────────────────┼──────────────────────────────────┤
│ Nikola Đuza Personal │ 98a16dfefca0e2ee27e1e79ba590d973 │
└──────────────────────┴──────────────────────────────────┘

Super, nous sommes prêts à créer un espace de noms KV.

Création d'un espace de noms KV

Un espace de noms KV peut être considéré comme une instance de KV sur le réseau Cloudflare. Nous allons créer deux espaces de noms KV : un pour la production où notre application vivra et fonctionnera et un autre pour l'environnement de prévisualisation. Nous utiliserons l'espace de noms d'aperçu pendant que nous testons et développons notre raccourcisseur d'URL.

Nous allons créer nos espaces de noms KV via Wrangler avec les commandes suivantes :

$ wrangler kv:namespace create SHORT_URLS
 Creating namespace with title "short-it-SHORT_URLS"
 Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "SHORT_URLS", id = "029d374ebd984e19b0bb98e37ab1a95e" }

$ wrangler kv:namespace create SHORT_URLS --preview
  wrangler 2.0.21
--------------------
 Creating namespace with title "short-it-SHORT_URLS_preview"
 Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "SHORT_URLS", preview_id = "99a72876e5f84cf58de722b1c2080604" }

Une fois ces deux commandes exécutées et les deux espaces de noms créés, nous devons dire à Wrangler d'utiliser ces espaces de noms lorsque nous exécutons wrangler dev.

Nous ajouterons des informations sur les espaces de noms KV au wrangler.tomlfichier à la racine de notre projet. Ça devrait ressembler a quelque chose comme ca:

name = "short-it"
main = "src/index.ts"
compatibility_date = "2022-07-15"

kv_namespaces = [
  { binding = "SHORT_URLS", id = "029d374ebd984e19b0bb98e37ab1a95e", preview_id = "99a72876e5f84cf58de722b1c2080604" }
]

Le wrangler.tomlfichier est un fichier de configuration qui donne wranglercertaines informations sur notre projet. Maintenant, nous sommes attachés et prêts à ajouter des données à notre KV.

Ajout de données au KV

Notre prochaine étape consiste à ensemencer les données au KV. N'oubliez pas que nous avons deux espaces de noms, nous devrons donc exécuter deux commandes pour avoir les données aux deux endroits. Ajoutons l' /blogentrée au KV :

$ wrangler kv:key put --binding SHORT_URLS "/blog" "https://pragmaticpineapple.com/" --preview false
  wrangler 2.0.21
--------------------
Writing the value "https://pragmaticpineapple.com/" to key "/blog" on namespace 029d374ebd984e19b0bb98e37ab1a95e.

$ wrangler kv:key put --binding SHORT_URLS "/blog" "https://pragmaticpineapple.com/" --preview
  wrangler 2.0.21
--------------------
Writing the value "https://pragmaticpineapple.com/" to key "/blog" on namespace 99a72876e5f84cf58de722b1c2080604.

Impressionnant. Maintenant, nous avons une entrée dans le KV. Ensuite, ajoutons une logique qui lit à partir du KV et redirige l'utilisateur.

Lecture du KV

Nous allons rapidement supprimer nos anciennes URL courtes codées en dur et ajouter un appel au KV, comme ceci :

// src/index.ts
export interface Env {
  SHORT_URLS: KVNamespace;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = await env.SHORT_URLS.get(pathname);

    if (!redirectURL) {
      return new Response(
        `There is no defined URL for the path: '${pathname}', sorry :(`
      );
    }

    return Response.redirect(redirectURL, 301);
  },
};

Ici, nous ajoutons SHORT_URLScomme KVNamespacetype. Cela nous permettra d'appeler les méthodes KV pour obtenir les données appropriées. Au lieu de l'objet codé en dur avec des URL, cette fois nous utilisons await env.SHORT_URLS.get(pathname).

L'appel à env.SHORT_URLS.get(pathname)essaie d'obtenir la clé du KV. S'il renvoie une promesse, nous devons await. Mais, s'il y a une valeur pour le given pathname, alors l'utilisateur est redirigé vers cette URL.

Maintenant, lorsque nous visitons http://localhost:8787/blog , nous serons redirigés vers l'URL du blog que nous avons mis dans le KV. Il ressemblera à ceci:

Redirige toujours le blog

Visiter /blog nous redirige toujours vers la page de blog actuelle.

Mais, si nous essayons maintenant de visiter l'une des autres URL que nous avons codées en dur, nous recevrons un message indiquant qu'il manque une redirection à ces URL :

URL de redirection manquante

La visite de /twitter entraîne un message indiquant qu'il manque une redirection à l'URL.

Ajoutons rapidement l'URL raccourcie de Twitter au KV à l'aide de ces commandes :

$ wrangler kv:key put --binding SHORT_URLS "/twitter" "https://twitter.com/nikolalsvk" --preview false
 wrangler 2.0.21
--------------------
Writing the value "https://twitter.com/nikolalsvk" to key "/twitter" on namespace 029d374ebd984e19b0bb98e37ab1a95e.

$ wrangler kv:key put --binding SHORT_URLS "/twitter" "https://twitter.com/nikolalsvk" --preview
  wrangler 2.0.21
--------------------
Writing the value "https://twitter.com/nikolalsvk" to key "/twitter" on namespace 99a72876e5f84cf58de722b1c2080604.

Maintenant, lorsque nous actualisons le http://localhost:8787/twitter , nous devrions être redirigés vers le compte Twitter.

Chargement Twitter

Twitter se charge après avoir ajouté l'URL raccourcie au KV.

Génial, nous avons maintenant deux URL courtes : /bloget /twitter. Essayons de déployer notre service et de le voir en production.

Déploiement des nœuds de calcul Cloudflare

L'étape de déploiement de Cloudflare Workers est assez simple. Nous utiliserons wrangler publish, comme ceci :

$ wrangler publish
  wrangler 2.0.21
--------------------
Retrieving cached values for userId from node_modules/.cache/wrangler
Your worker has access to the following bindings:
- KV Namespaces:
  - SHORT_URLS: 029d374ebd984e19b0bb98e37ab1a95e
Total Upload: 0.45 KiB / gzip: 0.29 KiB
Worker ID: short-it
Worker ETag: f8395cab29edf297137631b803b14c32daaae982758c23e3019b700e2468c277
Uploaded short-it (2.14 sec)
Published short-it (6.33 sec)
  short-it.nikolalsvk.workers.dev

Désormais, les services sont en ligne sur https://short-it.nikolalsvk.workers.dev . Yay!

Si vous suivez ce didacticiel, vos services devraient se trouver quelque part le long de l'URL https://short-it.YOUR_SUBDOMAIN.workers.dev , selon ce que vous avez sélectionné pour YOUR_SUBDOMAIN.

À ce stade, notre script Worker est déployé dans le monde entier sur le réseau Cloudflare Edge. Cela signifie que des amis et des étrangers à travers le monde peuvent être redirigés très rapidement vers notre compte Twitter s'ils visitent https://short-it.nikolalsvk.workers.dev/twitter .

Emballer

Merci d'avoir suivi le parcours de création d'un service de raccourcissement d'URL simple à l'aide de Cloudflare Workers. Dans cet article, nous avons présenté les concepts de Worker dans le contexte Cloudflare. Nous avons également montré comment créer et gérer des données dans le stockage KV de Cloudflare.

Nous avons pu exécuter tout cela en douceur à l'aide de Wrangler, qui offre une excellente expérience de développement. Mais, plus important encore, nous avons réussi à créer, tester et déployer notre petit service qui fonctionne rapidement aux quatre coins du monde.

Atteindre cet objectif dans une technologie ou un service similaire peut nécessiter beaucoup d'argent et d'efforts. Cependant, Cloudflare prend en charge un généreux niveau gratuit de 100 000 requêtes par jour. Ainsi, vous pouvez raccourcir de nombreuses URL et effectuer de nombreuses visites avant de passer à un forfait payant.

Tout le code de cet article est disponible dans le référentiel GitHub (veuillez le mettre en vedette, si vous l'aimez). Le service de raccourcissement est en direct sur https://short-it.nikolalsvk.workers.dev .

Si vous avez aimé le message, pensez à le partager avec vos amis et collègues.

Jusqu'à la prochaine fois, bravo!

Source : https://blog.logrocket.com/creating-url-shortener-cloudflare-workers/

  #cloudflare #url 

Créer Un Raccourcisseur D'URL Avec Cloudflare Workers
Saul  Alaniz

Saul Alaniz

1661794260

Crea Un Acortador De URL Con Cloudflare Workers

¿Alguna vez has usado herramientas como Bitly o TinyURL para acortar enlaces largos? ¿O te has preguntado cómo funcionan estos servicios? Tal vez quería crear un acortador de URL pero nunca encontró el tiempo o las herramientas adecuadas para hacerlo. En cualquier caso, si te interesa este tema, este artículo es perfecto para ti.

En esta publicación, demostraremos cómo crear un servicio básico de acortador de URL utilizando Cloudflare Workers . Brindaremos información detallada sobre cómo funcionan los servicios de reducción de URL, presentaremos varias características de Cloudflare Workers y daremos instrucciones paso a paso sobre cómo comenzar con Cloudflare Workers.

¡Empecemos!

¿Qué son los trabajadores de Cloudflare?

Cloudflare Workers es un servicio que le permite implementar código sin servidor en la red de Cloudflare. La red Cloudflare, o Edge, es una red de servidores web repartidos por todo el mundo. Una gran ventaja de Cloudflare Workers es que no tiene que preocuparse por escalar su código. Además, no tiene que preocuparse por las zonas horarias en las que vive su código; su código en Workers se distribuye por todo el mundo segundos después de su implementación.

Además de eso, Cloudflare Workers viene con un almacén de datos clave-valor simple, llamado KV. En este tutorial, usaremos una combinación de Cloudflare Workers y almacenamiento KV para acortar nuestra URL.

Descripción general del proyecto: servicio de acortador de URL

Comenzaremos creando un acortador de URL simple y no dinámico en el que codificará los sitios web a los que desea redirigir. Esto servirá como una introducción para aprender a usar Wrangler (la herramienta CLI oficial de Cloudflare) y demostrará conceptos básicos en el ámbito de los trabajadores.

A continuación, animaremos un poco las cosas y agregaremos soporte para URL dinámicas. Básicamente, interactuaremos con la tienda Cloudflare Workers KV e ingresaremos versiones cortas de la URL y la URL real a la que queremos redirigir. Los datos en la tienda KV serán similares a la siguiente estructura:

'short-url': 'https://my-cool-website.com'
'submit': 'https://my-cool-site.org/blog/ideas/submit'

Finalmente, implementaremos nuestro código en producción y lo veremos funcionar en vivo en todo el mundo.

¿Ya estás emocionado? ¡Genial, vamos a saltar!

Configuración del entorno

Para seguir este artículo, necesitará lo siguiente:

  • Node.js y npm
  • Vaquero
  • curl (o el navegador que elijas) para probar el acortador de URL

Uso la herramienta asdf para administrar mis dependencias locales, pero puede usar el administrador de versiones que prefiera. Al momento de escribir, aquí está mi versión de Node y npm:

$ node --version
v18.5.0
$ npm --version
8.12.1

Wrangler es una herramienta de línea de comandos para construir, y recientemente obtuvo su versión 2.0. A los efectos de esta publicación, Wrangler satisfará todas nuestras necesidades. En el futuro, podríamos usar Miniflare, un hermano más robusto y rico en funciones de Wrangler . Pero, por ahora, instalemos Wrangler globalmente a través de npm:

$ npm install -g wrangler@2.0.21

En el momento de escribir este artículo, la última versión de Wrangler es la 2.0.21, así que nos quedaremos con esa.

Enfriar. Ahora que tenemos todas las dependencias en su lugar, podemos usar la CLI de Wrangler para generar nuestro Cloudflare Worker de inicio.

Generando el proyecto

La herramienta Wrangler CLI resultará muy útil aquí.

Para comenzar, ejecutemos un comando para iniciar y configurar nuestro proyecto correctamente:

$ wrangler init short-it

Este comando le hará un par de preguntas. Por ahora, vamos a responder que sí (escribiendo y ) para todos ellos:

$ wrangler init short-it
  wrangler 2.0.21
--------------------
Using npm as package manager.
 Created short-it/wrangler.toml
Would you like to use git to manage this Worker? (y/n)
 Initialized git repository at short-it
No package.json found. Would you like to create one? (y/n)
 Created short-it/package.json
Would you like to use TypeScript? (y/n)
 Created short-it/tsconfig.json
Would you like to create a Worker at short-it/src/index.ts?
  None
❯ Fetch handler
  Scheduled handler
 Created short-it/src/index.ts

added 62 packages, and audited 63 packages in 1s

1 package is looking for funding
  run `npm fund` for details

found 0 vulnerabilities
 Installed @cloudflare/workers-types and typescript into devDependencies

To start developing your Worker, run `cd short-it && npm start`
To publish your Worker to the Internet, run `npm run deploy`

Si respondiste afirmativamente a todas las preguntas de Wrangler, entonces tendrás un nombre de proyecto short-it, con lo siguiente adentro:

  • .gitdirectorio en su proyecto, lo que significa que está listo para enviarlo a su proveedor de Git
  • package.jsonexpediente
  • tsconfig.jsonarchivo con toda la configuración de TypeScript
  • src/index.tsarchivo con una lógica sencilla para obtener una respuesta de nuestro trabajador

Impresionante. ¡Veamos si esto funciona!

Entremos cden el short-itdirectorio e iniciemos Wrangler en modo de desarrollo local:

$ cd short-it
$ wrangler dev --local

Esto debería ejecutar nuestro Worker en http://localhost:8787/ . Si visitamos localhost, deberíamos ver un simple "¡Hola mundo!" mensaje:

Mensaje de hola mundo

El trabajador generado muestra un "¡Hola mundo!" mensaje.

¡Hurra! Lo hicimos funcionar. ¿Pero cómo? Miremos más de cerca.

¿Cómo funciona Cloudflare Workers?

Recibimos nuestro primer mensaje localmente del Worker generado, pero ¿cómo funcionó exactamente?

Repasemos el src/index.tsarchivo generado para obtener una mejor comprensión de lo que está sucediendo allí.

// src/index.ts

/**
 * Welcome to Cloudflare Workers! This is your first worker.
 *
 * - Run `wrangler dev src/index.ts` in your terminal to start a development server
 * - Open a browser tab at http://localhost:8787/ to see your worker in action
 * - Run `wrangler publish src/index.ts --name my-worker` to publish your worker
 *
 * Learn more at https://developers.cloudflare.com/workers/
 */

export interface Env {
  // Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
  // MY_KV_NAMESPACE: KVNamespace;
  //
  // Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/
  // MY_DURABLE_OBJECT: DurableObjectNamespace;
  //
  // Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
  // MY_BUCKET: R2Bucket;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    return new Response("Hello World!");
  },
};

El código anterior incluye una definición para nuestro entorno (la Envinterfaz) y un par de comentarios relacionados con la ENVinterfaz.

Dado que la interfaz está fuera del alcance de este artículo, ignoraremos esa parte del código y nos centraremos únicamente en la lógica principal:

// src/index.ts

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    return new Response("Hello World!");
  },
};

Lo que sucede aquí es que nuestras index.tsexportaciones fetchfuncionan. Esta es una interfaz similar a Web Workers . De hecho, es a partir de esta interfaz que se origina el nombre “Cloudflare Workers”. Cloudflare Workers es similar a Web Workers, excepto que se ejecuta en la infraestructura de Cloudflare en lugar de en un navegador.

En el código anterior, la fetchfunción devuelve un nuevo Responseobjeto con "¡Hola mundo!" texto. Entonces, cuando ejecutamos nuestro Worker, fetchse invoca esta función. Luego, la fetchfunción invocada devuelve el mensaje "¡Hola mundo!" respuesta, y esto es lo que recogemos en el navegador (o a través de cualquier herramienta utilizada para invocar al Trabajador).

Bien, hemos aclarado los conceptos básicos de Cloudflare Workers. Podemos seguir adelante con confianza. Si es nuevo en TypeScript, no se preocupe; usaremos sus características solo a la ligera. Imagínese esto como una incorporación ligera al mundo de TypeScript.

¡Genial, sigamos adelante!

Agregar una primera redirección

Comenzaremos a trabajar en nuestra lógica con un comienzo suave. Primero, nuestro acortador de URL redirigirá a un usuario a un sitio web diferente. Esta será la base para cambios posteriores.

Por ahora, haremos que el usuario vaya a una página en el sitio web https://http.cat/ cuando visite nuestro Worker local.

Si no está familiarizado con https://http.cat/ , es un sitio divertido que muestra varias imágenes de gatos para diferentes estados de HTTP. Por ejemplo, si un usuario realiza una solicitud a nuestro Worker a http://localhost:8787/404 , será dirigido a https://http.cat/404 .

Para lograr esta redirección, editaremos el src/index.ts, así:

// src/index.ts
// ...

const basePath = "https://http.cat";

export default {
  async fetch(
    request: Request,
    _env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = basePath + pathname;

    if (pathname === "/") {
      return new Response("Hello World from our awesome Worker!");
    }

    return Response.redirect(redirectURL, 301);
  },
};

Ahora, si visitamos http://localhost:8787 , obtendremos un mensaje actualizado: "¡Hola, mundo, de parte de nuestro increíble trabajador!", como se muestra a continuación:

Hola mundo de Awesome Worker

Trabajador que muestra un mensaje actualizado de "Hola mundo".

Pero, si intentamos ir a http://localhost:8787/404 , seremos redirigidos a https://http.cat/404 .

Usuario redirigido

Se redirige al usuario a la web http.cat/404.

Genial, tenemos nuestro primer redireccionamiento en marcha. Ahora, hagamos que nuestro acortador de URL realmente acorte algunas URL.

Acortar la URL

Por ahora, agregaremos una pequeña estructura de datos para almacenar nuestras URL abreviadas. Podemos hacerlo así:

const shortURLs = {
  "/blog": "https://pragmaticpineapple.com/",
  "/twitter": "https://twitter.com/nikolalsvk",
  "/github": "https://github.com/nikolalsvk",
} as Record<any, string>;

export default {
  async fetch(
    request: Request,
    _env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = shortURLs[pathname];

    if (!redirectURL) {
      return new Response(
        `There is no defined URL for the path: '${pathname}', sorry :(`
      );
    }

    return Response.redirect(redirectURL, 301);
  },
};

Aquí, agregamos un par de URL abreviadas:

Puedes cambiarlo a lo que quieras solo para verlo funcionar. Ahora, cuando visito http://localhost:8787/blog , se me redirige a una URL más larga donde se encuentra mi blog. Aquí está el resultado:

Redirecciones al blog

Visitar /blog redirige a la página real del blog.

Pero, si solicitamos alguna ruta, como http://localhost:8787/missing , obtenemos el siguiente mensaje de error: “No hay una URL definida para la ruta: '/missing', lo siento :(“.

Falta el mensaje de error

Visiting/missing muestra un mensaje de error.

Impresionante, ahora estamos listos para mover nuestras URL codificadas y sus versiones abreviadas a un lugar de almacenamiento. Afortunadamente, usamos Cloudflare Workers y ofrece un almacenamiento de clave-valor simple llamado KV.

Agregar almacenamiento

Antes de crear el KV para nuestro proyecto, primero debemos iniciar sesión en Cloudflare Workers a través de Wrangler. Esto es necesario porque Wrangler luego deberá comunicarse con Cloudflare para crear una instancia de KV para nosotros.

Iniciar sesión en Cloudflare

Para iniciar sesión en Cloudflare, use el siguiente comando:

$ wrangler login

Se abrirá un navegador que le pedirá que inicie sesión en Cloudflare. No te preocupes; el plan gratuito cubre todo lo que necesitaremos para este tutorial y no se le pedirá que pague. Continúe y regístrese, o inicie sesión si ya tiene una cuenta.

A continuación, Cloudflare le preguntará si desea otorgar autorización a Wrangler. Después de aceptar, debería ver la siguiente pantalla:

Herramienta CLI de Wrangler

La herramienta Wrangler CLI ahora está correctamente conectada.

No debería haber contratiempos durante el proceso de registro. Pero, si se quedó atascado en algún momento, puede seguir la guía de Cloudflare para crear una cuenta .

¡Impresionante! Ahora que está registrado e iniciado sesión, verifiquemos si todo está conectado correctamente.

Usa el siguiente comando:

$ wrangler whoami
  wrangler 2.0.21
--------------------
Getting User settings...
 You are logged in with an OAuth Token, associated with the email 'nikolaseap@gmail.com'!
┌──────────────────────┬──────────────────────────────────┐
│ Account Name │ Account ID │
├──────────────────────┼──────────────────────────────────┤
│ Nikola Đuza Personal │ 98a16dfefca0e2ee27e1e79ba590d973 │
└──────────────────────┴──────────────────────────────────┘

Genial, estamos listos para crear un espacio de nombres KV.

Creación de un espacio de nombres KV

Se puede pensar en un espacio de nombres KV como una instancia de KV en la red de Cloudflare. Crearemos dos espacios de nombres KV: uno para producción donde vivirá y funcionará nuestra aplicación y otro para el entorno de vista previa. Usaremos el espacio de nombres de vista previa mientras probamos y desarrollamos nuestro acortador de URL.

Crearemos nuestros espacios de nombres KV a través de Wrangler con los siguientes comandos:

$ wrangler kv:namespace create SHORT_URLS
 Creating namespace with title "short-it-SHORT_URLS"
 Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "SHORT_URLS", id = "029d374ebd984e19b0bb98e37ab1a95e" }

$ wrangler kv:namespace create SHORT_URLS --preview
  wrangler 2.0.21
--------------------
 Creating namespace with title "short-it-SHORT_URLS_preview"
 Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "SHORT_URLS", preview_id = "99a72876e5f84cf58de722b1c2080604" }

Después de que se ejecuten estos dos comandos y se creen ambos espacios de nombres, debemos decirle a Wrangler que use estos espacios de nombres cuando ejecutemos wrangler dev.

Agregaremos información sobre los espacios de nombres KV al wrangler.tomlarchivo en la raíz de nuestro proyecto. Debería verse algo como esto:

name = "short-it"
main = "src/index.ts"
compatibility_date = "2022-07-15"

kv_namespaces = [
  { binding = "SHORT_URLS", id = "029d374ebd984e19b0bb98e37ab1a95e", preview_id = "99a72876e5f84cf58de722b1c2080604" }
]

El wrangler.tomlarchivo es un archivo de configuración que dice wranglercierta información sobre nuestro proyecto. Ahora, estamos atados y listos para agregar algunos datos a nuestro KV.

Adición de datos al KV

Nuestro siguiente paso es sembrar los datos en el KV. Recuerde, tenemos dos espacios de nombres, por lo que tendremos que ejecutar dos comandos para tener los datos en ambos lugares. Agreguemos la /blogentrada al KV:

$ wrangler kv:key put --binding SHORT_URLS "/blog" "https://pragmaticpineapple.com/" --preview false
  wrangler 2.0.21
--------------------
Writing the value "https://pragmaticpineapple.com/" to key "/blog" on namespace 029d374ebd984e19b0bb98e37ab1a95e.

$ wrangler kv:key put --binding SHORT_URLS "/blog" "https://pragmaticpineapple.com/" --preview
  wrangler 2.0.21
--------------------
Writing the value "https://pragmaticpineapple.com/" to key "/blog" on namespace 99a72876e5f84cf58de722b1c2080604.

Impresionante. Ahora tenemos una entrada en el KV. A continuación, agreguemos lógica que lea desde el KV y redireccione al usuario.

Lectura del KV

Eliminaremos rápidamente nuestras antiguas URL cortas codificadas y agregaremos una llamada al KV, así:

// src/index.ts
export interface Env {
  SHORT_URLS: KVNamespace;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    _ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    const { pathname } = url;

    const redirectURL = await env.SHORT_URLS.get(pathname);

    if (!redirectURL) {
      return new Response(
        `There is no defined URL for the path: '${pathname}', sorry :(`
      );
    }

    return Response.redirect(redirectURL, 301);
  },
};

Aquí, agregamos SHORT_URLScomo un KVNamespacetipo. Esto nos permitirá llamar a los métodos KV para obtener los datos adecuados. En lugar del objeto codificado con URL, esta vez usamos await env.SHORT_URLS.get(pathname).

La llamada a env.SHORT_URLS.get(pathname)intenta obtener la clave del KV. Si devuelve una promesa, debemos await. Pero, si hay un valor para el dado pathname, entonces el usuario es redirigido a esa URL.

Ahora, cuando visitemos http://localhost:8787/blog , seremos redirigidos a la URL real del blog que pusimos en el KV. Se verá así:

Aún redirige el blog

Visitar /blog aún nos redirige a la página real del blog.

Pero, si ahora intentamos visitar cualquiera de las otras URL que codificamos, recibiremos un mensaje que dice que a esas URL les falta una redirección:

Redirección de URL faltante

Visitar /twitter da como resultado un mensaje que indica que a la URL le falta una redirección.

Agreguemos rápidamente la URL abreviada de Twitter al KV usando estos comandos:

$ wrangler kv:key put --binding SHORT_URLS "/twitter" "https://twitter.com/nikolalsvk" --preview false
 wrangler 2.0.21
--------------------
Writing the value "https://twitter.com/nikolalsvk" to key "/twitter" on namespace 029d374ebd984e19b0bb98e37ab1a95e.

$ wrangler kv:key put --binding SHORT_URLS "/twitter" "https://twitter.com/nikolalsvk" --preview
  wrangler 2.0.21
--------------------
Writing the value "https://twitter.com/nikolalsvk" to key "/twitter" on namespace 99a72876e5f84cf58de722b1c2080604.

Ahora, cuando actualicemos http://localhost:8787/twitter , deberíamos ser redirigidos a la cuenta de Twitter.

Cargando Twitter

Twitter se carga después de que agregamos la URL abreviada al KV.

Impresionante, ahora tenemos dos URL cortas: /blogy /twitter. Intentemos implementar nuestro servicio y verlo en producción.

Implementación de trabajadores de Cloudflare

El paso de implementación de Cloudflare Workers es bastante sencillo. Utilizaremos wrangler publish, así:

$ wrangler publish
  wrangler 2.0.21
--------------------
Retrieving cached values for userId from node_modules/.cache/wrangler
Your worker has access to the following bindings:
- KV Namespaces:
  - SHORT_URLS: 029d374ebd984e19b0bb98e37ab1a95e
Total Upload: 0.45 KiB / gzip: 0.29 KiB
Worker ID: short-it
Worker ETag: f8395cab29edf297137631b803b14c32daaae982758c23e3019b700e2468c277
Uploaded short-it (2.14 sec)
Published short-it (6.33 sec)
  short-it.nikolalsvk.workers.dev

Ahora, los servicios están activos en https://short-it.nikolalsvk.workers.dev . ¡Hurra!

Si está siguiendo este tutorial, sus servicios deberían vivir en algún lugar a lo largo de la URL https://short-it.YOUR_SUBDOMAIN.workers.dev , según lo que haya seleccionado YOUR_SUBDOMAIN.

En este punto, nuestro script Worker se implementa en todo el mundo en la red Cloudflare Edge. Esto significa que amigos y extraños de todo el mundo pueden ser redirigidos increíblemente rápido a nuestra cuenta de Twitter si visitan https://short-it.nikolalsvk.workers.dev/twitter .

Terminando

Gracias por seguir el viaje de crear un servicio simple de acortador de URL con Cloudflare Workers. En este artículo, presentamos los conceptos de Worker dentro del contexto de Cloudflare. También demostramos cómo crear y administrar datos en el almacenamiento KV de Cloudflare.

Pudimos ejecutar todo esto sin problemas con Wrangler, que brinda una excelente experiencia de desarrollador. Pero, lo más importante, logramos crear, probar e implementar nuestro pequeño servicio que funciona rápido en todos los rincones del mundo.

Lograr esto en una tecnología o servicio similar puede requerir mucho dinero y esfuerzo. Sin embargo, Cloudflare admite un generoso nivel gratuito de 100 000 solicitudes por día. Por lo tanto, puede acortar muchas URL y tener muchas visitas antes de infringir un plan pago.

Todo el código de este artículo está disponible en el repositorio de GitHub (marcalo con una estrella, si te gusta). El servicio de acortador está disponible en https://short-it.nikolalsvk.workers.dev .

Si te gustó la publicación, considera compartirla con tus amigos y compañeros de trabajo.

Hasta la próxima, ¡salud!

Fuente: https://blog.logrocket.com/creating-url-shortener-cloudflare-workers/

   #cloudflare #url 

Crea Un Acortador De URL Con Cloudflare Workers

Create A URL Shortener with Cloudflare Workers

Have you ever used tools like Bitly or TinyURL to shorten long links? Or, have you wondered how these services work? Maybe you wanted to build a URL shortener but never found the time or the proper tools to do so. In any case, if you are interested in this topic, this article is perfect for you.

In this post, we’ll demonstrate how to build a basic URL shortener service using Cloudflare Workers. We’ll provide detailed information about how URL shortener services work, introduce several features of Cloudflare Workers, and give step-by-step instructions on how to get started with Cloudflare Workers.

Let’s get started!

Source: https://blog.logrocket.com/creating-url-shortener-cloudflare-workers/

#cloudflare #url 

Create A URL Shortener with Cloudflare Workers

GoShort: A URL Shortener Written in Go, BoltDB Is Used for In Memory

goShort

GoShort is a URL shortener written in Golang and BoltDB for persistent key/value storage and for routing it's using high performent HTTPRouter

REST Endpoints

Code

# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so

# Folders
_obj
_test

# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out

*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*

_testmain.go

*.exe
*.test
*.prof
.idea/
src/golang.org/
temp.go
shortCode.go
boltdb/
shortURL.db

Download Details:

Author: pankajkhairnar/
Source Code: https://github.com/pankajkhairnar/goShort 
License: Apache-2.0 license

#go #golang #url 

GoShort: A URL Shortener Written in Go, BoltDB Is Used for In Memory

Hugo NexT Theme: Easily & Powerful Theme for Hugo Engine

Hugo NexT

«Hugo NexT» is a high quality elegant Hugo theme. Migrate it from Hexo engine themes which name is NexT, keep all kind page layout designs and 4 different shcemes, start new journey with :heart: and hope you would like it.

All page designs & configuration compatibility with Hexo NexT, it's very easy migrate your site from Hexo to Hugo and enjoy yourself.

4 Scheme

  • :heart_decoration: Muse
  • :six_pointed_star: Mist
  • :pisces: Pisces
  • :gemini: Gemini (default)

Live Preview

Environment:gemini: (Default):heart_decoration::six_pointed_star::pisces:Status
PreliminaryGeminiMuseMistPiscesVercel
ProductionGeminiMuseMistPiscesVercel
  • Preliminary: all new feature developments will be previewed as soon as possible.
  • Production: Only the features that are tested stably and meet the requirements will be officially released.

Quick Start

Before use Hugo NexT theme, please make sure installed Git & Hugo Extened software in your PC, then use submodule to lead into the theme, see below commands:

$ hugo new site hugo-next-exmaple
$ cd hugo-next-exmaple
$ git init
$ git submodule add https://github.com/hugo-next/hugo-theme-next.git themes/hugo-theme-next
$ cp -r themes/hugo-theme-next/exampleSite/* .
$ rm -rf config.toml
$ hugo server

Done that input adrress http://127.0.0.1:1313/ on browser will see the effect & good luck for you! :tada::tada::tada:

Execute command as below in your site directory when the theme upgrade.

$ cd hugo-next-exmaple
$ git submodule update --remote

New Post

There had ready a new post template for you, It is recommended to use the following Hugo command to quickly create a new post:

$ hugo new posts/hello-world.md

All front matter parameters's description in post as below:

---
title: "{{ replace .Name "-" " " | title }}"
description: "{{ .Name }}"
keywords: "{{replace .Name "-" ","}}"

date: {{ .Date }}
lastmod: {{ .Date }}

categories:
 -
tags:
  -
  -

# Post's origin author name
#author:
# Post's origin link URL
#link:
# Image source link that will use in open graph and twitter card
#imgs:
# Expand content on the home page
#expand: true
# It's means that will redirecting to external links
#extlink:
# Switch to enabled or disabled comment plugins in this post
#comment:
# enable: false
# Enable table of content
#toc: false
# Absolute link for visit
#url: "{{ lower .Name }}.html"
# Sticky post set-top in home page and the smaller nubmer will more forward.
#weight: 1
---

Feedback

Join us

All features development progress and new request in Hugo NexT V4 Roadmap, welcome to join us and compelete it together.

Download details:

Author: hugo-next
Source code: https://github.com/hugo-next/hugo-theme-next
License: MIT license

#nextjs #react #reactjs #javascript #hugo

Hugo NexT Theme: Easily & Powerful Theme for Hugo Engine
Nat  Grady

Nat Grady

1660716780

Remotes: install R Packages From GitHub, GitLab, Bitbucket, Git

remotes

Install R Packages from remote or local repositories, including GitHub, GitLab, Bitbucket, and Bioconductor

Download and install R packages stored in GitHub, GitLab, Bitbucket, Bioconductor, or plain subversion or git repositories. This package is a lightweight replacement of the install_* functions in devtools. Indeed most of the code was copied over from devtools.

Features

  • Installers:
    • Install packages with their dependencies.
    • Install from GitHub, GitLab, Bitbucket.
    • Install from git and subversion repositories.
    • Install from local files or URLs.
    • Install the dependencies of a local package tree.
    • Install specific package versions from CRAN.
  • Supports Bioconductor packages.
  • Supports the Remotes field in DESCRIPTION. See more in the dependencies vignette.
  • Supports the Additional_repositories field in DESCRIPTION.
  • Can install itself from GitHub (see below).
  • Does not depend on other R packages.
  • Does not contain compiled code, so no compiler is needed.
  • Does not need any external software (for most of the functionality at least).

Installation

Install the released version of remotes from CRAN:

install.packages("remotes")

Usage

Note that most of the examples here use GitHub. See below for other supported repository types.

To install the latest version of a package in the default branch from GitHub, you can use the user/repo form. Note that user can also be an organization:

remotes::install_github("r-lib/conflicted")

If the R package is inside a subdirectory of the root directory, then give this subdirectory as well:

# build = FALSE because of some specificities of XGBoost package
install_github("dmlc/xgboost/R-package", build = FALSE)

To install a certain branch or commit or tag, append it to the repo name, after an @:

remotes::install_github("gaborcsardi/pkgconfig@v2.0.0")

To install the latest release, append @*release to the repo name:

remotes::install_github("gaborcsardi/pkgconfig@*release")

To install a pull request, append # and the id (an integer number) of the pull request to the repo name:

remotes::install_github("r-lib/pkgconfig#7")

Dependencies

Dependencies are automatically installed from CRAN. By default, outdated dependencies are automatically upgraded. In interactive sessions you can select a subset of the dependencies to upgrade.

Dependencies on GitHub

It is also possible to install dependencies from GitHub or other supported repositories. For this you need to add a Remotes field to the DESCRIPTION file. Its format is:

Remotes: [remote::]repo_spec, [remote::]repo_spec, ...

where repo_spec is any repository specification the corresponding install_() function can handle. If remote:: is missing, github:: is assumed. Other possible values: gitlab::,bitbucket::, git::, local::, svn::, url::, version::, cran::, bioc::.

See more about the Remotes field in this vignette.

Additional repositories

remotes supports the Additional_repositories field in DESCRIPTION. This is a way to specify dependencies from non-CRAN package repositories. See the Writing R extensions manual for details.

Bioconductor packages

Bioconductor packages are automatically detected and their dependencies are installed from Bioconductor.

Currently supported remote types

  • GitHub repositories via install_github.
  • Bitbucket repositories via install_bitbucket.
  • Generic git repositories via install_git. They need either a system git installation, or the git2r R package.
  • Local directories or package archive files via install_local.
  • Remote package archive files via install_url.
  • Packages in subversion repositories via install_svn. They need a system subversion installation.
  • Specific package versions from CRAN or other CRAN-like repositories via install_version. This includes outdated and archived packages as well.
  • All dependencies of a package in a local directory via install_deps.

Download methods

  • For R older than 3.2, the curl package is required as remotes falls back to curl::curl_download in that case
  • For R newer than 3.3, default download.file() method is used. (method = "auto")
  • For in between versions,
    • method = "wininet" is used on windows OS
    • method = "libcurl" is used on other OS, if available.

See help("download.file") for informations on these methods and for setting proxies if needed.

Standalone mode

remotes will use the curl, git2r and pkgbuild packages if they are installed to provide faster implementations for some aspects of the install process. However if you are using remotes to install or update these packages (or their reverse dependencies) using them during installation may fail (particularly on Windows).

If you set the environment variable R_REMOTES_STANDALONE="true" (e.g. in R Sys.setenv(R_REMOTES_STANDALONE="true")) you can force remotes to operate in standalone mode and use only its internal R implementations. This will allow successful installation of these packages.

Options

remotes uses the following standard R options, see ?options for their details:

download.file.method for the default download method. See ?download.file.

pkgType for the package type (source or binary, see manual) to install, download or look up dependencies for.

repos for the locations of the user's standard CRAN(-like) repositories.

It also uses some remotes specific options:

BioC_git for the URL of the default Bioconductor git mirror.

BioC_mirror for the URL of the Bioconductor mirror.

unzip for the path of the external unzip program.

Environment variables

The BITBUCKET_USER and BITBUCKET_PASSWORD environment variables are used for the default Bitbucket user name and password, in install_bitbucket()

The GITHUB_PAT environment variable is used as the default GitHub personal access token for all GitHub API queries.

The R_BIOC_MIRROR environment variable can be used to specify an alternative Bioconductor mirror. (The BioC_mirror option takes precedence over this.)

The R_BIOC_VERSION environment variable can be used to force a Bioconductor version.

The R_REMOTES_UPGRADE environment variable can be used to set a default preferred value for the upgrade = argument accepted by the various install_*() functions. For example, you can set R_REMOTES_UPGRADE="always" to upgrade dependent packages without asking the user.

Setting R_REMOTES_STANDALONE="true" forces remotes to work in standalone mode and avoid loading its optional dependencies (curl, git2 and pkgbuild currently. See "Standalone mode" above.

Setting R_REMOTES_NO_ERRORS_FROM_WARNINGS="false" will cause warning messages during calls to install.packages() to become errors. Often warning messages are caused by dependencies failing to install.

Download Details:

Author: r-lib
Source Code: https://github.com/r-lib/remotes 
License: Unknown, MIT licenses found

#r #git #github #url 

Remotes: install R Packages From GitHub, GitLab, Bitbucket, Git
Rocio  O'Keefe

Rocio O'Keefe

1660685040

Video_thumbnail: A Plugin Generates Thumbnail From Video File Or URL

video_thumbnail

This plugin generates thumbnail from video file or URL. It returns image in memory or writes into a file. It offers rich options to control the image format, resolution and quality. Supports iOS and Android. 

video-file video-url

Methods

functionparameterdescriptionreturn
thumbnailDataString [video], optional Map<String, dynamic> [headers], ImageFormat [imageFormat](JPEG/PNG/WEBP), int [maxHeight](0: for the original resolution of the video, or scaled by the source aspect ratio), [maxWidth](0: for the original resolution of the video, or scaled by the source aspect ratio), int [timeMs]generates the thumbnail from the frame around the specified millisecond, int[quality]`(0-100)generates thumbnail from [video][Future<Uint8List>]
thumbnailFileString [video], optional Map<String, dynamic> [headers], String [thumbnailPath](folder or full path where to store the thumbnail file, null to save to same folder as the video file), ImageFormat [imageFormat](JPEG/PNG/WEBP), int [maxHeight](0: for the original resolution of the video, or scaled by the source aspect ratio), int [maxWidth](0: for the original resolution of the video, or scaled by the source aspect ratio), int [timeMs] generates the thumbnail from the frame around the specified millisecond, int [quality](0-100)creates a file of the thumbnail from the [video][Future<String>]

Warning:

Giving both the maxHeight and maxWidth has different result on Android platform, it actually scales the thumbnail to the specified maxHeight and maxWidth. To generate the thumbnail from a network resource, the video must be properly URL encoded.

Usage

Installing add video_thumbnail as a dependency in your pubspec.yaml file.

dependencies:
  video_thumbnail: ^0.5.2

import

import 'package:video_thumbnail/video_thumbnail.dart';

Generate a thumbnail in memory from video file

final uint8list = await VideoThumbnail.thumbnailData(
  video: videofile.path,
  imageFormat: ImageFormat.JPEG,
  maxWidth: 128, // specify the width of the thumbnail, let the height auto-scaled to keep the source aspect ratio
  quality: 25,
);

Generate a thumbnail file from video URL

final fileName = await VideoThumbnail.thumbnailFile(
  video: "https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4",
  thumbnailPath: (await getTemporaryDirectory()).path,
  imageFormat: ImageFormat.WEBP,
  maxHeight: 64, // specify the height of the thumbnail, let the width auto-scaled to keep the source aspect ratio
  quality: 75,
);

Generate a thumbnail file from video Assets declared in pubspec.yaml

final byteData = await rootBundle.load("assets/my_video.mp4");
Directory tempDir = await getTemporaryDirectory();

File tempVideo = File("${tempDir.path}/assets/my_video.mp4")
  ..createSync(recursive: true)
  ..writeAsBytesSync(byteData.buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes));

final fileName = await VideoThumbnail.thumbnailFile(
  video: tempVideo.path,
  thumbnailPath: (await getTemporaryDirectory()).path,
  imageFormat: ImageFormat.PNG,  
  quality: 100,
);

Notes

Fork or pull requests are always welcome. Currently it seems have a little performance issue while generating WebP thumbnail by using libwebp under iOS.

Installing

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add video_thumbnail

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  video_thumbnail: ^0.5.2

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:video_thumbnail/video_thumbnail.dart';

example/lib/main.dart

import 'dart:async';
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'dart:io';

import 'package:video_thumbnail/video_thumbnail.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: DemoHome(),
    );
  }
}

class ThumbnailRequest {
  final String video;
  final String thumbnailPath;
  final ImageFormat imageFormat;
  final int maxHeight;
  final int maxWidth;
  final int timeMs;
  final int quality;

  const ThumbnailRequest(
      {this.video,
      this.thumbnailPath,
      this.imageFormat,
      this.maxHeight,
      this.maxWidth,
      this.timeMs,
      this.quality});
}

class ThumbnailResult {
  final Image image;
  final int dataSize;
  final int height;
  final int width;
  const ThumbnailResult({this.image, this.dataSize, this.height, this.width});
}

Future<ThumbnailResult> genThumbnail(ThumbnailRequest r) async {
  //WidgetsFlutterBinding.ensureInitialized();
  Uint8List bytes;
  final Completer<ThumbnailResult> completer = Completer();
  if (r.thumbnailPath != null) {
    final thumbnailPath = await VideoThumbnail.thumbnailFile(
        video: r.video,
        headers: {
          "USERHEADER1": "user defined header1",
          "USERHEADER2": "user defined header2",
        },
        thumbnailPath: r.thumbnailPath,
        imageFormat: r.imageFormat,
        maxHeight: r.maxHeight,
        maxWidth: r.maxWidth,
        timeMs: r.timeMs,
        quality: r.quality);

    print("thumbnail file is located: $thumbnailPath");

    final file = File(thumbnailPath);
    bytes = file.readAsBytesSync();
  } else {
    bytes = await VideoThumbnail.thumbnailData(
        video: r.video,
        headers: {
          "USERHEADER1": "user defined header1",
          "USERHEADER2": "user defined header2",
        },
        imageFormat: r.imageFormat,
        maxHeight: r.maxHeight,
        maxWidth: r.maxWidth,
        timeMs: r.timeMs,
        quality: r.quality);
  }

  int _imageDataSize = bytes.length;
  print("image size: $_imageDataSize");

  final _image = Image.memory(bytes);
  _image.image
      .resolve(ImageConfiguration())
      .addListener(ImageStreamListener((ImageInfo info, bool _) {
    completer.complete(ThumbnailResult(
      image: _image,
      dataSize: _imageDataSize,
      height: info.image.height,
      width: info.image.width,
    ));
  }));
  return completer.future;
}

class GenThumbnailImage extends StatefulWidget {
  final ThumbnailRequest thumbnailRequest;

  const GenThumbnailImage({Key key, this.thumbnailRequest}) : super(key: key);

  @override
  _GenThumbnailImageState createState() => _GenThumbnailImageState();
}

class _GenThumbnailImageState extends State<GenThumbnailImage> {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<ThumbnailResult>(
      future: genThumbnail(widget.thumbnailRequest),
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        if (snapshot.hasData) {
          final _image = snapshot.data.image;
          final _width = snapshot.data.width;
          final _height = snapshot.data.height;
          final _dataSize = snapshot.data.dataSize;
          return Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Center(
                child: Text(
                    "Image ${widget.thumbnailRequest.thumbnailPath == null ? 'data size' : 'file size'}: $_dataSize, width:$_width, height:$_height"),
              ),
              Container(
                color: Colors.grey,
                height: 1.0,
              ),
              _image,
            ],
          );
        } else if (snapshot.hasError) {
          return Container(
            padding: EdgeInsets.all(8.0),
            color: Colors.red,
            child: Text(
              "Error:\n${snapshot.error.toString()}",
            ),
          );
        } else {
          return Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                Text(
                    "Generating the thumbnail for: ${widget.thumbnailRequest.video}..."),
                SizedBox(
                  height: 10.0,
                ),
                CircularProgressIndicator(),
              ]);
        }
      },
    );
  }
}

class ImageInFile extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

class DemoHome extends StatefulWidget {
  @override
  _DemoHomeState createState() => _DemoHomeState();
}

class _DemoHomeState extends State<DemoHome> {
  final _editNode = FocusNode();
  final _video = TextEditingController(
      text:
          "https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4");
  ImageFormat _format = ImageFormat.JPEG;
  int _quality = 50;
  int _sizeH = 0;
  int _sizeW = 0;
  int _timeMs = 0;

  GenThumbnailImage _futreImage;

  String _tempDir;

  @override
  void initState() {
    super.initState();
    getTemporaryDirectory().then((d) => _tempDir = d.path);
  }

  @override
  Widget build(BuildContext context) {
    final _settings = <Widget>[
      Slider(
        value: _sizeH * 1.0,
        onChanged: (v) => setState(() {
          _editNode.unfocus();
          _sizeH = v.toInt();
        }),
        max: 256.0,
        divisions: 256,
        label: "$_sizeH",
      ),
      Center(
        child: (_sizeH == 0)
            ? const Text(
                "Original of the video's height or scaled by the source aspect ratio")
            : Text("Max height: $_sizeH(px)"),
      ),
      Slider(
        value: _sizeW * 1.0,
        onChanged: (v) => setState(() {
          _editNode.unfocus();
          _sizeW = v.toInt();
        }),
        max: 256.0,
        divisions: 256,
        label: "$_sizeW",
      ),
      Center(
        child: (_sizeW == 0)
            ? const Text(
                "Original of the video's width or scaled by source aspect ratio")
            : Text("Max width: $_sizeW(px)"),
      ),
      Slider(
        value: _timeMs * 1.0,
        onChanged: (v) => setState(() {
          _editNode.unfocus();
          _timeMs = v.toInt();
        }),
        max: 10.0 * 1000,
        divisions: 1000,
        label: "$_timeMs",
      ),
      Center(
        child: (_timeMs == 0)
            ? const Text("The beginning of the video")
            : Text("The closest frame at $_timeMs(ms) of the video"),
      ),
      Slider(
        value: _quality * 1.0,
        onChanged: (v) => setState(() {
          _editNode.unfocus();
          _quality = v.toInt();
        }),
        max: 100.0,
        divisions: 100,
        label: "$_quality",
      ),
      Center(child: Text("Quality: $_quality")),
      Padding(
        padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 8.0),
        child: InputDecorator(
          decoration: InputDecoration(
            border: OutlineInputBorder(),
            filled: true,
            isDense: true,
            labelText: "Thumbnail Format",
          ),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            mainAxisSize: MainAxisSize.max,
            children: <Widget>[
              Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    Radio<ImageFormat>(
                      groupValue: _format,
                      value: ImageFormat.JPEG,
                      onChanged: (v) => setState(() {
                        _format = v;
                        _editNode.unfocus();
                      }),
                    ),
                    const Text("JPEG"),
                  ]),
              Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    Radio<ImageFormat>(
                      groupValue: _format,
                      value: ImageFormat.PNG,
                      onChanged: (v) => setState(() {
                        _format = v;
                        _editNode.unfocus();
                      }),
                    ),
                    const Text("PNG"),
                  ]),
              Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    Radio<ImageFormat>(
                      groupValue: _format,
                      value: ImageFormat.WEBP,
                      onChanged: (v) => setState(() {
                        _format = v;
                        _editNode.unfocus();
                      }),
                    ),
                    const Text("WebP"),
                  ]),
            ],
          ),
        ),
      )
    ];
    return Scaffold(
        appBar: AppBar(
          title: const Text('Thumbnail Plugin example'),
        ),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 8.0),
              child: TextField(
                decoration: InputDecoration(
                  border: OutlineInputBorder(),
                  filled: true,
                  isDense: true,
                  labelText: "Video URI",
                ),
                maxLines: null,
                controller: _video,
                focusNode: _editNode,
                keyboardType: TextInputType.url,
                textInputAction: TextInputAction.done,
                onEditingComplete: () {
                  _editNode.unfocus();
                },
              ),
            ),
            for (var i in _settings) i,
            Expanded(
              child: Container(
                color: Colors.grey[300],
                child: Scrollbar(
                  child: ListView(
                    shrinkWrap: true,
                    children: <Widget>[
                      (_futreImage != null) ? _futreImage : SizedBox(),
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
        drawer: Drawer(
          child: Column(
            children: <Widget>[
              AppBar(
                title: const Text("Settings"),
                actions: <Widget>[
                  IconButton(
                    icon: Icon(Icons.close),
                    onPressed: () => Navigator.pop(context),
                  )
                ],
              ),
              for (var i in _settings) i,
            ],
          ),
        ),
        floatingActionButton: Row(
          mainAxisAlignment: MainAxisAlignment.end,
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            FloatingActionButton(
              onPressed: () async {
                File video =
                    await ImagePicker.pickVideo(source: ImageSource.camera);
                setState(() {
                  _video.text = video.path;
                });
              },
              child: Icon(Icons.videocam),
              tooltip: "Capture a video",
            ),
            const SizedBox(
              width: 5.0,
            ),
            FloatingActionButton(
              onPressed: () async {
                File video =
                    await ImagePicker.pickVideo(source: ImageSource.gallery);
                setState(() {
                  _video.text = video?.path;
                });
              },
              child: Icon(Icons.local_movies),
              tooltip: "Pick a video",
            ),
            const SizedBox(
              width: 20.0,
            ),
            FloatingActionButton(
              tooltip: "Generate a data of thumbnail",
              onPressed: () async {
                setState(() {
                  _futreImage = GenThumbnailImage(
                      thumbnailRequest: ThumbnailRequest(
                          video: _video.text,
                          thumbnailPath: null,
                          imageFormat: _format,
                          maxHeight: _sizeH,
                          maxWidth: _sizeW,
                          timeMs: _timeMs,
                          quality: _quality));
                });
              },
              child: const Text("Data"),
            ),
            const SizedBox(
              width: 5.0,
            ),
            FloatingActionButton(
              tooltip: "Generate a file of thumbnail",
              onPressed: () async {
                setState(() {
                  _futreImage = GenThumbnailImage(
                      thumbnailRequest: ThumbnailRequest(
                          video: _video.text,
                          thumbnailPath: _tempDir,
                          imageFormat: _format,
                          maxHeight: _sizeH,
                          maxWidth: _sizeW,
                          timeMs: _timeMs,
                          quality: _quality));
                });
              },
              child: const Text("File"),
            ),
          ],
        ));
  }
}

Download Details:

Author: justsoft
Source Code: https://github.com/justsoft/video_thumbnail 
License: MIT license

#flutter #dart #video #url 

Video_thumbnail: A Plugin Generates Thumbnail From Video File Or URL
Nat  Grady

Nat Grady

1658879220

A Command Line tool To Generate PDF From URL, HTML Or Markdown File

electron-pdf   

A command line tool to generate PDF from URL, HTML or Markdown files with electron.

Versioning

Starting with version 4.0.x the master branch will always have the latest electron version.

Semantic Versioning is used, and corresponds to electron versions in the following way:

  • electron-pdf 15.0.x (master) => electron=15.1.1, node=16.5.0, chrome=94.0.4606.61
  • electron-pdf 10.0.x => electron=10.1.3, node=12.16.3, chrome=85.0.4183.121
  • electron-pdf 7.0.x => electron 7.x (Chromium 78, Node 12.8.1)
  • electron-pdf 4.0.x => electron 4.x (Chromium 69, Node 10.11.0)
  • electron-pdf 1.3.x => electron 1.6.x (Chromium 56, Node 7.4)
  • electron-pdf 1.2.x => electron 1.4.x (Chromium 53, Node 6.5)

Note: The Chromium versions employed by electron have impacts based on the functionality you may be exporting.
Choose the version you need based on Chromium.

Install

npm install electron-pdf

Note: If you're installing electron-pdf as root using the system level npm (vs a user-level install like with NVM) then you may need to run the following command instead:

sudo npm install electron-pdf -g --unsafe-perm

Please see the npm docs for more information.

For gnu/linux installations without a graphical environment:

$ sudo apt-get install xvfb # or equivalent
$ export DISPLAY=':99.0'
$ Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
$ electron-pdf ...

There is also an example docker machine here.

Node Usage

Electron PDF can be used inside of an application, or more commonly as the engine for a pdf rendering service. For instance, to handle http requests using Express. The following snippets show you how you can get started.

The application must run in an Electron process

In package.json

"start": "DEBUG=electronpdf:* electron index.js",
"watch": "DEBUG=electronpdf:* nodemon --exec electron index.js"

You can use the same instance

var ElectronPDF = require('electron-pdf')
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
app.use(bodyParser.json())

var exporter = new ElectronPDF()
exporter.on('charged', () => {
    //Only start the express server once the exporter is ready
    app.listen(port, hostname, function() {
        console.log(`Export Server running at http://${hostname}:${port}`);
    })
})
exporter.start()

And handle multiple export job instances

app.post('/pdfexport', function(req,res){
    // derive job arguments from request here
    // 
    const jobOptions = {
      /**
        r.results[] will contain the following based on inMemory
          false: the fully qualified path to a PDF file on disk
          true: The Buffer Object as returned by Electron
        
        Note: the default is false, this can not be set using the CLI
       */
      inMemory: false 
    }
    const options = {
          pageSize : "A4"
    }
    exporter.createJob(source, target, options, jobOptions).then( job => {
    job.on('job-complete', (r) => {
            console.log('pdf files:', r.results)
            // Process the PDF file(s) here
        })
        job.render()
    })    
})

Using an in memory Buffer

If you set the inMemory setting to true, you must also set closeWindow=false or you will get a segmentation fault anytime the window is closed before the buffer is sent on the response. You then need to invoke job.destroy to close the window.

Sample Code:

const jobOptions = { inMemory: true, closeWindow: false }
exporter.createJob(source, target, options, jobOptions).then( job => {
    job.on('job-complete', (r) => {
      //Send the Buffer here
      process.nextTick(() => {job.destroy()})
    })
})

Events

The API is designed to emit noteworthy events rather than use callbacks. Full documentation of all events is a work in progress.

Environment Variables

  • ELECTRONPDF_RENDERER_MAX_MEMORY : The --max-old-space-size option for each Electron renderer process (browser window); default: 75% of total system memory up to 8GB
  • ELECTRONPDF_WINDOW_CLEANUP_INTERVAL : Interval for which to check for hung windows, in milliseconds; default: 30 seconds
  • ELECTRONPDF_WINDOW_LIFE_THRESHOLD : How long a window can remain open before it is terminated, in milliseconds; default: 5 minutes
  • ELECTRONPDF_PNG_CAPTURE_DELAY : Amount of millis to wait before invoking WebContents.capturePage for PNG exports; default: 100ms

Command Line Usage

For Ad-hoc conversions, Electron PDF comes with support for a CLI.

To generate a PDF from a HTML file

$ electron-pdf index.html ~/Desktop/index.pdf

To generate a PDF from a Markdown file

$ electron-pdf index.md ~/Desktop/index.pdf

To generate a PDF from a Markdown file with custom CSS (defaults to Github markdown style)

$ electron-pdf index.html ~/Desktop/index.pdf -c my-awesome-css.css

To generate a PDF from a URL

$ electron-pdf https://fraserxu.me ~/Desktop/fraserxu.pdf

Rendering Options

Electron PDF gives you complete control of how the BrowserWindow should be configured, and when the window contents should be captured.

To specify browser options

The BrowserWindow supports many options which you may define by passing a JSON Object to the --browserConfig option.

Some common use cases may include:

  • height and width - electron-pdf calculates the browser height and width based off of the dimensions of PDF page size multiplied by the HTML standard of 96 pixels/inch. So only set these values if you need to override this behavior
  • show - to display the browser window during generation
$ electron-pdf https://fraserxu.me ~/Desktop/fraserxu.pdf --browserConfig '{"show":true}'

To generate a PDF after the an async task in the HTML

electron-pdf ./index.html ~/Desktop/README.pdf -e

In your application, at the point which the view is ready for rendering

document.body.dispatchEvent(new Event('view-ready'))

Warning: It is possible that your application will be ready and emit the event before the main electron process has had a chance execute the javascript in the renderer process which listens for this event.

If you are finding that the event is not effective and your page waits until the full timeout has occurred, then you should use setInterval to emit the event until it is acknowledged like so:

  var eventEmitInterval = setInterval(function () {
    document.body.dispatchEvent(new Event('view-ready'))
  }, 25)

  document.body.addEventListener('view-ready-acknowledged', function(){
    clearInterval(eventEmitInterval)
  })

When the main process first receives your ready event it will emit a single acknowlegement on document.body with whatever event name you are using suffixed with -acknowledged. So the default would be view-ready-acknowledged

Observing your own event

If the page you are rending is under your control, and you wish to modify the behavior of the rendering process you can use a CustomEvent and an observer that will be triggered after the view is ready but before it is captured.

your-page.html

document.body.dispatchEvent(new CustomEvent('view-ready', { detail: {layout: landscape} }))

your-exporter.js

You are required to provide a function that accepts the detail object from the CustomEvent and returns a Promise. You may optionally fulfill the promise with and object that will amend/override any of the contextual attributes assigned to resource (url) currently being exported.

As an example, suppose you wanted to change the orientation of the PDF, and capture the output as PNG instead of a PDF.

job.observeReadyEvent( (detail) => {
    return new Promise( (resolve,reject) => {
      const context = {}
      if( detail && detail.landscape ){
        job.changeArgValue('landscape', true)
        context.type = 'png'
      }
      resolve(context)
    })
})

Note: Future versions of the library will only allow you to provide context overrides, and not allow you to change job level attributes.

All Available Options

Electron PDF exposes the printToPDF settings (i.e. pageSize, orientation, margins, etc.) available from the Electron API. See the following options for usage.


  A command line tool to generate PDF from URL, HTML or Markdown files

  Options
    --help                     Show this help
    --version                  Current version of package
    
    -i | --input               String - The path to the HTML file or url
    -o | --output              String - The path of the output PDF
    
    -b | --printBackground     Boolean - Whether to print CSS backgrounds.
    
    --acceptLanguage           String - A valid value for the 'Accept-Language' http request header
    
    --browserConfig            String - A valid JSON String that will be parsed into the options passed to electron.BrowserWindow
    
    -c | --css                 String - The path to custom CSS (can be specified more than once)
    
    -d | --disableCache        Boolean - Disable HTTP caching
                                 false - default
    
    -e | --waitForJSEvent      String - The name of the event to wait before PDF creation
                                 'view-ready' - default
    
    -l | --landscape           Boolean - true for landscape, false for portrait (don't pass a string on the CLI, just the `-l` flag)
                                 false - default
    
    -m | --marginsType         Integer - Specify the type of margins to use
                                 0 - default margins
                                 1 - no margins (electron-pdf default setting)
                                 2 - minimum margins
    
    --noprint                  Boolean - Do not run printToPDF, useful if the page downloads a file that needs captured instead of a PDF.  
                                         The Electron `win.webContents.session.on('will-download')` event will be implemented 
                                         and the file saved to the location provided in `--output`.
                                         Currently only supports a single import url.
                                         The page is responsible for initiating the download itself.
    
    -p | --pageSize            String - Can be A3, A4, A5, Legal, Letter, Tabloid or an Object containing height and width in microns
                                 "A4" - default
    
    -r | --requestHeaders      String - A valid JSON String that will be parsed into an Object where each key/value pair is: <headerName>: <headerValue>
                                 Example: '{"Authorization": "Bearer token", "X-Custom-Header": "Hello World"}'  
    
    -s | --printSelectionOnly  Boolean - Whether to print selection only
                                 false - default
                                 
    -t | --trustRemoteContent  Boolean - Whether to trust remote content loaded in the Electron webview.  False by default.
    --type                     String - The type of export, will dictate the output file type.  'png': PNG image, anything else: PDF File
    
    -w | --outputWait          Integer – Time to wait (in MS) between page load and PDF creation.  
                                         If used in conjunction with -e this will override the default timeout of 10 seconds    
    --ignoreCertificateErrors  Boolean - If true, all certificate errors thrown by Electron will be ignored.  This can be used to accept self-signed and untrusted certificates.  You should be aware of the security implications of setting this flag.
                             false - default

Find more information on Electron Security here.

Debugging

Sentry

If you have a Sentry account and setup a new app to get a new DSN, you can set a SENTRY_DSN environment variable which will activate sentry logs. See lib/sentry.js for implementation details.

This will allow you to easily see/monitor errors that are occuring inside of the Chromium renderer (browser window). It also automatically integrates with Electron's Crash Reporter

CLI Usage

You can see some additional logging (if you're getting errors or unexpected output) by setting DEBUG=electron* For example: DEBUG=electron* electron-pdf <input> <output> -l

  Usage
    $ electron-pdf <input> <output>
    $ electron-pdf <input> <output> -l

  Examples
    $ electron-pdf http://fraserxu.me ~/Desktop/fraserxu.pdf
    $ electron-pdf ./index.html ~/Desktop/index.pdf
    $ electron-pdf ./README.md ~/Desktop/README.pdf -l
    $ electron-pdf ./README.md ~/Desktop/README.pdf -l -c my-awesome-css.css

Inspired by electron-mocha

Other Formats

Want to use the same options, but export to PNG or snapshot the rendered HTML? Just set the output filename to end in .png or .html instead!

  Examples
    $ electron-pdf http://fraserxu.me ~/Desktop/fraserxu.pdf
    $ electron-pdf http://fraserxu.me ~/Desktop/fraserxu.html
    $ electron-pdf http://fraserxu.me ~/Desktop/fraserxu.png

Extensions

If you need powerpoint support, pdf-powerpoint picks up where Electron PDF leaves off by converting each page in the PDF to a PNG and placing them on individual slides.

Author: Fraserxu
Source Code: https://github.com/fraserxu/electron-pdf 
License: MIT license

#electron #pdf #url #html #markdown 

A Command Line tool To Generate PDF From URL, HTML Or Markdown File