EExcel 丞燕快速查詢2

EExcel 丞燕快速查詢2
EExcel 丞燕快速查詢2 https://sandk.ffbizs.com/

nestjs csrf

https://gitissue.com/repos/jiayisheji/blog

pass csrf https://github.com/expressjs/csurf/issues/21

main.ts



import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path'
import { AppModule } from './app.module';
import * as cookieSession from 'cookie-session';
import * as helmet from 'helmet';
import * as cookieParser from 'cookie-parser';
import * as csurf from 'csurf';
import * as rateLimit from 'express-rate-limit';

async function bootstrap() {
  const app = await NestFactory.create(
    AppModule,
  );

  app.init()
  
  app.useStaticAssets(join(__dirname, '..', 'public'));
  app.setBaseViewsDir(join(__dirname, '..', 'views'));
  app.setViewEngine('pug');

  app.set('trust proxy', 1);

  app.use(cookieSession({
    name: 'session',
    keys: ['key1', 'key2']
  }));

  //app.enableCors();
  app.use(helmet());
  app.use(cookieParser());
  //app.use(csurf({ cookie: true }));  //正常是這行,但有些API POST時需要略過csrf
  app.use(function (req, res, next) {
    var mw = csurf({ cookie: true });
    // console.log(req.url)  // check real get url
    if (req.url === '/testpostcsrf') return next();  //pass csrf check
    mw(req, res, next);
  });
  app.use(
    rateLimit({
      windowMs: 15 * 60 * 1000, // 15 minutes
      max: 100, // limit each IP to 100 requests per windowMs
    }),
  );

  await app.listen(3000);
}
bootstrap();


layout.pug



doctype html
html
  head
    title= title
    meta(content= csrfToken, name='csrf-token')
  body
    block content

login.pug



extends layout

block content
    h1 Please log in
    if error
        p.
            #{error}
    form(action="/login",method="POST")
        input(type="hidden",name="_csrf",value=csrfToken)
        input(type="hidden",name="challenge",value=challenge)
        table(style="")
            tr
                td
                    input(type="email",id="email",name="email",placeholder="email@foobar.com")
                td.
                    (Example: "foo@bar.com")
            tr
                td
                    input(type="password",id="password",name="password")
                td.
                    (Example: "foobar")
        input(type="checkbox",id="remember",name="remember",value="1")
        label(for="remember") Remember me
        br
        input(type="submit",id="accept",value="Log in")




nest.js 让我们用Nestjs来重写一个CNode

https://gitissue.com/repos/jiayisheji/blog

這網站中的 让我们用Nestjs来重写一个CNode(上、中、下) 幫了大忙,減少大量的浪費時間

===========
html -> jade/pug
http://html2jade.aaron-powell.com/

when you html meta want to become jade/pug

layout.pug

meta(content= csrfToken, name='csrf-token')

nodejs expressjs

https://expressjs.com/en/advanced/best-practice-security.html

OpenID hydra docker-compose hydra-login-consent-node mariadb


docker-compose


version: '3.3'

services:
  ory-hydra-postgres:
    image: postgres:9.6
    #restart: always
    environment:
      - POSTGRES_USER=hydra
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=hydra
    volumes:
      - hydradata:/var/lib/postgresql/data:rw
    networks:
      - openid
  # 第一次執行postgres要做資料庫格式建立 PS: network依佈屬環境為主 docker network ls 確認
  # docker run -it --rm \
  #   --network openid \
  #   oryd/hydra:latest \
  #   migrate sql --yes postgres://hydra:secret@ory-hydra-postgres:5432/hydra?sslmode=disable
  
  ory-hydra:
    image: oryd/hydra:latest
    restart: unless-stopped
    ports:
      - "9001:4444"
      - "9002:4445"
    environment:
      - SECRETS_SYSTEM=this_needs_to_be_the_same_a
      - DSN=postgres://hydra:secret@ory-hydra-postgres:5432/hydra?sslmode=disable
      - URLS_SELF_ISSUER=https://openid.hydra:9001/
      - URLS_CONSENT=http://192.168.99.100:9020/consent
      - URLS_LOGIN=http://192.168.99.100:9020/login
      - LOG_LEVEL=debug
      - OAUTH2_EXPOSE_INTERNAL_ERRORS=true
      - SERVE_PUBLIC_CORS_ENABLED=true
      - SERVE_PUBLIC_CORS_ALLOWED_METHODS=POST,GET,PUT,DELETE
      - SERVE_ADMIN_CORS_ENABLED=true
      - SERVE_ADMIN_CORS_ALLOWED_METHODS=POST,GET,PUT,DELETE
      - SERVE_TLS_KEY_BASE64=LS0tLS1CRUdJTiBFQyBQQVJBTUVURVJTLS0tLS0KQmdVcmdRUUFJZz09Ci0tLS0tRU5EIEVDIFBBUkFNRVRFUlMtLS0tLQotLS0tLUJFR0lOIEVDIFBSSVZBVEUgS0VZLS0tLS0KTUlHa0FnRUJCRENLbkdnVnFJVzdZaW5iUWV5UEd5UTQ0R3U2VVFEelU5SENLYjMzTWlmeFJYRTBkbnU2KzdadQowdEJUcUhQRHVMeWdCd1lGSzRFRUFDS2haQU5pQUFSbng1Nk9jeGNyRWRsYmU4TXRSdUVxWGV2OEREcmh6ZWJGCjM4NlI4Q2RQWDRlUWI2Zll6ekFUL3V3STBsTDdvRmlEWEM3Q0JLWmZUcTdFSzN4TzNXWlpSSjJrMEQ3TnNLd2cKVEpZenJxT0JpczBNeGtva2FUWVVyemhKMXBKY3lmWT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
      - SERVE_TLS_CERT_BASE64=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNQVENDQWNLZ0F3SUJBZ0lKQU13RjRiVDRvSnh0TUFvR0NDcUdTTTQ5QkFNQ01Gd3hDekFKQmdOVkJBWVQKQWtGVk1STXdFUVlEVlFRSURBcFRiMjFsTFZOMFlYUmxNU0V3SHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbgphWFJ6SUZCMGVTQk1kR1F4RlRBVEJnTlZCQU1NREc5d1pXNXBaQzVvZVdSeVlUQWVGdzB4T1RBMk1UY3dNVEl4Ck16ZGFGdzB5T1RBMk1UUXdNVEl4TXpkYU1Gd3hDekFKQmdOVkJBWVRBa0ZWTVJNd0VRWURWUVFJREFwVGIyMWwKTFZOMFlYUmxNU0V3SHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXhGVEFUQmdOVgpCQU1NREc5d1pXNXBaQzVvZVdSeVlUQjJNQkFHQnlxR1NNNDlBZ0VHQlN1QkJBQWlBMklBQkdmSG5vNXpGeXNSCjJWdDd3eTFHNFNwZDYvd01PdUhONXNYZnpwSHdKMDlmaDVCdnA5alBNQlArN0FqU1V2dWdXSU5jTHNJRXBsOU8KcnNRcmZFN2RabGxFbmFUUVBzMndyQ0JNbGpPdW80R0t6UXpHU2lScE5oU3ZPRW5Xa2x6SjlxTlFNRTR3SFFZRApWUjBPQkJZRUZHK3Z6ZkIxYmVnM1VadEpYRXZWOWRNa1hvNmdNQjhHQTFVZEl3UVlNQmFBRkcrdnpmQjFiZWczClVadEpYRXZWOWRNa1hvNmdNQXdHQTFVZEV3UUZNQU1CQWY4d0NnWUlLb1pJemowRUF3SURhUUF3WmdJeEFMUHYKODZFSFRUVElLcEJHdlQrY2NWN3djSC84SFIrc2xhZC9ZUFhLUlZwd2RDbzUyZVRPV3BDS2dGamtHNEJhd1FJeApBTGxGZFgwbEk2ZzhXS3lhRTVmKzJGZEkxYWVqQ0Ftd0xPTTZTRFJhNFVHbitDa2VwOEljeG1CTDIvQmUzSVZ6CjhnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
    networks:
      - openid


# 快速建立 auth-doce-client PS: network依佈屬環境為主 docker network ls 確認
#docker run --rm -it \
#  -e HYDRA_ADMIN_URL=https://ory-hydra:4445 \
#  --network openid \
#  oryd/hydra:latest \
#  clients create --skip-tls-verify \
#    --id auth-code-client \
#    --secret secret \
#    --grant-types authorization_code,refresh_token \
#    --response-types code,id_token,token \
#    --scope openid,offline,photos.read \
#    --callbacks https://t.tt:9010/callback

  ory-hydra-login-consent:
    #image: oryd/hydra-login-consent-node:latest
    build:
      context: hydra-login-consent-node/
    restart: unless-stopped
    ports:
      - "9020:3000"
    environment:
      - HYDRA_ADMIN_URL=https://ory-hydra:4445
      - NODE_TLS_REJECT_UNAUTHORIZED=0
    volumes:
      - hydraloginconsent:/usr/src/app:rw
    depends_on:
      - mariadb
    networks:
      - openid
  
  mariadb:
    image: mariadb:10.4.6
    #restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_DATABASE=openid
    command: ['--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci']
    #第一次使執行db_init_sql.txt
    networks:
      - openid

  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080
    depends_on:
      - mariadb
    networks:
      - openid

volumes:  
  hydradata: 
  hydraloginconsent:
    
networks:
  openid:
    driver: bridge


Use adminer test maraidb: http://192.168.99.100:8080 root/secret


mariadb init


DROP DATABASE IF EXISTS `openid`;
CREATE DATABASE `openid` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */;
USE `openid`;

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` text COLLATE utf8mb4_unicode_ci NOT NULL,
  `email` text COLLATE utf8mb4_unicode_ci NOT NULL,
  `password` text COLLATE utf8mb4_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT INTO `user` (`id`, `name`, `email`, `password`) VALUES
(1, 'foobar', 'foo@bar.com', '3858f62230ac3c915f300c664312c63f');

ory-hydra-login-consent modify package.json add


"md5": "^2.2.1",
"mysql": "^2.17.1"

ory-hydra-login-consent add db/database.js


var mysql = require('mysql');

var pool = mysql.createPool({
  host     : 'mariadb',
  user     : 'root',
  password : 'secret',
  database: 'openid'
});

var query=function(sql,options,callback){  
  pool.getConnection(function(err,conn){  
    pool.query
    if(err){  
      callback(err,null,null);  
    }else{  
      conn.query(sql,options,function(err,results,fields){  
        //释放连接  
        conn.release();  
        //事件驱动回调  
        callback(err,results,fields);  
      });  
    }  
  });  
}; 

module.exports = {query, pool}

ory-hydra-login-consent modify routes/login.js


...

router.post('/', csrfProtection, function (req, res, next) {
  // The challenge is now a hidden input field, so let's take it from the request body instead
  var challenge = req.body.challenge;

  var sql = "select count(*) as count from user where email = ? and password = ?"
  var params = [req.body.email, md5(req.body.password)]
  //db.get(sql, params, (err, row) => {
  pool.query(sql, params, (err, row) => {
    if (err) {
      res.status(400).json({"db error":err.message});
      return;
    }

    if(!(row.count==1)){ //找不到
      res.render('login', {
        csrfToken: req.csrfToken(),
  
        challenge: challenge,
  
        error: 'The username / password combination is not correct'
      });
      return;
    }

    hydra.acceptLoginRequest(challenge, {
      // Subject is an alias for user ID. A subject can be a random string, a UUID, an email address, ....
      subject: req.body.email,
  
      // This tells hydra to remember the browser and automatically authenticate the user in future requests. This will
      // set the "skip" parameter in the other route to true on subsequent requests!
      remember: Boolean(req.body.remember),
  
      // When the session expires, in seconds. Set this to 0 so it will never expire.
      remember_for: 3600,
  
      // Sets which "level" (e.g. 2-factor authentication) of authentication the user has. The value is really arbitrary
      // and optional. In the context of OpenID Connect, a value of 0 indicates the lowest authorization level.
      // acr: '0',
    })
    .then(function (response) {
      // All we need to do now is to redirect the user back to hydra!
      res.redirect(response.redirect_to);
    })
    // This will handle any error that happens when making HTTP calls to hydra
    .catch(function (error) {
      next(error);
    });

  });

  // Let's check if the user provided valid credentials. Of course, you'd use a database or some third-party service
  // for this!
  // if (!(req.body.email === 'foo@bar.com' && req.body.password === 'foobar')) {
  //   // Looks like the user provided invalid credentials, let's show the ui again...

  //   res.render('login', {
  //     csrfToken: req.csrfToken(),

  //     challenge: challenge,

  //     error: 'The username / password combination is not correct'
  //   });
  //   return;
  // }

  // Seems like the user authenticated! Let's tell hydra...
  // hydra.acceptLoginRequest(challenge, {
  //   // Subject is an alias for user ID. A subject can be a random string, a UUID, an email address, ....
  //   subject: 'foo@bar.com',

  //   // This tells hydra to remember the browser and automatically authenticate the user in future requests. This will
  //   // set the "skip" parameter in the other route to true on subsequent requests!
  //   remember: Boolean(req.body.remember),

  //   // When the session expires, in seconds. Set this to 0 so it will never expire.
  //   remember_for: 3600,

  //   // Sets which "level" (e.g. 2-factor authentication) of authentication the user has. The value is really arbitrary
  //   // and optional. In the context of OpenID Connect, a value of 0 indicates the lowest authorization level.
  //   // acr: '0',
  // })
  //   .then(function (response) {
  //     // All we need to do now is to redirect the user back to hydra!
  //     res.redirect(response.redirect_to);
  //   })
  //   // This will handle any error that happens when making HTTP calls to hydra
  //   .catch(function (error) {
  //     next(error);
  //   });

  // You could also deny the login request which tells hydra that no one authenticated!
  // hydra.rejectLoginRequest(challenge, {
  //   error: 'invalid_request',
  //   error_description: 'The user did something stupid...'
  // })
  //   .then(function (response) {
  //     // All we need to do now is to redirect the browser back to hydra!
  //     res.redirect(response.redirect_to);
  //   })
  //   // This will handle any error that happens when making HTTP calls to hydra
  //   .catch(function (error) {
  //     next(error);
  //   });
});

https://t.tt:9010 When login id/pwd, can use adminer change database user email/password.

OpenID hydra docker-compose


docker-compose

version: '3.3'

services:
  ory-hydra-postgres:
    image: postgres:9.6
    #restart: always
    environment:
      - POSTGRES_USER=hydra
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=hydra
    volumes:
      - hydradata:/var/lib/postgresql/data:rw
    networks:
      - openid

# 第一次執行postgres要做資料庫格式建立 PS: network依佈屬環境為主 docker network ls 確認
# docker run -it --rm \
#   --network openid \
#   oryd/hydra:latest \
#   migrate sql --yes postgres://hydra:secret@ory-hydra-postgres:5432/hydra?sslmode=disable

  ory-hydra:
    image: oryd/hydra:latest
    restart: unless-stopped
    ports:
      - "9001:4444"
      - "9002:4445"
    environment:
      - SECRETS_SYSTEM=this_needs_to_be_the_same_a
      - DSN=postgres://hydra:secret@ory-hydra-postgres:5432/hydra?sslmode=disable
      - URLS_SELF_ISSUER=https://openid.hydra:9001/
      - URLS_CONSENT=http://192.168.99.100:9020/consent
      - URLS_LOGIN=http://192.168.99.100:9020/login
      - LOG_LEVEL=debug
      - OAUTH2_EXPOSE_INTERNAL_ERRORS=true
      - SERVE_PUBLIC_CORS_ENABLED=true
      - SERVE_PUBLIC_CORS_ALLOWED_METHODS=POST,GET,PUT,DELETE
      - SERVE_ADMIN_CORS_ENABLED=true
      - SERVE_ADMIN_CORS_ALLOWED_METHODS=POST,GET,PUT,DELETE
      - SERVE_TLS_KEY_BASE64=LS0tLS1CRUdJTiBFQyBQQVJBTUVURVJTLS0tLS0KQmdVcmdRUUFJZz09Ci0tLS0tRU5EIEVDIFBBUkFNRVRFUlMtLS0tLQotLS0tLUJFR0lOIEVDIFBSSVZBVEUgS0VZLS0tLS0KTUlHa0FnRUJCRENLbkdnVnFJVzdZaW5iUWV5UEd5UTQ0R3U2VVFEelU5SENLYjMzTWlmeFJYRTBkbnU2KzdadQowdEJUcUhQRHVMeWdCd1lGSzRFRUFDS2haQU5pQUFSbng1Nk9jeGNyRWRsYmU4TXRSdUVxWGV2OEREcmh6ZWJGCjM4NlI4Q2RQWDRlUWI2Zll6ekFUL3V3STBsTDdvRmlEWEM3Q0JLWmZUcTdFSzN4TzNXWlpSSjJrMEQ3TnNLd2cKVEpZenJxT0JpczBNeGtva2FUWVVyemhKMXBKY3lmWT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo=
      - SERVE_TLS_CERT_BASE64=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNQVENDQWNLZ0F3SUJBZ0lKQU13RjRiVDRvSnh0TUFvR0NDcUdTTTQ5QkFNQ01Gd3hDekFKQmdOVkJBWVQKQWtGVk1STXdFUVlEVlFRSURBcFRiMjFsTFZOMFlYUmxNU0V3SHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbgphWFJ6SUZCMGVTQk1kR1F4RlRBVEJnTlZCQU1NREc5d1pXNXBaQzVvZVdSeVlUQWVGdzB4T1RBMk1UY3dNVEl4Ck16ZGFGdzB5T1RBMk1UUXdNVEl4TXpkYU1Gd3hDekFKQmdOVkJBWVRBa0ZWTVJNd0VRWURWUVFJREFwVGIyMWwKTFZOMFlYUmxNU0V3SHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXhGVEFUQmdOVgpCQU1NREc5d1pXNXBaQzVvZVdSeVlUQjJNQkFHQnlxR1NNNDlBZ0VHQlN1QkJBQWlBMklBQkdmSG5vNXpGeXNSCjJWdDd3eTFHNFNwZDYvd01PdUhONXNYZnpwSHdKMDlmaDVCdnA5alBNQlArN0FqU1V2dWdXSU5jTHNJRXBsOU8KcnNRcmZFN2RabGxFbmFUUVBzMndyQ0JNbGpPdW80R0t6UXpHU2lScE5oU3ZPRW5Xa2x6SjlxTlFNRTR3SFFZRApWUjBPQkJZRUZHK3Z6ZkIxYmVnM1VadEpYRXZWOWRNa1hvNmdNQjhHQTFVZEl3UVlNQmFBRkcrdnpmQjFiZWczClVadEpYRXZWOWRNa1hvNmdNQXdHQTFVZEV3UUZNQU1CQWY4d0NnWUlLb1pJemowRUF3SURhUUF3WmdJeEFMUHYKODZFSFRUVElLcEJHdlQrY2NWN3djSC84SFIrc2xhZC9ZUFhLUlZwd2RDbzUyZVRPV3BDS2dGamtHNEJhd1FJeApBTGxGZFgwbEk2ZzhXS3lhRTVmKzJGZEkxYWVqQ0Ftd0xPTTZTRFJhNFVHbitDa2VwOEljeG1CTDIvQmUzSVZ6CjhnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
    networks:
      - openid
    #這行非常重要,docker成功運行後,要進geht console執行 admin.addPeer("enode://444a16729d32431bbdaa594272e3509cdeaaf3c995ffb583589163d35f8b36ad14394ab037ac186525f579700e6500cacfb1f953fdf066fa05da0e1d409f7f79@140.110.18.199:30301")

  ory-hydra-login-consent:
    #image: oryd/hydra-login-consent-node:latest
    build:
      context: hydra-login-consent-node/
    restart: unless-stopped
    ports:
      - "9020:3000"
    environment:
      - HYDRA_ADMIN_URL=https://ory-hydra:4445
      - NODE_TLS_REJECT_UNAUTHORIZED=0
    volumes:
      - hydraloginconsent:/usr/src/app:rw
    networks:
      - openid

# 快速建立 auth-doce-client PS: network依佈屬環境為主 docker network ls 確認
#docker run --rm -it \
#  -e HYDRA_ADMIN_URL=https://ory-hydra:4445 \
#  --network openid \
#  oryd/hydra:latest \
#  clients create --skip-tls-verify \
#    --id auth-code-client \
#    --secret secret \
#    --grant-types authorization_code,refresh_token \
#    --response-types code,id_token,token \
#    --scope openid,offline,photos.read \
#    --callbacks https://t.tt:9010/callback

volumes:  
  hydradata: 
  hydraloginconsent:
    
networks:
  openid:
    driver: bridge

ory-hydra-login-consent download

https://github.com/ory/hydra-login-consent-node
Directory name is hydra-login-consent-node


go run main.go

https://sueboy.blogspot.com/2019/06/openid-hydra-cant-finish-error.html


Broswer https://t.tt:9010

9-HyperLedger-Fabric原理-MSP详解(一)-MSP基础

https://zhuanlan.zhihu.com/p/35683522

Helm charts for running and operating Hyperledger Fabric in Kubernetes

https://github.com/apggroeifabriek/pivt

hyperledger crypetogen

https://hyperledger-fabric.readthedocs.io/en/release-1.4/commands/cryptogen.html

cryptogen is an utility for generating Hyperledger Fabric key material. It is provided as a means of preconfiguring a network for

testing purposes

. It would normally not be used in the operation of a production network.

Firebase auth and upload image

bootstrap + web firebase realtime + firebase storage

Auth:Use Email&password then input one user with email & password. Login use be added user.

index.html


<!doctype html>
<html lang="zh-Hant-TW">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
    <link type="text/css" rel="stylesheet" href="https://cdn.firebase.com/libs/firebaseui/4.0.0/firebaseui.css" />

    <title>Hello, world!</title>
  </head>
  <body>
    <div class="container">
        <div id="firebaseui-auth-container" class="alert alert-light" role="alert"></div>
    </div>
    
    <div class="container my-1">
        <div class="row">
            <div class="col-sm">Login Status
                <div class="user-signed-in" style="display: none;"><span class="badge badge-pill badge-success">user-signed-in</span></div>
                <div class="user-signed-out" style="display: none;"><span class="badge badge-pill badge-secondary">user-signed-out</span></div>
            </div>
            <div class="col-sm"><a class="btn btn-outline-primary" data-toggle="collapse" href="#multiCollapseExample1" role="button" aria-expanded="false" aria-controls="multiCollapseExample1">Account Details</a>
                <div class="collapse multi-collapse" id="multiCollapseExample1">
                    <div class="card card-body">
                        <pre id="account-details">...</pre>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <div class="container my-1">
        <div class="row justify-content-end">
            <div class="col-2">
                <div id="sign-in" class="btn btn-outline-primary" style="display: none;">sign-in</div>
                <div id="sign-out" class="btn btn-outline-danger" style="display: none;">sign-out</div>
            </div>
        </div>
    </div>

    <div class="container my-1">
        <div class="row justify-content-center">
            <div id="loading" class="spinner-border" role="status">
                <span class="sr-only">Loading...</span>
            </div>
        </div>
    </div>
    
    <div class="container my-1">
        <div id="loaded" class="user-signed-in" style="display: none;">檔案上傳
            <div id="filesubmit">
                <input type="file" class="file-select" accept="image/*"/>
                <button class="file-submit">SUBMIT</button>
            </div>
        </div>
    </div>

    <div class="container my-1">
        <div class="row">
            <div class="col-2">
                <div id="test" class="btn btn-outline-primary">test</div>
            </div>
        </div>
    </div>
    
        <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
    <script src="https://cdn.firebase.com/libs/firebaseui/4.0.0/firebaseui.js"></script>
    <script src="https://www.gstatic.com/firebasejs/ui/4.0.0/firebase-ui-auth__zh_tw.js"></script>

    <script defer src="https://www.gstatic.com/firebasejs/6.3.0/firebase-app.js"></script>
    <script defer src="https://www.gstatic.com/firebasejs/6.3.0/firebase-auth.js"></script>
    <script defer src="https://www.gstatic.com/firebasejs/6.3.0/firebase-database.js"></script>
    <!--<script defer src="https://www.gstatic.com/firebasejs/6.3.0/firebase-firestore.js"></script>-->
    <script defer src="https://www.gstatic.com/firebasejs/6.3.0/firebase-storage.js"></script>
    
    <script defer src="./init-firebase.js"></script>
    <script>
        
    </script>
    <script>
        document.getElementById('test').addEventListener('click', function() {
            var fbdbpath = getfbdbPath('images/@/default/');
            putimageurl(fbdbpath, 'url');
        });
        
        function fileupload(){ //== File upload ========================
            document.querySelector('.file-select').addEventListener('change', handleFileUploadChange);
            document.querySelector('.file-submit').addEventListener('click', handleFileUploadSubmit);
            
            let selectedFile;

            function handleFileUploadChange(e) {
                selectedFile = e.target.files[0];
            }

            function handleFileUploadSubmit(e) {
                var metadata = {
                    contentType: 'image/jpeg'
                };

                var fbdbpath = getfbdbPath('images/@/default/');
                var newfbdbPostKey= getfbdbPostKey(fbdbpath);
                var uploadTask = imagesRef.child(`${newfbdbPostKey}`).put(selectedFile, metadata);

                uploadTask.on('state_changed', function(snapshot){
                    var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
                    console.log('Upload is ' + progress + '% done');
                    switch (snapshot.state) {
                        case firebase.storage.TaskState.PAUSED: // or 'paused'
                        console.log('Upload is paused');
                        break;
                        case firebase.storage.TaskState.RUNNING: // or 'running'
                        console.log('Upload is running');
                        break;
                    }
                    }, function(error) {
                        // Handle unsuccessful uploads
                        alert('Upload is Failed;');
                        console.log(error);
                    }, function() {
                        uploadTask.snapshot.ref.getDownloadURL().then(function(downloadURL) {
                            console.log('File available at', downloadURL);
                            putimageurl(fbdbpath, downloadURL);
                        });
                    }
                );
            }
        }


        function initsign(){ //== Auth ===============================
            document.getElementById('sign-in').addEventListener('click', function() {
                if($('.user-signed-out').css('display') === 'block') ui.start('#firebaseui-auth-container', getUiConfig());
            });

            document.getElementById('sign-out').addEventListener('click', function() {
                var user = firebase.auth().currentUser;

                if (user) {
                    firebase.auth().signOut().then((res)=>{
                        console.log('signOut ok', res);
                    }).catch((err)=>{
                        alert('Logout Failed!');
                        console.warn('signOut error',res);
                    }).finally((res)=>{
                        console.log('signOut resolved', res);
                        location.reload();
                    });
                }
            });
        }

    </script>

  </body>
</html>


init-firebase.js


var firebaseConfig  = {
  apiKey: 'AIzaSyBxxxxxxooooo',
  authDomain: 'product-xxxxxxooooo.firebaseapp.com',
  databaseURL: 'https://product-xxxxxxooooo.firebaseio.com',
  storageBucket: 'gs://product-xxxxxxooooo.appspot.com'
};
firebase.initializeApp(firebaseConfig);

//== database ===========================
var database = firebase.database();

function getfbdbPath(type){
  var userId = firebase.auth().currentUser.uid;
  return type.replace("@", userId);
}

function getfbdbPostKey(path){
  return firebase.database().ref().child(path).push().key;
}

function putimageurl(path, url){
  //var userId = firebase.auth().currentUser.uid;
  var path = getfbdbPath('images/@/default/');
  var postImg = {
    active: true,
    url: url,
  };

  var newPostKey = getfbdbPostKey(path);
  var images = {};
  images[path + newPostKey] = postImg;
  //updates['/user-posts/' + userId + '/' + newPostKey] = postData;

  var uploadImagesResult = firebase.database().ref().update(images);
  console.log('uploadImagesResult');
  console.log(uploadImagesResult);
}

function test(){
  var userId = firebase.auth().currentUser.uid;

  firebase.database().ref('users/' + userId).set({
    username: 'name',
    email: 'email',
    profile_picture : 'imageUrl'
  }, function(error) {
    if (error) {
      console.log(error)
      // The write failed...
    } else {
      // Data saved successfully!
      console.log("successfullly!")
    }
  });

  var c = firebase.database().ref('/users/' + userId).once('value').then(function(snapshot) {
    var username = (snapshot.val() && snapshot.val().username) || 'Anonymous';
    // ...
    console.log("username:", username);
  });
  console.log('c');
  console.log(c);

  var postData = {
    author: 'username',
    uid: userId,
  };
  var newPostKey = firebase.database().ref().child('posts').push().key;
  var updates = {};
  updates['/posts/' + newPostKey] = postData;
  //updates['/user-posts/' + userId + '/' + newPostKey] = postData;
  //firebase.database().ref('posts/' + userId).set(postData);

  var t = firebase.database().ref().update(updates);
  console.log('t');
  console.log(t);
}


//== File upload ========================
var storageRef = firebase.storage().ref();
var imagesRef = storageRef.child('images');

// var otherProject = firebase.initializeApp(firebaseConfig, 'other');
// console.log(otherProject.name);    // "otherProject"
// var otherStorage = otherProject.storage();

//== Auth ===============================
function getUiConfig() {
  return {
    signInSuccessUrl: this.location.href,
    signInOptions: [
      firebase.auth.EmailAuthProvider.PROVIDER_ID,
    ],
    //immediateFederatedRedirect: false,
  };
}

// Initialize the FirebaseUI Widget using Firebase.
var ui = new firebaseui.auth.AuthUI(firebase.auth());
// The start method will wait until the DOM is loaded.
if (ui.isPendingRedirect()) {
  ui.start('#firebaseui-auth-container', getUiConfig());
}
// Disable auto-sign in.
// ui.disableAutoSignIn();

var handleSignedInUser = function(user) {
  $('.user-signed-in').show();
  $('.user-signed-out').hide();
  $('#sign-in').hide();
  $('#sign-out').show();
  document.getElementById('account-details').textContent = user.displayName;
  user.getIdToken().then(function(accessToken) {
    document.getElementById('account-details').textContent = 
      JSON.stringify({
        displayName: user.displayName,
        email: user.email,
        emailVerified: user.emailVerified,
        phoneNumber: user.phoneNumber,
        photoURL: user.photoURL,
        uid: user.uid,
        accessToken: user.accessToken,
        providerData: user.providerData
      }, null, '  ');
  });
};

var handleSignedOutUser = function() {
  $('.user-signed-in').hide();
  $('.user-signed-out').show();
  $('#sign-in').show();
  $('#sign-out').hide();
  //ui.start('#firebaseui-container', getUiConfig());
};

function handleConfigChange() {
  // Reset the inline widget so the config changes are reflected.
  ui.reset();
  ui.start('#firebaseui-container', getUiConfig());
}

firebase.auth().onAuthStateChanged(function(user) {
  document.getElementById('loading').style.display = 'none';
  document.getElementById('loaded').style.display = 'block';
  user ? handleSignedInUser(user) : handleSignedOutUser();
}, function(error) {
  console.log(error);
});

initsign();
fileupload();


firebase realtime database rule

95dWpHhg5wOk1loIj0iTneWdfwG2 is admin user <= userId = firebase.auth().currentUser.uid;

{
"rules": {
".read": "'95dWpHhg5wOk1loIj0iTneWdfwG2' === auth.uid",
"users": {
"$uid": {
".write": "$uid === auth.uid"
}
},
"images": {
".write": "'95dWpHhg5wOk1loIj0iTneWdfwG2' === auth.uid",
}
}
}


firebase storage rule

95dWpHhg5wOk1loIj0iTneWdfwG2 is admin user <= userId = firebase.auth().currentUser.uid;

rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read;
allow write: if '95dWpHhg5wOk1loIj0iTneWdfwG2' == request.auth.uid;
}
}
}