20190929のJavaScriptに関する記事は15件です。

█║▌ Distributed Fault-Tolerant Authentication Management & Identification Control System

At the rate at which technology today is moving forward with the Internet speeds increasing manifold, with IoT gaining prominence and organizations more distributed across the globe than before, the authentication software, systems and architectures remain fairly primitive.

Among the many reasons attributing to this is corporates that build these authenticating systems and software hold on to these products as their main source of income. The insight and research in these areas has also been fairly mundane. Though there's been enough research funding, what's missing has been the intellect and knowledge required to build large-scale distributed and decentralized authentication systems and architectures.

Large-scale authentication systems and architectures used in building them must allow both manned [computers, tablets, phones, virtual machines etc] and unmanned [IoT devices etc] to authenticate and authorize themselves without a centralized bottleneck, as seen in authentication systems like LDAP, Active Directory and others.

As experienced on a daily basis, these centralized authentication systems are not scalable or fault-tolerant without a sane fail-over MTBF [Mean Time Between Failure] causing business disruptions on a regular and long-lasting basis.

■ What can be done about this?

Let us acquaint ourselves with AuthControl. SynchroKnot designed and developed AuthControl as a result of realizing inadequacies in the centralized authentication systems [LDAP & Active Directory].

AuthControl was designed with the following flexibility in mind:

  • Ability for authentication to be either centralized, distributed, decentralized or a combination of these.
  • Ability to be seamlessly and transparently scaled on-demand across the globe with no downtime.
  • Ability to be used by standard operating systems within their security framework without custom or proprietary software, enhancements, modifications or hacks.
  • Ability to be used across all devices that can make a simple https call. and much more.

■ What is AuthControl?

AuthControl is SynchroKnot's unique Distributed Fault-Tolerant Authentication Management & Identification Control System that serves as a scalable, secure and simple alternative to LDAP, Active Directory and other authentication systems.

In AuthControl, the user[s] can be delegated and made responsible for managing their password. Furthermore, the user's password SHA512/GOST checksum is kept encrypted.

■ Password + Pin

The user[s] can log in to their virtual machines or physical hardware [eg. computers, tablets, mobile phones etc] with their standard username and password + 5 digit unique pin.

This 5 digit pin is not set by the user, but is rather auto or manually generated per the preference of the organization. Without having to manage separate pins for each user, and the ability to change them on a regular basis, makes logging into systems and authentication for various purposes more secure without adding the burden of lengthy procedures/steps.

Depending on the nature of the circumstance, user access can be restricted/limited by simply changing the PIN.

■ Algorithmically-ascertained decentralized numeric User and Group ID

Authcontrol also has the unique capability of creating operating system specific user and group identities that are unique. For example, AuthControl can create a Linux User ID and Group ID that are unique and always return the same numeric value for the ID.

This unique numeric user and group ID is algorithmically created in a decentralized manner without having to generate, store and poll centralized or distributed databases.

Due to the uniqueness of the user and group IDs, they can be instantly checked for changes/manipulations and reinstated automatically if changed without having to poll, check and compare with central or distributed databases. It can also report/alert in the similar manner.

AuthControl's strong security is strengthened with the use of inter-leaved mapping of Usernames to their Blockchain IDs and further using blockchain cryptography [not the blockchain network] to ascertain authenticity. This is another unique feature you will not find anywhere else but with SynchroKnot.

■ Fault Tolerant

AuthControl algorithmically checks for failures across multiple geographically-dispersed locations [configurable up to 10] before returning unreachable.

■ Load Balanced

Each user or groups of users can be assigned different geographically-dispersed locations for load balancing [with additional option of fault-tolerance].

■ Scalable

Enable AuthControl in virtual or physical machines, point more users to them, and scale seamlessly and transparently across the globe.

■ Simple

Very easy to set up and manage. Works transparently with Linux PAM without modifying standard PAM modules, and is end-to-end encrypted [uses standard HTTPS for communication].

Since this is just an article for getting acquainted with AuthControl, we refrain from getting into technicalities which might be better reflected in a whitepaper.

■ Below are examples of different methods that users can log in or access resources transparently with their standard Username and Password + 5 Digit PIN:

├─> Graphical Login
├─> Graphical Screen Saver Login [eg. screen lock]
├─> Non-Graphical Login
├─> SUDO - Execute a command as another user
├─> SU - Super User
├─> SSH - Secure Shell
├─> SCP - Secure Copy
├─> SFTP - Secure File Transfer Protocol
├─> SSHFS - Secure Shell File System
├─> FTP - File Transfer Protocol
├─> VNC - Virtual Network Computing
├─> RDP - Remote Desktop Protocol
├─> CUPs - standards based open source printing system
├─> CRON - Execution of scheduled commands
├─> SAMBA - Windows AD and SMB/CIFS fileserver for UNIX
├─> File Manager - Create Network Place with SFTP, SAMBA and FTP
├─> All password requirements via Control Center
├─> Practically anything that uses Standard PAM for authentication!

Below is a direct link to the demonstration video:

http://synchroknot.tokyo/AuthControl.mp4

Description of the demonstration:

This is a very basic impromptu demonstration of AuthControl. Here both of the virtual machines are enabled with AuthControl and show the following:

■ Login via Graphical Interface

■ Login via Non-Graphical Interface

■ Run a command with SU as another user

■ Run a command with SUDO as another user

■ Login to a remote system via SSH

■ Mount a remote filesystem via SSHFS

■ Use File Manager to create a Network Place using SFTP

All these different types of logins use AuthControl with standard Linux users and password + 5 digit pin. The basic HTTPS traffic is captured using TCPDUMP to show realtime interaction with the SynchroKnot AuthControl when the password is entered in the virtual machines for the purposes of authentication.

Note: This demo was recorded on a severely resource-constrained system. It is up to you to determine the performance.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

StorybookのReactのチュートリアルを見ながら基本を勉強してみた

React、Storybook初心者です。チュートリアルをやってみただけの投稿です。よろしくお願いします:bow:

私が実際に試したコードはこちらです。

https://github.com/okumurakengo/react-storybook-sample

Storybook for React tutorial

Get started | Storybook Tutorial

Setup React Storybook

# Reactのプロジェクト作成
npx create-react-app taskbox
cd taskbox

# Storybookを追加
npx -p @storybook/cli sb init

# port:9009 でコンポーネントエクスプローラーが起動する
yarn run storybook

Screen Shot 2019-09-29 at 16.19.33.png

Reuse CSS

1 チュートリアルを進める為に、cssをこちらかコピペする

↓このcssをコピーして
https://github.com/chromaui/learnstorybook-code/blob/master/src/index.css

./src/index.cssにコピペする

2 ./.storybook/config.jsindex.css を読み込む

.storybook/config.js
import { configure } from '@storybook/react';

// 追加
import '../src/index.css';

// automatically import all files ending in *.stories.js
configure(require.context('../src/stories', true, /\.stories\.js$/), module);

css読み込み前

Screen Shot 2019-09-29 at 17.05.36.png

css読み込み後

Screen Shot 2019-09-29 at 17.05.17.png

Add assets

フォントとアイコンも読み込む

↓ここのiconとfontフォルダをそのまま./public/フォルダに追加しておく
https://github.com/chromaui/learnstorybook-code/tree/master/public

Build a simple component

Build a simple component | Storybook Tutorial

Task

タスクコンポーネントを作成する

Get setup

src/components/Task.jssrc/components/Task.stories.js を作成する

src/components/Task.js
import React from 'react';

export default function Task({ task: { id, title, state }, onArchiveTask, onPinTask }) {
  return (
    <div className="list-item">
      <input type="text" value={title} readOnly={true} />
    </div>
  );
}
src/components/Task.stories.js
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';

import Task from './Task';

export const task = {
  id: '1',
  title: 'Test Task',
  state: 'TASK_INBOX',
  updatedAt: new Date(2018, 0, 1, 9, 0),
};

export const actions = {
  onPinTask: action('onPinTask'),
  onArchiveTask: action('onArchiveTask'),
};

storiesOf('Task', module)
  .add('default', () => <Task task={task} {...actions} />)
  .add('pinned', () => <Task task={{ ...task, state: 'TASK_PINNED' }} {...actions} />)
  .add('archived', () => <Task task={{ ...task, state: 'TASK_ARCHIVED' }} {...actions} />);

Storybookはコンポーネントと、それのストーリーで構成されます。
コンポーネント1つに対して、必要な数のストーリーを作成できます。

  • Component
    • Story
    • Story
    • Story

Config

./.storybook/config.jsを変更して作成した、stories.jsファイルを読み込めるようにします

.storybook/config.js
import { configure } from '@storybook/react';
import '../src/index.css';

const req = require.context('../src', true, /\.stories.js$/);

function loadStories() {
  req.keys().forEach(filename => req(filename));
}

configure(loadStories, module);

このように表示されます。

Screen Shot 2019-09-29 at 18.00.26.png

storiesOf とするたびにメニューが増え、
add とするたびに下の階層にメニューが増えてきます。

Screen Shot 2019-09-29 at 18.png

Build out the states

storybookでコンポーネントを表示までできたので、
次にコンポーネントに状態やイベントを設定、UIの見た目を設定します。
storybookで見た目を確認しながら作成することができます。

src/components/Task.js
import React from 'react';

export default function Task({ task: { id, title, state }, onArchiveTask, onPinTask }) {
  return (
    <div className={`list-item ${state}`}>
      <label className="checkbox">
        <input
          type="checkbox"
          defaultChecked={state === 'TASK_ARCHIVED'}
          disabled={true}
          name="checked"
        />
        <span className="checkbox-custom" onClick={() => onArchiveTask(id)} />
      </label>
      <div className="title">
        <input type="text" value={title} readOnly={true} placeholder="Input title" />
      </div>

      <div className="actions" onClick={event => event.stopPropagation()}>
        {state !== 'TASK_ARCHIVED' && (
          <a onClick={() => onPinTask(id)}>
            <span className={`icon-star`} />
          </a>
        )}
      </div>
    </div>
  );
}

storybookでイベントを拾えたのを確認でき、状態ごとに見た目が変わることも確認できました。

gCY4ipVxK2.gif

Specify data requirements

コンポーネントの見た目、イベントを作成完了まで行い、その後にpropTypesを設定すると良いようです。

src/components/Task.js
import React from 'react';
import PropTypes from 'prop-types';

export default function Task({ task: { id, title, state }, onArchiveTask, onPinTask }) {
  return (
    <div className={`list-item ${state}`}>
      <label className="checkbox">
        <input
          type="checkbox"
          defaultChecked={state === 'TASK_ARCHIVED'}
          disabled={true}
          name="checked"
        />
        <span className="checkbox-custom" onClick={() => onArchiveTask(id)} />
      </label>
      <div className="title">
        <input type="text" value={title} readOnly={true} placeholder="Input title" />
      </div>

      <div className="actions" onClick={event => event.stopPropagation()}>
        {state !== 'TASK_ARCHIVED' && (
          <a onClick={() => onPinTask(id)}>
            <span className={`icon-star`} />
          </a>
        )}
      </div>
    </div>
  );
}

Task.propTypes = {
  task: PropTypes.shape({
    id: PropTypes.string.isRequired,
    title: PropTypes.string.isRequired,
    state: PropTypes.string.isRequired,
  }),
  onArchiveTask: PropTypes.func,
  onPinTask: PropTypes.func,
};

※参考ページによると、この後にjestによるTask.jsのコンポーネントを書くという流れのようですが、今回はそこまでやりませんでした。

Assemble a composite component

Assemble a composite component | Storybook Tutorial

コンポーネントを組み合わせた時の場合を見ていきます。

Tasklist

Taskを組み合わせたTaskListをstorybookで表示します

Get setup

src/components/TaskList.jssrc/components/TaskList.stories.js を作成する

src/components/TaskList.js
import React from 'react';

import Task from './Task';

function TaskList({ loading, tasks, onPinTask, onArchiveTask }) {
  const events = {
    onPinTask,
    onArchiveTask,
  };

  if (loading) {
    return <div className="list-items">loading</div>;
  }

  if (tasks.length === 0) {
    return <div className="list-items">empty</div>;
  }

  return (
    <div className="list-items">
      {tasks.map(task => <Task key={task.id} task={task} {...events} />)}
    </div>
  );
}

export default TaskList;
src/components/TaskList.stories.js
import React from 'react';
import { storiesOf } from '@storybook/react';

import TaskList from './TaskList';
import { task, actions } from './Task.stories';

export const defaultTasks = [
  { ...task, id: '1', title: 'Task 1' },
  { ...task, id: '2', title: 'Task 2' },
  { ...task, id: '3', title: 'Task 3' },
  { ...task, id: '4', title: 'Task 4' },
  { ...task, id: '5', title: 'Task 5' },
  { ...task, id: '6', title: 'Task 6' },
];

export const withPinnedTasks = [
  ...defaultTasks.slice(0, 5),
  { id: '6', title: 'Task 6 (pinned)', state: 'TASK_PINNED' },
];

storiesOf('TaskList', module)
  .addDecorator(story => <div style={{ padding: '3rem' }}>{story()}</div>)
  .add('default', () => <TaskList tasks={defaultTasks} {...actions} />)
  .add('withPinnedTasks', () => <TaskList tasks={withPinnedTasks} {...actions} />)
  .add('loading', () => <TaskList loading tasks={[]} {...actions} />)
  .add('empty', () => <TaskList tasks={[]} {...actions} />);

s2guCvO2VM.gif

  • デフォルト状態のリスト
  • pinned状態のタスクありのリスト
  • リスト読み込み中
  • リストが0個

↑の状態でそれぞれstorybookで見れるとこまでできました。

Build out the states

状態ごとのコンポーネントのUIを作り込んでいきます。

src/components/TaskList.js
import React from 'react';

import Task from './Task';

function TaskList({ loading, tasks, onPinTask, onArchiveTask }) {
  const events = {
    onPinTask,
    onArchiveTask,
  };

  const LoadingRow = (
    <div className="loading-item">
      <span className="glow-checkbox" />
      <span className="glow-text">
        <span>Loading</span> <span>cool</span> <span>state</span>
      </span>
    </div>
  );

  if (loading) {
    return (
      <div className="list-items">
        {LoadingRow}
        {LoadingRow}
        {LoadingRow}
        {LoadingRow}
        {LoadingRow}
        {LoadingRow}
      </div>
    );
  }

  if (tasks.length === 0) {
    return (
      <div className="list-items">
        <div className="wrapper-message">
          <span className="icon-check" />
          <div className="title-message">You have no tasks</div>
          <div className="subtitle-message">Sit back and relax</div>
        </div>
      </div>
    );
  }

  const tasksInOrder = [
    ...tasks.filter(t => t.state === 'TASK_PINNED'),
    ...tasks.filter(t => t.state !== 'TASK_PINNED'),
  ];

  return (
    <div className="list-items">
      {tasksInOrder.map(task => <Task key={task.id} task={task} {...events} />)}
    </div>
  );
}

export default TaskList;

今回はただコピペしただけですが、storybookでコンポーネントの表示を見ながら作成できるのでやりやすいと思いました。

bqxVjYSAvl.gif

Data requirements and props

コンポーネントの見た目、イベントを作成完了まで行ったので、propTypesも設定しておく

src/components/TaskList.js
import React from 'react';

import Task from './Task';
import PropTypes from 'prop-types';

function TaskList({ loading, tasks, onPinTask, onArchiveTask }) {
    const events = {
        onPinTask,
        onArchiveTask,
    };

    const LoadingRow = (
        <div className="loading-item">
            <span className="glow-checkbox" />
            <span className="glow-text">
                <span>Loading</span> <span>cool</span> <span>state</span>
            </span>
        </div>
    );

    if (loading) {
        return (
            <div className="list-items">
                {LoadingRow}
                {LoadingRow}
                {LoadingRow}
                {LoadingRow}
                {LoadingRow}
                {LoadingRow}
            </div>
        );
    }

    if (tasks.length === 0) {
        return (
            <div className="list-items">
                <div className="wrapper-message">
                    <span className="icon-check" />
                    <div className="title-message">You have no tasks</div>
                    <div className="subtitle-message">Sit back and relax</div>
                </div>
            </div>
        );
    }

    const tasksInOrder = [
        ...tasks.filter(t => t.state === 'TASK_PINNED'),
        ...tasks.filter(t => t.state !== 'TASK_PINNED'),
    ];

    return (
        <div className="list-items">
            {tasksInOrder.map(task => <Task key={task.id} task={task} {...events} />)}
        </div>
    );
}

TaskList.propTypes = {
    loading: PropTypes.bool,
    tasks: PropTypes.arrayOf(Task.propTypes.task).isRequired,
    onPinTask: PropTypes.func.isRequired,
    onArchiveTask: PropTypes.func.isRequired,
};

TaskList.defaultProps = {
    loading: false,
};

export default TaskList;

※参考ページによると、この後にjestによるTaskList.jsのコンポーネントを書くという流れのようですが、今回はそこまでやりませんでした。


最後まで読んでいただいてありがとうございました

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初めてのホームページ開発手記 part0

経緯

  • 知り合いが農業をやっており、事業拡大を今後考えるためにホームページを作成したいという話をされた。

  • 自分は普段画像処理ミドルウェアの開発なのでWeb系はHTML, CSS, javascriptが読み書きできるくらい、node.jsはちょっと触ったことあるレベルだから正直わからん、、、けどスキルアップしたいということでまずはモックを作ってみるよ!ということにした。

開発環境

開発マシンどうする

  • とりあえず自分のPCを汚したくなかったので、virtual boxで仮想マシンを構築して開発する方針にする

  • virtual boxにubuntu18.04を入れてみたところ重い・・・自分のPCはメモリが8GBしかないためちょっときついか、、、→ xUbuntuは軽い、という記事をみたので virtual box + xUbuntu18.04 で開発してみることにする。

なにつかって開発すればいいの?

  • 調べてみたところ、WordPressを使用してホームページを作ることが多いらしい、ブログとかは特に。
    CMS(Contents Management System)というらしい、専門的な知識がない人でも簡単にホームページを作れるようになる、というところが目的らしい。

  • でも自分は、javascriptコードとか書いてガチャガチャ動かせるようにしたかったのでちょっと違うかな、という感じ。一応WordPressでもできるみたいだけど、、、webアプリっぽい感じで開発したかった。
    あとWordPress自体がPHPベースで作られているみたいで、PHPかぁ~という感じであった。会社であんま使われているの聞いたことないから勉強してもなぁと。

  • 有名どころのweb開発フレームワークとしては
    PHP Laravel
    Ruby on Rails
    あたりが有名みたいだった。

  • 個人的にjavascriptを極めていきたかったのでサーバーサイドもjavascriptでやっていきたいということで調べたところ出てきたキーワード node.js, Express, jQuery, Angular, vue.js, React...沢山あるけど微妙に役割が違うみたいよくわからん、、、
    どれがフレームワークで、どれがライブラリ?サーバーサイド?クライアントサイドで動作?ということで整理する?

  • というかそもそもWebサイトを作りたいのか、Webアプリを作りたいのかにもよって使うものが違う気がするのでそこも整理したい。

私はWebサイトとWebアプリどっちつくるの?

今回やりたいと考えたこと

  • 情報の発信
    生産者のこだわりとかアクセスマップとか、そういう情報を閲覧者に伝えたい。
  • 問い合わせフォーム、取り扱い商品の販売のようにレスポンシブな感じ

以上を踏まえて以下のサイトを参考にすると、対話成分が入る場合はWebアプリケーションという考えとして設計していった方が良いみたいだ。
参考リンク: IBM様 Web サイトから Web アプリケーションへの変換

ということで今回は情報提供を中心としたWebアプリケーション開発をすることにします!!

node.jsってなに?

  • Node.jsとはサーバーサイドでjavascriptを使用できるようにしたもの、Webサーバーとしても動作する。
  • あくまで実行環境を提供するようなイメージであって、これ自体はフレームワークとは言わないようである(フレームワークとしては大きすぎる)。

Express

  • Node.js上で動作するMVCフレームワーク。これはフレームワーク、ふむ。サーバーサイドにいれるものということか。

  • MVCとはModel View Controller モデル・ビュー・コントローラ を意味していて、そういうデザインパターンのこと、UIとロジックを分ける、みたいなイメージですね。

  • 「Express」「Meteor」「Sails」がJavaScriptの三大フレームワークらしい。

  • Expressを使用したCMSとしてKeyStoneJSというものがあるみたい、本番では使ってみようかな。

jQuery

  • JavaScriptのライブラリの一つ。ふむ、これはフレームワークではないのね、なるほどね。
  • フロントエンド(クライアントサイド)で使用されることが多いらしい、JavaScriptのライブラリなのでサーバーサイドでも使えるらしい。

Angular

  • シングルページアプリケーション (SPA)の開発に向いているJavaScriptフレームワークの一つ、なるほどこれはフレームワーク。
    だけどこれはフロントエンドフレームワーク、つまりExpressとは用途が違うということ

  • SPAとはクライアント側でHTMLを動的生成するアプリケーションのこと、一々サーバー側には問合せしない、なるほど。

  • Angular / vue.js / Reactで比較されることが多い。AngularはGoogleが開発している。

  • ルーティングなど必要な機能が全部入ったフルスタックフレームワークらしい。実はクライアントサイドでルーティングという意味がよくわかってないです、サーバーサイドのルーティングと共存するの?

React

  • FaceBookが開発

  • Angularと同じくフロントエンドで使用されるJavaScriptフレームワークの一つ、として扱われることも多いが、FaceBookはJavaScriptライブラリ、と言っているらしいのでライブラリとして考えたほうが良い。

  • UIに特化しているみたいなのでライブラリの方がしっくりくる。

Vue.js

  • Angularと同じくフロントエンドで使用されるJavaScriptフレームワークの一つ。

  • Googleの中の人(エヴァン・ヨー氏)がAngularの前身のAngularJSを開発に携わったが、そこから好ましい部分を抜き出して再構築しなおしたものがVue.jsらしい。
    エヴァン氏世の中に貢献しすぎ、すごい。

  • UIに特化しているようで、ルーティング等は別のライブラリを使用するらしい。じゃあReactと同じでライブラリじゃない?と思ったけど使ってみたら違いがわかるのかもです。

  • こちらの記事に比較がありました、わかりやすいです。

最終的に決めた環境

クライアントサイドのルーティングとサーバーサイドのルーティングを混ぜるとちょっと開発が大変そうにみえたので、初学者ということもあるのでまずはサーバーサイドでレンダリングすることに注力したいと思います。
SPAだと機能提供メインのアプリって感じがしますし。
ということで

  • サーバーサイド
    node.js, Express でいきます!DB使う場合はMongoDBと相性がよさそう。

  • クライアントサイド
    CSSフレームワークはbootstrapでレスポンシブデザインなサイトを目指します!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Angular8+Rails5でアプリを作ってみる

AngularとRailsを組み合わせてみる

1_nmQ30kViITehpW9xALP2Rg.jpeg
前回の記事ではAngular+NestJSの組み合わせについてピックアップをしましたが、今回はRuby on Rails5とAngular8の組み合わせでwebアプリを作成している記事があり、読んでいてとても面白い内容だったので翻訳に挑戦しました!

どちらも個人的に気に入っているフレームワークなので、世の中にこの組み合わせでの開発が増えてくると嬉しいです。

Angular8とRails5 (*以下記事の翻訳)

あたなはどうか知らないが、学びやすく、素早く書けて、オープンソースという理由で私はRuby on Railsが大好きだ。また、Ivyというクールな新しいレンダーエンジンを導入したAngularの新バージョン、Angular8のリリースにもワクワクしている。

もしあなたが私と同じでこれら2つのフレームワークに関する興味をわかってくれて、でも、その素晴らしい2つをどう始めたら良いのかさっぱりわからない場合は、ぜひこれを読んでほしい。

さっそく初めてみよう!

慣習的にJavascriptフレームワークと一緒にRailsを使う場合は、webpackが必要とされていた。しかし、Railsにはwebpackerと呼ばれるこれにうってつけのgemがあり、Angularプロジェクトを動かしてくれる。しかし、最新バージョン(7系以上)のAngularでは内部でwebpackシステムを持っており、そのようなプロジェクト構成が、素晴らしいAngularCLIを使う妨げとなってしまうのだ。

その代わりに私たちがしなければならないのは、Angularからの出力ファイルを受け取り、それらを提供するコントローラを備えたAPIとしてのRailsアプリを使うことだ。

始める前に下記をインストールしてほしい。

  • node (8.9以上)
  • NPM (5.5.1以上)
  • Angular CLI
  • Rails 5+ (インストールにRVMまたはRbenvを使うことをおすすめします)
  • Postgresql

1. Angularプロジェクトを作成する

好きなディレクトリの場所でターミナルを開いて、Rails/Angularプロジェクトを作成する。

ng new rails-angular-project

Angular routingを使うか聞かれたら、迷わず「YES」を押しちゃってください!(かなり良いですよ!)
その次に、好きなスタイルシートの形式を選択して下さい。(個人的にはSCSSで進めてます)
そして、全て完了したら先ほど作ったプロジェクトにジャンプしましょう!

cd rails-angular-project

2. Railsプロジェクトを作成する

では、現在のディレクトリにRailsプロジェクトを作成しましょう!

rails new . -database=postgresql

これによって、Angularプロジェクトの内部にRailsプロジェクトを作成し始めるが、全てのマージを拒否した場合、コンフリクトが発生する。
Railsプロジェクトが作成されたなら、.gitignoreファイルにいくつかファイルを追加する必要があります。
.gitignoreファイルを開いて、下記をコピペして下さい。

# Ignore bundler config.
/.bundle

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep

# Ignore uploaded files in development
/storage/*
!/storage/.keep

.byebug_history

# Ignore master key for decrypting credentials and more.
/config/master.key

# Ignore application configuration
/config/application.yml

3. 上記2つを合体する

今の段階で、Angularプロジェクトと一緒になったRailsアプリとなっており、これはAPIとしてのRailsを使用し提供している一方で、Angularフレームワークを用いたSPAの利益も享受していることと言える。しかしその前にRailsにAngularが提供するファイルにアクセスできるようにしなければならない。

通常はAngularアプリでは、ng serveを使用して開発を進めるのだが、今回はng buildを使用してRailsのパブリックディレクトリに出力するように設定する。

ルートディレクトリにあるAngular.jsonを開いて、出力先ディレクトリをdist/rails-angular-appからpublicに変更する。

angular.json
...

"options": {
    "outputPath": "public",
...

次に、app/controllersにstatic_controller.rbという名前のファイルを作成する。

app/controllers/static_controller.rb
class StaticController < Rails::ApplicationController
    def index
        render file: Rails.root.join('public', 'index.html')
    end
end

static_controllerはAngularによって提供されるindex.htmlをレンダリングする役目を持ちます。

そして、下記のコードをroutes.rbに追加して下さい。

routes.rb
Rails.application.routes.draw do
    get '*other', to: 'static#index'
end

これで、Railsにルートをキャッチさせて、static_controllerにリダイレクトすることができます。また、上記のコードによってapiに必要となるどのルートも定義することができるのです。でもそれは次の機会に回しましょう!

4. Foremanを使って便利にする

Foremanを導入することによって、Webアプリを提供しやすくしましょう。

gemにforemanを追加することによってインストールして下さい。

Gemfile
...
# Use foreman
gem 'foreman'
...

bundle installで新しいgemをインストールします。

bundle install

Procfileと呼ばれるファイルを新しくルートディレクトリに追加します。これは組み合わせされる全てのコマンドをforemanが読み込めるようにするためのファイルです。

Procfile
web: rails s -p 3000
client: ng build --watch=true

これでforemanを起動できます。

foreman start

RailsアプリとAngularアプリが同時に起動されるはずです。そして、localhost:3000にアクセスすると以下のような画面が表示されるでしょう。

1_RCUJQELq4Gpw5AYRc7wUCQ.png

完了です!これで自由にWebアプリを開発することができますし、apiにシンプルかつパワフルなフレームワークを与えるのと同時に、Angular CLIの力を用いることができます。

5.終わりに

今回、Rails5のバックエンドとAngular8のフロントエンドで、同じディレクトリに存在し、それぞれの力を最大限に活用するアプリケーションを作りました。このチュートリアルがあなたにとって役に立てれば幸いです。

参考記事

Angular 8+ with Rails 5+ - Sam Green

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ESLint v6.5.0

v6.4.0 | 次 (2019/10/12 JST or 2019/10/26 JST)

ESLint 6.5.0 がリリースされました。
小さな機能追加とバグ修正が行われています。

質問やバグ報告等ありましたら、お気軽にこちらまでお寄せください。

? 日本語 Issue 管理リポジトリ
? 日本語サポート チャット
? 本家リポジトリ
? 本家サポート チャット

? 本体への機能追加

--env-info CLI option

? #12270

ESLint の実行環境を表示する CLI オプションが追加されました。

バグ報告の際にご利用ください。

$ eslint --env-info
Environment Info:

Node version: v12.11.0
npm version: v6.11.3
Local ESLint version: v6.5.0
Global ESLint version: Not found

? 新しいルール

特になし。

? オプションが追加されたルール

use-isnan enforceForSwitchCase

? #12192, #12207

浮動小数点数の仕様により、NaNとの比較は常にfalseになります。これを報告するuse-isnanルールに、switch文も報告するオプションが追加されました。

/*eslint use-isnan: [error, { enforceForSwitchCase: true }]*/

//✘BAD
switch (foo) {
    case NaN:
        bar();
        break;
}

Online demo

✒️ eslint --fix をサポートしたルール

特になし。

⚠️ 非推奨になったルール

特になし。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

連絡先の検索

// html

<label for="search">連絡先の名前 ; </label>
<input type="text" id="search">
<button>検索</button>

<p></p>

// javascript

'use strict';
{
var contacts = ['クリス:2232322','サラ:3453456','ビル:7654322','メアリー:9998769','ダイアン:9384975'];
var para = document.querySelector('p');
var input = document.querySelector('input');
var btn = document.querySelector('button');

btn.addEventListener('click', function(){
    var searchName = input.value;
    input.focus();
    for(let i = 0; i < contacts.length; i++){
        var splitContact = contacts[i].split(':'); //ここで個々の項目を項目が2つの配列にする
        if(splitContact[0] === searchName){
            para.textContent = splitContact[0] + 'の電話番号は ' + splitContact[1] + ' です。';
            break;
        }else{
            para.textContent = '連絡先が見つかりません。';
        }
    }
});

}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

指定の要素を取得する方法

まえがき

引き続き発掘作業中に見つけたメモ書き。
素のJavaScriptでどうにかしなきゃ行けない時にいろいろメモしていたらしい。
以下、jQueryが使えるなら、そっち導入してやる方が良いと思う。

id指定で取得

var test = document.getElementById("idName");
var url = test.getAttribute("href");
url += url + "いじる。加えたり";
test.href = url;  // aタグの位置に戻す

※idはhtml一つのファイルにつき一意となっている。

class指定で取得

  • ちょっと調べるのめんどかった。下の方に記述したやり方の方がスマート
var test = document.getElementsByClassName("className");
test = Array.prototype.filter.call(test, function(test){
        return test.nodeName === "A"; // aタグの意。DIVとかもある。この場合は、classNameに一致するaタグという意。
});

for (var i =0; i < test.length; i++) {
    var url = test[i].getAttribute("href");
    url += url + "いじる。加えたり";
    test[i].href = url;  // aタグの位置に戻す
}
  • getElementsByClassName()は、 Array.prototypeメソッドに与えて利用することができる
  • test.nodeName === "A"
    • nodeNameでclass内部の要素を指定できる。くわしくはその都度ググるか、コンソール上で名前確認する。
  • classは複数指定可能なので、配列で返ってくる。そのためfor文でまわす。

↑よりも、まずタグ指定で要素を配列にまとめて、指定のclassが存在するか確認する方がシンプル

     ① var t = document.getElementTagname('a').... ※うろ覚え
     ② t.getAttribute('class') && t.getAttribute('class').indexOf('クラス名')

タグ指定で取得

var test = document.getElementsByTagName("a");

for (var i =0; i < test.length; i++) {
    var url = test[i].getAttribute("href");
    url += url + "いじる。加えたり";
    test[i].href = url;  // aタグの位置に戻す
}

自分へ

見直しが必要です。暇ができたらやってください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

...(三点リーダー的な奴)の使い方

スプレットオペレーター

ES6の記法

http://analogic.jp/spread-operator/
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax

配列を連結したり、連結して関数に投げたりできる。

function getTest(x,y,z) {
    return x + y + z;
};
var test = [1,2,3];
console.log(getTest(...test));
// output : 6
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

cookieの操作可能範囲について

まえがき

2年ほど前に調べたcookieの操作可能範囲。
発掘したので、そのままぺたり。
操作手段はJavaScript。

参考

前提

cookieのpath指定はすべて「/」である。

cookieの読み書き範囲の調査

■ トップドメイン(.com, .jp等)で指定してcookie付与は不可

document.cookie = "_testsunTop=hogehoge; path=/; domain=.com;";

=> 付与できない

■ ドメインで指定してcookieを付与した場合、指定した階層以下からは参照可能

参照可否

同一 上位 下位
×

現在の場所 test.aaa.domain.com

document.cookie = "_testsun=ttttt; path=/; domain=.aaa.domain.com;";

参照可能範囲

  • test.aaa.domain.com
  • tttt.aaa.domain.com
  • aaa.domain.com

参照不可

  • domain.com

■ 現在いる場所のドメインと指定するドメインの場所が異なる場合、書き込み不可

現在の場所 test.aaa.domain.com

document.cookie = "_testsunAno=anotherdomain; path=/; domain=.tttt.aaa.domain.com;";

■ ドメインを指定せずにcookieを書き込むと、そのドメインからしか見ることができない

現在の場所 aaa.domain.com

document.cookie = "_testsunNoSetDomain=foobaa; path=/;";
  • 設置場所 aaa.domain.com
  • サブドメイン test.aaa.domain.com

【IEの場合】、ルートドメインでcookieが発行されるとサブドメインまで読み込むことができる。

① root.com/hogehoge.htmlで以下のように設定

document.cookie = "_sun_tes_no_set_domain=noDomain; path=/";
document.cookie = "_sun_tes_no_priod=setNoDomain; domain=root.com; path=/";
document.cookie = "_sun_tes_set=setDomain; domain=.root.com; path=/";

② sub.root.com/fugafuga.htmlでcookieを確認すると、すべての値を確認可能。

削除のことば

expires=Thu, 01 Jan 1970 00:00:00 GMT
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsでModule hack (require hack)する

Node.jsにはrequireというモジュールを読み込む機能があります。これはNode.js Module APIで提供されている機能で、割と簡単にハックすることができます。よく Module hack とか require hack と呼ばれるものです。

などが有名どころでしょうか。これらのモジュールでModule hackをすると、本来のNode.jsでは動作しないTypeScriptや拡張されたJavaScriptが動作します。

この記事では Module hack のやり方について説明します。

Module hackのやり方

// TypeScriptの場合
export const Module = require('module') as any

まず module を読み込みます。

拡張子に応じたハックをするのは簡単です。

Module._extensions[ext] = function(m: any, filename: string) {
  return m._compile('console.log("hoge")', filename)
}

先ほどから、Module を as anyany アサーションをしたり、今回の関数の第一引数 m もanyで定義していますが、これは、@types/node で定義されている Module 型の定義では、非公開プロパティ、たとえば _extensions_compile にアクセスできないためです。

実例

// test.js
const Module = require('module')

Module._extensions['.js'] = function(m, name) {
  return m._compile('console.log("piyopiyo")', name)
}

require('./hoge')
// hoge.js
console.log('hoge')

これら2つのファイルを同じディレクトリにおいて、node test.jsを実行すると、Module hack をしてなければ本来 hoge と表示されるところが piyopiyo と表示されます。

解説

Module._extensions は、拡張子ごとにファイルの処理をする関数群です。.js のようにドット込みの拡張子を指定します。

ts-node/registerや@babel/registerのように、ソースコードをトランスパイルして実行する場合には、大体定番となるのが、

Module._extensions['.js'] = function(m, name) {
  return m._compile('console.log("piyopiyo")', name)
}

のように最終的にNode.jsが解釈できる状態にまでトランスパイルしたソースコードを、m._compile(code, name) のようにすることです。

補足

Module._extensionsを書き換えると、もどす時に工夫は必要となります。

export const originalExts: { [props: string]: any } = {}

Object.keys(Module._extensions).forEach(ext => {
  originalExts[ext] = Module._extensions[ext]
})

大体はこのように、本来の関数を保存しておきます。

もっとも、このような hack を元に戻す必要がある機会はあまりないでしょう。

モジュール読み込み自体をhackする

ここまでの hack で、拡張子ごとの hack は可能になりましたが、モジュールそのものを hack するためには別の隠しプロパティにアクセスする必要があります。

const Module = require('module')

const originalLoad = Module._load

Module._load = function(name, parent, isMain) {
  if (name === 'hoge') {
    return { hoge: () => console.log('hoge') }
  }
  return originalLoad(name, parent, isMain)
}

const { hoge } = require('hoge')
hoge()

Module._loadを書き換えると、モジュール読み込みそのものにちょっかいを出せます。ただし本来の関数は保存しておいた方が良いため、const originalLoad = Module._loadで保存しておきます。

このコードでは、hogeという名前のモジュールだけhackします。他のモジュールを読み込んだ場合は、従来読み込むべきモジュールが読み込まれます。

任意のソースコードをモジュールとして返す

先ほどのhackでは、ダイレクトに書いたコードをそのまま実行しています。もちろん、それはそれで用途もあるかもしれませんが、拡張子hackのときと同じように任意のコードを実行して、その結果を返したいときもあるでしょう。

const module = new Module()
module._compile(code, filename)
return module.exports

さきほどもでてきた _compileは、Node.jsの実行環境に応じた処理をやってくれるので便利です。同等の処理を自前、たとえばeval Function vm.runInContextなどでやろうとするとかなり面倒です。

注意点

ここに書いた undocumented なものです。これで今後も含めて正しく動作することは保証されていません。また他の黒魔術を使ったソースコードで問題が生じる可能性があります。

pirates というnpmモジュールを使うと、より安全に Module hack を試すことはできます。自前でやるよりはいいかもしれません。

参考: https://github.com/nodejs/node/blob/master/lib/internal/modules/cjs/loader.js

宣伝

技術書典7で出した本のPDF電子版を、Pixiv Boothで頒布しております。よろしければどうぞ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

カレンダー機能

..html

<select name="month" id="month">
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
    <option value="6">6</option>
    <option value="7">7</option>
    <option value="8">8</option>
    <option value="9">9</option>
    <option value="10">10</option>
    <option value="11">11</option>
    <option value="12">12</option>
</select>           

ulタグ

h1タグ

..javascript

'use strict';
{
var select = document.querySelector('select');
var list = document.querySelector('ul');
var h1 = document.querySelector('h1');

select.onchange = function() {
var choice = select.value;

// 条件式をここに書く
var days = 31;

if(choice === '4' || choice === '6' || choice === '9' || choice === '11'){
    days = 30;
}else if(choice === '2'){
    days = 28;
}    
  createCalendar(days, choice + ' 月');
}



function createCalendar(days, choice) {
  list.innerHTML = '';
  h1.textContent = choice;
  for (var i = 1; i <= days; i++) {
    var listItem = document.createElement('li');
    listItem.textContent = i;
    list.appendChild(listItem);
  }
}

createCalendar(31,'1 月');

}

..css

body{
padding: 0;
margin: 0;
box-sizing: border-box;
}
ul{
padding: 0;
margin: 0;
width: 100%;
float: left;

}
ul li{
list-style: none;
background: pink;
text-align: center;
margin: 3px;
float: inherit;
width: 20%;
user-select: none;
}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

redux-sagaのthrottleとdebounceを試してみる

はじめに

redux-sagaのAPI referenceを眺めていて、読んだだけでは動きがイメージしにくかったthrottleとdebounceについて、実際に動かしてイメージを掴んでみました。(英語難しい)
実際の開発で使用したわけではないので、サンプルを作って動かしてみただけになります。サンプルのソースはこちらからどうぞ。

使用したredux-sagaのバージョン: 1.0.5

TL;DR

基本的にはどちらのAPIもtakeと同様にactionのdispatchを待ち受けてタスクを起動します。同じactionが短時間内に複数dispatchされた際の挙動が異なります。

  • throttle: actionがdispatchされたらタスクを起動します。指定時間内に同じactionがdispatchされた場合はタスクを起動せずに最新のactionを1個だけ保持しておき、指定時間経過後にタスクを起動します。
  • debounce: actionがdispatchがされたら、actionを保持して指定時間待ってからタスクを起動します。待っている間に同じactionがdispatchされた場合は、新しいactionを保持してまた指定時間待ちます。

サンプルコード

文字だけだと「なるほど、わからん」状態になるので、実際に動かしてみました。

サンプルは前回の記事で使用したものにthrottleとdebounceを追加したものを使用しました。throttleとdecounceのボタンをクリックしたらそれぞれonClickThrottleButtononClickDebounceButtonが実行されます。

スクリーンショット 2019-09-29 12.02.09.png

sampleContainer.js
  // 省略

  onClickThrottleButton: () => {
    let count = 0
    const interval = setInterval(() => {
    dispatch(throttleSampleStart(count))
      count++
      if(count >= 6) {
        clearInterval(interval)
      }
    }, 500)
  },
  onClickDebounceButton: () => {
    let count = 0
    const interval = setInterval(() => {
    dispatch(debounceSampleStart(count))
      count++
      if(count >= 6) {
        clearInterval(interval)
      }
    }, 500)
  },

  // 省略
sampleSaga.js
// 省略

function* handleThrottleSampleStart() {
  yield throttle(1800, THROTTLE_SAMPLE_START, runThrottleSampleStart)
}

function* runThrottleSampleStart(action) {
  console.log(`take action ${JSON.stringify(action)}`)
  yield call(sleepAsync, action.payload.count)
  yield put(throttleSampleSuccess())
}

function* handleDebounceSampleStart() {
  yield debounce(1200, DEBOUNCE_SAMPLE_START, runDebounceSampleStart)
}

function* runDebounceSampleStart(action) {
  console.log(`take action ${JSON.stringify(action)}`)
  yield call(sleepAsync, action.payload.count)
  yield put(debounceSampleSuccess())
}

const sleepAsync = async (count) => {
  await new Promise(r => setTimeout(r, 5000))
}

// 省略

動作確認

throttle

実行結果は以下になります。まずdispatchされたaction(payloadのcountが0)でタスクが起動し、1800ミリ秒待ちます。その間dispathされたaction(payloadのcountが1、2、3)ではタスクは起動しません。
1800ミリ秒経過後、保持していた最新のaction(payloadのcountが3)で再度タスクを起動します。

スクリーンショット 2019-09-29 12.13.09.png

画像ではcount4のactionではタスクが起動されていません。これは、指定時間経過後にタスク(count3のaction)が起動されていますが、このタスクが起動してからも指定時間が経過するまで同じactionを保持する状態になっているからです。(ややこしい‥)
count3のタスクが起動してから1800ミリ秒以内にcount4と5のactionがdispatchされているので、最新のaction(countが5)が保持されて1800ミリ秒経過後にタスクが起動されているようです。

debounce

実行結果は以下になります。throttleと比較していくらかシンプルですね。同じactionがdispatchされる度に最新のactionを保持して新たに指定秒数待ちます。待っている間に同じactionが来なかった場合にタスクを起動しています。

スクリーンショット 2019-09-29 12.35.28.png

まとめ

throttleはtakeEveryの代わりに使えそうだと感じました。takeEveryはdispatchされたactionをすべて拾うので、負荷の面でちょっと心配がありますが、throttleならある程度コントロールができます。そのため、actionを取りこぼしたくない、かといって負荷もあまりかけたくない、といった場面では出番がありそうですね。
debounceは‥ちょっと思いつきませんでした。開発を進める中で有効に活用できる場面が出てきたらまた紹介したいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonのフレームワークFlaskを使用してWebアプリ作成の物語(6)非同期通信でFlaskにアクセスしてよう

目的

  • 非同期通信でflaskに問い合わせをしてみよう

本編

前回まで

PythonのフレームワークFlaskを使用してWebアプリ作成の物語(5)nginxの登場

前回まではnginxを使用して静的コンテンツの分離を行ってみました。
今回はJavaScriptを使用して、Webサーバ(flask)にアクセスしてみましょう。

今回のソースコード

https://github.com/oq-Yuki-po/flask-tutorial/tree/chapter_006

フォルダ構成

.
├── README.md
├── client
│   ├── css
│   │   └── index.css
│   ├── html
│   │   └── index.html
│   └── js
│       └── index.js
├── docker-compose.yml
├── nginx
│   ├── Dockerfile
│   └── nginx.conf
├── postgresql
│   └── init
│       ├── 1_create_db.sql
│       └── 2_create_table.sh
├── requirements.txt
├── src
│   ├── UserModel.py
│   ├── main.py
│   └── setting.py
└── test.http

イメージ図

flask.png

投稿数が多くなってきたので、一旦整理してみます。

まず、サーバは全部で3つあります。

  • nginx ・・・ htmlやcssなどの静的コンテンツの置き場所
  • Flask ・・・ アプリケーションのCRUD処理を担当(データの保存や読み込み)
  • PostgreSQL ・・・ DBとして使用

FlaskとPostgreSQLの繋ぎ役はSQLAlchmyにお願いしています。
今回の記事では、NginxとFlaskとの繋ぎ役の部分をJavaScriptで実装していきます。

Flaskの準備

Flask側の準備からしていきましょう。
処理は簡単に、登録と取得の処理を実装します。
単純に、登録と取得の結果を返しているのみです。

from flask import Flask, request
from UserModel import User
from setting import session
from sqlalchemy import *
from sqlalchemy.orm import *
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

# 登録処理
@app.route('/', methods=["POST"])
def register_record():

    name = request.form['name']

    session.add(User(name))

    session.commit()

    message = name + "の登録が完了しました!"

    return message

# 取得処理
@app.route('/', methods=["GET"])
def fetch_record():

    name = request.args.get('name')

    db_user = session.query(User.name).\
        filter(User.name == name).\
        all()

    if len(db_user) == 0:
        message = "は登録されていません。"
    else:
        message = "は登録されています。"

    return name + message

if __name__ == '__main__':
    app.run()

非同期通信の準備(JavaScript)

次にJavaScriptでの非同期通信の実装を行います。

最初にボタン押下時の関数(checkregister)をそれぞれセットしています。

ここでのポイントはXMLHttpRequestオブジェクトです。

このオブジェクトは簡単に言うと非同期通を実現させる為に使用しています。

非同期通信を行うことで、ページ全体を再読み込みさせることなく部分的に更新させることが可能になります。

身近な例だとGoogleMapとかがそうです。

拡大や縮小をする度に、ページ全体が更新されてたら検索しにくいですよね??

下記の記事で詳しく説明してくれています。

https://qiita.com/hisamura333/items/e3ea6ae549eb09b7efb9

https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest

document.getElementById('check').onclick = function () {
    check();
}

document.getElementById('register').onclick = function () {
    register();
}


function check() {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {
            document.getElementById('message').innerText = this.responseText;
        }
    };
    const user_name = document.getElementById('user_name').value;
    // ここのURLはFlask起動時に表示されるURLにすること
    xhttp.open("GET", `http://127.0.0.1:5000/?name=${user_name}`, true);
    xhttp.send();
}

function register() {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {
            document.getElementById('message').innerText = this.responseText;
        }
    };
    const user_name = document.getElementById('user_name').value;
    xhttp.open("POST", "http://127.0.0.1:5000/", true);
    // POSTはURLにパラメータを載せないので、以下のようにやるよ
    xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xhttp.send(`name=${user_name}`);
}

動作確認

ここまで準備ができたら

docker-compose up -dでコンテナを立ち上げて

falskも起動して、動作を確認してみよう

ブラウザ上から
http://localhost/
にアクセス

次回

  • flaskをコンテナ化してみよう(uwsgiの利用)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonのフレームワークFlaskを使用してWebアプリ作成の物語(6)非同期通信でFlaskにアクセスしてみよう

目的

  • 非同期通信でflaskに問い合わせをしてみよう

本編

前回まで

PythonのフレームワークFlaskを使用してWebアプリ作成の物語(5)nginxの登場

前回まではnginxを使用して静的コンテンツの分離を行ってみました。
今回はJavaScriptを使用して、Webサーバ(flask)にアクセスしてみましょう。

今回のソースコード

https://github.com/oq-Yuki-po/flask-tutorial/tree/chapter_006

フォルダ構成

.
├── README.md
├── client
│   ├── css
│   │   └── index.css
│   ├── html
│   │   └── index.html
│   └── js
│       └── index.js
├── docker-compose.yml
├── nginx
│   ├── Dockerfile
│   └── nginx.conf
├── postgresql
│   └── init
│       ├── 1_create_db.sql
│       └── 2_create_table.sh
├── requirements.txt
├── src
│   ├── UserModel.py
│   ├── main.py
│   └── setting.py
└── test.http

イメージ図

flask.png

投稿数が多くなってきたので、一旦整理してみます。

まず、サーバは全部で3つあります。

  • nginx ・・・ htmlやcssなどの静的コンテンツの置き場所
  • Flask ・・・ アプリケーションのCRUD処理を担当(データの保存や読み込み)
  • PostgreSQL ・・・ DBとして使用

FlaskとPostgreSQLの繋ぎ役はSQLAlchmyにお願いしています。
今回の記事では、NginxとFlaskとの繋ぎ役の部分をJavaScriptで実装していきます。

Flaskの準備

Flask側の準備からしていきましょう。
処理は簡単に、登録と取得の処理を実装します。
単純に、登録と取得の結果を返しているのみです。

from flask import Flask, request
from UserModel import User
from setting import session
from sqlalchemy import *
from sqlalchemy.orm import *
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

# 登録処理
@app.route('/', methods=["POST"])
def register_record():

    name = request.form['name']

    session.add(User(name))

    session.commit()

    message = name + "の登録が完了しました!"

    return message

# 取得処理
@app.route('/', methods=["GET"])
def fetch_record():

    name = request.args.get('name')

    db_user = session.query(User.name).\
        filter(User.name == name).\
        all()

    if len(db_user) == 0:
        message = "は登録されていません。"
    else:
        message = "は登録されています。"

    return name + message

if __name__ == '__main__':
    app.run()

非同期通信の準備(JavaScript)

次にJavaScriptでの非同期通信の実装を行います。

最初にボタン押下時の関数(checkregister)をそれぞれセットしています。

ここでのポイントはXMLHttpRequestオブジェクトです。

このオブジェクトは簡単に言うと非同期通を実現させる為に使用しています。

非同期通信を行うことで、ページ全体を再読み込みさせることなく部分的に更新させることが可能になります。

身近な例だとGoogleMapとかがそうです。

拡大や縮小をする度に、ページ全体が更新されてたら検索しにくいですよね??

下記の記事で詳しく説明してくれています。

https://qiita.com/hisamura333/items/e3ea6ae549eb09b7efb9

https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest

document.getElementById('check').onclick = function () {
    check();
}

document.getElementById('register').onclick = function () {
    register();
}


function check() {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {
            document.getElementById('message').innerText = this.responseText;
        }
    };
    const user_name = document.getElementById('user_name').value;
    // ここのURLはFlask起動時に表示されるURLにすること
    xhttp.open("GET", `http://127.0.0.1:5000/?name=${user_name}`, true);
    xhttp.send();
}

function register() {
    var xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function () {
        if (this.readyState == 4 && this.status == 200) {
            document.getElementById('message').innerText = this.responseText;
        }
    };
    const user_name = document.getElementById('user_name').value;
    xhttp.open("POST", "http://127.0.0.1:5000/", true);
    // POSTはURLにパラメータを載せないので、以下のようにやるよ
    xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xhttp.send(`name=${user_name}`);
}

動作確認

ここまで準備ができたら

docker-compose up -dでコンテナを立ち上げて

falskも起動して、動作を確認してみよう

ブラウザ上から
http://localhost/
にアクセス

次回

  • flaskをコンテナ化してみよう(uwsgiの利用)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google Authenticator のブラウザ版を作ってみた

はじめに

多要素認証(MFA)に使われる Google AuthenticatorMicrosoft Authenticator は、モバイル端末で使えるソフトウェアトークンだ。
ソフトウェアトークンとはワンタイムパスワード(使い捨てパスワード)を表示するアプリのことで、2段階認証アプリとも呼ばれる。
auth.png
今回たまたま、2段階認証アプリのブラウザ動作版をサクっと作る機会があったので、その実装方法を解説する。

例えば・・・従業員アカウントの秘密鍵(QRコードのotpauth URIにあるsecret)を情報システム部門で管理し、秘密鍵からワンタイムパスワードを表示する社内イントラのサイトを作れば、社内からしかログインできないシステムを容易に構築できるという寸法だ。

ワンタイムパスワードの計算方法

GoogleやMicrosoftの2段階認証アプリでは、次のワンタイムパスワード(以下OTPと略す)に対応している。

TOTPの計算方法(アルゴリズム)はRFCで公開されているから仕様書を読めば誰でも実装できる。RFC6238ではJavaの実装例も載っているので、Javaが読めれば他のプログラミング言語に移植するのも容易だろう。
TOTPは一定時間ごとに変化するHOTP(RFC4226)であり、ハッシュアルゴリズムにHMAC-SHA-1を使っているからRFC2404の実装でもある。

TOTPの仕組みはシンプルで、【サーバとクライアントで共有する秘密鍵】と【現在時刻(UNIX Time)をtime step(デフォルト30秒)で除したカウンター値】をSHA1でハッシュし、クライアントとサーバで一致するか確認しているだけだ。

ということで、秘密鍵から6桁のOTPを求めるコードをPHPで書くと、次のようになる。

PHP
function secret2otp($secret) {
  // 秘密鍵はBase32文字列で来るのでデコード
  $n = $bs = 0;
  $seed = '';
  for ($i = 0; $i < strlen($secret); $i++) {
    $n <<= 5;
    $n += stripos('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', $secret[$i]);
    $bs = ($bs + 5) % 8;
    @$seed .= $bs < 5 ? chr(($n & (255 << $bs)) >> $bs) : null;
  }
  // UNIX Timeをtime stepで除したカウンタ値と鍵を合わせ、SHA1でハッシュ
  $hash = hash_hmac('sha1', str_pad(pack('N', intval(time() / 30)), 8, "\x00", STR_PAD_LEFT), $seed, false);
  // 下6桁を返す
  return sprintf('%06u', (hexdec(substr($hash, hexdec($hash[39]) * 2, 8)) & 0x7fffffff) % 1000000);
}

PHPにはBase32をデコードする関数は無いので、160ビット(32文字)の秘密鍵を最初にデコードする処理があるぶん長くなっているが、実処理がシンプルなのはお分かりいただけると思う。

ブラウザで動かすのならJavaScriptで書く方法もあるが、秘密鍵をクライアント側に渡さないで済む方法を思いつかなかったので、サーバ側で処理することにした。

実装方法

では、ブラウザで動かすための実装について具体的に解説していこう。
デモを用意したので、まずは見てもらいたい。もちろん設定された秘密鍵は適当である。
http://demo.mindwood.jp/WebAuthenticator.php?uid=example
image.png

IDから秘密鍵を求める

アカウントに相当するIDはGETパラメータで受け取ることにした。つまり、ID(デモでは "example")はURLに含まれる。
秘密鍵をハードコーディングしているが、実際には、CSVファイルやRDB、LDAP等のディレクトリサービスから秘密鍵を取得するのが現実的だろう。

PHP
$uid=$_GET['uid'];
if($uid=='example') $sec='MINDWOOD2QIITA3SECRETKEY4DEMO567';  // 秘密鍵(もちろん架空)
if($uid=='xxxxxxx') $sec='AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
if(is_null($sec)) {
    header('HTTP/1.1 404 Not Found');
    exit;
}
$title="$uid のワンタイムパスワード";  // タイトルを設定

未知のIDなら404 Not Foundをブラウザに返す。

なお、Apacheのmod_rewriteモジュールが許可された環境なら、パラメータ指定の動的なURLを、静的なURLにマッピングできる。

動的URL(パラメータ渡し)
http://example.com/WebAuthenticator.php?uid=example
静的URL(ディレクトリ渡し)
http://example.com/WebAuthenticator/example

参考までにmod_rewriteのApache定義例を示す。htaccessAliasを使っているため環境に応じて修正を要する。

/var/www/html/auth/.htaccess
RewriteEngine on
RewriteBase /WebAuthenticator
RewriteRule ^([0-9A-Za-z]+)$ WebAuthenticator.php?uid=$1
/etc/httpd/conf.d/auth.conf
Alias /WebAuthenticator /var/www/html/auth/
<Directory "/var/www/html/auth">
    Options -Indexes
    AllowOverride All
    Require all granted
</Directory>

HTMLコード

HTMLも至ってシンプルである。PHPタグで、冒頭の関数からOTPを算出し表示しているだけだ。

HTML
<body>
    <h1><?php echo $title; ?></h1>
    <div id="code">
        <span>
            <?php echo secret2otp($sec); ?><!-- OTPを表示 -->
        </span>
    </div>
</body>

以上で完成としても良いのだが、少し味気ないので、jQueryで肉付けする。

残り時間を表示させてみよう

OTPは30秒ごとに更新される。つまり使用期限がある。
30秒経つとOTPは変わってしまうため、ソフトウェアトークン同様、残り時間を画面に表示してみる。
ここでは、進捗度を表すサークルバー(プログレスサークル)のjQuery実装、jquery-circle-progressで飾る。

まず、プログレスサークルの表示エリアをDIVタグで確保する。

HTML
<div id="circle">
    <strong></strong>
</div>

setIntervalで1秒ごとに残り時間を更新する。

JavaScript
setInterval(function() {
    var now = new Date();
    var left = 30 - (now.getSeconds() % 30);  // 秒を30で割った余りを残り時間とする
    $('#code span').css('color', left < 6 ? 'red':'green');  // 残り5秒以下なら赤くする
    $('#circle strong').show();
    $('#circle strong').fadeOut(900);  // 文字をふわっと表示させるアニメーション効果
    $('#circle strong').text(left);
    $('#circle').circleProgress({  // ここの書き方は公式ドキュメントを参照
        startAngle: -1.55,
        reverse: true,
        value: left / 30,
        animationStartValue: (1 + left) / 30,
        size: 100,
        thickness: 16,
        fill: { gradient: ['#0681c4', '#07c6c1'] }
    });
    if (left == 30) {
        location.reload();
    }
}, 1000);

NTPで時刻同期している前提だが、もしWebサーバとクライアントの間でタイムラグがあるなら次のように調整すると良い。

JavaScript
now.setSeconds(now.getSeconds() - 3);  // 3秒ずらす

なお、プログレスサークルを使って、サーバ側の進捗をリアルタイムに表示させる方法については、別記事に拙稿を書いているので興味のある人は参照して欲しい。
:point_right_tone2: プログレスサークルをLaravelの非同期処理でリアルタイムに表示

OTPをコピペできるようにしよう

クリップボードにOTPをコピーできると便利なので、コピーボタンを追加する。

HTML
<div id="copy">
    <button>COPY</button>
</div>

コピーボタンがクリックされたときの挙動を書く。

JavaScript
$('#copy').on('click', function() {
    var clipboard = $('<textarea></textarea>');
    clipboard.addClass('clipboard');
    clipboard.html($('#code span').html().trim());
    $(this).append(clipboard);
    clipboard.select();  // 選択させる
    document.execCommand('copy');  // 現在選択している部分をコピー
    clipboard.remove();
    // イベント発火をユーザーに分かり易くするためアニメーション効果を入れる
    $('#code').addClass('animated rubberBand').one('animationend', function () {
        $('#code').removeClass('animated rubberBand');
    });
});

完成版

完成したソースコードはGistに置いた。
https://gist.github.com/mindwood2/5ad399b33a03c0cc47c1e7ef57194cd9

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む