/** * WPML compatibility functions * * @global array $duplicated_posts Array to store the posts being duplicated. * * @package Yoast\WP\Duplicate_Post * @since 3.2 */ add_action( 'admin_init', 'duplicate_post_wpml_init' ); /** * Add handlers for WPML compatibility. */ function duplicate_post_wpml_init() { if ( defined( 'ICL_SITEPRESS_VERSION' ) ) { add_action( 'dp_duplicate_page', 'duplicate_post_wpml_copy_translations', 10, 3 ); add_action( 'dp_duplicate_post', 'duplicate_post_wpml_copy_translations', 10, 3 ); add_action( 'shutdown', 'duplicate_wpml_string_packages', 11 ); } } global $duplicated_posts; // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: Renaming a global variable is a BC break. $duplicated_posts = []; /** * Copy post translations. * * @global SitePress $sitepress Instance of the Main WPML class. * @global array $duplicated_posts Array of duplicated posts. * * @param int $post_id ID of the copy. * @param WP_Post $post Original post object. * @param string $status Status of the new post. */ function duplicate_post_wpml_copy_translations( $post_id, $post, $status = '' ) { global $sitepress; global $duplicated_posts; remove_action( 'dp_duplicate_page', 'duplicate_post_wpml_copy_translations', 10 ); remove_action( 'dp_duplicate_post', 'duplicate_post_wpml_copy_translations', 10 ); $current_language = $sitepress->get_current_language(); $trid = $sitepress->get_element_trid( $post->ID ); if ( ! empty( $trid ) ) { $translations = $sitepress->get_element_translations( $trid ); $new_trid = $sitepress->get_element_trid( $post_id ); foreach ( $translations as $code => $details ) { if ( $code !== $current_language ) { if ( $details->element_id ) { $translation = get_post( $details->element_id ); if ( ! $translation ) { continue; } $new_post_id = duplicate_post_create_duplicate( $translation, $status ); if ( ! is_wp_error( $new_post_id ) ) { $sitepress->set_element_language_details( $new_post_id, 'post_' . $translation->post_type, $new_trid, $code, $current_language ); } } } } // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: see above. $duplicated_posts[ $post->ID ] = $post_id; } } /** * Duplicate string packages. * * @global array() $duplicated_posts Array of duplicated posts. */ function duplicate_wpml_string_packages() { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: renaming the function would be a BC-break. global $duplicated_posts; foreach ( $duplicated_posts as $original_post_id => $duplicate_post_id ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. $original_string_packages = apply_filters( 'wpml_st_get_post_string_packages', false, $original_post_id ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. $new_string_packages = apply_filters( 'wpml_st_get_post_string_packages', false, $duplicate_post_id ); if ( is_array( $original_string_packages ) ) { foreach ( $original_string_packages as $original_string_package ) { $translated_original_strings = $original_string_package->get_translated_strings( [] ); foreach ( $new_string_packages as $new_string_package ) { $cache = new WPML_WP_Cache( 'WPML_Package' ); $cache->flush_group_cache(); $new_strings = $new_string_package->get_package_strings(); foreach ( $new_strings as $new_string ) { if ( isset( $translated_original_strings[ $new_string->name ] ) ) { foreach ( $translated_original_strings[ $new_string->name ] as $language => $translated_string ) { do_action( // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals -- Reason: using WPML native filter. 'wpml_add_string_translation', $new_string->id, $language, $translated_string['value'], $translated_string['status'] ); } } } } } } } } バックエンド起動 – Raqqa

バックエンド起動

最短ルート(おすすめ:Viteプロキシ + ポートフォワード)

前提

docker desktop 起動 コンテナを個別に起動しない

docker desktopのエンジンだけ動いてればOK。左下Engine Runnningの表示

  1. minikube 起動
minikube start -p dev34
kubectl config use-context dev34

cd ~/dev-portal && source .venv/bin/activate 
を実行してください(抜けるときは deactivate)。
今後の検証は全部これで統一しましょう:

Ingress前提:BASE=http://127.0.0.1:18081 + -H "Host: api.local"

変更系は Idempotency-Key 必須

PATCH/DELETE は -X PATCH / -X DELETE 必須

例(Ingress経由で View patch/delete):

BASE=http://127.0.0.1:18081
HOST=api.local
VID=23   # 適宜

IDEMP="$(uuidgen)"
PATCH_JSON='{"xml_code":"<odoo><data ingress ok/></odoo>"}'
curl -sS -i \
  -H "Host: $HOST" \
  -H "Idempotency-Key: $IDEMP" \
  -H "Content-Type: application/json" \
  -d "$PATCH_JSON" \
  -X PATCH \
  "$BASE/portal/view/$VID"

IDEMP="$(uuidgen)"
curl -sS -i \
  -H "Host: $HOST" \
  -H "Idempotency-Key: $IDEMP" \
  -X DELETE \
  "$BASE/portal/view/$VID"




  1. API がデプロイ済みか確認(無ければ apply)
kubectl get svc -n portal-dev | grep portal-api || 
kubectl apply -k k8s/overlays/dev
# ローカルでビルドしたイメージを使うなら:
# minikube -p dev33 image load portal-api:dev
  1. ポートフォワード(全IFで待受け)
kubectl -n portal-dev port-forward svc/portal-api 8080:80 --address 0.0.0.0

これで ホストの http://127.0.0.1:8080 に API が来ます。
--address 0.0.0.0 は、Dockerコンテナ(Vite)から host.docker.internal:8080 へ届かせるため。

  1. フロント(各アプリ)側は Vite プロキシで /api に集約
  • .env.development VITE_API_BASE_URL=/api
  • vite.config.ts server: { proxy: { "/api": { target: "http://host.docker.internal:8080", changeOrigin: true, rewrite: p => p.replace(/^\/api/, ""), }, }, }
  • sdk.ts const BASE = import.meta.env.VITE_API_BASE_URL ?? "/api";
  1. 動作確認(DoD)
  • ブラウザで:http://localhost:<vite-port>/api/openapi.json → 200 が返る
  • アプリからの fetch('/api/…') → 200 が返る

Swagger UI を見たければ http://localhost:<vite-port>/api/docs(=プロキシ越し)で表示できます。別サーバー不要です。


代替ルート(NodePort / Ingress を使う場合)

  • NodePortminikube -p dev33 service portal-api -n portal-dev --url # 例: http://192.168.67.2:30080 フロントの VITE_API_BASE_URL をそのURLにして直接叩く(この場合はCORS設定が必要になることが多い)。
  • Ingress を張るなら minikube addons enable ingress + Ingress設定に合わせて
    VITE_API_BASE_URL をホスト名へ。こちらもCORS考慮が必要。

CORSを避けて手戻りゼロにしたいなら、Viteプロキシ + PFが一番安定です。


まとめ

  • 起動は minikube start -p dev33 →(必要なら)apply → PF
  • フロントは /api に投げる → ViteがPF先に中継(CORS不要)。
  • Swagger用に別サーバーは要りません。 FastAPIの /docs をそのまま見ればOK。

Comments

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です