0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

短縮URLサービスを作成した話

Posted at

前置き

大学生になり、金銭的余裕もできたので、レンタルサーバーを借りました!
レンタルサーバーがあるとやれることがめっちゃ増えますよね~
ということで、1~2年前くらいに oto.im というドメインをノリで取ったときから短縮URLサービス作りたいな~と思っていたので、満を持して作成しました!

今回は、大まかな設計と仕様、短縮URLサービスを始める・運営するにあたって大切なことについてまとめていきます。

仕様


バックエンド


いつもはExpressを使っていたのですが、たまには別の技術を、と思いHonoに触れてみました!
昔、Cloudflare Actionsで自分用の短縮リンクサービスを建てたときにも使用したことがあるのである程度は戦えるはず…

完成したバックエンド

svg-captcha を利用した文字列Captchaと nonstress Captcha を利用したトークン認証方式を採用し、BOTなどによる不正利用を防ぐ仕様にしたほか、自分用のAPIキーを登録できるようにして外部サービスにも組み込めるようにしてみました!
より正確にBOTを防ぐように一定回数以上Captchaに失敗したIPアドレスをブロックする機能も搭載されています。
短縮リンクはランダムにIDが決定されるシステムと、自分でカスタムIDを登録するシステムを用意しました。
短縮リンクはデフォルトでbackend/lib/database.dburlsテーブルに保存されます。
同じリンクを短縮しようとするとデータベースを圧迫するので、そうならないように重複判定も導入しました。

backend/database.js
import "dotenv/config";
import Database from "better-sqlite3";
import path from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const dbPath = path.resolve(__dirname, process.env.DATABASE_PATH || "./lib/database.db");

const db = new Database(dbPath);

db.exec(`
  CREATE TABLE IF NOT EXISTS urls (
    id TEXT PRIMARY KEY,
    original_url TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  );

  CREATE TABLE IF NOT EXISTS users (
    id TEXT PRIMARY KEY,
    score INTEGER NOT NULL DEFAULT 0,
    count INTEGER NOT NULL DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
  );
`);

console.log(`Database connected at: ${dbPath}`);

export default db;

フロントエンド


フロントエンドはシンプルに済ませたかったのでhtml/cssで作成しました。
サービス名や説明を.envから埋め込むようにするため、vitevite-plugin-htmlを使用しています。

frontend/vite.config.js
import { defineConfig, loadEnv } from "vite";
import { createHtmlPlugin } from "vite-plugin-html";

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), "");

  return {
    root: process.cwd(),
    plugins: [
      createHtmlPlugin({
        minify: true,
        inject: {
          data: {
            serviceName: env?.VITE_SERVICE_NAME || "URL Shortener",
            serviceDescription: env.VITE_SERVICE_DESCRIPTION || "A simple link shortener."
          },
        },
      }),
    ],
    build: {
      outDir: "build",
      emptyOutDir: true,
    },
  };
});

Nginxの設定


ドメインやポートは隠していますが以下のようになっています。
www.example.comexample.com にリダイレクトさせるような設定となっているはずです。

server {
    listen 80;
    listen [::]:80;
    server_name %DOMAIN$ www.%DOMAIN%;

    location /.well-known/acme-challenge/ {
        root /var/www/html;
        allow all;
    }
    location / {
        return 301 https://%DOMAIN%$request_uri;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name www.%DOMAIN%;

    ssl_certificate /etc/letsencrypt/live/%DOMAIN%/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/%DOMAIN%/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    return 301 https://%DOMAIN%$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name %DOMAIN%;

    root /var/www/shorturl-service/frontend/build;
    index index.html;

    ssl_certificate /etc/letsencrypt/live/%DOMAIN%/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/%DOMAIN%/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location /api/ {
        proxy_pass http://localhost:%PORT%;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location / {
        try_files $uri $uri/ @backend;
    }

    location @backend {
        proxy_pass http://localhost:%PORT%;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

あとはドメインにSSLを渡したりすることで設定は完了です!

短縮URLサービスを始めてみよう!

以下のリポジトリをcloneしてセットアップすることで自前のカスタムドメインで同様のサービスを作成することができます!


大切なこと


短縮URLサービスを作成するうえで何が一番重要でしょうか?
技術力?サイトのデザイン?
いいえ、違います。正解は ドメインの短さ です!!!
でも、短いドメインはプレミアムドメインだったりして、なかなか手の出にくい値段だったりすることも稀じゃありませんよね。
そんな方々に Gandi.net.im ドメインを取得することをお勧めします。
私の場合、 oto.im は初年度2100円で、更新料も4000円程度とかなりお手頃です。
短縮URLサービス最大手の bit.ly と同じ文字数が維持費年間4000円と考えるとかなりお得だと思います。
皆さんもこの際、短いドメインを取得して短縮リンクサービスを立ち上げてみませんか?

まとめ

以下が完成したサービスです。
皆さんの参考になれば幸いです。

それでは、また。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?