npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

kdan-cloud

v1.0.0

Published

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Version: 1.0.0](https://img.shields.io/badge/Version-1.0.0-Green.svg)](https://bitbucket.org/kdanmobile/kdan-cloud)

Downloads

2

Readme

Kdan Cloud

License: MIT Version: 1.0.0

Table of Contents

[TOC]


About New Kdan Cloud for v4

  • 兼容 v3 版本 share folder 流程,folder owner 若是從 v3 API 發送邀請的就會進到 pages/share/folder/index 走接受邀請流程。
  • 支援 request link 頁面,但不支援建立 request link。

Get Started Immediately

  1. install dependency package in project

    yarn

  2. run development mode

    OPENSSL_PASS=${secret_key} yarn dev

  3. run test

    yarn test

  4. run production mode

    yarn build OPENSSL_PASS=${secret_key} yarn start

  5. visit http://localhost:3000 with browser


Introduction

file storage and sharing file or folder web app

  • 檔案上傳或下載
  • 共享檔案或文件夾
  • 線上預覽檔案
  • 新增刪除檔案或資料夾
  • 搬移檔案或資料夾

Folder Structure

__test__---------測試腳本
apis-------------API 腳本
components---- UI 元件(通常是無狀態)
containers------ 綁定狀態的元件(通常有狀態)
config---------- 環境變數設定檔
constants-------共用常數
helpers--------- 幫忙函示
global---------- 共用樣式
pages---------- 根據檔案名稱與路由做關聯
hoc-------------high order component
modules--------landing page 元件和彈出視窗模組
public---------- 靜態檔案放這裡
redux-----------redux action, reducer and saga
server----------透過 express 自訂路由和 server 端預處理


Deploy

preparing

merge 到 preparing 分支後,由 gitlab 自動部署到 preparing 環境

production

merge 到 master 分支後建立 tag,由 gitlab 自動部署到 production 環境


Coding Rule

coding rule 使用 airbnb 的設定,設定檔為.eslintrc.js

eslint-config-airbnb


Layout and Style

我們拆成數個組件寫 CSS-in-JS,使用 styled-components package,將 css class 組件化。部分共用的樣式放在 global 資料夾裡。

styled-components


Locales

多語使用了 next-i18next,添加的方式為請到 public/locales/[locale]/[namespace],在 namespace 這隻 json 檔新增翻譯的內容,根據你的命名空間使用 useTranslation 這個 HOC 即可。

import React from 'react'

import { useTranslation } from 'next-i18next'

const Footer = ({ t }) => {
  render() {
    return (
      <footer>{t('description')}</footer>
    )
  }
}

export default useTranslation('footer')(Footer)

next-i18next


Features

Routes

由於需要 SSR 和快速開發故選用了 next.js 當作伺服器端的宣染框架,在 next.js 的路由都是透過 pages 資料夾裡的檔案名增來做路由關聯。若要自訂路由請參照 next.js 教學

custom server

Lazy Loading

為了讓使用者有更好的體驗以及搜尋引擎優化,當使用者訪問頁面時我們只載入上方 Header 和 Banner,剩下的部分透過 next/dynamic 延遲載入。

Redirect To CN

使用https://pro.ip-api.com判斷IP是否為中國,若是中國就轉址到https://cloud.kdan.cn

pro-ip

server/index.js

server.use((req, res, _next) => {
    const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;

    fetch(`https://pro.ip-api.com/json/${ip}?key=Y6uuwTkrwOiyozZ`)
      .then(_res => _res.json)
      .then((_res) => {
        if (_res.countryCode === 'CN' && req.headers.host !== 'cloud.kdan.cn') {
          res.redirect(config.HOST_CN);
        } else {
          _next();
        }
      });
  });

Login Flow


使用者登入實作了 Oauth2 機制,當使用者按下登入會轉址到 member center,並輸入帳號密碼登入成功後返回/kdanmobile/callback,整個過程在使用了 passport.js 完成。

passport.js

server/authStrategy.js

const kdanStrategy = new OAuth2Strategy(
    {
      authorizationURL: `${config.MEMBER_CENTER}/oauth/authorize`,
      tokenURL: `${config.MEMBER_CENTER}/oauth/token`,
      clientID: global.env.CLIENT_ID,
      clientSecret: global.env.CLIENT_SECRET,
      callbackURL: `${config.HOST}${kdanCallbackPath}`,
    },
    (accessToken, refreshToken, profile, done) => {
      done(null, { accessToken, refreshToken });
    },
  );

server/middleware/auth.js

router.get(
  '/kdanmobile/callback',
  passport.authenticate('provider', {
    failureRedirect: '/error',
    session: false,
  }),
  (req, res) => {
    res.cookie('access_token', req.user.accessToken, {
      expires: new Date(Date.now() + 2 * 3600 * 1000),
      sameSite: 'None',
      secure: !isDev,
    });

    ...

    res.redirect('/files');
  },
);

Upload Files

上傳檔案使用 AWS SDK 所提供的 upload 方法上傳至 S3,流程是先呼叫 createUploadMission API,後端會返回 credentials、missionId、objectKey,再將返回的 data 跟檔案一同當成參數傳給 upload method。

helpers/aws.js

export const uploadFile = ({
  credentials, file, missionId, objectKey, accessToken, bucket,
}) => {
  const options = {
    accessKeyId: credentials.access_key_id,
    secretAccessKey: credentials.secret_access_key,
    sessionToken: credentials.session_token,
    region: 'us-east-1',
  };

  const s3 = new S3(options);

  ...

  return s3.upload(param);
};

Modal

在此專案呢,使用了很多的彈出視窗,在 modules/modal 裡新增彈出視窗的內容,之後在 containers/Modal.js 裡 import 在 modules 資料夾裡的元件,透過 switch case 根據 modalType 切換內容。

使用方法

import React from 'react';

const Header = () => (
  <Modal
    modalType="email_input"
    ...
  />
);

export default Header;

Redux and Redux-Saga

我們使用 Redux 將跨組件共用的狀態統一管理。在 Redux 的世界裡我們呼叫 action 來描述狀態的改變。而某些情境需要複雜的業務邏輯,為了讓 action 和 reducer 保持單純,我們使用 redux-saga 把邏輯寫在這個 middleware,透過 saga 提供的 effects api 可以監聽 action 並執行邏輯返回一個新的 action 把結果寫到 redux store。

Redux-saga

為了確保每次狀態更動都是 Immutable,使用 Immer 提供的 produce function 他接受兩個參數分別是 current state 和 producer funtion 執行後將會回傳一個 new immutable tree。

import produce from "immer"

const initialState = [
  {
    todo: "Try immer",
    done: false
  }
];

const reducer = (state = initialState, action) => (
  produce(baseState, draftState => {
    switch (action.type) {
      case ACTION_TYPE:
        draftState.push({todo: "Tweet about it"})
        draftState[1].done = true
      break;
      ...
    }
  })
);

Immer


Test

用 Jest 搭配 testing-library 做單元測試,使用後者來宣染出 DOM 節點並取得內容,再用 Jest 提供的方法斷言。

note: 部分元件我們會調用到package的HOC,像是多語useTranslation和redux的connect,但因為單元測試時只會實例當前元件,所以我們要在__mocks__資料夾裡撰寫對應的mock function

config/jest.config.js

module.exports = {
  rootDir: '../',
  transform: {
    '\\.(js|jsx)?$': 'babel-jest',
  },
  testMatch: ['<rootDir>/__test__/(*.)test.{js, jsx}'],
  moduleFileExtensions: ['js', 'jsx', 'json'],
  testPathIgnorePatterns: ['<rootDir>/.next/', '<rootDir>/node_modules/'],
};

__test__/header.test.js

import React from 'react';
import { render, cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';

import Header from '../components/Header';

describe('test Header', () => {
  afterEach(cleanup);

  test('test Landing Page Header', () => {
    const {
      getByTestId,
    } = render(<Header isLanding />);

    expect(getByTestId('display_logo').innerHTML).toContain('data-kind="main-logo"');
  });
});

Jest @testing-library/react