网络日志

搭建个人知识付费应用系统-(1)框架初始化、用户身份集成

视频地址: https://www.bilibili.com/vide...


初始化项目

Remix 官网:https://remix.run/

创建命令:

npx create-remix@latest

目前项目源码位于: https://github.com/willin/bet...

配置

  • eslint
  • prettier
  • editorconfig

可选项:

  • lint-staged
  • husky

安装依赖

  • tailwindcss
  • daisyui
  • @tailwindcss/typography
  • postcss
  • pm2

创建 Authing 用户池及应用

创建登录、注销接口

登录接口

import { redirect } from '@remix-run/node';

export const loader = async () => {
  return redirect(
    `${process.env.AUTHING_SSO_URL}/login?app_id=${process.env.AUTHING_APP_ID}`
  );
};

注销接口

import { redirect } from '@remix-run/node';

export const loader = async () => {
  return redirect(
    `${process.env.AUTHING_SSO_URL}/logout?redirectUri=${encodeURIComponent(
      process.env.HOMEPAGE || 'https://willin.wang'
    )}`
  );
};

登录回调 callback

import { json, redirect } from '@remix-run/node';

export type OidcResponse = {
  error?: string;
  error_description?: string;

  access_token: string;
  expires_in: number;
  id_token: string;
  scope: string;
  token_type: string;
};

export const loader = async ({ request }) => {
  const url = new URL(request.url);
  const code = url.searchParams.get('code');
  if (code === null) {
    return redirect('/login');
  }

  const body = {
    client_id: process.env.AUTHING_APP_ID,
    client_secret: process.env.AUTHING_APP_SECRET,
    grant_type: 'authorization_code',
    code
  };

  const formBody = [];
  // eslint-disable-next-line
  for (const property in body) {
    const encodedKey = encodeURIComponent(property);
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
    const encodedValue = encodeURIComponent(body[property]);
    formBody.push(`${encodedKey}=${encodedValue}`);
  }
  const res = await fetch(`${process.env.AUTHING_APP_DOMAIN}/oidc/token`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
    },
    body: formBody.join('&')
  });

  const oidcToken = (await res.json()) as OidcResponse;
  if (oidcToken.error) {
    console.error(oidcToken);
    return redirect('/login');
  }
  // 以上获取 oidc token 为核心部分
  
  // 下面根据业务需要去操作其他
  const resInfo = await fetch(
    `${process.env.AUTHING_APP_DOMAIN}/oidc/me?access_token=${oidcToken.access_token}`
  );
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
  const user = await resInfo.json();
  return json(user);
};