Skip to content

Commit

Permalink
deploy: 6e260c7
Browse files Browse the repository at this point in the history
  • Loading branch information
sammrai committed Dec 2, 2023
0 parents commit 69a4afb
Show file tree
Hide file tree
Showing 23 changed files with 5,091 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
webpack.config.js
dist
node_modules
18 changes: 18 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
env:
es2021: true
node: true
extends:
- standard
- plugin:@typescript-eslint/recommended
- prettier
parser: "@typescript-eslint/parser"
parserOptions:
ecmaVersion: latest
sourceType: module
project: ./tsconfig.json
plugins:
- "@typescript-eslint"
rules:
"@typescript-eslint/no-unused-vars":
- error
- argsIgnorePattern: "^_next$"
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
node_modules/
dist/
data/*
!data/.gitkeep
config.json
*.env
src/package.json
src/yarn.lock
src/index.html
Empty file added .nojekyll
Empty file.
12 changes: 12 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch",
"runtimeExecutable": "yarn",
"cwd": "${workspaceFolder}",
"runtimeArgs": ["dev"],
}
]
}
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"editor.tabSize": 2
}
36 changes: 36 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
FROM zenika/alpine-chrome:with-puppeteer-xvfb as runner

# hadolint ignore=DL3002
USER root

# hadolint ignore=DL3018
RUN apk upgrade --no-cache --available && \
apk update && \
apk add --no-cache \
x11vnc \
&& \
apk add --update --no-cache tzdata && \
cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
echo "Asia/Tokyo" > /etc/timezone && \
apk del tzdata

WORKDIR /app

COPY package.json yarn.lock ./

RUN echo network-timeout 600000 > .yarnrc && \
yarn install --frozen-lockfile && \
yarn cache clean

COPY src/ src/
COPY tsconfig.json .eslintignore .eslintrc.yml .

COPY entrypoint.sh .
RUN chmod +x entrypoint.sh

ENV TZ Asia/Tokyo
ENV DISPLAY :99
ENV CHROMIUM_PATH /usr/bin/chromium-browser

ENTRYPOINT ["tini", "--"]
CMD ["/app/entrypoint.sh"]
Empty file added data/.gitkeep
Empty file.
12 changes: 12 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
version: '3.8'

services:
app:
build: .
volumes:
- type: bind
source: ./data
target: /data
- type: bind
source: ./config.json
target: /app/config.json
3 changes: 3 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

yarn start || true
453 changes: 453 additions & 0 deletions index.html

Large diffs are not rendered by default.

56 changes: 56 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"name": "mfapi",
"version": "0.0.1",
"description": "",
"main": "dist/main.js",
"repository": "git@github.com:sammrai/mfapi.git",
"author": "sammrai",
"license": "MIT",
"private": true,
"scripts": {
"start": "ts-node -r tsconfig-paths/register ./src/main.ts",
"dev": "ts-node-dev --poll --watch src -r tsconfig-paths/register ./src/main.ts",
"package": "run-s clean compile packing",
"packing": "ncc build ./dist/main.js -o output/",
"compile": "tsc -p .",
"clean": "rimraf dist output",
"lint": "run-p -c lint:prettier lint:eslint lint:tsc",
"lint:prettier": "prettier --check src",
"lint:eslint": "eslint . --ext ts,tsx",
"lint:tsc": "tsc",
"fix": "run-s fix:prettier fix:eslint",
"fix:eslint": "eslint . --ext ts,tsx --fix",
"fix:prettier": "prettier --write src",
"watch-openapi": "nodemon --watch ./src/openapi.yaml --exec \"npx @redocly/cli build-docs ./src/openapi.yaml -o ./src/index.html --theme.openapi.disableSearch=true --theme.openapi.expandResponses='200,201'\"",
"serve-docs": "http-server src -p 3001",
"redoc": "npm-run-all --parallel watch-openapi serve-docs"
},
"devDependencies": {
"@types/node": "20.10.1",
"@types/swagger-ui-express": "^4.1.6",
"@types/yamljs": "^0.2.34",
"@typescript-eslint/eslint-plugin": "6.13.1",
"@typescript-eslint/parser": "6.13.1",
"@vercel/ncc": "0.38.1",
"axios": "1.6.2",
"cheerio": "^1.0.0-rc.12",
"eslint": "8.54.0",
"eslint-config-prettier": "9.0.0",
"eslint-config-standard": "17.1.0",
"eslint-plugin-import": "2.29.0",
"eslint-plugin-n": "16.3.1",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "6.1.1",
"express": "^4.18.2",
"express-openapi-validator": "^5.1.1",
"prettier": "2.8.8",
"puppeteer-core": "21.5.2",
"swagger-ui-express": "^5.0.0",
"ts-node": "10.9.1",
"ts-node-dev": "2.0.0",
"typescript": "5.3.2",
"yamljs": "^0.3.0",
"yarn-run-all": "3.1.1",
"redoc-express": "^2.1.0"
}
}
84 changes: 84 additions & 0 deletions src/actions/account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { ApiResponseHandler } from "./api";

export enum AccountCategory {
Bank = 2, // 銀行
Securities = 3, // 証券
InvestmentTrust = 4, // 投信
CryptoFXMetals = 5, // 暗号資産・FX・貴金属
Card = 6, // カード
Pension = 7, // 年金
EMoneyPrepaid = 8, // 電子マネー・プリペイド
Points = 9, // ポイント
OnlineShopping = 12, // 通販
Supermarket = 16, // スーパー
OtherAssets = 14, // その他保有資産
}

export interface ManualAccount {
name: string;
id: string;
subAccountIdHash: string;
accountString: string;
}

export default AccountCategory;

export class Account extends ApiResponseHandler {
public async addManualAccount(category: AccountCategory, name: string) {
const postData = {
"new_manual_account[category_id]": category,
"new_manual_account[name]": name,
};

return this.post("/accounts/manual_account", postData);
}

public async getManualAccounts(): Promise<ManualAccount[]> {
const accounts = await this.get("/accounts", ($) => {
const results: { name: string; id: string }[] = [];
const cssSelector = "section.manual_accounts tr td:first-child a";

$(cssSelector).each((_, el) => {
const elem = el;
const name = $(elem).text().trim();
const href = $(elem).attr("href") || "";
const id = href.split("/").pop() || "";
results.push({ name, id });
});

return results;
});

// 各アカウントに対して getSubAccountIdHash を非同期的に呼び出す
const enrichedAccounts = await Promise.all(
accounts.map(async (account) => {
const subAccountIdHash = await this.getSubAccountIdHash(account.id);
const accountString = `${account.id}@${subAccountIdHash}`;
return { ...account, subAccountIdHash, accountString };
})
);

return enrichedAccounts;
}

public async getSubAccountIdHash(accountId: string): Promise<string> {
const url = `/accounts/show_manual/${accountId}`;

return this.get(url, ($) => {
const value =
$("#user_asset_det_sub_account_id_hash > option")
.first()
.attr("value") || "";
return value;
});
}

public async deleteAccount(accountIdHash: string) {
const postData = {
_method: "delete",
account_id_hash: accountIdHash,
};

return this.post("/accounts", postData);
}
}
66 changes: 66 additions & 0 deletions src/actions/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import axios, { AxiosInstance } from "axios";
import { LoginSession } from "../authentication";
import cheerio, { CheerioAPI } from "cheerio";

export abstract class ApiResponseHandler {
private axiosInstance: AxiosInstance;

constructor(session: LoginSession) {
this.axiosInstance = axios.create({
baseURL: "https://moneyforward.com",
headers: {
cookie: this.formatCookies(session.cookie),
origin: "https://moneyforward.com",
referer: "https://moneyforward.com/",
"x-csrf-token": session["csrf-token"],
"x-requested-with": "XMLHttpRequest",
"Content-Type": "application/x-www-form-urlencoded",
},
transformRequest: [
function (data) {
if (data) {
return Object.entries(data)
.map(
([key, value]) =>
`${encodeURIComponent(key)}=${encodeURIComponent(
String(value)
)}`
)
.join("&");
}
return data;
},
],
});
}

private formatCookies(
cookies: Array<{ name: string; value: string }>
): string {
return cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join("; ");
}

protected async post<T, U = T>(url: string, data?: T): Promise<U> {
try {
const response = await this.axiosInstance.post<U>(url, data);
// console.log("SUCCESS:", response.data);
console.log("Status Code: ", response.status);
// console.log("Response Headers: ", response.headers);
return response.data;
} catch (error) {
console.error("ERROR:", error);
throw error;
}
}

protected async get<T>(
url: string,
decorator: (cheerio: CheerioAPI) => T
): Promise<T> {
const response = await this.axiosInstance.get(url);
console.log("Status Code: ", response.status);
// console.log("Response Headers: ", response.headers);
const $ = cheerio.load(response.data);
return decorator($);
}
}
Loading

0 comments on commit 69a4afb

Please sign in to comment.