├── .github └── ISSUE_TEMPLATE │ └── feature_request.md ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── anthos ├── README.md ├── assets │ └── dashboard.json ├── k8s-manifest │ ├── backendconfig.yaml │ ├── bookinfo │ │ ├── bookinfo-gateway.yaml │ │ ├── bookinfo.yaml │ │ └── namespace.yaml │ ├── cartservice-fault.yaml │ ├── clusterregistry │ │ ├── clusters.yaml │ │ └── selectors.yaml │ ├── config-management.yaml │ ├── frontend-dr.yaml │ ├── frontend-v1.yaml │ ├── frontend-v2.yaml │ ├── istio-manifests.yaml │ ├── istio-manifests.yaml.patch │ ├── istio-system │ │ ├── backendconfig.yaml │ │ ├── mci.yaml │ │ ├── mcs.yaml │ │ └── namespace.yaml │ ├── kubernetes-manifests.yaml │ ├── mci.yaml │ ├── mcs.yaml │ ├── namespace-ob.yaml │ ├── zonedeploy.yaml │ └── zonens.yaml ├── scripts │ ├── 01_create_clusters.sh │ ├── 02_get_credentials.sh │ ├── 03_create_rolebindings.sh │ ├── 04_register_clusters_to_hub.sh │ ├── 05_install_asm.sh │ ├── 06_install_acm.sh │ ├── 07_ingressforanthos.sh │ └── init.env └── tutorial.md ├── appdev_genai_googlecloud ├── assets │ ├── cors.json │ ├── cors_origin.json │ └── lb.sh ├── scripts │ └── setup_eventarc_subscription.sh ├── src │ ├── genai-app-firestore │ │ ├── Dockerfile │ │ ├── main.py │ │ └── requirements.txt │ ├── genai-app │ │ ├── Dockerfile │ │ ├── main.py │ │ └── requirements.txt │ ├── knowledge-drive-firestore │ │ ├── .env │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── .prettierrc │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── components.json │ │ ├── next.config.mjs │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── postcss.config.js │ │ ├── public │ │ │ └── images │ │ │ │ ├── 404.png │ │ │ │ ├── check.png │ │ │ │ ├── error.png │ │ │ │ ├── logo.png │ │ │ │ └── notfound.png │ │ ├── src │ │ │ ├── app │ │ │ │ ├── (pages) │ │ │ │ │ ├── folders │ │ │ │ │ │ └── [slug] │ │ │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── items │ │ │ │ │ │ └── [slug] │ │ │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── my-drive │ │ │ │ │ │ └── page.tsx │ │ │ │ │ └── search │ │ │ │ │ │ └── page.tsx │ │ │ │ ├── api │ │ │ │ │ └── question │ │ │ │ │ │ └── route.ts │ │ │ │ ├── error.tsx │ │ │ │ ├── favicon.ico │ │ │ │ ├── globals.css │ │ │ │ ├── layout.tsx │ │ │ │ └── not-found.tsx │ │ │ ├── components │ │ │ │ ├── app-name.tsx │ │ │ │ ├── avatar.tsx │ │ │ │ ├── breadcrumb-folder.tsx │ │ │ │ ├── breadcrumb.tsx │ │ │ │ ├── content-header.tsx │ │ │ │ ├── content.tsx │ │ │ │ ├── details-button.tsx │ │ │ │ ├── empty-list.tsx │ │ │ │ ├── file-upload-button.tsx │ │ │ │ ├── header.tsx │ │ │ │ ├── item-header.tsx │ │ │ │ ├── item-selector-button.tsx │ │ │ │ ├── item-selector.tsx │ │ │ │ ├── item-table.tsx │ │ │ │ ├── item.tsx │ │ │ │ ├── link-item.tsx │ │ │ │ ├── llm-search-dialog.tsx │ │ │ │ ├── llm-search-result.tsx │ │ │ │ ├── logo.tsx │ │ │ │ ├── new-folder-button.tsx │ │ │ │ ├── new-folder-form.tsx │ │ │ │ ├── new-item-button.tsx │ │ │ │ ├── refresh.tsx │ │ │ │ ├── screen.tsx │ │ │ │ ├── search-content-text.tsx │ │ │ │ ├── search-content.tsx │ │ │ │ ├── search-form.tsx │ │ │ │ ├── search-item-table.tsx │ │ │ │ ├── sidebar.tsx │ │ │ │ ├── skeleton-breadcrumb-folder.tsx │ │ │ │ ├── skeleton-breadcrumb.tsx │ │ │ │ ├── skeleton-item-header.tsx │ │ │ │ ├── skeleton-item-table.tsx │ │ │ │ ├── skeleton-item.tsx │ │ │ │ └── ui │ │ │ │ │ ├── button.tsx │ │ │ │ │ ├── dialog.tsx │ │ │ │ │ ├── dropdown-menu.tsx │ │ │ │ │ ├── hover-card.tsx │ │ │ │ │ ├── label.tsx │ │ │ │ │ └── skeleton.tsx │ │ │ ├── context │ │ │ │ └── toaster-context.tsx │ │ │ ├── lib │ │ │ │ ├── actions.ts │ │ │ │ ├── color.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── firebase │ │ │ │ │ └── admin.ts │ │ │ │ ├── logging.ts │ │ │ │ ├── types.ts │ │ │ │ └── utils.ts │ │ │ ├── middleware.ts │ │ │ └── providers │ │ │ │ └── query-client.tsx │ │ ├── tailwind.config.ts │ │ └── tsconfig.json │ └── knowledge-drive │ │ ├── .env │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── .prettierrc │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── components.json │ │ ├── next.config.mjs │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── postcss.config.js │ │ ├── public │ │ └── images │ │ │ ├── 404.png │ │ │ ├── check.png │ │ │ ├── error.png │ │ │ ├── logo.png │ │ │ └── notfound.png │ │ ├── src │ │ ├── app │ │ │ ├── (pages) │ │ │ │ ├── folders │ │ │ │ │ └── [slug] │ │ │ │ │ │ └── page.tsx │ │ │ │ ├── items │ │ │ │ │ └── [slug] │ │ │ │ │ │ └── page.tsx │ │ │ │ ├── my-drive │ │ │ │ │ └── page.tsx │ │ │ │ └── search │ │ │ │ │ └── page.tsx │ │ │ ├── api │ │ │ │ └── question │ │ │ │ │ └── route.ts │ │ │ ├── error.tsx │ │ │ ├── favicon.ico │ │ │ ├── globals.css │ │ │ ├── layout.tsx │ │ │ └── not-found.tsx │ │ ├── components │ │ │ ├── app-name.tsx │ │ │ ├── avatar.tsx │ │ │ ├── breadcrumb-folder.tsx │ │ │ ├── breadcrumb.tsx │ │ │ ├── content-header.tsx │ │ │ ├── content.tsx │ │ │ ├── details-button.tsx │ │ │ ├── empty-list.tsx │ │ │ ├── file-upload-button.tsx │ │ │ ├── header.tsx │ │ │ ├── item-header.tsx │ │ │ ├── item-selector-button.tsx │ │ │ ├── item-selector.tsx │ │ │ ├── item-table.tsx │ │ │ ├── item.tsx │ │ │ ├── link-item.tsx │ │ │ ├── llm-search-dialog.tsx │ │ │ ├── llm-search-result.tsx │ │ │ ├── logo.tsx │ │ │ ├── new-folder-button.tsx │ │ │ ├── new-folder-form.tsx │ │ │ ├── new-item-button.tsx │ │ │ ├── refresh.tsx │ │ │ ├── screen.tsx │ │ │ ├── search-content-text.tsx │ │ │ ├── search-content.tsx │ │ │ ├── search-form.tsx │ │ │ ├── search-item-table.tsx │ │ │ ├── sidebar.tsx │ │ │ ├── skeleton-breadcrumb-folder.tsx │ │ │ ├── skeleton-breadcrumb.tsx │ │ │ ├── skeleton-item-header.tsx │ │ │ ├── skeleton-item-table.tsx │ │ │ ├── skeleton-item.tsx │ │ │ └── ui │ │ │ │ ├── button.tsx │ │ │ │ ├── dialog.tsx │ │ │ │ ├── dropdown-menu.tsx │ │ │ │ ├── hover-card.tsx │ │ │ │ ├── label.tsx │ │ │ │ └── skeleton.tsx │ │ ├── context │ │ │ └── toaster-context.tsx │ │ ├── lib │ │ │ ├── actions.ts │ │ │ ├── color.ts │ │ │ ├── constants.ts │ │ │ ├── db.ts │ │ │ ├── logging.ts │ │ │ ├── types.ts │ │ │ └── utils.ts │ │ ├── middleware.ts │ │ └── providers │ │ │ └── query-client.tsx │ │ ├── tailwind.config.ts │ │ └── tsconfig.json ├── tutorial_ja.md ├── tutorial_serverless_ja.md └── tutorial_short_ja.md ├── appdev_with_generative_ai ├── scripts │ └── firebase_config.sh ├── src │ ├── genai-app │ │ ├── Dockerfile │ │ ├── main.py │ │ └── requirements.txt │ └── knowledge-drive │ │ ├── .env │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── app │ │ ├── (site) │ │ │ ├── components │ │ │ │ ├── AuthForm.tsx │ │ │ │ ├── Button.tsx │ │ │ │ └── Input.tsx │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── api │ │ │ ├── query │ │ │ │ └── route.ts │ │ │ ├── register │ │ │ │ └── route.ts │ │ │ └── search │ │ │ │ └── route.ts │ │ ├── context │ │ │ └── ToasterContext.tsx │ │ ├── drive │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── favicon.ico │ │ ├── folders │ │ │ └── [id] │ │ │ │ ├── layout.tsx │ │ │ │ └── page.tsx │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── search │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ └── types │ │ │ └── auth.ts │ │ ├── components.json │ │ ├── components │ │ ├── CurrentFolder.tsx │ │ ├── Header.tsx │ │ ├── Main.tsx │ │ ├── MainHeader.tsx │ │ ├── MainItems.tsx │ │ ├── MainItemsContent.tsx │ │ ├── MainItemsHeader.tsx │ │ ├── NavItem.tsx │ │ ├── Navbar.tsx │ │ ├── NewButton.tsx │ │ ├── RecommendedList.tsx │ │ ├── SearchDialog.tsx │ │ ├── SearchInput.tsx │ │ ├── SearchItems.tsx │ │ ├── SearchResult.tsx │ │ ├── SingleItem.tsx │ │ ├── SourceFile.tsx │ │ └── ui │ │ │ ├── LoadingPage.tsx │ │ │ ├── avatar.tsx │ │ │ ├── button.tsx │ │ │ ├── dialog.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── form.tsx │ │ │ ├── hover-card.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── skeleton.tsx │ │ │ ├── toast.tsx │ │ │ ├── toaster.tsx │ │ │ └── use-toast.ts │ │ ├── lib │ │ ├── api │ │ │ └── auth.ts │ │ ├── firebase │ │ │ ├── admin.ts │ │ │ ├── auth.ts │ │ │ ├── client.ts │ │ │ └── firestore.ts │ │ └── utils.ts │ │ ├── middleware.ts │ │ ├── next.config.js │ │ ├── package.json │ │ ├── postcss.config.js │ │ ├── public │ │ └── images │ │ │ ├── check.png │ │ │ ├── error.png │ │ │ ├── logo.png │ │ │ ├── notfound.png │ │ │ └── placeholder.png │ │ ├── tailwind.config.ts │ │ ├── tsconfig.json │ │ └── yarn.lock ├── tf │ ├── application.tf │ ├── artifactregistry.tf │ ├── firebase.tf │ ├── firebase_config.sh │ ├── firestore.rules │ ├── iam.tf │ ├── main.tf │ ├── output.tf │ ├── storage.rules │ └── variables.tf └── tutorial_ja.md ├── data_analytics ├── README.md ├── sample.csv ├── sample.ipynb └── sample.sql ├── fundamental ├── gce │ └── index.html ├── gke │ └── helloNode │ │ ├── Dockerfile │ │ ├── hello-node-deployment.yaml │ │ ├── hello-node-service.yaml │ │ └── server.js └── readme.md ├── gke-ai ├── lab-01 │ ├── Dockerfile │ ├── deployment_template.yaml │ ├── main.py │ ├── requirements.txt │ └── service.yaml ├── lab-02 │ ├── .gitignore │ ├── Dockerfile │ ├── finetune_gozaru.py │ ├── finetune_job_template.yaml │ └── requirements_finetune.txt ├── lab-03 │ ├── Dockerfile │ ├── deployment_template.yaml │ ├── gateway.yaml │ ├── gozaru-gemma-pool-epp-svc.yaml │ ├── httproute.yaml │ ├── inferencemodel.yaml │ ├── inferencepool.yaml │ ├── main_gozaru.py │ ├── requirements_gozaru.txt │ └── service.yaml └── tutorial.md ├── gke-basic ├── lab-01 │ ├── app │ │ ├── adservice.yaml │ │ ├── cartservice.yaml │ │ ├── checkoutservice.yaml │ │ ├── currencyservice.yaml │ │ ├── emailservice.yaml │ │ ├── frontend.yaml │ │ ├── paymentservice.yaml │ │ ├── productcatalogservice.yaml │ │ ├── recommendationservice.yaml │ │ ├── redis.yaml │ │ └── shippingservice.yaml │ └── gateway │ │ ├── gateway.yaml │ │ └── httproute.yaml ├── lab-02 │ ├── balloon-deploy.yaml │ └── balloon-priority.yaml ├── lab-ex01 │ ├── .python-version │ ├── Procfile │ ├── cloudbuild.yaml │ ├── clouddeploy.yaml │ ├── k8s │ │ ├── deployment.yaml │ │ └── service.yaml │ ├── main.py │ ├── requirements.txt │ └── skaffold.yaml └── tutorial.md ├── gke-basics-2025 ├── lab-01 │ └── app │ │ ├── app.yaml │ │ └── svc.yaml ├── lab-02 │ ├── balloon-deploy.yaml │ └── balloon-priority.yaml ├── lab-ex01 │ ├── .python-version │ ├── Procfile │ ├── cloudbuild.yaml │ ├── clouddeploy.yaml │ ├── k8s │ │ ├── deployment.yaml │ │ └── service.yaml │ ├── main.py │ ├── requirements.txt │ └── skaffold.yaml └── tutorial.md ├── machine_learning ├── README.md ├── cloud_ai_building_blocks │ ├── README.md │ ├── automl_ltv_sample.csv │ ├── cloud_automl_tutorial.md │ ├── conversation_ja.ipynb │ ├── language_ja.ipynb │ ├── sight_ja.ipynb │ └── speech-to-speech │ │ ├── README.md │ │ ├── requirements.txt │ │ ├── speech-to-speech.py │ │ ├── speech_to_speech.ipynb │ │ └── voice_recorder.sh ├── cloud_ai_platform │ └── bigquery_ml.ipynb └── ml_infrastructure │ └── inference-server-performance │ ├── LICENSE │ ├── client │ ├── Dockerfile │ ├── data │ │ ├── 00001.jpg │ │ ├── 00002.jpg │ │ └── 00003.jpg │ ├── deployment_master.yaml │ ├── deployment_slave.yaml │ ├── locust │ │ └── trtis_grpc_client.py │ ├── service_master.yaml │ └── setup_locust.ipynb │ └── server │ ├── Dockerfile │ ├── clusterRole.yml │ ├── grafana-deployment.yml │ ├── grafana-service.yml │ ├── models │ └── resnet │ │ └── original │ │ └── 00001 │ │ └── README │ ├── old │ └── tensorrt-optimization.ipynb │ ├── original │ ├── config.pbtxt │ └── imagenet1k_labels.txt │ ├── prometheus-configmap.yml │ ├── prometheus-deployment.yml │ ├── prometheus-service.yml │ ├── prometheus.yaml │ ├── scripts │ └── tensorrt-optimization.py │ ├── setup_trtis.ipynb │ ├── tftrt_fp16 │ ├── config.pbtxt │ └── imagenet1k_labels.txt │ ├── tftrt_fp16_bs8_count4 │ ├── config.pbtxt │ └── imagenet1k_labels.txt │ ├── tftrt_fp32 │ ├── config.pbtxt │ └── imagenet1k_labels.txt │ ├── tftrt_int8_bs8_count4 │ ├── config.pbtxt │ └── imagenet1k_labels.txt │ ├── trtis_deploy.yaml │ └── trtis_service.yaml ├── microservices ├── README.md ├── hello_world │ ├── .dockerignore │ ├── Dockerfile │ ├── main.py │ └── requirements.txt ├── message_board │ ├── .dockerignore │ ├── Dockerfile │ ├── index.yaml │ ├── main.py │ └── requirements.txt ├── storage_logging │ ├── .dockerignore │ ├── Dockerfile │ ├── main.py │ └── requirements.txt ├── tutorial-1.md ├── tutorial-2.md └── tutorial-3.md ├── movie_search ├── custom_ver │ ├── backend │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── main.py │ │ ├── prompt_content_search.py │ │ ├── requirements.txt │ │ └── utils.py │ └── frontend │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── main.py │ │ └── requirements.txt └── final_ver │ ├── backend │ ├── .gitignore │ ├── Dockerfile │ ├── main.py │ ├── prompt_content_search.py │ ├── requirements.txt │ ├── scene_search.py │ ├── search_document.py │ ├── static_content.py │ └── utils.py │ └── frontend │ ├── .gitignore │ ├── Dockerfile │ ├── main.py │ └── requirements.txt ├── movie_search_metadata ├── Notebooks │ ├── create_metadata_explanation.ipynb │ ├── create_metadata_handson1.ipynb │ ├── create_metadata_handson2.ipynb │ ├── movie_search.ipynb │ └── preprocess_movie_files.ipynb ├── Notebooks2 │ ├── create_metadata_explanation.ipynb │ ├── create_metadata_handson1.ipynb │ ├── create_metadata_handson2.ipynb │ ├── preprocess_movie_files.ipynb │ └── temp.txt └── demo_app │ ├── .gitignore │ ├── README.md │ ├── README_en.md │ ├── _vais_setup.py │ ├── backend │ ├── .gitignore │ ├── Dockerfile │ ├── file_search.py │ ├── main.py │ ├── prompt_content_search.py │ ├── requirements.txt │ ├── scene_search.py │ ├── search_document.py │ └── utils.py │ ├── deploy.sh │ ├── docs │ └── images │ │ └── movie_search_demo.gif │ ├── frontend │ ├── .gitignore │ ├── Dockerfile │ ├── main.py │ └── requirements.txt │ └── vais_setup.sh ├── pfe-adv-sep ├── lab-01 │ ├── .gitignore │ ├── Dockerfile │ ├── app.py │ ├── app.txt │ ├── cloudbuild-2.yaml │ ├── cloudbuild.yaml │ ├── clouddeploy-2.yaml │ ├── clouddeploy.yaml │ ├── kubernetes-manifests │ │ ├── deployment.yaml │ │ └── service.yaml │ ├── kustomization.yaml │ ├── requirements.txt │ ├── skaffold.yaml │ └── test_app.py ├── lab-02 │ ├── Dockerfile │ ├── app.py │ ├── cloudbuild.yaml │ ├── clouddeploy.yaml │ ├── kubernetes-manifests │ │ ├── allow-myrepo.yaml │ │ ├── deployment.yaml │ │ ├── maven-vulns.yaml │ │ └── service.yaml │ ├── requirements.txt │ ├── skaffold.yaml │ └── test_app.py ├── lab-03 │ ├── allow-hostpath.yaml │ └── bad-pod.yaml └── tutorial.md ├── pfe-basic-sep ├── lab-01 │ └── sampleapp.yaml ├── lab-02 │ ├── cloudbuild.yaml │ └── workstations │ │ └── Dockerfile ├── lab-03 │ ├── crashloop-currency.yaml │ └── unschedulable-hello.yaml └── tutorial.md ├── pfe-basic ├── lab-01 │ └── sampleapp.yaml ├── lab-02 │ └── workstations │ │ └── Dockerfile └── tutorial.md ├── pfe-cicd ├── .gitignore ├── Dockerfile ├── app.py ├── app.txt ├── cloudbuild-2.yaml ├── cloudbuild.yaml ├── clouddeploy.yaml ├── kubernetes-manifests │ ├── deployment.yaml │ └── service.yaml ├── kustomization.yaml ├── requirements.txt ├── skaffold.yaml ├── test_app.py └── tutorial.md ├── workstations └── tutorial_ws.md └── workstations_with_generative_ai ├── images └── reconnect_cloudshell.png └── tutorial_ja.md /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .venv/ 2 | .env 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google.com/conduct/). 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GCP Hands on materials 2 | 3 | **This is not an official Google product**. 4 | 5 | The repository contains Google Colaboratory notebooks and scripts to learn how to use Google Cloud Platform. They are supposed to be used at GCP hands-on trainings for Japanese speakers, and some contents are written in Japanese. You can find instructions either in README or notebooks themselves. 6 | -------------------------------------------------------------------------------- /anthos/README.md: -------------------------------------------------------------------------------- 1 | # GCP Hands on materials for Anthos Workshop 2 | 3 | **This is not an officially supported Google product**. This directory 4 | contains some scripts that are used to teach GCP beginners the benefits and capabilities of Anthos. 5 | 6 | see [tutorial.md](tutorial.md) for more details. 7 | 8 | click [HERE!](https://console.cloud.google.com/cloudshell/editor?cloudshell_git_repo=github.com/GoogleCloudPlatform/gcp-getting-started-lab-jp.git&cloudshell_working_dir=anthos&cloudshell_tutorial=tutorial.md&shellonly=true) to start the hands-on. 9 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/backendconfig.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cloud.google.com/v1 2 | kind: BackendConfig 3 | metadata: 4 | name: gke-ingress-config 5 | namespace: istio-system 6 | spec: 7 | healthCheck: 8 | requestPath: /healthz/ready 9 | port: 15021 10 | type: HTTP 11 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/bookinfo/bookinfo-gateway.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: Gateway 3 | metadata: 4 | name: bookinfo-gateway 5 | spec: 6 | selector: 7 | istio: ingressgateway # use istio default controller 8 | servers: 9 | - port: 10 | number: 80 11 | name: http 12 | protocol: HTTP 13 | hosts: 14 | - "*" 15 | --- 16 | apiVersion: networking.istio.io/v1alpha3 17 | kind: VirtualService 18 | metadata: 19 | name: bookinfo 20 | spec: 21 | hosts: 22 | - "*" 23 | gateways: 24 | - bookinfo-gateway 25 | http: 26 | - match: 27 | - uri: 28 | exact: /productpage 29 | - uri: 30 | prefix: /static 31 | - uri: 32 | exact: /login 33 | - uri: 34 | exact: /logout 35 | - uri: 36 | prefix: /api/v1/products 37 | route: 38 | - destination: 39 | host: productpage 40 | port: 41 | number: 9080 42 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/bookinfo/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: bookinfo 5 | labels: 6 | istio-injection: enabled 7 | 8 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/cartservice-fault.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: VirtualService 3 | metadata: 4 | name: cartservice 5 | spec: 6 | hosts: 7 | - cartservice 8 | http: 9 | - fault: 10 | abort: 11 | httpStatus: 500 12 | percentage: 13 | value: 20 14 | route: 15 | - destination: 16 | host: cartservice 17 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/clusterregistry/clusters.yaml: -------------------------------------------------------------------------------- 1 | kind: Cluster 2 | apiVersion: clusterregistry.k8s.io/v1alpha1 3 | metadata: 4 | name: anthos-sample-cluster2 5 | labels: 6 | ifa: primary 7 | --- 8 | kind: Cluster 9 | apiVersion: clusterregistry.k8s.io/v1alpha1 10 | metadata: 11 | name: anthos-sample-cluster3 12 | labels: 13 | ifa: secondary 14 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/clusterregistry/selectors.yaml: -------------------------------------------------------------------------------- 1 | kind: ClusterSelector 2 | apiVersion: configmanagement.gke.io/v1 3 | metadata: 4 | name: ifa 5 | spec: 6 | selector: 7 | matchLabels: 8 | ifa: primary 9 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/config-management.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: configmanagement.gke.io/v1 2 | kind: ConfigManagement 3 | metadata: 4 | name: config-management 5 | namespace: config-management-system 6 | spec: 7 | clusterName: 8 | git: 9 | syncRepo: 10 | syncBranch: master 11 | secretType: gcenode 12 | policyDir: "." 13 | syncWait: 2 14 | policyController: 15 | enabled: true 16 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/frontend-dr.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: DestinationRule 3 | metadata: 4 | name: frontend-dr 5 | spec: 6 | host: frontend 7 | subsets: 8 | - name: v1 9 | labels: 10 | version: v1 11 | - name: v2 12 | labels: 13 | version: v2 14 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/istio-manifests.yaml.patch: -------------------------------------------------------------------------------- 1 | --- istio-manifests.yaml 2020-11-16 01:43:27.000000000 +0900 2 | +++ istio-manifest.yaml 2020-11-16 01:43:27.000000000 +0900 3 | @@ -44,8 +44,31 @@ 4 | - route: 5 | - destination: 6 | host: frontend 7 | + subset: v1 8 | port: 9 | number: 80 10 | + weight: 50 11 | + - destination: 12 | + host: frontend 13 | + subset: v2 14 | + port: 15 | + number: 80 16 | + weight: 50 17 | +--- 18 | +apiVersion: networking.istio.io/v1alpha3 19 | +kind: ServiceEntry 20 | +metadata: 21 | + name: currency-provider-external 22 | +spec: 23 | + hosts: 24 | + - www.ecb.europa.eu 25 | + ports: 26 | + - number: 80 27 | + name: http 28 | + protocol: HTTP 29 | + - number: 443 30 | + name: https 31 | + protocol: HTTPS 32 | --- 33 | apiVersion: networking.istio.io/v1alpha3 34 | kind: VirtualService 35 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/istio-system/backendconfig.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cloud.google.com/v1 2 | kind: BackendConfig 3 | metadata: 4 | name: gke-ingress-config 5 | spec: 6 | healthCheck: 7 | requestPath: /healthz/ready 8 | port: 15021 9 | type: HTTP 10 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/istio-system/mci.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.gke.io/v1 2 | kind: MultiClusterIngress 3 | metadata: 4 | annotations: 5 | configmanagement.gke.io/cluster-selector: ifa 6 | name: mci-igw 7 | spec: 8 | template: 9 | spec: 10 | backend: 11 | serviceName: istio-mcsingressgw 12 | servicePort: 80 13 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/istio-system/mcs.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.gke.io/v1 2 | kind: MultiClusterService 3 | metadata: 4 | annotations: 5 | beta.cloud.google.com/backend-config: '{"ports": {"80":"gke-ingress-config"}}' 6 | configmanagement.gke.io/cluster-selector: ifa 7 | labels: 8 | app: istio-ingressgateway 9 | istio: ingressgateway 10 | release: istio 11 | name: istio-mcsingressgw 12 | spec: 13 | template: 14 | spec: 15 | ports: 16 | - name: http2 17 | port: 80 18 | targetPort: 80 19 | - name: https 20 | port: 443 21 | - name: tls 22 | port: 15443 23 | targetPort: 15443 24 | - name: tcp 25 | port: 31400 26 | selector: 27 | app: istio-ingressgateway 28 | istio: ingressgateway 29 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/istio-system/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: istio-system 5 | 6 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/mci.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.gke.io/v1 2 | kind: MultiClusterIngress 3 | metadata: 4 | name: zone-ingress 5 | namespace: zoneprinter 6 | spec: 7 | template: 8 | spec: 9 | backend: 10 | serviceName: zone-mcs 11 | servicePort: 8080 12 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/mcs.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.gke.io/v1 2 | kind: MultiClusterService 3 | metadata: 4 | name: zone-mcs 5 | namespace: zoneprinter 6 | spec: 7 | template: 8 | spec: 9 | selector: 10 | app: zoneprinter 11 | ports: 12 | - name: web 13 | protocol: TCP 14 | port: 8080 15 | targetPort: 8080 16 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/namespace-ob.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: Namespace 17 | metadata: 18 | name: ob 19 | labels: 20 | env: istio 21 | istio-injection: enabled 22 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/zonedeploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: zone-ingress 5 | namespace: zoneprinter 6 | labels: 7 | app: zoneprinter 8 | spec: 9 | selector: 10 | matchLabels: 11 | app: zoneprinter 12 | template: 13 | metadata: 14 | labels: 15 | app: zoneprinter 16 | spec: 17 | containers: 18 | - name: frontend 19 | image: gcr.io/google-samples/zone-printer:0.2 20 | ports: 21 | - containerPort: 8080 22 | -------------------------------------------------------------------------------- /anthos/k8s-manifest/zonens.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: zoneprinter 5 | -------------------------------------------------------------------------------- /anthos/scripts/01_create_clusters.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./scripts/init.env 4 | 5 | function create_cluster { 6 | cluster_name=${1} 7 | cluster_zone=${2} 8 | gcloud container clusters create ${cluster_name} \ 9 | --machine-type=e2-standard-4 \ 10 | --num-nodes=4 \ 11 | --workload-pool=${PROJECT_ID}.svc.id.goog \ 12 | --enable-stackdriver-kubernetes \ 13 | --labels=mesh_id=proj-${PROJECT_NUMBER} \ 14 | --release-channel=rapid \ 15 | --zone ${cluster_zone} \ 16 | --enable-ip-alias \ 17 | --scopes=https://www.googleapis.com/auth/cloud-platform \ 18 | --service-account=anthos-sample-service-account@${PROJECT_ID}.iam.gserviceaccount.com 19 | } 20 | 21 | create_cluster ${CLUSTER2} ${CLUSTER2_ZONE} 22 | -------------------------------------------------------------------------------- /anthos/scripts/02_get_credentials.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./scripts/init.env 4 | 5 | function cleanup { 6 | cluster_name=${1} 7 | kubectl config delete-context ${cluster_name} 8 | } 9 | 10 | function get_credential_and_rename_context { 11 | cluster_name=${1} 12 | cluster_zone=${2} 13 | gcloud container clusters get-credentials ${cluster_name} --zone ${cluster_zone} 14 | kubectl config rename-context gke_${PROJECT_ID}_${cluster_zone}_${cluster_name} ${cluster_name} 15 | } 16 | 17 | cleanup ${CLUSTER2} 18 | get_credential_and_rename_context ${CLUSTER2} ${CLUSTER2_ZONE} 19 | -------------------------------------------------------------------------------- /anthos/scripts/03_create_rolebindings.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./scripts/init.env 4 | 5 | function create_clusterrolebinding { 6 | cluster_name=${1} 7 | kubectl create clusterrolebinding cluster-admin-binding \ 8 | --clusterrole=cluster-admin \ 9 | --user=anthos-sample-service-account@${PROJECT}.iam.gserviceaccount.com \ 10 | --context ${cluster_name} 11 | } 12 | 13 | create_clusterrolebinding ${CLUSTER2} 14 | -------------------------------------------------------------------------------- /anthos/scripts/04_register_clusters_to_hub.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./scripts/init.env 4 | 5 | function register_cluster_to_hub { 6 | cluster_name=${1} 7 | cluster_zone=${2} 8 | gcloud alpha container hub memberships register ${cluster_name} \ 9 | --enable-workload-identity \ 10 | --gke-cluster="${cluster_zone}/${cluster_name}" \ 11 | --project=${PROJECT_ID} \ 12 | --public-issuer-url="https://container.googleapis.com/v1/projects/${PROJECT_ID}/locations/${cluster_zone}/clusters/${cluster_name}" 13 | } 14 | 15 | register_cluster_to_hub ${CLUSTER2} ${CLUSTER2_ZONE} 16 | -------------------------------------------------------------------------------- /anthos/scripts/07_ingressforanthos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | source ./script/init.env 4 | 5 | gcloud services enable multiclusteringress.googleapis.com 6 | gcloud alpha container hub ingress enable \ 7 | --config-membership=projects/${PROJECT_ID}/locations/global/memberships/${CLUSTER2} -------------------------------------------------------------------------------- /anthos/scripts/init.env: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export PROJECT_ID=$(gcloud config get-value core/project) 4 | export PROJECT_NUMBER=$(gcloud projects describe ${PROJECT_ID} --format json | jq -r '.projectNumber') 5 | export CLUSTER2="anthos-sample-cluster2" 6 | export CLUSTER2_ZONE="us-west2-b" 7 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/assets/cors.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "origin": ["*"], 4 | "method": ["*"], 5 | "maxAgeSeconds": 3600, 6 | "responseHeader": ["*"] 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/assets/cors_origin.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "origin": ["ORIGIN"], 4 | "method": ["*"], 5 | "maxAgeSeconds": 3600, 6 | "responseHeader": ["*"] 7 | } 8 | ] 9 | 10 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/scripts/setup_eventarc_subscription.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | EVENTARC_NAME=$1 4 | 5 | DEAD_LETTER_TOPIC=genai-app-dead-letter 6 | 7 | PROJECT_NUMBER=$(gcloud projects describe $GOOGLE_CLOUD_PROJECT --format="value(projectNumber)") 8 | 9 | SUBSCRIPTION=$(gcloud pubsub subscriptions list --format json | jq -r '.[].name' | grep $EVENTARC_NAME) 10 | 11 | gcloud pubsub subscriptions add-iam-policy-binding $SUBSCRIPTION \ 12 | --member="serviceAccount:service-$PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com" \ 13 | --role="roles/pubsub.subscriber" 14 | 15 | gcloud pubsub subscriptions update $SUBSCRIPTION \ 16 | --ack-deadline 60 \ 17 | --dead-letter-topic $DEAD_LETTER_TOPIC \ 18 | --min-retry-delay 60 \ 19 | --max-retry-delay 600 20 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/genai-app-firestore/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==3.0.3 2 | gunicorn==23.0.0 3 | cloudevents==1.11.0 4 | google-cloud-firestore==2.18.0 5 | google-cloud-aiplatform==1.65.0 6 | google-cloud-storage==2.18.2 7 | tenacity==8.3.0 8 | langchain-community==0.2.16 9 | langchain-google-community==1.0.8 10 | langchain-text-splitters==0.2.4 11 | pypdf==4.3.1 12 | nanoid==2.0.0 -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/genai-app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.12.3-slim 2 | 3 | # Allow statements and log messages to immediately appear in the Cloud Run logs 4 | ENV PYTHONUNBUFFERED True 5 | 6 | # Copy application dependency manifests to the container image. 7 | # Copying this separately prevents re-running pip install on every code change. 8 | COPY requirements.txt ./ 9 | 10 | # Install production dependencies. 11 | RUN pip install --upgrade pip 12 | RUN pip install -r requirements.txt 13 | 14 | # Copy local code to the container image. 15 | ENV APP_HOME /app 16 | WORKDIR $APP_HOME 17 | COPY . ./ 18 | 19 | # Run the web service on container startup. 20 | # Use gunicorn webserver with one worker process and 8 threads. 21 | # For environments with multiple CPU cores, increase the number of workers 22 | # to be equal to the cores available. 23 | CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app 24 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/genai-app/requirements.txt: -------------------------------------------------------------------------------- 1 | flask[async]==3.0.3 2 | gunicorn==22.0.0 3 | cloudevents==1.10.1 4 | pypdf==4.2.0 5 | google-cloud-aiplatform==1.49.0 6 | vertexai==1.49.0 7 | langchain==0.2.3 8 | langchain-community==0.2.4 9 | langchain-google-vertexai==1.0.5 10 | pgvector==0.2.5 11 | asyncio==3.4.3 12 | asyncpg==0.29.0 13 | cloud-sql-python-connector[asyncpg]==1.9.2 14 | numpy==1.26.4 15 | transformers==4.41.2 -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/.env: -------------------------------------------------------------------------------- 1 | DB_USER= 2 | DB_PASSWORD= 3 | DB_NAME= 4 | INSTANCE_CONNECTION_NAME= 5 | BUCKET_NAME= 6 | SEARCH_HOST= 7 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-tailwindcss"] 3 | } 4 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | output: "standalone", 4 | }; 5 | 6 | export default nextConfig; 7 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/public/images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_genai_googlecloud/src/knowledge-drive-firestore/public/images/404.png -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/public/images/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_genai_googlecloud/src/knowledge-drive-firestore/public/images/check.png -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/public/images/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_genai_googlecloud/src/knowledge-drive-firestore/public/images/error.png -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_genai_googlecloud/src/knowledge-drive-firestore/public/images/logo.png -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/public/images/notfound.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_genai_googlecloud/src/knowledge-drive-firestore/public/images/notfound.png -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/app/(pages)/folders/[slug]/page.tsx: -------------------------------------------------------------------------------- 1 | import Content from "@/components/content"; 2 | import Screen from "@/components/screen"; 3 | import Sidebar from "@/components/sidebar"; 4 | 5 | export default async function FoldersPage({ 6 | params, 7 | }: { 8 | params: { slug: string }; 9 | }) { 10 | const { slug } = params; 11 | return ( 12 | 13 | 14 | 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/app/(pages)/items/[slug]/page.tsx: -------------------------------------------------------------------------------- 1 | import { getDownloadURL, getOwner, getSourceIP } from "@/lib/actions"; 2 | import { logWarn } from "@/lib/logging"; 3 | import { headers } from "next/headers"; 4 | import { notFound, redirect } from "next/navigation"; 5 | 6 | export default async function PerItemPage({ 7 | params, 8 | }: { 9 | params: { slug: string }; 10 | }) { 11 | const action = "showItemDetails"; 12 | const headersList = headers(); 13 | const owner = await getOwner(headersList); 14 | const sourceIP = await getSourceIP(headersList); 15 | const { slug: id } = params; 16 | 17 | const res = await getDownloadURL(id); 18 | 19 | if (!res.url) { 20 | logWarn({ 21 | owner: owner, 22 | sourceIP: sourceIP, 23 | action: action, 24 | message: `${sourceIP}/${owner}/${action}/${id}: Failed to find item with id ${id}`, 25 | }); 26 | return notFound(); 27 | } 28 | 29 | redirect(res.url); 30 | } 31 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/app/(pages)/my-drive/page.tsx: -------------------------------------------------------------------------------- 1 | import Content from "@/components/content"; 2 | import Screen from "@/components/screen"; 3 | import Sidebar from "@/components/sidebar"; 4 | import { ROOT_FOLDER_ID } from "@/lib/constants"; 5 | 6 | export default async function MyDrivePage() { 7 | return ( 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/app/(pages)/search/page.tsx: -------------------------------------------------------------------------------- 1 | import SearchContent from "@/components/search-content"; 2 | import Sidebar from "@/components/sidebar"; 3 | import { MY_DRIVE_URL } from "@/lib/constants"; 4 | import { redirect } from "next/navigation"; 5 | import Screen from "@/components/screen"; 6 | 7 | type SearchPageProps = { 8 | searchParams: { [key: string]: string | string[] }; 9 | }; 10 | 11 | export default async function SearchPage({ searchParams }: SearchPageProps) { 12 | const queryText = searchParams["q"]; 13 | if (queryText === undefined) { 14 | redirect(MY_DRIVE_URL); 15 | } 16 | 17 | return ( 18 | 19 | 20 | 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/app/error.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | export default function Error({ 4 | error, 5 | reset, 6 | }: { 7 | error: Error & { digest?: string }; 8 | reset: () => void; 9 | }) { 10 | return ( 11 |
12 |

Something went wrong!

13 | 14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_genai_googlecloud/src/knowledge-drive-firestore/src/app/favicon.ico -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | import ToasterContext from "@/context/toaster-context"; 5 | import Providers from "@/providers/query-client"; 6 | 7 | const inter = Inter({ subsets: ["latin"] }); 8 | 9 | export const metadata: Metadata = { 10 | title: "Knowledge Drive", 11 | description: "Store your knowledge into Cloud", 12 | }; 13 | 14 | export default function RootLayout({ 15 | children, 16 | }: Readonly<{ 17 | children: React.ReactNode; 18 | }>) { 19 | return ( 20 | 21 | 24 | 25 | {children} 26 | 27 | 28 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/app/not-found.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | function NotFoundPage() { 4 | return ( 5 |
6 |
7 |
8 |
9 | Logo 10 |
11 |

12 | 404.{" "} 13 | エラーが発生しました。 14 |

15 |

16 | 17 | リクエストされた URL はこのサーバーで見つかりませんでした。 18 | 19 | 確認できたことは以上です。 20 |

21 |
22 |
23 | Logo 24 |
25 |
26 |
27 | ); 28 | } 29 | 30 | export default NotFoundPage; 31 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/app-name.tsx: -------------------------------------------------------------------------------- 1 | const AppName = () => { 2 | return

ドライブ

; 3 | }; 4 | 5 | export default AppName; 6 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/avatar.tsx: -------------------------------------------------------------------------------- 1 | import { getOwner } from "@/lib/actions"; 2 | import { getAvatarColor } from "@/lib/color"; 3 | import { DEFAULT_OWNER } from "@/lib/constants"; 4 | import { headers } from "next/headers"; 5 | import Link from "next/link"; 6 | import { FaUser } from "react-icons/fa6"; 7 | 8 | const Avatar = async () => { 9 | const headersList = headers(); 10 | const owner = await getOwner(headersList); 11 | const color = getAvatarColor(owner); 12 | const bgColor = `bg-${color}`; 13 | 14 | return ( 15 |
16 | {owner === DEFAULT_OWNER ? ( 17 |
18 | 19 |
20 | ) : ( 21 | 22 |
25 | {owner.charAt(0).toUpperCase()} 26 |
27 | 28 | )} 29 |
30 | ); 31 | }; 32 | 33 | export default Avatar; 34 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/breadcrumb-folder.tsx: -------------------------------------------------------------------------------- 1 | import { MY_DRIVE_URL, ROOT_FOLDER_ID } from "@/lib/constants"; 2 | import Link from "next/link"; 3 | 4 | type BreadcrumbFolder = { 5 | id: string; 6 | name: string; 7 | // parent: string; 8 | }; 9 | 10 | type BreadcrumbFolderProps = { 11 | folder: BreadcrumbFolder; 12 | }; 13 | 14 | const BreadcrumbFolder = ({ folder }: BreadcrumbFolderProps) => { 15 | const getFolderLink = (id: string) => { 16 | if (id === ROOT_FOLDER_ID) return MY_DRIVE_URL; 17 | return `/folders/${id}`; 18 | }; 19 | 20 | return ( 21 | 22 |
23 |

24 | {folder.name} 25 |

26 |
27 | 28 | ); 29 | }; 30 | 31 | export default BreadcrumbFolder; 32 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/breadcrumb.tsx: -------------------------------------------------------------------------------- 1 | import BreadcrumbFolder from "@/components/breadcrumb-folder"; 2 | import { getParents } from "@/lib/actions"; 3 | import { MdKeyboardArrowRight } from "react-icons/md"; 4 | 5 | type BreadcrumbProps = { 6 | parent: string; 7 | }; 8 | 9 | const Breadcrumb = async ({ parent: id }: BreadcrumbProps) => { 10 | const folders = await getParents(id); 11 | return ( 12 |
13 | {folders.map((folder, index) => ( 14 |
15 | {index !== 0 && ( 16 | 20 | )} 21 | 22 |
23 | ))} 24 |
25 | ); 26 | }; 27 | 28 | export default Breadcrumb; 29 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/content-header.tsx: -------------------------------------------------------------------------------- 1 | import { IoMdCheckmark } from "react-icons/io"; 2 | import { RiMenuLine } from "react-icons/ri"; 3 | import { PiSquaresFour } from "react-icons/pi"; 4 | import DetailsButton from "@/components/details-button"; 5 | 6 | type ContentHeaderProps = { 7 | children: React.ReactNode; 8 | }; 9 | 10 | const ContentHeader = ({ children }: ContentHeaderProps) => { 11 | return ( 12 |
13 | {children} 14 |
15 |
16 | 17 | 18 |
19 |
20 | 21 |
22 | 23 |
24 |
25 | ); 26 | }; 27 | 28 | export default ContentHeader; 29 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/content.tsx: -------------------------------------------------------------------------------- 1 | import ContentHeader from "@/components/content-header"; 2 | import ItemSelector from "@/components/item-selector"; 3 | import ItemTable from "@/components/item-table"; 4 | import Breadcrumb from "@/components/breadcrumb"; 5 | import { Suspense } from "react"; 6 | import SkeletonBreadcrumb from "./skeleton-breadcrumb"; 7 | import SkeletonItemTable from "./skeleton-item-table"; 8 | import { randomUUID } from "crypto"; 9 | 10 | type ContentProps = { 11 | parent: string; 12 | }; 13 | 14 | const Content = ({ parent }: ContentProps) => { 15 | return ( 16 |
17 |
18 | 19 | }> 20 | 21 | 22 | 23 | 24 | }> 25 | 26 | 27 |
28 |
29 |
30 | ); 31 | }; 32 | 33 | export default Content; 34 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/details-button.tsx: -------------------------------------------------------------------------------- 1 | import { IoMdInformationCircleOutline } from "react-icons/io"; 2 | 3 | const DetailsButton = () => { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | }; 10 | 11 | export default DetailsButton; 12 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/empty-list.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | type EmptyListProps = { 4 | children: React.ReactNode; 5 | }; 6 | 7 | const EmptyList = ({ children }: EmptyListProps) => { 8 | return ( 9 |
10 |
11 | NotFound 17 |
18 | {children} 19 |
20 | ); 21 | }; 22 | 23 | export default EmptyList; 24 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/header.tsx: -------------------------------------------------------------------------------- 1 | import Logo from "@/components/logo"; 2 | import AppName from "@/components/app-name"; 3 | import SearchForm from "@/components/search-form"; 4 | import Avatar from "@/components/avatar"; 5 | import Link from "next/link"; 6 | import { Suspense } from "react"; 7 | 8 | const Header = () => { 9 | return ( 10 |
11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | ); 24 | }; 25 | 26 | export default Header; 27 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/item-header.tsx: -------------------------------------------------------------------------------- 1 | import { BsThreeDotsVertical } from "react-icons/bs"; 2 | const ItemHeader = () => { 3 | return ( 4 |
5 |

名前

6 |

オーナー

7 |

作成日時

8 |

ファイルサイズ

9 | 10 |
11 | ); 12 | }; 13 | 14 | export default ItemHeader; 15 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/item-selector-button.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | import { IoMdArrowDropdown } from "react-icons/io"; 3 | 4 | type ItemSelectorButtonProps = { 5 | text: string; 6 | }; 7 | 8 | const ItemSelectorButton = ({ text }: ItemSelectorButtonProps) => { 9 | return ( 10 |
11 |

{text}

12 | 13 |
14 | ); 15 | }; 16 | 17 | export default ItemSelectorButton; 18 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/item-selector.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ItemSelectorButton from "@/components/item-selector-button"; 3 | import type { ItemSelector } from "@/lib/types"; 4 | 5 | const itemSelectors: ItemSelector[] = [ 6 | { text: "種類" }, 7 | { text: "ユーザー" }, 8 | { text: "最終更新" }, 9 | ]; 10 | 11 | const ItemSelector = () => { 12 | return ( 13 |
14 | {itemSelectors.map((itemSelector) => { 15 | return ( 16 | 20 | ); 21 | })} 22 |
23 | ); 24 | }; 25 | 26 | export default ItemSelector; 27 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/llm-search-dialog.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Dialog, DialogContent } from "@/components/ui/dialog"; 4 | import LLMSearchResult from "@/components/llm-search-result"; 5 | 6 | type LLMSearchDialogProps = { 7 | open: boolean; 8 | onOpenChange: (open: boolean) => void; 9 | question: string; 10 | }; 11 | 12 | const LLMSearchDialog = ({ 13 | open, 14 | onOpenChange, 15 | question, 16 | }: LLMSearchDialogProps) => { 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | ); 24 | }; 25 | 26 | export default LLMSearchDialog; 27 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/logo.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | const Logo = () => { 4 | return ( 5 |
6 | Logo 7 |
8 | ); 9 | }; 10 | 11 | export default Logo; 12 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/new-folder-button.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { AiOutlineFolderAdd } from "react-icons/ai"; 4 | import { Dialog, DialogTrigger } from "@/components/ui/dialog"; 5 | import NewFolderForm from "@/components/new-folder-form"; 6 | import { useState } from "react"; 7 | 8 | type NewFolderButtonProps = { 9 | closeDropdownMenu: () => void; 10 | }; 11 | 12 | const NewFolderButton = ({ closeDropdownMenu }: NewFolderButtonProps) => { 13 | const [dialogOpen, setDialogOpen] = useState(false); 14 | 15 | const closeDialog = () => { 16 | setDialogOpen(false); 17 | }; 18 | 19 | return ( 20 | 21 | 22 |
23 | 24 |

新しいフォルダ

25 |
26 |
27 | 31 |
32 | ); 33 | }; 34 | 35 | export default NewFolderButton; 36 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/refresh.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useRouter } from "next/navigation"; 4 | import { useEffect } from "react"; 5 | 6 | type RefreshProps = { 7 | intervalSecond: number; 8 | }; 9 | 10 | const Refresh = ({ intervalSecond }: RefreshProps) => { 11 | const router = useRouter(); 12 | 13 | useEffect(() => { 14 | const id = setInterval(() => { 15 | console.log("refreshed"); 16 | router.refresh(); 17 | }, intervalSecond * 1000); 18 | return () => clearInterval(id); 19 | }, []); 20 | 21 | return <>; 22 | }; 23 | 24 | export default Refresh; 25 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/screen.tsx: -------------------------------------------------------------------------------- 1 | import Header from "@/components/header"; 2 | 3 | type ScreenProps = { 4 | children: React.ReactNode; 5 | }; 6 | 7 | const Screen = ({ children }: ScreenProps) => { 8 | return ( 9 | <> 10 |
11 |
{children}
12 | 13 | ); 14 | }; 15 | 16 | export default Screen; 17 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/search-content-text.tsx: -------------------------------------------------------------------------------- 1 | const SearchContentText = () => { 2 | return ( 3 |
4 |
5 |

6 | 検索結果 7 |

8 |
9 |
10 | ); 11 | }; 12 | 13 | export default SearchContentText; 14 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/search-content.tsx: -------------------------------------------------------------------------------- 1 | import ItemSelector from "@/components/item-selector"; 2 | import ContentHeader from "@/components/content-header"; 3 | import SearchContentText from "@/components/search-content-text"; 4 | import SearchItemTable from "@/components/search-item-table"; 5 | import { Suspense } from "react"; 6 | import SkeletonItemTable from "./skeleton-item-table"; 7 | import { randomUUID } from "crypto"; 8 | 9 | type SearchContentProps = { 10 | queryText: string; 11 | }; 12 | 13 | const SearchContent = ({ queryText }: SearchContentProps) => { 14 | return ( 15 |
16 |
17 | 18 | 19 | 20 | 21 | }> 22 | 23 | 24 |
25 |
26 |
27 | ); 28 | }; 29 | 30 | export default SearchContent; 31 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/skeleton-breadcrumb-folder.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from "@/components/ui/skeleton"; 2 | 3 | const SkeletonBreadcrumbFolder = () => { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | }; 10 | 11 | export default SkeletonBreadcrumbFolder; 12 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/skeleton-breadcrumb.tsx: -------------------------------------------------------------------------------- 1 | import { MdKeyboardArrowRight } from "react-icons/md"; 2 | import SkeletonBreadcrumbFolder from "./skeleton-breadcrumb-folder"; 3 | 4 | const SkeletonBreadcrumb = () => { 5 | return ( 6 |
7 |
8 | 9 | 13 | 14 | 18 | 19 |
20 |
21 | ); 22 | }; 23 | 24 | export default SkeletonBreadcrumb; 25 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/skeleton-item-header.tsx: -------------------------------------------------------------------------------- 1 | import { BsThreeDotsVertical } from "react-icons/bs"; 2 | import { Skeleton } from "@/components/ui/skeleton"; 3 | 4 | const SkeletonItemHeader = () => { 5 | return ( 6 |
7 |

8 | 9 |

10 |

11 | 12 |

13 |

14 | 15 |

16 |

17 | 18 |

19 | 20 |
21 | ); 22 | }; 23 | 24 | export default SkeletonItemHeader; 25 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/skeleton-item-table.tsx: -------------------------------------------------------------------------------- 1 | import SkeletonItemHeader from "@/components/skeleton-item-header"; 2 | import SkeletonItem from "@/components/skeleton-item"; 3 | 4 | const SkeletonItemTable = () => { 5 | return ( 6 |
7 | 8 |
9 | 10 | 11 | 12 | 13 |
14 |
15 | ); 16 | }; 17 | 18 | export default SkeletonItemTable; 19 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/skeleton-item.tsx: -------------------------------------------------------------------------------- 1 | import { BsThreeDotsVertical } from "react-icons/bs"; 2 | import { Skeleton } from "@/components/ui/skeleton"; 3 | 4 | const SkeletonItem = () => { 5 | return ( 6 |
7 | 8 |

9 | 10 |

11 |

12 | 13 |

14 |

15 | 16 |

17 |

18 | 19 |

20 | 21 |
22 | ); 23 | }; 24 | 25 | export default SkeletonItem; 26 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 | ) 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )) 24 | Label.displayName = LabelPrimitive.Root.displayName 25 | 26 | export { Label } 27 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ) 13 | } 14 | 15 | export { Skeleton } 16 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/context/toaster-context.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Toaster } from "react-hot-toast"; 4 | 5 | const ToasterContext = () => { 6 | return ; 7 | }; 8 | 9 | export default ToasterContext; 10 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/lib/color.ts: -------------------------------------------------------------------------------- 1 | const colors: readonly string[] = [ 2 | "slate", 3 | "red", 4 | "orange", 5 | "amber", 6 | "yellow", 7 | "lime", 8 | "green", 9 | "emerald", 10 | "teal", 11 | "cyan", 12 | "sky", 13 | "blue", 14 | "indigo", 15 | "violet", 16 | "purple", 17 | "fuchsia", 18 | "pink", 19 | "rose", 20 | ]; 21 | 22 | const intensities: readonly string[] = ["300", "500", "700"]; 23 | 24 | export const getAvatarColor = (owner: string): string => { 25 | return ( 26 | colors[owner.charCodeAt(0) % colors.length] + 27 | "-" + 28 | intensities[owner.charCodeAt(1) % intensities.length] 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | export const MY_DRIVE_URL: string = "/my-drive"; 2 | export const DEFAULT_OWNER: string = "anonymous"; 3 | export const ROOT_FOLDER_ID: string = "Root"; 4 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/lib/firebase/admin.ts: -------------------------------------------------------------------------------- 1 | import "server-only"; 2 | 3 | import { 4 | initializeApp, 5 | applicationDefault, 6 | AppOptions, 7 | getApp, 8 | App, 9 | } from "firebase-admin/app"; 10 | 11 | const ADMIN = "ADMIN"; 12 | const firebaseAdminConfig: AppOptions = { 13 | credential: applicationDefault(), 14 | }; 15 | 16 | const initialize = () => { 17 | let adminApp: App; 18 | try { 19 | adminApp = getApp(ADMIN); 20 | } catch (e) { 21 | adminApp = initializeApp(firebaseAdminConfig, ADMIN); 22 | } 23 | return adminApp; 24 | }; 25 | 26 | const firebaseAdminApp = initialize(); 27 | 28 | export default firebaseAdminApp; 29 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/lib/logging.ts: -------------------------------------------------------------------------------- 1 | export const logError = (obj: Object) => { 2 | console.log(JSON.stringify({ ...obj, severity: "ERROR" })); 3 | }; 4 | 5 | export const logDebug = (obj: Object) => { 6 | console.log(JSON.stringify({ ...obj, severity: "DEBUG" })); 7 | }; 8 | 9 | export const logInfo = (obj: Object) => { 10 | console.log(JSON.stringify({ ...obj, severity: "INFO" })); 11 | }; 12 | 13 | export const logWarn = (obj: Object) => { 14 | console.log(JSON.stringify({ ...obj, severity: "WARN" })); 15 | }; 16 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/lib/types.ts: -------------------------------------------------------------------------------- 1 | export type ItemSelector = { 2 | text: string; 3 | }; 4 | 5 | export type TItem = { 6 | id: string; 7 | name: string; 8 | size: number | null; 9 | type: string | null; 10 | isFolder: boolean; 11 | createdAt: string; 12 | parent: string | null; 13 | owner: string | null; 14 | description: string | null; 15 | embedded: boolean; 16 | }; 17 | 18 | export type FolderForBreadcrumb = { 19 | id: string; 20 | name: string; 21 | }; 22 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | import type { NextRequest } from "next/server"; 3 | import { MY_DRIVE_URL } from "./lib/constants"; 4 | 5 | export const middleware = (request: NextRequest) => { 6 | return NextResponse.redirect(new URL(MY_DRIVE_URL, request.url)); 7 | }; 8 | 9 | export const config = { 10 | matcher: "/", 11 | }; 12 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/src/providers/query-client.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 5 | 6 | export default function Providers({ children }: { children: React.ReactNode }) { 7 | const [queryClient] = useState( 8 | () => 9 | new QueryClient({ 10 | defaultOptions: { 11 | queries: { 12 | staleTime: 60 * 1000, 13 | }, 14 | }, 15 | }), 16 | ); 17 | 18 | return ( 19 | {children} 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive-firestore/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next", 18 | }, 19 | ], 20 | "paths": { 21 | "@/*": ["./src/*"], 22 | }, 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"], 26 | } 27 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/.env: -------------------------------------------------------------------------------- 1 | DB_USER= 2 | DB_PASSWORD= 3 | DB_NAME= 4 | INSTANCE_CONNECTION_NAME= 5 | BUCKET_NAME= 6 | SEARCH_HOST= 7 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-tailwindcss"] 3 | } 4 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "src/app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | output: "standalone", 4 | }; 5 | 6 | export default nextConfig; 7 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/public/images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_genai_googlecloud/src/knowledge-drive/public/images/404.png -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/public/images/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_genai_googlecloud/src/knowledge-drive/public/images/check.png -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/public/images/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_genai_googlecloud/src/knowledge-drive/public/images/error.png -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_genai_googlecloud/src/knowledge-drive/public/images/logo.png -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/public/images/notfound.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_genai_googlecloud/src/knowledge-drive/public/images/notfound.png -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/app/(pages)/folders/[slug]/page.tsx: -------------------------------------------------------------------------------- 1 | import Content from "@/components/content"; 2 | import Screen from "@/components/screen"; 3 | import Sidebar from "@/components/sidebar"; 4 | 5 | export default async function FoldersPage({ 6 | params, 7 | }: { 8 | params: { slug: string }; 9 | }) { 10 | const { slug } = params; 11 | return ( 12 | 13 | 14 | 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/app/(pages)/items/[slug]/page.tsx: -------------------------------------------------------------------------------- 1 | import { getDownloadURL, getOwner, getSourceIP } from "@/lib/actions"; 2 | import { logWarn } from "@/lib/logging"; 3 | import { headers } from "next/headers"; 4 | import { notFound, redirect } from "next/navigation"; 5 | 6 | export default async function PerItemPage({ 7 | params, 8 | }: { 9 | params: { slug: string }; 10 | }) { 11 | const action = "showItemDetails"; 12 | const headersList = headers(); 13 | const owner = await getOwner(headersList); 14 | const sourceIP = await getSourceIP(headersList); 15 | const { slug: id } = params; 16 | 17 | const res = await getDownloadURL(id); 18 | 19 | if (!res.url) { 20 | logWarn({ 21 | owner: owner, 22 | sourceIP: sourceIP, 23 | action: action, 24 | message: `${sourceIP}/${owner}/${action}/${id}: Failed to find item with id ${id}`, 25 | }); 26 | return notFound(); 27 | } 28 | 29 | redirect(res.url); 30 | } 31 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/app/(pages)/my-drive/page.tsx: -------------------------------------------------------------------------------- 1 | import Content from "@/components/content"; 2 | import Screen from "@/components/screen"; 3 | import Sidebar from "@/components/sidebar"; 4 | import { ROOT_FOLDER_ID } from "@/lib/constants"; 5 | 6 | export default async function MyDrivePage() { 7 | return ( 8 | 9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/app/(pages)/search/page.tsx: -------------------------------------------------------------------------------- 1 | import SearchContent from "@/components/search-content"; 2 | import Sidebar from "@/components/sidebar"; 3 | import { MY_DRIVE_URL } from "@/lib/constants"; 4 | import { redirect } from "next/navigation"; 5 | import Screen from "@/components/screen"; 6 | 7 | type SearchPageProps = { 8 | searchParams: { [key: string]: string | string[] }; 9 | }; 10 | 11 | export default async function SearchPage({ searchParams }: SearchPageProps) { 12 | const queryText = searchParams["q"]; 13 | if (queryText === undefined) { 14 | redirect(MY_DRIVE_URL); 15 | } 16 | 17 | return ( 18 | 19 | 20 | 21 | 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/app/error.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | export default function Error({ 4 | error, 5 | reset, 6 | }: { 7 | error: Error & { digest?: string }; 8 | reset: () => void; 9 | }) { 10 | return ( 11 |
12 |

Something went wrong!

13 | 14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_genai_googlecloud/src/knowledge-drive/src/app/favicon.ico -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | import ToasterContext from "@/context/toaster-context"; 5 | import Providers from "@/providers/query-client"; 6 | 7 | const inter = Inter({ subsets: ["latin"] }); 8 | 9 | export const metadata: Metadata = { 10 | title: "Knowledge Drive", 11 | description: "Store your knowledge into Cloud", 12 | }; 13 | 14 | export default function RootLayout({ 15 | children, 16 | }: Readonly<{ 17 | children: React.ReactNode; 18 | }>) { 19 | return ( 20 | 21 | 24 | 25 | {children} 26 | 27 | 28 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/app/not-found.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | function NotFoundPage() { 4 | return ( 5 |
6 |
7 |
8 |
9 | Logo 10 |
11 |

12 | 404.{" "} 13 | エラーが発生しました。 14 |

15 |

16 | 17 | リクエストされた URL はこのサーバーで見つかりませんでした。 18 | 19 | 確認できたことは以上です。 20 |

21 |
22 |
23 | Logo 24 |
25 |
26 |
27 | ); 28 | } 29 | 30 | export default NotFoundPage; 31 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/app-name.tsx: -------------------------------------------------------------------------------- 1 | const AppName = () => { 2 | return

ドライブ

; 3 | }; 4 | 5 | export default AppName; 6 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/avatar.tsx: -------------------------------------------------------------------------------- 1 | import { getOwner } from "@/lib/actions"; 2 | import { getAvatarColor } from "@/lib/color"; 3 | import { DEFAULT_OWNER } from "@/lib/constants"; 4 | import { headers } from "next/headers"; 5 | import Link from "next/link"; 6 | import { FaUser } from "react-icons/fa6"; 7 | 8 | const Avatar = async () => { 9 | const headersList = headers(); 10 | const owner = await getOwner(headersList); 11 | const color = getAvatarColor(owner); 12 | const bgColor = `bg-${color}`; 13 | 14 | return ( 15 |
16 | {owner === DEFAULT_OWNER ? ( 17 |
18 | 19 |
20 | ) : ( 21 | 22 |
25 | {owner.charAt(0).toUpperCase()} 26 |
27 | 28 | )} 29 |
30 | ); 31 | }; 32 | 33 | export default Avatar; 34 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/breadcrumb-folder.tsx: -------------------------------------------------------------------------------- 1 | import { MY_DRIVE_URL, ROOT_FOLDER_ID } from "@/lib/constants"; 2 | import Link from "next/link"; 3 | 4 | type BreadcrumbFolder = { 5 | id: string; 6 | name: string; 7 | parent: string; 8 | }; 9 | 10 | type BreadcrumbFolderProps = { 11 | folder: BreadcrumbFolder; 12 | }; 13 | 14 | const BreadcrumbFolder = ({ folder }: BreadcrumbFolderProps) => { 15 | const getFolderLink = (id: string) => { 16 | if (id === ROOT_FOLDER_ID) return MY_DRIVE_URL; 17 | return `/folders/${id}`; 18 | }; 19 | 20 | return ( 21 | 22 |
23 |

24 | {folder.name} 25 |

26 |
27 | 28 | ); 29 | }; 30 | 31 | export default BreadcrumbFolder; 32 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/breadcrumb.tsx: -------------------------------------------------------------------------------- 1 | import BreadcrumbFolder from "@/components/breadcrumb-folder"; 2 | import { getParents } from "@/lib/actions"; 3 | import { MdKeyboardArrowRight } from "react-icons/md"; 4 | 5 | type BreadcrumbProps = { 6 | parent: string; 7 | }; 8 | 9 | const Breadcrumb = async ({ parent: id }: BreadcrumbProps) => { 10 | const folders = await getParents(id); 11 | return ( 12 |
13 | {folders.map((folder, index) => ( 14 |
15 | {index !== 0 && ( 16 | 20 | )} 21 | 22 |
23 | ))} 24 |
25 | ); 26 | }; 27 | 28 | export default Breadcrumb; 29 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/content-header.tsx: -------------------------------------------------------------------------------- 1 | import { IoMdCheckmark } from "react-icons/io"; 2 | import { RiMenuLine } from "react-icons/ri"; 3 | import { PiSquaresFour } from "react-icons/pi"; 4 | import DetailsButton from "@/components/details-button"; 5 | 6 | type ContentHeaderProps = { 7 | children: React.ReactNode; 8 | }; 9 | 10 | const ContentHeader = ({ children }: ContentHeaderProps) => { 11 | return ( 12 |
13 | {children} 14 |
15 |
16 | 17 | 18 |
19 |
20 | 21 |
22 | 23 |
24 |
25 | ); 26 | }; 27 | 28 | export default ContentHeader; 29 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/content.tsx: -------------------------------------------------------------------------------- 1 | import ContentHeader from "@/components/content-header"; 2 | import ItemSelector from "@/components/item-selector"; 3 | import ItemTable from "@/components/item-table"; 4 | import Breadcrumb from "@/components/breadcrumb"; 5 | import { Suspense } from "react"; 6 | import SkeletonBreadcrumb from "./skeleton-breadcrumb"; 7 | import SkeletonItemTable from "./skeleton-item-table"; 8 | import { randomUUID } from "crypto"; 9 | 10 | type ContentProps = { 11 | parent: string; 12 | }; 13 | 14 | const Content = ({ parent }: ContentProps) => { 15 | return ( 16 |
17 |
18 | 19 | }> 20 | 21 | 22 | 23 | 24 | }> 25 | 26 | 27 |
28 |
29 |
30 | ); 31 | }; 32 | 33 | export default Content; 34 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/details-button.tsx: -------------------------------------------------------------------------------- 1 | import { IoMdInformationCircleOutline } from "react-icons/io"; 2 | 3 | const DetailsButton = () => { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | }; 10 | 11 | export default DetailsButton; 12 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/empty-list.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | type EmptyListProps = { 4 | children: React.ReactNode; 5 | }; 6 | 7 | const EmptyList = ({ children }: EmptyListProps) => { 8 | return ( 9 |
10 |
11 | NotFound 17 |
18 | {children} 19 |
20 | ); 21 | }; 22 | 23 | export default EmptyList; 24 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/header.tsx: -------------------------------------------------------------------------------- 1 | import Logo from "@/components/logo"; 2 | import AppName from "@/components/app-name"; 3 | import SearchForm from "@/components/search-form"; 4 | import Avatar from "@/components/avatar"; 5 | import Link from "next/link"; 6 | import { Suspense } from "react"; 7 | 8 | const Header = () => { 9 | return ( 10 |
11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | ); 24 | }; 25 | 26 | export default Header; 27 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/item-header.tsx: -------------------------------------------------------------------------------- 1 | import { BsThreeDotsVertical } from "react-icons/bs"; 2 | const ItemHeader = () => { 3 | return ( 4 |
5 |

名前

6 |

オーナー

7 |

作成日時

8 |

ファイルサイズ

9 | 10 |
11 | ); 12 | }; 13 | 14 | export default ItemHeader; 15 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/item-selector-button.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | import { IoMdArrowDropdown } from "react-icons/io"; 3 | 4 | type ItemSelectorButtonProps = { 5 | text: string; 6 | }; 7 | 8 | const ItemSelectorButton = ({ text }: ItemSelectorButtonProps) => { 9 | return ( 10 |
11 |

{text}

12 | 13 |
14 | ); 15 | }; 16 | 17 | export default ItemSelectorButton; 18 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/item-selector.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ItemSelectorButton from "@/components/item-selector-button"; 3 | import type { ItemSelector } from "@/lib/types"; 4 | 5 | const itemSelectors: ItemSelector[] = [ 6 | { text: "種類" }, 7 | { text: "ユーザー" }, 8 | { text: "最終更新" }, 9 | ]; 10 | 11 | const ItemSelector = () => { 12 | return ( 13 |
14 | {itemSelectors.map((itemSelector) => { 15 | return ( 16 | 20 | ); 21 | })} 22 |
23 | ); 24 | }; 25 | 26 | export default ItemSelector; 27 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/llm-search-dialog.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Dialog, DialogContent } from "@/components/ui/dialog"; 4 | import LLMSearchResult from "@/components/llm-search-result"; 5 | 6 | type LLMSearchDialogProps = { 7 | open: boolean; 8 | onOpenChange: (open: boolean) => void; 9 | question: string; 10 | }; 11 | 12 | const LLMSearchDialog = ({ 13 | open, 14 | onOpenChange, 15 | question, 16 | }: LLMSearchDialogProps) => { 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | ); 24 | }; 25 | 26 | export default LLMSearchDialog; 27 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/logo.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | const Logo = () => { 4 | return ( 5 |
6 | Logo 7 |
8 | ); 9 | }; 10 | 11 | export default Logo; 12 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/new-folder-button.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { AiOutlineFolderAdd } from "react-icons/ai"; 4 | import { Dialog, DialogTrigger } from "@/components/ui/dialog"; 5 | import NewFolderForm from "@/components/new-folder-form"; 6 | import { useState } from "react"; 7 | 8 | type NewFolderButtonProps = { 9 | closeDropdownMenu: () => void; 10 | }; 11 | 12 | const NewFolderButton = ({ closeDropdownMenu }: NewFolderButtonProps) => { 13 | const [dialogOpen, setDialogOpen] = useState(false); 14 | 15 | const closeDialog = () => { 16 | setDialogOpen(false); 17 | }; 18 | 19 | return ( 20 | 21 | 22 |
23 | 24 |

新しいフォルダ

25 |
26 |
27 | 31 |
32 | ); 33 | }; 34 | 35 | export default NewFolderButton; 36 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/refresh.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useRouter } from "next/navigation"; 4 | import { useEffect } from "react"; 5 | 6 | type RefreshProps = { 7 | intervalSecond: number; 8 | }; 9 | 10 | const Refresh = ({ intervalSecond }: RefreshProps) => { 11 | const router = useRouter(); 12 | 13 | useEffect(() => { 14 | const id = setInterval(() => { 15 | console.log("refreshed"); 16 | router.refresh(); 17 | }, intervalSecond * 1000); 18 | return () => clearInterval(id); 19 | }, []); 20 | 21 | return <>; 22 | }; 23 | 24 | export default Refresh; 25 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/screen.tsx: -------------------------------------------------------------------------------- 1 | import Header from "@/components/header"; 2 | 3 | type ScreenProps = { 4 | children: React.ReactNode; 5 | }; 6 | 7 | const Screen = ({ children }: ScreenProps) => { 8 | return ( 9 | <> 10 |
11 |
{children}
12 | 13 | ); 14 | }; 15 | 16 | export default Screen; 17 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/search-content-text.tsx: -------------------------------------------------------------------------------- 1 | const SearchContentText = () => { 2 | return ( 3 |
4 |
5 |

6 | 検索結果 7 |

8 |
9 |
10 | ); 11 | }; 12 | 13 | export default SearchContentText; 14 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/search-content.tsx: -------------------------------------------------------------------------------- 1 | import ItemSelector from "@/components/item-selector"; 2 | import ContentHeader from "@/components/content-header"; 3 | import SearchContentText from "@/components/search-content-text"; 4 | import SearchItemTable from "@/components/search-item-table"; 5 | import { Suspense } from "react"; 6 | import SkeletonItemTable from "./skeleton-item-table"; 7 | import { randomUUID } from "crypto"; 8 | 9 | type SearchContentProps = { 10 | queryText: string; 11 | }; 12 | 13 | const SearchContent = ({ queryText }: SearchContentProps) => { 14 | return ( 15 |
16 |
17 | 18 | 19 | 20 | 21 | }> 22 | 23 | 24 |
25 |
26 |
27 | ); 28 | }; 29 | 30 | export default SearchContent; 31 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/search-item-table.tsx: -------------------------------------------------------------------------------- 1 | import { getOwner, searchItemsByQueryAndOwner } from "@/lib/actions"; 2 | import EmptyList from "@/components/empty-list"; 3 | import Item from "@/components/item"; 4 | import ItemHeader from "@/components/item-header"; 5 | import { headers } from "next/headers"; 6 | 7 | type SearchItemProps = { 8 | queryText: string; 9 | }; 10 | 11 | const SearchItemTable = async ({ queryText }: SearchItemProps) => { 12 | const headersList = headers(); 13 | const owner = await getOwner(headersList); 14 | 15 | const items = await searchItemsByQueryAndOwner(queryText, owner); 16 | 17 | if (items.length === 0) { 18 | return ( 19 | 20 |

21 | 検索条件にヒットするファイル、 22 |
23 | フォルダが見つかりませんでした 24 |

25 |
26 | ); 27 | } 28 | 29 | return ( 30 |
31 | 32 |
33 | {items.map((item) => ( 34 | 35 | ))} 36 |
37 |
38 | ); 39 | }; 40 | 41 | export default SearchItemTable; 42 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/skeleton-breadcrumb-folder.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from "@/components/ui/skeleton"; 2 | 3 | const SkeletonBreadcrumbFolder = () => { 4 | return ( 5 |
6 | 7 |
8 | ); 9 | }; 10 | 11 | export default SkeletonBreadcrumbFolder; 12 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/skeleton-breadcrumb.tsx: -------------------------------------------------------------------------------- 1 | import { MdKeyboardArrowRight } from "react-icons/md"; 2 | import SkeletonBreadcrumbFolder from "./skeleton-breadcrumb-folder"; 3 | 4 | const SkeletonBreadcrumb = () => { 5 | return ( 6 |
7 |
8 | 9 | 13 | 14 | 18 | 19 |
20 |
21 | ); 22 | }; 23 | 24 | export default SkeletonBreadcrumb; 25 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/skeleton-item-header.tsx: -------------------------------------------------------------------------------- 1 | import { BsThreeDotsVertical } from "react-icons/bs"; 2 | import { Skeleton } from "@/components/ui/skeleton"; 3 | 4 | const SkeletonItemHeader = () => { 5 | return ( 6 |
7 |

8 | 9 |

10 |

11 | 12 |

13 |

14 | 15 |

16 |

17 | 18 |

19 | 20 |
21 | ); 22 | }; 23 | 24 | export default SkeletonItemHeader; 25 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/skeleton-item-table.tsx: -------------------------------------------------------------------------------- 1 | import SkeletonItemHeader from "@/components/skeleton-item-header"; 2 | import SkeletonItem from "@/components/skeleton-item"; 3 | 4 | const SkeletonItemTable = () => { 5 | return ( 6 |
7 | 8 |
9 | 10 | 11 | 12 | 13 |
14 |
15 | ); 16 | }; 17 | 18 | export default SkeletonItemTable; 19 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/skeleton-item.tsx: -------------------------------------------------------------------------------- 1 | import { BsThreeDotsVertical } from "react-icons/bs"; 2 | import { Skeleton } from "@/components/ui/skeleton"; 3 | 4 | const SkeletonItem = () => { 5 | return ( 6 |
7 | 8 |

9 | 10 |

11 |

12 | 13 |

14 |

15 | 16 |

17 |

18 | 19 |

20 | 21 |
22 | ); 23 | }; 24 | 25 | export default SkeletonItem; 26 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 | ) 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )) 24 | Label.displayName = LabelPrimitive.Root.displayName 25 | 26 | export { Label } 27 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ) 13 | } 14 | 15 | export { Skeleton } 16 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/context/toaster-context.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Toaster } from "react-hot-toast"; 4 | 5 | const ToasterContext = () => { 6 | return ; 7 | }; 8 | 9 | export default ToasterContext; 10 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/lib/color.ts: -------------------------------------------------------------------------------- 1 | const colors: readonly string[] = [ 2 | "slate", 3 | "red", 4 | "orange", 5 | "amber", 6 | "yellow", 7 | "lime", 8 | "green", 9 | "emerald", 10 | "teal", 11 | "cyan", 12 | "sky", 13 | "blue", 14 | "indigo", 15 | "violet", 16 | "purple", 17 | "fuchsia", 18 | "pink", 19 | "rose", 20 | ]; 21 | 22 | const intensities: readonly string[] = ["300", "500", "700"]; 23 | 24 | export const getAvatarColor = (owner: string): string => { 25 | return ( 26 | colors[owner.charCodeAt(0) % colors.length] + 27 | "-" + 28 | intensities[owner.charCodeAt(1) % intensities.length] 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/lib/constants.ts: -------------------------------------------------------------------------------- 1 | export const MY_DRIVE_URL = "/my-drive"; 2 | export const DEFAULT_OWNER = "anonymous"; 3 | export const ROOT_FOLDER_ID = "Root"; 4 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/lib/db.ts: -------------------------------------------------------------------------------- 1 | import pg from "pg"; 2 | import { Connector, IpAddressTypes } from "@google-cloud/cloud-sql-connector"; 3 | const { Pool } = pg; 4 | 5 | declare global { 6 | var db: { 7 | pool: pg.Pool | null; 8 | }; 9 | } 10 | 11 | if (!global.db) { 12 | global.db = { pool: null }; 13 | } 14 | 15 | export const createPoolWithConnector = async () => { 16 | try { 17 | if (!global.db.pool) { 18 | const connector = new Connector(); 19 | const clientOpts = await connector.getOptions({ 20 | instanceConnectionName: process.env.INSTANCE_CONNECTION_NAME || "", 21 | ipType: IpAddressTypes.PUBLIC, 22 | }); 23 | 24 | global.db.pool = new Pool({ 25 | ...clientOpts, 26 | user: process.env.DB_USER, 27 | password: process.env.DB_PASSWORD, 28 | database: process.env.DB_NAME, 29 | max: 5, 30 | }); 31 | } 32 | } catch (error) { 33 | console.error(`Database connection failed: ${error}`); 34 | throw new Error("Database connection failed"); 35 | } 36 | return global.db.pool; 37 | }; 38 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/lib/logging.ts: -------------------------------------------------------------------------------- 1 | export const logError = (obj: Object) => { 2 | console.log(JSON.stringify({ ...obj, severity: "ERROR" })); 3 | }; 4 | 5 | export const logDebug = (obj: Object) => { 6 | console.log(JSON.stringify({ ...obj, severity: "DEBUG" })); 7 | }; 8 | 9 | export const logInfo = (obj: Object) => { 10 | console.log(JSON.stringify({ ...obj, severity: "INFO" })); 11 | }; 12 | 13 | export const logWarn = (obj: Object) => { 14 | console.log(JSON.stringify({ ...obj, severity: "WARN" })); 15 | }; 16 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/lib/types.ts: -------------------------------------------------------------------------------- 1 | export type ItemSelector = { 2 | text: string; 3 | }; 4 | 5 | export type TItem = { 6 | id: string; 7 | name: string; 8 | size: number | null; 9 | type: string | null; 10 | isFolder: boolean; 11 | createdAt: string; 12 | parent: string | null; 13 | owner: string | null; 14 | description: string | null; 15 | }; 16 | 17 | export type DBItem = { 18 | id: string; 19 | name: string; 20 | size: number | null; 21 | type: string | null; 22 | is_folder: boolean; 23 | created_at: Date; 24 | parent: string | null; 25 | owner: string | null; 26 | description: string | null; 27 | }; 28 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | import type { NextRequest } from "next/server"; 3 | import { MY_DRIVE_URL } from "./lib/constants"; 4 | 5 | export const middleware = (request: NextRequest) => { 6 | return NextResponse.redirect(new URL(MY_DRIVE_URL, request.url)); 7 | }; 8 | 9 | export const config = { 10 | matcher: "/", 11 | }; 12 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/src/providers/query-client.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; 5 | 6 | export default function Providers({ children }: { children: React.ReactNode }) { 7 | const [queryClient] = useState( 8 | () => 9 | new QueryClient({ 10 | defaultOptions: { 11 | queries: { 12 | staleTime: 60 * 1000, 13 | }, 14 | }, 15 | }), 16 | ); 17 | 18 | return ( 19 | {children} 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /appdev_genai_googlecloud/src/knowledge-drive/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next", 18 | }, 19 | ], 20 | "paths": { 21 | "@/*": ["./src/*"], 22 | }, 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"], 26 | } 27 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/genai-app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10-slim 2 | 3 | # Allow statements and log messages to immediately appear in the Cloud Run logs 4 | ENV PYTHONUNBUFFERED True 5 | 6 | # Copy application dependency manifests to the container image. 7 | # Copying this separately prevents re-running pip install on every code change. 8 | COPY requirements.txt ./ 9 | 10 | # Install production dependencies. 11 | RUN pip install --upgrade pip 12 | RUN pip install -r requirements.txt 13 | 14 | # Copy local code to the container image. 15 | ENV APP_HOME /app 16 | WORKDIR $APP_HOME 17 | COPY . ./ 18 | 19 | # Run the web service on container startup. 20 | # Use gunicorn webserver with one worker process and 8 threads. 21 | # For environments with multiple CPU cores, increase the number of workers 22 | # to be equal to the cores available. 23 | CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app 24 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/genai-app/requirements.txt: -------------------------------------------------------------------------------- 1 | flask[async]==2.3.2 2 | gunicorn==20.1.0 3 | cloudevents==1.9.0 4 | pypdf==3.16.1 5 | google-cloud-aiplatform==1.30.1 6 | langchain==0.0.294 7 | pgvector==0.1.8 8 | asyncio==3.4.3 9 | asyncpg==0.27.0 10 | cloud-sql-python-connector[asyncpg]==1.2.3 11 | numpy==1.22.4 12 | firebase-admin==6.2.0 13 | transformers==4.34.0 -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/.env: -------------------------------------------------------------------------------- 1 | NEXT_TELEMETRY_DISABLED=1 2 | 3 | NEXT_PUBLIC_FIREBASE_API_KEY= 4 | NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN= 5 | NEXT_PUBLIC_FIREBASE_PROJECT_ID= 6 | NEXT_PUBLIC_FIREBASE_APP_ID= 7 | NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET= 8 | 9 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/app/(site)/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Metadata } from "next"; 2 | import { headers } from "next/headers"; 3 | 4 | export async function generateMetadata(): Promise { 5 | const csrfToken = headers().get("X-CSRF-Token") || "missing"; 6 | 7 | return { 8 | other: { 9 | "x-csrf-token": csrfToken, 10 | }, 11 | }; 12 | } 13 | 14 | export default function Layout({ children }: { children: React.ReactNode }) { 15 | return children; 16 | } 17 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/app/api/search/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | 3 | export async function POST(request: Request) { 4 | return NextResponse.json({ 5 | answer: 6 | "(テスト返答)今日は良い天気ですね。今日のような日は外に出るのが良いでしょう", 7 | metadata: { 8 | source: "UiXQ7c9jYP2lCbFMwShS", 9 | page: 5, 10 | }, 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/app/context/ToasterContext.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Toaster } from "react-hot-toast"; 4 | 5 | const ToasterContext = () => { 6 | return ; 7 | }; 8 | 9 | export default ToasterContext; 10 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/app/drive/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Metadata } from "next"; 2 | import { headers } from "next/headers"; 3 | 4 | export async function generateMetadata(): Promise { 5 | const csrfToken = headers().get("X-CSRF-Token") || "missing"; 6 | 7 | return { 8 | other: { 9 | "x-csrf-token": csrfToken, 10 | }, 11 | }; 12 | } 13 | 14 | export default function Layout({ children }: { children: React.ReactNode }) { 15 | return children; 16 | } 17 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_with_generative_ai/src/knowledge-drive/app/favicon.ico -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/app/folders/[id]/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Metadata } from "next"; 2 | import { headers } from "next/headers"; 3 | 4 | export async function generateMetadata(): Promise { 5 | const csrfToken = headers().get("X-CSRF-Token") || "missing"; 6 | 7 | return { 8 | other: { 9 | "x-csrf-token": csrfToken, 10 | }, 11 | }; 12 | } 13 | 14 | export default function Layout({ children }: { children: React.ReactNode }) { 15 | return children; 16 | } 17 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "./globals.css"; 2 | import type { Metadata } from "next"; 3 | import { Inter } from "next/font/google"; 4 | import ToasterContext from "./context/ToasterContext"; 5 | 6 | const inter = Inter({ subsets: ["latin"] }); 7 | 8 | export const metadata: Metadata = { 9 | title: "Knowledge Drive", 10 | description: "Generated by create next app", 11 | }; 12 | 13 | export default function RootLayout({ 14 | children, 15 | }: { 16 | children: React.ReactNode; 17 | }) { 18 | return ( 19 | 20 | 21 | 22 | {children} 23 | 24 | 25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/app/search/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Metadata } from "next"; 2 | import { headers } from "next/headers"; 3 | 4 | export async function generateMetadata(): Promise { 5 | const csrfToken = headers().get("X-CSRF-Token") || "missing"; 6 | 7 | return { 8 | other: { 9 | "x-csrf-token": csrfToken, 10 | }, 11 | }; 12 | } 13 | 14 | export default function Layout({ children }: { children: React.ReactNode }) { 15 | return children; 16 | } 17 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/app/types/auth.ts: -------------------------------------------------------------------------------- 1 | type SignInRequest = { 2 | email: string; 3 | password: string; 4 | }; 5 | 6 | type SignUpRequest = { 7 | name: string; 8 | email: string; 9 | password: string; 10 | }; 11 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "default", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true 11 | }, 12 | "aliases": { 13 | "components": "@/components", 14 | "utils": "@/lib/utils" 15 | } 16 | } -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/components/CurrentFolder.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useDocument } from "react-firebase-hooks/firestore"; 4 | import { usePathname } from "next/navigation"; 5 | import { getFirestore, doc } from "firebase/firestore"; 6 | 7 | import firebaseClientApp from "@/lib/firebase/client"; 8 | import { getFolderId } from "@/lib/utils"; 9 | import { User } from "firebase/auth"; 10 | 11 | const firestore = getFirestore(firebaseClientApp); 12 | 13 | type CurrentFolderProps = { 14 | user: User; 15 | }; 16 | 17 | const CurrentFolder = ({ user }: CurrentFolderProps) => { 18 | const pathname = usePathname(); 19 | 20 | const [document, loading, error] = useDocument( 21 | doc(firestore, "users", user.uid, "items", getFolderId(pathname)), 22 | { 23 | snapshotListenOptions: { includeMetadataChanges: true }, 24 | } 25 | ); 26 | if (loading) return <>; 27 | if (document === undefined || !document.data()) return <>; 28 | 29 | return {document.data()?.name}; 30 | }; 31 | 32 | export default CurrentFolder; 33 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/components/MainItemsContent.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import SingleItem from "./SingleItem"; 4 | import { QueryDocumentSnapshot, DocumentData } from "firebase/firestore"; 5 | 6 | type MainItemsProps = { 7 | docs: QueryDocumentSnapshot[]; 8 | }; 9 | 10 | const MainItemsContent = ({ docs }: MainItemsProps) => { 11 | return ( 12 | <> 13 | {docs.map((doc) => ( 14 | 24 | ))} 25 | 26 | ); 27 | }; 28 | 29 | export default MainItemsContent; 30 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/components/MainItemsHeader.tsx: -------------------------------------------------------------------------------- 1 | const MainItemsHeader = () => { 2 | return ( 3 |
4 |
5 |
名前
6 |
オーナー
7 |
最終更新(自分)
8 |
ファイルサイズ
9 |
10 |
11 |
12 | ); 13 | }; 14 | 15 | export default MainItemsHeader; 16 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/components/RecommendedList.tsx: -------------------------------------------------------------------------------- 1 | const RecommendedList = () => { 2 | return ( 3 | <> 4 | {/*
5 |
6 |
7 |

候補リスト

8 |
9 |
10 |
11 |
12 |
aaaa
13 |
14 |
15 |
16 |
17 |
*/} 18 | 19 | ); 20 | }; 21 | 22 | export default RecommendedList; 23 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/components/SearchDialog.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Dialog, DialogContent } from "@/components/ui/dialog"; 4 | import SearchResult from "./SearchResult"; 5 | import { User } from "firebase/auth"; 6 | 7 | type SearchDialogProps = { 8 | open: boolean; 9 | onOpenChange: (open: boolean) => void; 10 | query: string; 11 | csrfToken: string; 12 | user: User; 13 | }; 14 | 15 | const SearchDialog = ({ 16 | open, 17 | onOpenChange, 18 | query, 19 | csrfToken, 20 | user, 21 | }: SearchDialogProps) => { 22 | return ( 23 | 24 | 25 | 26 | 27 | 28 | ); 29 | }; 30 | 31 | export default SearchDialog; 32 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/components/SourceFile.tsx: -------------------------------------------------------------------------------- 1 | import { User } from "firebase/auth"; 2 | import { getFirestore, doc } from "firebase/firestore"; 3 | import { useDocumentDataOnce } from "react-firebase-hooks/firestore"; 4 | 5 | import firebaseClientApp from "@/lib/firebase/client"; 6 | 7 | const firestore = getFirestore(firebaseClientApp); 8 | 9 | type SourceFileProps = { 10 | user: User; 11 | fileId: string; 12 | }; 13 | const SourceFile = ({ user, fileId }: SourceFileProps) => { 14 | const [value, loading, error] = useDocumentDataOnce( 15 | doc(firestore, "users", user.uid, "items", fileId) 16 | ); 17 | 18 | if (loading) return ""; 19 | 20 | if (error) { 21 | return エラーが発生しました; 22 | } 23 | 24 | if (!value) { 25 | return エラーが発生しました; 26 | } 27 | 28 | return {value.name}; 29 | }; 30 | 31 | export default SourceFile; 32 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/components/ui/LoadingPage.tsx: -------------------------------------------------------------------------------- 1 | import ClockLoader from "react-spinners/ClockLoader"; 2 | 3 | const LoadingPage = () => { 4 | return ( 5 | <> 6 |
7 |
8 | 9 |
10 |
11 | 12 | ); 13 | }; 14 | 15 | export default LoadingPage; 16 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | 5 | export interface InputProps 6 | extends React.InputHTMLAttributes {} 7 | 8 | const Input = React.forwardRef( 9 | ({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ); 21 | } 22 | ); 23 | Input.displayName = "Input"; 24 | 25 | export { Input }; 26 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | import { cva, type VariantProps } from "class-variance-authority" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const labelVariants = cva( 10 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 11 | ) 12 | 13 | const Label = React.forwardRef< 14 | React.ElementRef, 15 | React.ComponentPropsWithoutRef & 16 | VariantProps 17 | >(({ className, ...props }, ref) => ( 18 | 23 | )) 24 | Label.displayName = LabelPrimitive.Root.displayName 25 | 26 | export { Label } 27 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ) 13 | } 14 | 15 | export { Skeleton } 16 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/components/ui/toaster.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import { 4 | Toast, 5 | ToastClose, 6 | ToastDescription, 7 | ToastProvider, 8 | ToastTitle, 9 | ToastViewport, 10 | } from "@/components/ui/toast" 11 | import { useToast } from "@/components/ui/use-toast" 12 | 13 | export function Toaster() { 14 | const { toasts } = useToast() 15 | 16 | return ( 17 | 18 | {toasts.map(function ({ id, title, description, action, ...props }) { 19 | return ( 20 | 21 |
22 | {title && {title}} 23 | {description && ( 24 | {description} 25 | )} 26 |
27 | {action} 28 | 29 |
30 | ) 31 | })} 32 | 33 |
34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/lib/api/auth.ts: -------------------------------------------------------------------------------- 1 | export const signUpApi = async (request: SignUpRequest, csrfToken: string) => { 2 | await fetch("/api/register", { 3 | method: "POST", 4 | headers: { 5 | "X-CSRF-Token": csrfToken, 6 | }, 7 | body: JSON.stringify(request), 8 | }); 9 | }; 10 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/lib/firebase/admin.ts: -------------------------------------------------------------------------------- 1 | import { 2 | initializeApp, 3 | applicationDefault, 4 | AppOptions, 5 | getApp, 6 | App, 7 | } from "firebase-admin/app"; 8 | 9 | const ADMIN = "ADMIN"; 10 | const firebaseAdminConfig: AppOptions = { 11 | credential: applicationDefault(), 12 | }; 13 | 14 | const initialize = () => { 15 | let adminApp: App; 16 | try { 17 | adminApp = getApp(ADMIN); 18 | } catch (e) { 19 | adminApp = initializeApp(firebaseAdminConfig, ADMIN); 20 | } 21 | return adminApp; 22 | }; 23 | 24 | const firebaseAdminApp = initialize(); 25 | 26 | export default firebaseAdminApp; 27 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/lib/firebase/auth.ts: -------------------------------------------------------------------------------- 1 | import { 2 | signInWithEmailAndPassword, 3 | getAuth, 4 | UserCredential, 5 | } from "firebase/auth"; 6 | import firebaseClientApp from "./client"; 7 | import { FirebaseError } from "firebase/app"; 8 | import { signUpApi } from "../api/auth"; 9 | 10 | const auth = getAuth(firebaseClientApp); 11 | 12 | export const signIn = async ({ 13 | email, 14 | password, 15 | }: SignInRequest): Promise => { 16 | try { 17 | const user = await signInWithEmailAndPassword(auth, email, password); 18 | return user; 19 | } catch (e) { 20 | if (e instanceof FirebaseError) { 21 | console.error(e.code); 22 | throw e; 23 | } 24 | throw e; 25 | } 26 | }; 27 | 28 | export const signUp = async ( 29 | request: SignUpRequest, 30 | csrfToken: string 31 | ): Promise => { 32 | try { 33 | await signUpApi(request, csrfToken); 34 | const user = await signInWithEmailAndPassword( 35 | auth, 36 | request.email, 37 | request.password 38 | ); 39 | return user; 40 | } catch (e) { 41 | if (e instanceof FirebaseError) { 42 | console.error(e.code); 43 | throw e; 44 | } 45 | throw e; 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/lib/firebase/client.ts: -------------------------------------------------------------------------------- 1 | import { 2 | initializeApp, 3 | FirebaseOptions, 4 | getApp, 5 | FirebaseApp, 6 | } from "firebase/app"; 7 | 8 | const CLIENT = "CLIENT"; 9 | 10 | const firebaseClientConfig: FirebaseOptions = { 11 | apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, 12 | authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, 13 | projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, 14 | storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, 15 | messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_ID, 16 | appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, 17 | }; 18 | 19 | const initialize = () => { 20 | let clientApp: FirebaseApp; 21 | try { 22 | clientApp = getApp(CLIENT); 23 | } catch (e) { 24 | clientApp = initializeApp(firebaseClientConfig, CLIENT); 25 | } 26 | return clientApp; 27 | }; 28 | 29 | const firebaseClientApp = initialize(); 30 | 31 | export default firebaseClientApp; 32 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/lib/firebase/firestore.ts: -------------------------------------------------------------------------------- 1 | import firebaseClientApp from "./client"; 2 | import { 3 | getFirestore, 4 | addDoc, 5 | serverTimestamp, 6 | collection, 7 | setDoc, 8 | doc, 9 | } from "firebase/firestore"; 10 | 11 | const firestore = getFirestore(firebaseClientApp); 12 | 13 | type Folder = { 14 | name: string; 15 | parent: string; 16 | uid: string; 17 | }; 18 | 19 | type File = { 20 | id: string; 21 | name: string; 22 | parent: string; 23 | uid: string; 24 | size: number; 25 | url: string; 26 | }; 27 | 28 | export const createFolder = async ({ name, parent, uid }: Folder) => { 29 | await addDoc(collection(firestore, "users", uid, "items"), { 30 | name: name, 31 | parent: parent, 32 | isFolder: true, 33 | timestamp: serverTimestamp(), 34 | size: 0, 35 | }); 36 | }; 37 | 38 | export const createFile = async ({ 39 | id, 40 | name, 41 | parent, 42 | uid, 43 | size, 44 | url, 45 | }: File) => { 46 | await setDoc(doc(firestore, "users", uid, "items", id), { 47 | name: name, 48 | parent: parent, 49 | isFolder: false, 50 | timestamp: serverTimestamp(), 51 | size: size, 52 | url: url, 53 | }); 54 | }; 55 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | 8 | export const printFilesize = (filesize: number) => { 9 | if (filesize >= 1024 * 1024 * 1024) 10 | return (filesize / (1024 * 1024 * 1024)).toFixed(2).toString() + " GB"; 11 | if (filesize >= 1024 * 1024) 12 | return (filesize / (1024 * 1024)).toFixed(2).toString() + " MB"; 13 | if (filesize >= 1024) return (filesize / 1024).toFixed(2).toString() + " KB"; 14 | return filesize.toString() + " B"; 15 | }; 16 | 17 | export const printTimestamp = (timestamp: Date) => { 18 | if (!timestamp) return ""; 19 | return ( 20 | timestamp.getFullYear().toString() + 21 | "年" + 22 | (timestamp.getMonth() + 1).toString() + 23 | "月" + 24 | timestamp.getDate().toString() + 25 | "日" 26 | ); 27 | }; 28 | 29 | export const getFolderId = (pathname: string) => { 30 | if (pathname === "/drive") return "ROOT_FOLDER"; 31 | return pathname.replace("/folders", ""); 32 | }; 33 | 34 | export const isSearchPage = (pathname: string) => { 35 | return pathname === "/search"; 36 | }; 37 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/middleware.ts: -------------------------------------------------------------------------------- 1 | import csrf from "edge-csrf"; 2 | import { NextResponse } from "next/server"; 3 | import type { NextRequest } from "next/server"; 4 | 5 | // initalize protection function 6 | const csrfProtect = csrf({ 7 | cookie: { 8 | secure: process.env.NODE_ENV === "production", 9 | }, 10 | excludePathPrefixes: ["/api/search"], 11 | }); 12 | 13 | export async function middleware(request: NextRequest) { 14 | const response = NextResponse.next(); 15 | 16 | // csrf protection 17 | const csrfError = await csrfProtect(request, response); 18 | 19 | // check result 20 | if (csrfError) { 21 | return new NextResponse("invalid csrf token", { status: 403 }); 22 | } 23 | 24 | return response; 25 | } 26 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | output: "standalone", 4 | images: { 5 | remotePatterns: [ 6 | { 7 | protocol: "https", 8 | hostname: "upload.wikimedia.org", 9 | port: "", 10 | }, 11 | ], 12 | }, 13 | }; 14 | 15 | module.exports = nextConfig; 16 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/public/images/check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_with_generative_ai/src/knowledge-drive/public/images/check.png -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/public/images/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_with_generative_ai/src/knowledge-drive/public/images/error.png -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_with_generative_ai/src/knowledge-drive/public/images/logo.png -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/public/images/notfound.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_with_generative_ai/src/knowledge-drive/public/images/notfound.png -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/public/images/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/appdev_with_generative_ai/src/knowledge-drive/public/images/placeholder.png -------------------------------------------------------------------------------- /appdev_with_generative_ai/src/knowledge-drive/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/tf/artifactregistry.tf: -------------------------------------------------------------------------------- 1 | resource "time_sleep" "wait_2_minutes_for_artifact_registry" { 2 | create_duration = "2m" 3 | } 4 | 5 | resource "google_artifact_registry_repository" "drive_repo" { 6 | location = var.region 7 | repository_id = "drive-repo" 8 | description = "Docker repository for knowledge drive" 9 | format = "DOCKER" 10 | depends_on = [ 11 | time_sleep.wait_2_minutes_for_artifact_registry, 12 | null_resource.update_dot_env_for_knowledge_drive 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/tf/firebase_config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CONFIG_FILE=firebase-config.json 4 | ENV_PATH=../src/knowledge-drive 5 | 6 | function reset_configuration() { 7 | env_key=$1 8 | sed -i -e "s/$env_key=.*$/$env_key=/g" $ENV_PATH/.env 9 | } 10 | 11 | function update_env() { 12 | json_key=$1 13 | env_key=$2 14 | reset_configuration $env_key 15 | json_value=$(cat $CONFIG_FILE | jq ".$json_key") 16 | sed -i -e "s/$env_key=$/$env_key=$json_value/g" $ENV_PATH/.env 17 | } 18 | 19 | update_env projectId NEXT_PUBLIC_FIREBASE_PROJECT_ID 20 | update_env appId NEXT_PUBLIC_FIREBASE_APP_ID 21 | update_env storageBucket NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET 22 | update_env apiKey NEXT_PUBLIC_FIREBASE_API_KEY 23 | update_env authDomain NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN 24 | update_env messagingSenderId NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID 25 | 26 | echo "Updated configuration..." 27 | echo 28 | cat $ENV_PATH/.env 29 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/tf/firestore.rules: -------------------------------------------------------------------------------- 1 | rules_version = '2'; 2 | service cloud.firestore { 3 | match /databases/{database}/documents { 4 | match /users/{userId} { 5 | allow read: if request.auth != null 6 | && request.auth.uid == userId; 7 | match /items/{itemId} { 8 | allow read, write: if request.auth != null 9 | && request.auth.uid == userId; 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/tf/iam.tf: -------------------------------------------------------------------------------- 1 | resource "google_service_account" "knowledge_drive" { 2 | account_id = "knowledge-drive" 3 | } 4 | 5 | resource "google_project_iam_member" "knowledge_drive" { 6 | project = var.project_id 7 | for_each = toset([ 8 | "roles/firebase.sdkAdminServiceAgent", 9 | "roles/firebaseauth.admin", 10 | "roles/iam.serviceAccountTokenCreator", 11 | ]) 12 | role = each.key 13 | member = "serviceAccount:${google_service_account.knowledge_drive.email}" 14 | depends_on = [ 15 | google_service_account.knowledge_drive 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/tf/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | google = { 4 | source = "hashicorp/google" 5 | version = "5.8.0" 6 | } 7 | google-beta = { 8 | source = "hashicorp/google-beta" 9 | version = "5.8.0" 10 | } 11 | } 12 | } 13 | 14 | provider "google" { 15 | project = var.project_id 16 | region = var.region 17 | } 18 | 19 | provider "google-beta" { 20 | project = var.project_id 21 | region = var.region 22 | } 23 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/tf/output.tf: -------------------------------------------------------------------------------- 1 | output "Service_URL" { 2 | value = google_cloud_run_service.knowledge_drive.status[0].url 3 | } -------------------------------------------------------------------------------- /appdev_with_generative_ai/tf/storage.rules: -------------------------------------------------------------------------------- 1 | rules_version = '2'; 2 | service firebase.storage { 3 | match /b/{bucket}/o { 4 | match /files/{userId}/{itemId} { 5 | allow read, write: if request.auth != null 6 | && request.auth.uid == userId; 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /appdev_with_generative_ai/tf/variables.tf: -------------------------------------------------------------------------------- 1 | variable "project_id" {} 2 | 3 | variable "region" { 4 | default = "asia-northeast1" 5 | } 6 | 7 | variable "zone" { 8 | default = "asia-northeast1-c" 9 | } 10 | -------------------------------------------------------------------------------- /data_analytics/README.md: -------------------------------------------------------------------------------- 1 | # GCP Hands on materials for Data Analytics 2 | 3 | **This is not an officially supported Google product**. This directory 4 | contains some scripts that are used to teach GCP beginners how 5 | to use GCP's data analytics products in more efficient way. 6 | -------------------------------------------------------------------------------- /data_analytics/sample.csv: -------------------------------------------------------------------------------- 1 | Taro,20,Osaka 2 | Hanako,25,Tokyo 3 | Jiro,30,Aichi 4 | -------------------------------------------------------------------------------- /data_analytics/sample.sql: -------------------------------------------------------------------------------- 1 | # 分析1のクエリ 2 | SELECT 3 | COUNT(DISTINCT station_id) as cnt 4 | FROM 5 | `bigquery-public-data.new_york_citibike.citibike_stations` 6 | 7 | 8 | # 分析2のクエリ 9 | SELECT 10 | COUNT(station_id) as cnt 11 | FROM 12 | `bigquery-public-data.new_york_citibike.citibike_stations` 13 | WHERE 14 | is_installed = TRUE 15 | AND is_renting = TRUE 16 | AND is_returning = TRUE 17 | 18 | 19 | # 分析3のクエリ 20 | SELECT 21 | usertype, 22 | gender, 23 | COUNT(gender) AS cnt 24 | FROM 25 | `bigquery-public-data.new_york_citibike.citibike_trips` 26 | GROUP BY 27 | usertype, 28 | gender 29 | ORDER BY 30 | cnt DESC 31 | 32 | 33 | # 分析4のクエリ 34 | SELECT 35 | start_station_name, 36 | end_station_name, 37 | COUNT(end_station_name) AS cnt 38 | FROM 39 | `bigquery-public-data.new_york_citibike.citibike_trips` 40 | GROUP BY 41 | start_station_name, 42 | end_station_name 43 | ORDER BY 44 | cnt DESC 45 | -------------------------------------------------------------------------------- /fundamental/gce/index.html: -------------------------------------------------------------------------------- 1 | 13 | 14 | 17 | 18 | 19 | 20 |

Hello World! running on `hostname`

21 | 22 | 23 | -------------------------------------------------------------------------------- /fundamental/gke/helloNode/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12.4.0 2 | EXPOSE 8080 3 | COPY server.js /server.js 4 | CMD node server.js 5 | -------------------------------------------------------------------------------- /fundamental/gke/helloNode/hello-node-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | name: hello-node 6 | name: hello-node 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | name: hello-node 12 | template: 13 | metadata: 14 | labels: 15 | name: hello-node 16 | spec: 17 | containers: 18 | - image: asia.gcr.io//hello-node:v1 19 | name: hello-node 20 | ports: 21 | - containerPort: 8080 22 | -------------------------------------------------------------------------------- /fundamental/gke/helloNode/hello-node-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | name: hello-node 6 | name: hello-node 7 | spec: 8 | ports: 9 | - port: 8080 10 | selector: 11 | name: hello-node 12 | type: LoadBalancer 13 | -------------------------------------------------------------------------------- /fundamental/gke/helloNode/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* 18 | * This file is used for GCP handson. 19 | */ 20 | 21 | var http = require('http'); 22 | var handleRequest = function(request, response) { 23 | var os = require('os'); 24 | var hostname = os.hostname(); 25 | response.writeHead(200); 26 | response.end("

Hello World! == " + hostname + "

\n"); 27 | } 28 | var www = http.createServer(handleRequest); 29 | www.listen(8080); 30 | -------------------------------------------------------------------------------- /fundamental/readme.md: -------------------------------------------------------------------------------- 1 | # GCP Hands on materials for Fundamental 2 | 3 | **This is not an officially supported Google product**. This directory 4 | contains some files that are used to teach GCP beginners how 5 | to use GCP's products. 6 | -------------------------------------------------------------------------------- /gke-ai/lab-01/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime 2 | 3 | WORKDIR /app 4 | 5 | RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/* 6 | 7 | COPY requirements.txt . 8 | RUN pip install --no-cache-dir -r requirements.txt 9 | 10 | COPY main.py . 11 | 12 | EXPOSE 8080 13 | 14 | CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080", "--timeout-keep-alive", "60", "--workers", "1"] 15 | -------------------------------------------------------------------------------- /gke-ai/lab-01/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi 2 | uvicorn[standard] 3 | torch --index-url https://download.pytorch.org/whl/cu121 4 | transformers 5 | peft 6 | accelerate 7 | trl 8 | datasets 9 | sentencepiece 10 | protobuf -------------------------------------------------------------------------------- /gke-ai/lab-01/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: gemma3-server-service 5 | spec: 6 | type: LoadBalancer 7 | selector: 8 | app: gemma3-server # deployment.yamlのラベルと一致 9 | ports: 10 | - protocol: TCP 11 | port: 80 12 | targetPort: 8080 -------------------------------------------------------------------------------- /gke-ai/lab-02/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/gke-ai/lab-02/.gitignore -------------------------------------------------------------------------------- /gke-ai/lab-02/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime 2 | 3 | WORKDIR /app 4 | 5 | # 必要なツールをインストール (git は Hugging Face モデルダウンロードに必要) 6 | RUN apt-get update && apt-get install -y \ 7 | git \ 8 | && rm -rf /var/lib/apt/lists/* 9 | 10 | # requirements.txt をコピーしてライブラリをインストール 11 | COPY requirements_finetune.txt . 12 | RUN pip install --no-cache-dir -r requirements_finetune.txt 13 | 14 | # 学習スクリプトをコピー 15 | COPY finetune_gozaru.py . 16 | 17 | # スクリプトを実行可能にする 18 | RUN chmod +x finetune_gozaru.py 19 | 20 | # コンテナ起動時に学習スクリプトを実行 21 | ENTRYPOINT ["python3", "finetune_gozaru.py"] 22 | -------------------------------------------------------------------------------- /gke-ai/lab-02/requirements_finetune.txt: -------------------------------------------------------------------------------- 1 | trl==0.17.0 2 | peft==0.10.0 3 | tensorboard 4 | sentencepiece 5 | protobuf<4 6 | -------------------------------------------------------------------------------- /gke-ai/lab-03/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime 2 | 3 | WORKDIR /app 4 | 5 | # 必要なツールをインストール (git は Hugging Face モデルダウンロードに必要) 6 | RUN apt-get update && apt-get install -y \ 7 | git \ 8 | && rm -rf /var/lib/apt/lists/* 9 | 10 | # requirements.txtをコピーしてライブラリをインストール 11 | COPY requirements_gozaru.txt . 12 | RUN pip install --no-cache-dir -r requirements_gozaru.txt 13 | 14 | # アプリケーションコードをコピー 15 | COPY main_gozaru.py . 16 | 17 | # ポート8080を公開 18 | EXPOSE 8080 19 | 20 | # アプリケーションの起動コマンド 21 | CMD ["uvicorn", "main_gozaru:app", "--host", "0.0.0.0", "--port", "8080", "--timeout-keep-alive", "60", "--workers", "1"] 22 | -------------------------------------------------------------------------------- /gke-ai/lab-03/gateway.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1 2 | kind: Gateway 3 | metadata: 4 | name: inference-gateway 5 | spec: 6 | gatewayClassName: gke-l7-regional-external-managed 7 | listeners: 8 | - name: http 9 | protocol: HTTP 10 | port: 80 11 | -------------------------------------------------------------------------------- /gke-ai/lab-03/gozaru-gemma-pool-epp-svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: gozaru-gemma-pool-epp # ★ InferencePool から参照する名前 5 | spec: 6 | selector: 7 | app: gozaru-gemma-server # モデル Pod のラベル 8 | ports: 9 | - name: grpc 10 | port: 9002 # ここは自由 (9002 がデフォルト推奨) 11 | targetPort: 9002 12 | -------------------------------------------------------------------------------- /gke-ai/lab-03/httproute.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1 2 | kind: HTTPRoute 3 | metadata: 4 | name: gemma-route 5 | spec: 6 | parentRefs: 7 | - name: inference-gateway 8 | rules: 9 | # ござる LoRA 10 | - matches: 11 | - headers: 12 | - name: model 13 | value: gemma-gozaru 14 | backendRefs: 15 | - group: inference.networking.x-k8s.io 16 | kind: InferencePool 17 | name: gozaru-gemma-pool 18 | weight: 100 19 | # ベース 20 | - matches: 21 | - headers: 22 | - name: model 23 | value: gemma-base 24 | backendRefs: 25 | - group: inference.networking.x-k8s.io 26 | kind: InferencePool 27 | name: gozaru-gemma-pool 28 | weight: 100 29 | -------------------------------------------------------------------------------- /gke-ai/lab-03/inferencemodel.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: inference.networking.x-k8s.io/v1alpha2 2 | kind: InferenceModel 3 | metadata: 4 | name: gemma-gozaru 5 | spec: 6 | modelName: tarota0226/gemma-gozaru-adapter # LoRA 7 | criticality: Standard 8 | poolRef: 9 | name: gozaru-gemma-pool 10 | --- 11 | apiVersion: inference.networking.x-k8s.io/v1alpha2 12 | kind: InferenceModel 13 | metadata: 14 | name: gemma-base 15 | spec: 16 | modelName: google/gemma-3-4b-it # ベース 17 | criticality: Critical 18 | poolRef: 19 | name: gozaru-gemma-pool 20 | -------------------------------------------------------------------------------- /gke-ai/lab-03/inferencepool.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: inference.networking.x-k8s.io/v1alpha2 2 | kind: InferencePool 3 | metadata: 4 | name: gozaru-gemma-pool 5 | spec: 6 | # --- Pod を選ぶラベル (単なる key:value の map) --- 7 | selector: 8 | app: gozaru-gemma-server 9 | 10 | # --- Pod 内で公開しているポート番号 --- 11 | targetPortNumber: 8080 12 | 13 | # --- Gateway が呼び出す拡張サービス (固定値で OK) --- 14 | extensionRef: 15 | name: gke # Service 名。変更しない 16 | kind: Service # 既定値なので省略可 17 | group: "" # Core API group(省略可) 18 | -------------------------------------------------------------------------------- /gke-ai/lab-03/requirements_gozaru.txt: -------------------------------------------------------------------------------- 1 | fastapi 2 | uvicorn[standard] 3 | torch --index-url https://download.pytorch.org/whl/cu121 4 | transformers 5 | peft 6 | accelerate 7 | trl 8 | datasets 9 | bitsandbytes 10 | sentencepiece 11 | protobuf 12 | -------------------------------------------------------------------------------- /gke-ai/lab-03/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: gozaru-gemma-service 5 | spec: 6 | type: LoadBalancer 7 | selector: 8 | app: gozaru-gemma-server 9 | ports: 10 | - protocol: TCP 11 | port: 80 12 | targetPort: 8080 13 | -------------------------------------------------------------------------------- /gke-basic/lab-01/gateway/gateway.yaml: -------------------------------------------------------------------------------- 1 | kind: Gateway 2 | apiVersion: gateway.networking.k8s.io/v1beta1 3 | metadata: 4 | name: external-http 5 | spec: 6 | gatewayClassName: gke-l7-gxlb 7 | listeners: 8 | - name: http 9 | protocol: HTTP 10 | port: 80 11 | addresses: 12 | - type: NamedAddress 13 | value: gatewayip -------------------------------------------------------------------------------- /gke-basic/lab-01/gateway/httproute.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: gateway.networking.k8s.io/v1beta1 2 | kind: HTTPRoute 3 | metadata: 4 | name: frontend-route 5 | spec: 6 | parentRefs: 7 | - name: external-http 8 | hostnames: 9 | - "x-x-x-x.nip.io" 10 | rules: 11 | - matches: 12 | - path: 13 | value: / 14 | backendRefs: 15 | - name: frontend 16 | port: 80 17 | -------------------------------------------------------------------------------- /gke-basic/lab-02/balloon-deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: balloon-deploy 5 | spec: 6 | replicas: 10 7 | selector: 8 | matchLabels: 9 | app: balloon 10 | template: 11 | metadata: 12 | labels: 13 | app: balloon 14 | spec: 15 | priorityClassName: balloon-priority 16 | terminationGracePeriodSeconds: 0 17 | containers: 18 | - name: busybox 19 | image: busybox:latest 20 | command: ["sleep"] 21 | args: ["infinity"] 22 | resources: 23 | requests: 24 | cpu: 200m 25 | memory: 250Mi -------------------------------------------------------------------------------- /gke-basic/lab-02/balloon-priority.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scheduling.k8s.io/v1 2 | kind: PriorityClass 3 | metadata: 4 | name: balloon-priority 5 | value: -10 6 | preemptionPolicy: Never 7 | globalDefault: false 8 | description: "Balloon pod priority." -------------------------------------------------------------------------------- /gke-basic/lab-ex01/.python-version: -------------------------------------------------------------------------------- 1 | >= 3.10 -------------------------------------------------------------------------------- /gke-basic/lab-ex01/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app -------------------------------------------------------------------------------- /gke-basic/lab-ex01/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'gcr.io/k8s-skaffold/pack' 3 | entrypoint: 'pack' 4 | args: ['build', '--builder=gcr.io/buildpacks/builder', '--publish', 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/gke-dojo/gke-dojo-app:v1'] 5 | 6 | -------------------------------------------------------------------------------- /gke-basic/lab-ex01/clouddeploy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: deploy.cloud.google.com/v1beta1 3 | kind: DeliveryPipeline 4 | metadata: 5 | name: gke-dojo 6 | description: gke-dojo 7 | serialPipeline: 8 | stages: 9 | - targetId: staging 10 | - targetId: production 11 | --- 12 | apiVersion: deploy.cloud.google.com/v1beta1 13 | kind: Target 14 | metadata: 15 | name: staging 16 | description: Staging Environment 17 | gke: 18 | cluster: projects/PROJECT_ID/locations/asia-northeast1/clusters/gke-dojo-cluster 19 | --- 20 | apiVersion: deploy.cloud.google.com/v1beta1 21 | kind: Target 22 | metadata: 23 | name: production 24 | description: Production Environment 25 | gke: 26 | cluster: projects/PROJECT_ID/locations/asia-northeast1/clusters/gke-dojo-cluster-prod -------------------------------------------------------------------------------- /gke-basic/lab-ex01/k8s/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: gke-dojo 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: gke-dojo 9 | template: 10 | metadata: 11 | labels: 12 | app: gke-dojo 13 | spec: 14 | containers: 15 | - name: gke-dojo 16 | image: gke-dojo 17 | ports: 18 | - containerPort: 8080 -------------------------------------------------------------------------------- /gke-basic/lab-ex01/k8s/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: gke-dojo-service 5 | spec: 6 | ports: 7 | - port: 80 8 | targetPort: 8080 9 | type: LoadBalancer 10 | selector: 11 | app: gke-dojo -------------------------------------------------------------------------------- /gke-basic/lab-ex01/main.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template_string 2 | 3 | app = Flask(__name__) 4 | 5 | @app.route('/') 6 | def index(): 7 | template = """ 8 | 9 | 10 | 11 | GKE Dojo Handson 2023 12 | 24 | 25 | 26 |
27 | Welcome to gke-dojo 2023 28 |
29 | 30 | 31 | """ 32 | 33 | return render_template_string(template) 34 | 35 | if __name__ == '__main__': 36 | app.run(host='0.0.0.0', port=8080) 37 | -------------------------------------------------------------------------------- /gke-basic/lab-ex01/requirements.txt: -------------------------------------------------------------------------------- 1 | # https://pypi.org/project/flask/ 2 | Flask >= 2.2 3 | 4 | # https://pypi.org/project/gunicorn/ 5 | gunicorn==20.1.0 6 | -------------------------------------------------------------------------------- /gke-basic/lab-ex01/skaffold.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: skaffold/v2beta16 2 | kind: Config 3 | deploy: 4 | kubectl: 5 | manifests: ["k8s/*.yaml"] -------------------------------------------------------------------------------- /gke-basics-2025/lab-01/app/app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: helloweb 5 | labels: 6 | app: hello 7 | spec: 8 | selector: 9 | matchLabels: 10 | app: hello 11 | tier: web 12 | template: 13 | metadata: 14 | labels: 15 | app: hello 16 | tier: web 17 | spec: 18 | containers: 19 | - name: hello-app 20 | image: us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0 21 | ports: 22 | - containerPort: 8080 23 | resources: 24 | requests: 25 | cpu: 200m 26 | memory: 256Mi 27 | limits: 28 | cpu: 500m 29 | memory: 512Mi -------------------------------------------------------------------------------- /gke-basics-2025/lab-01/app/svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: helloweb-lb 5 | labels: 6 | app: hello 7 | spec: 8 | type: LoadBalancer 9 | selector: 10 | app: hello 11 | tier: web 12 | ports: 13 | - port: 80 14 | targetPort: 8080 15 | protocol: TCP -------------------------------------------------------------------------------- /gke-basics-2025/lab-02/balloon-deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: balloon-deploy 5 | spec: 6 | replicas: 4 7 | selector: 8 | matchLabels: 9 | app: balloon 10 | template: 11 | metadata: 12 | labels: 13 | app: balloon 14 | spec: 15 | priorityClassName: balloon-priority 16 | terminationGracePeriodSeconds: 0 17 | containers: 18 | - name: busybox 19 | image: busybox:latest 20 | command: ["sleep"] 21 | args: ["infinity"] 22 | resources: 23 | requests: 24 | cpu: 200m 25 | memory: 250Mi -------------------------------------------------------------------------------- /gke-basics-2025/lab-02/balloon-priority.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scheduling.k8s.io/v1 2 | kind: PriorityClass 3 | metadata: 4 | name: balloon-priority 5 | value: -10 6 | preemptionPolicy: Never 7 | globalDefault: false 8 | description: "Balloon pod priority." -------------------------------------------------------------------------------- /gke-basics-2025/lab-ex01/.python-version: -------------------------------------------------------------------------------- 1 | >= 3.10 -------------------------------------------------------------------------------- /gke-basics-2025/lab-ex01/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app -------------------------------------------------------------------------------- /gke-basics-2025/lab-ex01/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'gcr.io/k8s-skaffold/pack' 3 | entrypoint: 'pack' 4 | args: ['build', '--builder=gcr.io/buildpacks/builder', '--publish', 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/gke-dojo/gke-dojo-app:v1'] 5 | 6 | -------------------------------------------------------------------------------- /gke-basics-2025/lab-ex01/clouddeploy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: deploy.cloud.google.com/v1beta1 3 | kind: DeliveryPipeline 4 | metadata: 5 | name: gke-dojo 6 | description: gke-dojo 7 | serialPipeline: 8 | stages: 9 | - targetId: staging 10 | - targetId: production 11 | --- 12 | apiVersion: deploy.cloud.google.com/v1beta1 13 | kind: Target 14 | metadata: 15 | name: staging 16 | description: Staging Environment 17 | gke: 18 | cluster: projects/PROJECT_ID/locations/asia-northeast1/clusters/gke-dojo-cluster 19 | --- 20 | apiVersion: deploy.cloud.google.com/v1beta1 21 | kind: Target 22 | metadata: 23 | name: production 24 | description: Production Environment 25 | gke: 26 | cluster: projects/PROJECT_ID/locations/asia-northeast1/clusters/gke-dojo-cluster-prod -------------------------------------------------------------------------------- /gke-basics-2025/lab-ex01/k8s/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: gke-dojo 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: gke-dojo 9 | template: 10 | metadata: 11 | labels: 12 | app: gke-dojo 13 | spec: 14 | containers: 15 | - name: gke-dojo 16 | image: gke-dojo 17 | ports: 18 | - containerPort: 8080 -------------------------------------------------------------------------------- /gke-basics-2025/lab-ex01/k8s/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: gke-dojo-service 5 | spec: 6 | ports: 7 | - port: 80 8 | targetPort: 8080 9 | type: LoadBalancer 10 | selector: 11 | app: gke-dojo -------------------------------------------------------------------------------- /gke-basics-2025/lab-ex01/main.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template_string 2 | 3 | app = Flask(__name__) 4 | 5 | @app.route('/') 6 | def index(): 7 | template = """ 8 | 9 | 10 | 11 | GKE Dojo Handson 2023 12 | 24 | 25 | 26 |
27 | Welcome to gke-dojo 2025 28 |
29 | 30 | 31 | """ 32 | 33 | return render_template_string(template) 34 | 35 | if __name__ == '__main__': 36 | app.run(host='0.0.0.0', port=8080) 37 | -------------------------------------------------------------------------------- /gke-basics-2025/lab-ex01/requirements.txt: -------------------------------------------------------------------------------- 1 | # https://pypi.org/project/flask/ 2 | Flask >= 2.2 3 | 4 | # https://pypi.org/project/gunicorn/ 5 | gunicorn==20.1.0 6 | -------------------------------------------------------------------------------- /gke-basics-2025/lab-ex01/skaffold.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: skaffold/v2beta16 2 | kind: Config 3 | deploy: 4 | kubectl: 5 | manifests: ["k8s/*.yaml"] -------------------------------------------------------------------------------- /machine_learning/README.md: -------------------------------------------------------------------------------- 1 | # GCP Hands on materials for Machine Learning 2 | 3 | **This is not an officially supported Google product**. This directory 4 | contains Colab-based hands-on materials that are used to teach GCP beginners how 5 | to use Google Cloud AI in more efficient way. 6 | -------------------------------------------------------------------------------- /machine_learning/cloud_ai_building_blocks/speech-to-speech/README.md: -------------------------------------------------------------------------------- 1 | # Simple Speech-to-speech 2 | 3 | This directory contains simple speech-to-speech demo which uses Google Cloud 4 | Machine Learning APIs. 5 | 6 | 1. Record your voice 7 | 8 | ```bash 9 | bash voice_recorder.sh 10 | ``` 11 | 12 | 2. Install required packages 13 | 14 | ```bash 15 | pip install -r requirements.txt 16 | ``` 17 | 18 | 3. Execute speech-to-speech 19 | 20 | ```bash 21 | python speech-to-speech.py 22 | ``` 23 | 24 | 4. Check output audio file with your music player 25 | 26 | An audio file should be saved as 'en-sample.mp3'. 27 | -------------------------------------------------------------------------------- /machine_learning/cloud_ai_building_blocks/speech-to-speech/requirements.txt: -------------------------------------------------------------------------------- 1 | google-api-python-client 2 | -------------------------------------------------------------------------------- /machine_learning/cloud_ai_building_blocks/speech-to-speech/voice_recorder.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | read -p "エンターキーを押下すると音声録音(10秒間)が始まります" rec 4 | if [ -z $rec ]; then 5 | rec --channels=1 --bits=16 --rate=16000 ja-sample.flac trim 0 10 6 | fi 7 | 8 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcr.io/YOUR-PROJECT-ID/tensorrtserver_client 2 | 3 | RUN pip install --upgrade locust 4 | 5 | COPY locust locust 6 | COPY data data 7 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/client/data/00001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/machine_learning/ml_infrastructure/inference-server-performance/client/data/00001.jpg -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/client/data/00002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/machine_learning/ml_infrastructure/inference-server-performance/client/data/00002.jpg -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/client/data/00003.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/machine_learning/ml_infrastructure/inference-server-performance/client/data/00003.jpg -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/client/deployment_master.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: extensions/v1beta1 3 | kind: Deployment 4 | metadata: 5 | name: locust-master 6 | labels: 7 | name: locust-master 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: locust-master 13 | template: 14 | metadata: 15 | labels: 16 | app: locust-master 17 | spec: 18 | containers: 19 | - name: locust-master 20 | image: gcr.io/YOUR-PROJECT-ID/locust_tester 21 | ports: 22 | - name: loc-master 23 | containerPort: 8089 24 | protocol: TCP 25 | - name: loc-master-p1 26 | containerPort: 5557 27 | protocol: TCP 28 | - name: loc-master-p2 29 | containerPort: 5558 30 | protocol: TCP 31 | command: ["locust","-f","locust/trtis_grpc_client.py"] 32 | args: ["--host", "CLUSTER-IP-TRTIS", "--master"] 33 | resources: 34 | requests: 35 | cpu: 200m 36 | env: 37 | - name: MODEL_NAME 38 | valueFrom: 39 | configMapKeyRef: 40 | name: locust-config 41 | key: model 42 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/client/deployment_slave.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: extensions/v1beta1 3 | kind: Deployment 4 | metadata: 5 | name: locust-slave 6 | labels: 7 | name: locust-slave 8 | spec: 9 | replicas: 3 10 | selector: 11 | matchLabels: 12 | app: locust-slave 13 | template: 14 | metadata: 15 | labels: 16 | app: locust-slave 17 | spec: 18 | containers: 19 | - name: locust-slave 20 | image: gcr.io/YOUR-PROJECT-ID/locust_tester 21 | command: ["locust","-f","locust/trtis_grpc_client.py"] 22 | args: ["--slave", "--master-host=CLUSTER-IP-LOCUST-MASTER"] 23 | resources: 24 | requests: 25 | cpu: 100m 26 | env: 27 | - name: MODEL_NAME 28 | valueFrom: 29 | configMapKeyRef: 30 | name: locust-config 31 | key: model 32 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/client/service_master.yaml: -------------------------------------------------------------------------------- 1 | 2 | kind: Service 3 | apiVersion: v1 4 | metadata: 5 | name: locust-master 6 | labels: 7 | app: locust-master 8 | spec: 9 | ports: 10 | - name: loc-master 11 | port: 8089 12 | targetPort: 8089 13 | protocol: TCP 14 | - name: loc-master-p1 15 | port: 5557 16 | targetPort: 5557 17 | protocol: TCP 18 | - name: loc-master-p2 19 | port: 5558 20 | targetPort: 5558 21 | protocol: TCP 22 | selector: 23 | app: locust-master 24 | type: LoadBalancer 25 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nvcr.io/nvidia/tensorflow:19.05-py3 2 | 3 | COPY scripts scripts 4 | 5 | ENTRYPOINT ["python3", "scripts/tensorrt-optimization.py"] 6 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/server/clusterRole.yml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: rbac.authorization.k8s.io/v1beta1 3 | kind: ClusterRole 4 | metadata: 5 | name: prometheus 6 | rules: 7 | - apiGroups: [""] 8 | resources: 9 | - nodes 10 | - nodes/proxy 11 | - services 12 | - endpoints 13 | - pods 14 | verbs: ["get", "list", "watch"] 15 | - apiGroups: 16 | - extensions 17 | resources: 18 | - ingresses 19 | verbs: ["get", "list", "watch"] 20 | - nonResourceURLs: ["/metrics"] 21 | verbs: ["get"] 22 | --- 23 | apiVersion: rbac.authorization.k8s.io/v1beta1 24 | kind: ClusterRoleBinding 25 | metadata: 26 | name: prometheus 27 | roleRef: 28 | apiGroup: rbac.authorization.k8s.io 29 | kind: ClusterRole 30 | name: prometheus 31 | subjects: 32 | - kind: ServiceAccount 33 | name: default 34 | namespace: monitoring 35 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/server/grafana-deployment.yml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: extensions/v1beta1 3 | kind: Deployment 4 | metadata: 5 | name: grafana-deployment 6 | namespace: monitoring 7 | spec: 8 | replicas: 1 9 | template: 10 | metadata: 11 | labels: 12 | app: grafana-server 13 | spec: 14 | containers: 15 | - name: grafana 16 | image: grafana/grafana:latest 17 | #args: 18 | # - "--config.file=/root/prometheus.yml" 19 | # - "--storage.tsdb.path=/prometheus/" 20 | ports: 21 | - containerPort: 3000 22 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/server/grafana-service.yml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: grafana-service 6 | spec: 7 | selector: 8 | app: grafana-server 9 | type: LoadBalancer 10 | ports: 11 | - port: 8100 12 | targetPort: 3000 13 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/server/models/resnet/original/00001/README: -------------------------------------------------------------------------------- 1 | This model is created based on Cloud TPU Resnet-50 example. One difference from Cloud TPU Resnet-50 example is we change input_tensor signature from tf.string to Tensor shape [None, 224, 224, 3] with tf.float32. 2 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/server/original/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "original" 2 | platform: "tensorflow_savedmodel" 3 | max_batch_size: 64 4 | input { 5 | name: "input" 6 | data_type: TYPE_FP32 7 | format: FORMAT_NHWC 8 | dims: [ 224, 224, 3 ] 9 | } 10 | output { 11 | name: "probabilities" 12 | data_type: TYPE_FP32 13 | dims: 1000 14 | label_filename: "imagenet1k_labels.txt" 15 | } 16 | default_model_filename: "model" 17 | instance_group [ 18 | { 19 | count: 1 20 | kind: KIND_GPU 21 | } 22 | ] 23 | dynamic_batching { 24 | preferred_batch_size: [ 64 ] 25 | max_queue_delay_microseconds: 20000 26 | } 27 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/server/prometheus-deployment.yml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: prometheus-deployment 5 | namespace: monitoring 6 | spec: 7 | replicas: 1 8 | template: 9 | metadata: 10 | labels: 11 | app: prometheus-server 12 | spec: 13 | containers: 14 | - name: prometheus 15 | image: prom/prometheus:latest 16 | args: 17 | - "--config.file=/etc/prometheus/prometheus.yml" 18 | - "--storage.tsdb.path=/prometheus/" 19 | ports: 20 | - containerPort: 9090 21 | volumeMounts: 22 | - name: prometheus-config-volume 23 | mountPath: /etc/prometheus 24 | - name: prometheus-storage-volume 25 | mountPath: /prometheus 26 | volumes: 27 | - name: prometheus-config-volume 28 | configMap: 29 | defaultMode: 420 30 | name: prometheus-server-conf 31 | - name: prometheus-storage-volume 32 | emptyDir: {} -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/server/prometheus-service.yml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: prometheus-service 6 | spec: 7 | selector: 8 | app: prometheus-server 9 | type: ClusterIP 10 | ports: 11 | - port: 8080 12 | targetPort: 9090 13 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/server/prometheus.yaml: -------------------------------------------------------------------------------- 1 | # my global config 2 | global: 3 | scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. 4 | evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute. 5 | # scrape_timeout is set to the global default (10s). 6 | 7 | # Alertmanager configuration 8 | alerting: 9 | alertmanagers: 10 | - static_configs: 11 | - targets: 12 | # - alertmanager:9093 13 | 14 | # Load rules once and periodically evaluate them according to the global 'evaluation_interval'. 15 | rule_files: 16 | # - "first_rules.yml" 17 | # - "second_rules.yml" 18 | 19 | # A scrape configuration containing exactly one endpoint to scrape: 20 | # Here it's Prometheus itself. 21 | scrape_configs: 22 | # The job name is added as a label `job=` to any timeseries scraped from this config. 23 | - job_name: 'prometheus' 24 | 25 | # metrics_path defaults to '/metrics' 26 | # scheme defaults to 'http'. 27 | 28 | static_configs: 29 | - targets: ['${CLUSTER_EXTERNAL_IP}:8002'] 30 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/server/tftrt_fp16/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "tftrt_fp16" 2 | platform: "tensorflow_savedmodel" 3 | max_batch_size: 64 4 | input { 5 | name: "input" 6 | data_type: TYPE_FP32 7 | format: FORMAT_NHWC 8 | dims: [ 224, 224, 3 ] 9 | } 10 | output { 11 | name: "probabilities" 12 | data_type: TYPE_FP32 13 | dims: 1000 14 | label_filename: "imagenet1k_labels.txt" 15 | } 16 | default_model_filename: "model" 17 | instance_group [ 18 | { 19 | count: 1 20 | kind: KIND_GPU 21 | } 22 | ] 23 | dynamic_batching { 24 | preferred_batch_size: [ 64 ] 25 | max_queue_delay_microseconds: 20000 26 | } 27 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/server/tftrt_fp16_bs8_count4/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "tftrt_fp16_bs8_count4" 2 | platform: "tensorflow_savedmodel" 3 | max_batch_size: 8 4 | input { 5 | name: "input" 6 | data_type: TYPE_FP32 7 | format: FORMAT_NHWC 8 | dims: [ 224, 224, 3 ] 9 | } 10 | output { 11 | name: "probabilities" 12 | data_type: TYPE_FP32 13 | dims: 1000 14 | label_filename: "imagenet1k_labels.txt" 15 | } 16 | default_model_filename: "model" 17 | instance_group [ 18 | { 19 | count: 4 20 | kind: KIND_GPU 21 | } 22 | ] 23 | dynamic_batching { 24 | preferred_batch_size: [ 8 ] 25 | max_queue_delay_microseconds: 20000 26 | } 27 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/server/tftrt_fp32/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "tftrt_fp32" 2 | platform: "tensorflow_savedmodel" 3 | max_batch_size: 64 4 | input { 5 | name: "input" 6 | data_type: TYPE_FP32 7 | format: FORMAT_NHWC 8 | dims: [ 224, 224, 3 ] 9 | } 10 | output { 11 | name: "probabilities" 12 | data_type: TYPE_FP32 13 | dims: 1000 14 | label_filename: "imagenet1k_labels.txt" 15 | } 16 | default_model_filename: "model" 17 | instance_group [ 18 | { 19 | count: 1 20 | kind: KIND_GPU 21 | } 22 | ] 23 | dynamic_batching { 24 | preferred_batch_size: [ 64 ] 25 | max_queue_delay_microseconds: 20000 26 | } 27 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/server/tftrt_int8_bs8_count4/config.pbtxt: -------------------------------------------------------------------------------- 1 | name: "tftrt_int8_bs8_count4" 2 | platform: "tensorflow_savedmodel" 3 | max_batch_size: 8 4 | input { 5 | name: "input" 6 | data_type: TYPE_FP32 7 | format: FORMAT_NHWC 8 | dims: [ 224, 224, 3 ] 9 | } 10 | output { 11 | name: "probabilities" 12 | data_type: TYPE_FP32 13 | dims: 1000 14 | label_filename: "imagenet1k_labels.txt" 15 | } 16 | default_model_filename: "model" 17 | instance_group [ 18 | { 19 | count: 4 20 | kind: KIND_GPU 21 | } 22 | ] 23 | dynamic_batching { 24 | preferred_batch_size: [ 8 ] 25 | max_queue_delay_microseconds: 20000 26 | } 27 | -------------------------------------------------------------------------------- /machine_learning/ml_infrastructure/inference-server-performance/server/trtis_service.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | labels: 6 | name: inference-server 7 | name: inference-server 8 | namespace: default 9 | spec: 10 | #externalTrafficPolicy: Cluster 11 | ports: 12 | - name: http-inference-server 13 | port: 8000 14 | protocol: TCP 15 | targetPort: 8000 16 | - name: grpc-inference-server 17 | port: 8001 18 | protocol: TCP 19 | targetPort: 8001 20 | - name: metrics-inference-server 21 | port: 8002 22 | protocol: TCP 23 | targetPort: 8002 24 | selector: 25 | app: inference-server 26 | sessionAffinity: None 27 | type: ClusterIP 28 | -------------------------------------------------------------------------------- /microservices/README.md: -------------------------------------------------------------------------------- 1 | # GCP Hands-on material for microservices development 2 | 3 | **This is not an official Google product**. 4 | 5 | This directory contains hands-on lab material for microservices development on GCP. 6 | 7 | See [tutorial-1.md](tutorial-1.md), [tutorial-2.md](tutorial-2.md) and [tutorial-3.md](tutorial-3.md) for more details. 8 | -------------------------------------------------------------------------------- /microservices/hello_world/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | README.md 3 | *.pyc 4 | *.pyo 5 | *.pyd 6 | __pycache__ 7 | .pytest_cache 8 | -------------------------------------------------------------------------------- /microservices/hello_world/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official lightweight Python image. 2 | # https://hub.docker.com/_/python 3 | FROM python:3.8-slim 4 | 5 | # Allow statements and log messages to immediately appear in the Knative logs 6 | ENV PYTHONUNBUFFERED True 7 | 8 | # Copy local code to the container image. 9 | ENV APP_HOME /app 10 | WORKDIR $APP_HOME 11 | COPY . ./ 12 | 13 | # Install production dependencies. 14 | RUN pip install -r requirements.txt 15 | 16 | # Run the web service on container startup. Here we use the gunicorn 17 | # webserver, with one worker process and 8 threads. 18 | # For environments with multiple CPU cores, increase the number of workers 19 | # to be equal to the cores available. 20 | CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app 21 | -------------------------------------------------------------------------------- /microservices/hello_world/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | gunicorn 3 | -------------------------------------------------------------------------------- /microservices/message_board/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | README.md 3 | index.yaml 4 | *.pyc 5 | *.pyo 6 | *.pyd 7 | __pycache__ 8 | .pytest_cache 9 | -------------------------------------------------------------------------------- /microservices/message_board/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official lightweight Python image. 2 | # https://hub.docker.com/_/python 3 | FROM python:3.8-slim 4 | 5 | # Allow statements and log messages to immediately appear in the Knative logs 6 | ENV PYTHONUNBUFFERED True 7 | 8 | # Copy local code to the container image. 9 | ENV APP_HOME /app 10 | WORKDIR $APP_HOME 11 | COPY . ./ 12 | 13 | # Install production dependencies. 14 | RUN pip install -r requirements.txt 15 | 16 | # Run the web service on container startup. Here we use the gunicorn 17 | # webserver, with one worker process and 8 threads. 18 | # For environments with multiple CPU cores, increase the number of workers 19 | # to be equal to the cores available. 20 | CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app 21 | -------------------------------------------------------------------------------- /microservices/message_board/index.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | indexes: 16 | - kind: Message 17 | properties: 18 | - name: name 19 | - name: timestamp 20 | -------------------------------------------------------------------------------- /microservices/message_board/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | gunicorn 3 | google-cloud-datastore 4 | -------------------------------------------------------------------------------- /microservices/storage_logging/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | README.md 3 | *.pyc 4 | *.pyo 5 | *.pyd 6 | __pycache__ 7 | .pytest_cache 8 | -------------------------------------------------------------------------------- /microservices/storage_logging/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use the official lightweight Python image. 2 | # https://hub.docker.com/_/python 3 | FROM python:3.8-slim 4 | 5 | # Allow statements and log messages to immediately appear in the Knative logs 6 | ENV PYTHONUNBUFFERED True 7 | 8 | # Copy local code to the container image. 9 | ENV APP_HOME /app 10 | WORKDIR $APP_HOME 11 | COPY . ./ 12 | 13 | # Install production dependencies. 14 | RUN pip install -r requirements.txt 15 | 16 | # Run the web service on container startup. Here we use the gunicorn 17 | # webserver, with one worker process and 8 threads. 18 | # For environments with multiple CPU cores, increase the number of workers 19 | # to be equal to the cores available. 20 | CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app 21 | -------------------------------------------------------------------------------- /microservices/storage_logging/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | gunicorn 3 | google-cloud-datastore 4 | -------------------------------------------------------------------------------- /movie_search/custom_ver/backend/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .venv -------------------------------------------------------------------------------- /movie_search/custom_ver/backend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9 2 | 3 | WORKDIR /code 4 | 5 | COPY ./requirements.txt /code/requirements.txt 6 | 7 | RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt 8 | 9 | COPY . /code 10 | 11 | CMD ["fastapi", "run", "main.py", "--port", "8080"] 12 | -------------------------------------------------------------------------------- /movie_search/custom_ver/backend/main.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/movie_search/custom_ver/backend/main.py -------------------------------------------------------------------------------- /movie_search/custom_ver/backend/prompt_content_search.py: -------------------------------------------------------------------------------- 1 | PROMPT_CONTENT_SEARCH = """ 2 | You are a video content editor. 3 | 4 | Given the following information of a movie: 5 | - The [summary] section contains the summary of the movie. 6 | - The [important scenes] section contains the important scenes of the movie with timestamps. 7 | - The [visual info] section contains the visual information on what's happening in each scene with timestamps. 8 | - The [transcription] section contains speech transcription with timestamps. 9 | - The [text] section contains text information with timestamps. 10 | 11 | Find one to three scenes that matches the user query with timestamps. 12 | 13 | [format instruction] 14 | Output in Japanese. Output is a JSON list with "scene dict". 15 | Each "scene dict" is a JSON dict with the following format: 16 | {{ 17 | "Timestamp": "", 18 | "Description": "" 19 | }} 20 | 21 | 22 | [user query] 23 | {query} 24 | 25 | {metatext} 26 | """ 27 | -------------------------------------------------------------------------------- /movie_search/custom_ver/backend/requirements.txt: -------------------------------------------------------------------------------- 1 | google-cloud-discoveryengine==0.13.3 2 | google-cloud-aiplatform==1.70.0 3 | google-cloud-storage==2.18.2 4 | google-auth==2.36.0 5 | fastapi[standard]==0.115.4 6 | uvicorn==0.32.0 -------------------------------------------------------------------------------- /movie_search/custom_ver/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .venv 3 | -------------------------------------------------------------------------------- /movie_search/custom_ver/frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-slim 2 | 3 | WORKDIR /app 4 | 5 | COPY . /app 6 | 7 | RUN pip3 install -r requirements.txt 8 | 9 | EXPOSE 8080 10 | 11 | HEALTHCHECK CMD curl --fail http://localhost:8080/_stcore/health 12 | 13 | ENTRYPOINT ["streamlit", "run", "main.py", "--server.port=8080", "--server.address=0.0.0.0"] 14 | -------------------------------------------------------------------------------- /movie_search/custom_ver/frontend/main.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/movie_search/custom_ver/frontend/main.py -------------------------------------------------------------------------------- /movie_search/custom_ver/frontend/requirements.txt: -------------------------------------------------------------------------------- 1 | streamlit==1.40.0 2 | requests==2.32.3 3 | google-auth==2.36.0 4 | 5 | -------------------------------------------------------------------------------- /movie_search/final_ver/backend/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .venv -------------------------------------------------------------------------------- /movie_search/final_ver/backend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9 2 | 3 | WORKDIR /code 4 | 5 | COPY ./requirements.txt /code/requirements.txt 6 | 7 | RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt 8 | 9 | COPY . /code 10 | 11 | CMD ["fastapi", "run", "main.py", "--port", "8080"] 12 | -------------------------------------------------------------------------------- /movie_search/final_ver/backend/prompt_content_search.py: -------------------------------------------------------------------------------- 1 | PROMPT_CONTENT_SEARCH = """ 2 | You are a video content editor. 3 | 4 | Given the following information of a movie: 5 | - The [summary] section contains the summary of the movie. 6 | - The [important scenes] section contains the important scenes of the movie with timestamps. 7 | - The [visual info] section contains the visual information on what's happening in each scene with timestamps. 8 | - The [transcription] section contains speech transcription with timestamps. 9 | - The [text] section contains text information with timestamps. 10 | 11 | Find one to three scenes that matches the user query with timestamps. 12 | 13 | [format instruction] 14 | Output in Japanese. Output is a JSON list with "scene dict". 15 | Each "scene dict" is a JSON dict with the following format: 16 | {{ 17 | "Timestamp": "", 18 | "Description": "" 19 | }} 20 | 21 | 22 | [user query] 23 | {query} 24 | 25 | {metatext} 26 | """ 27 | -------------------------------------------------------------------------------- /movie_search/final_ver/backend/requirements.txt: -------------------------------------------------------------------------------- 1 | google-cloud-discoveryengine==0.13.3 2 | google-cloud-aiplatform==1.70.0 3 | google-cloud-storage==2.18.2 4 | google-auth==2.36.0 5 | fastapi[standard]==0.115.4 6 | uvicorn==0.32.0 7 | -------------------------------------------------------------------------------- /movie_search/final_ver/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .venv 3 | -------------------------------------------------------------------------------- /movie_search/final_ver/frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-slim 2 | 3 | WORKDIR /app 4 | 5 | COPY . /app 6 | 7 | RUN pip3 install -r requirements.txt 8 | 9 | EXPOSE 8080 10 | 11 | HEALTHCHECK CMD curl --fail http://localhost:8080/_stcore/health 12 | 13 | ENTRYPOINT ["streamlit", "run", "main.py", "--server.port=8080", "--server.address=0.0.0.0"] -------------------------------------------------------------------------------- /movie_search/final_ver/frontend/requirements.txt: -------------------------------------------------------------------------------- 1 | streamlit==1.40.0 2 | requests==2.32.3 3 | google-auth==2.36.0 -------------------------------------------------------------------------------- /movie_search_metadata/Notebooks2/temp.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /movie_search_metadata/demo_app/.gitignore: -------------------------------------------------------------------------------- 1 | handson_resource* 2 | _setup_env 3 | -------------------------------------------------------------------------------- /movie_search_metadata/demo_app/backend/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /movie_search_metadata/demo_app/backend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-slim 2 | 3 | WORKDIR /app 4 | 5 | COPY . /app 6 | 7 | RUN pip3 install -r requirements.txt 8 | 9 | CMD ["fastapi", "run", "main.py", "--port", "8080"] 10 | -------------------------------------------------------------------------------- /movie_search_metadata/demo_app/backend/file_search.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from utils import get_bucket_and_blobnames 4 | from utils import generate_download_signed_url_v4 5 | from search_document import search_documents_by_query 6 | 7 | 8 | def search_files(query: str) -> List[dict]: 9 | response = search_documents_by_query(query) 10 | results = [] 11 | results.append({'summary': response.summary.summary_text}) 12 | 13 | for c, item in enumerate(response.results): 14 | title = item.document.derived_struct_data['title'] 15 | url = item.document.derived_struct_data['link'] 16 | bucket_name, _, blob_mp4 = get_bucket_and_blobnames(url) 17 | signed_url = generate_download_signed_url_v4(bucket_name, blob_mp4) 18 | results.append({ 19 | 'id': c+1, 'title': title, 20 | 'bucket_name': bucket_name, 'blob_name': blob_mp4, 21 | 'url': url, 'signed_url': signed_url 22 | }) 23 | 24 | return results 25 | -------------------------------------------------------------------------------- /movie_search_metadata/demo_app/backend/prompt_content_search.py: -------------------------------------------------------------------------------- 1 | PROMPT_CONTENT_SEARCH = """ 2 | You are a video content editor. 3 | 4 | Given the following information of a movie: 5 | - The [summary] section contains the summary of the movie. 6 | - The [important scenes] section contains the important scenes of the movie with timestamps. 7 | - The [visual info] section contains the visual information on what's happening in each scene with timestamps. 8 | - The [transcription] section contains speech transcription with timestamps. 9 | - The [text] section contains text information with timestamps. 10 | 11 | Find one to three scenes that match the user query with timestamps. 12 | 13 | [format instruction] 14 | Output in Japanese. Output is a JSON list with "scene dict". 15 | Each "scene dict" is a JSON dict with the following format: 16 | {{ 17 | "Timestamp": "", 18 | "Description": "" 19 | }} 20 | 21 | 22 | [user query] 23 | {query} 24 | 25 | {metatext} 26 | """ 27 | -------------------------------------------------------------------------------- /movie_search_metadata/demo_app/backend/requirements.txt: -------------------------------------------------------------------------------- 1 | google-cloud-discoveryengine==0.13.3 2 | google-cloud-aiplatform==1.70.0 3 | google-cloud-storage==2.18.2 4 | google-auth==2.36.0 5 | fastapi[standard]==0.115.4 6 | uvicorn==0.32.0 7 | -------------------------------------------------------------------------------- /movie_search_metadata/demo_app/docs/images/movie_search_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/movie_search_metadata/demo_app/docs/images/movie_search_demo.gif -------------------------------------------------------------------------------- /movie_search_metadata/demo_app/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /movie_search_metadata/demo_app/frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-slim 2 | 3 | WORKDIR /app 4 | 5 | COPY . /app 6 | 7 | RUN pip3 install -r requirements.txt 8 | 9 | EXPOSE 8080 10 | 11 | HEALTHCHECK CMD curl --fail http://localhost:8080/_stcore/health 12 | 13 | CMD ["streamlit", "run", "main.py", "--server.port=8080", "--server.address=0.0.0.0"] 14 | -------------------------------------------------------------------------------- /movie_search_metadata/demo_app/frontend/requirements.txt: -------------------------------------------------------------------------------- 1 | streamlit==1.40.0 2 | requests==2.32.3 3 | google-auth==2.36.0 -------------------------------------------------------------------------------- /movie_search_metadata/demo_app/vais_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export PROJECT_ID=$(gcloud config list --format "value(core.project)") 4 | export BUCKET="gs://${PROJECT_ID}-movie-search" 5 | 6 | echo "" 7 | echo "## Enabling APIs..." 8 | 9 | services=( 10 | "discoveryengine.googleapis.com" 11 | ) 12 | services_list="(""$(IFS='|'; echo "${services[*]}")"")" 13 | enabled=$(gcloud services list --format json | jq .[].config.name |\ 14 | grep -E "$services_list" | wc -l) 15 | if [[ $enabled != ${#services[@]} ]]; then 16 | echo "Enabling APIs." 17 | services_list="$(IFS=' '; echo "${services[*]}")" 18 | gcloud services enable $services_list 19 | 20 | echo "Wait 60 seconds for APIs to be ready." 21 | sleep 60 22 | fi 23 | 24 | echo "" 25 | echo "## Creating bucket..." 26 | gsutil mb -b on -l us-central1 $BUCKET 27 | 28 | curl -OL https://github.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/releases/download/v0.1.0/handson_resource.tgz 29 | tar -xvzf handson_resource.tgz 30 | pushd handson_resource 31 | gsutil -m cp -r metadata $BUCKET/ 32 | gsutil -m cp -r mp4 $BUCKET/ 33 | popd 34 | 35 | python3 -m venv _setup_env 36 | source _setup_env/bin/activate 37 | pip3 install google-cloud-discoveryengine==0.13.3 38 | 39 | python3 _vais_setup.py 40 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-01/.gitignore: -------------------------------------------------------------------------------- 1 | venv -------------------------------------------------------------------------------- /pfe-adv-sep/lab-01/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.12-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY requirements.txt requirements.txt 6 | RUN pip install -r requirements.txt 7 | 8 | COPY app.py app.py 9 | 10 | EXPOSE 5000 11 | CMD ["python", "app.py"] -------------------------------------------------------------------------------- /pfe-adv-sep/lab-01/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, jsonify 2 | import random 3 | 4 | app = Flask(__name__) 5 | 6 | breeds = [ 7 | "Labrador Retriever", 8 | "German Shepherd", 9 | "Golden Retriever", 10 | "French Bulldog", 11 | "Bulldog", 12 | "Poodle", 13 | "Beagle", 14 | "Rottweiler", 15 | "German Shorthaired Pointer", 16 | "Yorkshire Terrier" 17 | ] 18 | 19 | 20 | @app.route('/random-pets', methods=['GET']) 21 | def get_random_dog(): 22 | random_breed = random.choice(breeds) 23 | return jsonify({'breed': random_breed}) 24 | 25 | 26 | if __name__ == '__main__': 27 | app.run(host='0.0.0.0', port=5000) 28 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-01/app.txt: -------------------------------------------------------------------------------- 1 | from flask import Flask, jsonify 2 | import random 3 | 4 | app = Flask(__name__) 5 | 6 | 7 | dog_breeds = [ 8 | "Labrador Retriever", 9 | "German Shepherd", 10 | "Golden Retriever", 11 | "French Bulldog", 12 | "Bulldog", 13 | "Poodle", 14 | "Beagle", 15 | "Rottweiler", 16 | "German Shorthaired Pointer", 17 | "Yorkshire Terrier" 18 | ] 19 | 20 | 21 | cat_breeds = [ 22 | "Persian Cat", 23 | "Maine Coon", 24 | "Ragdoll", 25 | "Siamese Cat", 26 | "British Shorthair", 27 | "Sphynx Cat", 28 | "Scottish Fold", 29 | "Abyssinian", 30 | "Bengal Cat", 31 | "Russian Blue" 32 | ] 33 | 34 | 35 | @app.route('/random-pets', methods=['GET']) 36 | def get_random_pet(): 37 | if random.choice([True, False]): 38 | random_breed = random.choice(dog_breeds) 39 | animal_type = "dog" 40 | else: 41 | random_breed = random.choice(cat_breeds) 42 | animal_type = "cat" 43 | return jsonify({"animal": animal_type, "breed": random_breed}) 44 | 45 | 46 | if __name__ == '__main__': 47 | app.run(host='0.0.0.0', port=5000) 48 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-01/cloudbuild-2.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'python:3.12-alpine' 3 | entrypoint: 'sh' 4 | args: 5 | - '-c' 6 | - | 7 | pip install -r requirements.txt 8 | pip install flake8 9 | flake8 . 10 | 11 | - name: 'python:3.12-alpine' 12 | entrypoint: 'sh' 13 | args: 14 | - '-c' 15 | - | 16 | pip install -r requirements.txt 17 | python -m unittest discover 18 | 19 | - name: 'gcr.io/cloud-builders/docker' 20 | args: ['build', '-t', 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets:v2', '.'] 21 | 22 | - name: 'gcr.io/cloud-builders/docker' 23 | args: ['push', 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets:v2'] 24 | 25 | - name: 'gcr.io/cloud-builders/gcloud' 26 | entrypoint: 'bash' 27 | args: 28 | - '-c' 29 | - | 30 | sed -i "s|image: .*|image: asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets:v2|g" kubernetes-manifests/deployment.yaml 31 | 32 | - name: 'gcr.io/cloud-builders/gcloud' 33 | entrypoint: 'bash' 34 | args: 35 | - '-c' 36 | - | 37 | gcloud deploy releases create release-$(date +%Y%m%d%H%M%S) --delivery-pipeline=pfe-cicd --region=asia-northeast1 --source=./ --project=$PROJECT_ID 38 | 39 | images: 40 | - 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets:v2' 41 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-01/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'python:3.12-alpine' 3 | entrypoint: 'sh' 4 | args: 5 | - '-c' 6 | - | 7 | pip install -r requirements.txt 8 | pip install flake8 9 | flake8 . 10 | 11 | - name: 'python:3.12-alpine' 12 | entrypoint: 'sh' 13 | args: 14 | - '-c' 15 | - | 16 | pip install -r requirements.txt 17 | python -m unittest discover 18 | 19 | - name: 'gcr.io/cloud-builders/docker' 20 | args: ['build', '-t', 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets:v1', '.'] 21 | 22 | - name: 'gcr.io/cloud-builders/docker' 23 | args: ['push', 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets:v1'] 24 | 25 | images: 26 | - 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets:v1' 27 | 28 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-01/clouddeploy-2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: deploy.cloud.google.com/v1beta1 2 | kind: DeliveryPipeline 3 | metadata: 4 | name: pfe-cicd 5 | description: pfe-cicd 6 | serialPipeline: 7 | stages: 8 | - targetId: dev 9 | - targetId: prod 10 | --- 11 | apiVersion: deploy.cloud.google.com/v1beta1 12 | kind: Target 13 | metadata: 14 | name: dev 15 | description: Dev Environment 16 | gke: 17 | cluster: projects/${PROJECT_ID}/locations/asia-northeast1/clusters/dev-cluster 18 | --- 19 | apiVersion: deploy.cloud.google.com/v1beta1 20 | kind: Target 21 | metadata: 22 | name: prod 23 | description: Production Environment 24 | gke: 25 | cluster: projects/${PROJECT_ID}/locations/asia-northeast1/clusters/prod-cluster 26 | --- 27 | apiVersion: deploy.cloud.google.com/v1 28 | kind: Automation 29 | metadata: 30 | name: pfe-cicd/promote 31 | description: promotes a release 32 | suspended: false 33 | serviceAccount: ${PROJECT_NUMBER}-compute@developer.gserviceaccount.com 34 | selector: 35 | targets: 36 | - id: dev 37 | rules: 38 | - promoteReleaseRule: 39 | name: "promote-release" 40 | wait: 1m 41 | toTargetId: "@next" 42 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-01/clouddeploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: deploy.cloud.google.com/v1beta1 2 | kind: DeliveryPipeline 3 | metadata: 4 | name: pfe-cicd 5 | description: pfe-cicd 6 | serialPipeline: 7 | stages: 8 | - targetId: dev 9 | - targetId: prod 10 | --- 11 | apiVersion: deploy.cloud.google.com/v1beta1 12 | kind: Target 13 | metadata: 14 | name: dev 15 | description: Dev Environment 16 | gke: 17 | cluster: projects/${PROJECT_ID}/locations/asia-northeast1/clusters/dev-cluster 18 | --- 19 | apiVersion: deploy.cloud.google.com/v1beta1 20 | kind: Target 21 | metadata: 22 | name: prod 23 | description: Production Environment 24 | gke: 25 | cluster: projects/${PROJECT_ID}/locations/asia-northeast1/clusters/prod-cluster 26 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-01/kubernetes-manifests/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: pets-deployment 5 | labels: 6 | app: pets 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: pets 12 | template: 13 | metadata: 14 | labels: 15 | app: pets 16 | spec: 17 | containers: 18 | - name: pets 19 | image: pets 20 | ports: 21 | - containerPort: 5000 22 | env: 23 | - name: FLASK_ENV 24 | value: production 25 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-01/kubernetes-manifests/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: pets-service 5 | spec: 6 | selector: 7 | app: pets 8 | ports: 9 | - protocol: TCP 10 | port: 80 11 | targetPort: 5000 12 | type: LoadBalancer 13 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-01/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - deployment.yaml 3 | - service.yaml 4 | 5 | images: 6 | - name: asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets 7 | newName: asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets 8 | newTag: ${SHORT_SHA} 9 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-01/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | flake8 3 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-01/skaffold.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: skaffold/v2beta28 2 | kind: Config 3 | metadata: 4 | name: pets-app 5 | deploy: 6 | kubectl: 7 | manifests: ["kubernetes-manifests/*.yaml"] 8 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-01/test_app.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from app import app 3 | 4 | 5 | class FlaskTestCase(unittest.TestCase): 6 | 7 | def setUp(self): 8 | self.app = app.test_client() 9 | self.app.testing = True 10 | 11 | def test_random_dog(self): 12 | response = self.app.get('/random-pets') 13 | self.assertEqual(response.status_code, 200) 14 | self.assertIn('breed', response.json) 15 | 16 | 17 | if __name__ == '__main__': 18 | unittest.main() 19 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-02/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.12 2 | 3 | WORKDIR /app 4 | 5 | COPY requirements.txt requirements.txt 6 | RUN pip install -r requirements.txt 7 | 8 | COPY app.py app.py 9 | 10 | EXPOSE 5000 11 | CMD ["python", "app.py"] -------------------------------------------------------------------------------- /pfe-adv-sep/lab-02/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, jsonify 2 | import random 3 | 4 | app = Flask(__name__) 5 | 6 | 7 | dog_breeds = [ 8 | "Labrador Retriever", 9 | "German Shepherd", 10 | "Golden Retriever", 11 | "French Bulldog", 12 | "Bulldog", 13 | "Poodle", 14 | "Beagle", 15 | "Rottweiler", 16 | "German Shorthaired Pointer", 17 | "Yorkshire Terrier" 18 | ] 19 | 20 | 21 | cat_breeds = [ 22 | "Persian Cat", 23 | "Maine Coon", 24 | "Ragdoll", 25 | "Siamese Cat", 26 | "British Shorthair", 27 | "Sphynx Cat", 28 | "Scottish Fold", 29 | "Abyssinian", 30 | "Bengal Cat", 31 | "Russian Blue" 32 | ] 33 | 34 | 35 | @app.route('/random-pets', methods=['GET']) 36 | def get_random_pet(): 37 | if random.choice([True, False]): 38 | random_breed = random.choice(dog_breeds) 39 | animal_type = "dog" 40 | else: 41 | random_breed = random.choice(cat_breeds) 42 | animal_type = "cat" 43 | return jsonify({"animal": animal_type, "breed": random_breed}) 44 | 45 | 46 | if __name__ == '__main__': 47 | app.run(host='0.0.0.0', port=5000) 48 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-02/clouddeploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: deploy.cloud.google.com/v1beta1 2 | kind: DeliveryPipeline 3 | metadata: 4 | name: pfe-cicd 5 | description: pfe-cicd 6 | serialPipeline: 7 | stages: 8 | - targetId: dev 9 | - targetId: prod 10 | --- 11 | apiVersion: deploy.cloud.google.com/v1beta1 12 | kind: Target 13 | metadata: 14 | name: dev 15 | description: Dev Environment 16 | gke: 17 | cluster: projects/${PROJECT_ID}/locations/asia-northeast1/clusters/dev-cluster 18 | --- 19 | apiVersion: deploy.cloud.google.com/v1beta1 20 | kind: Target 21 | metadata: 22 | name: prod 23 | description: Production Environment 24 | gke: 25 | cluster: projects/${PROJECT_ID}/locations/asia-northeast1/clusters/prod-cluster 26 | --- 27 | apiVersion: deploy.cloud.google.com/v1 28 | kind: Automation 29 | metadata: 30 | name: pfe-cicd/promote 31 | description: promotes a release 32 | suspended: false 33 | serviceAccount: ${PROJECT_NUMBER}-compute@developer.gserviceaccount.com 34 | selector: 35 | targets: 36 | - id: dev 37 | rules: 38 | - promoteReleaseRule: 39 | name: "promote-release" 40 | wait: 1m 41 | toTargetId: "@next" 42 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-02/kubernetes-manifests/allow-myrepo.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: constraints.gatekeeper.sh/v1beta1 2 | kind: K8sAllowedRepos 3 | metadata: 4 | name: allow-my-app-repo 5 | spec: 6 | match: 7 | kinds: 8 | - apiGroups: 9 | - "" 10 | kinds: 11 | - Pod 12 | namespaces: 13 | - default 14 | parameters: 15 | repos: 16 | - asia-northeast1-docker.pkg.dev/PROJECT_ID/app-repo/ -------------------------------------------------------------------------------- /pfe-adv-sep/lab-02/kubernetes-manifests/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: pets-deployment 5 | labels: 6 | app: pets 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: pets 12 | template: 13 | metadata: 14 | labels: 15 | app: pets 16 | spec: 17 | containers: 18 | - name: pets 19 | image: pets 20 | ports: 21 | - containerPort: 5000 22 | env: 23 | - name: FLASK_ENV 24 | value: production 25 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-02/kubernetes-manifests/maven-vulns.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: maven-vulns 5 | spec: 6 | containers: 7 | - name: maven-vulns-app 8 | image: us-docker.pkg.dev/google-samples/containers/gke/security/maven-vulns 9 | # This app listens on port 8080 for web traffic by default. 10 | ports: 11 | - containerPort: 8080 12 | env: 13 | - name: PORT 14 | value: "8080" -------------------------------------------------------------------------------- /pfe-adv-sep/lab-02/kubernetes-manifests/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: pets-service 5 | spec: 6 | selector: 7 | app: pets 8 | ports: 9 | - protocol: TCP 10 | port: 80 11 | targetPort: 5000 12 | type: LoadBalancer 13 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-02/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | flake8 3 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-02/skaffold.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: skaffold/v2beta28 2 | kind: Config 3 | metadata: 4 | name: pets-app 5 | deploy: 6 | kubectl: 7 | manifests: ["kubernetes-manifests/*.yaml"] 8 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-02/test_app.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from app import app 3 | 4 | 5 | class FlaskTestCase(unittest.TestCase): 6 | 7 | def setUp(self): 8 | self.app = app.test_client() 9 | self.app.testing = True 10 | 11 | def test_random_dog(self): 12 | response = self.app.get('/random-pets') 13 | self.assertEqual(response.status_code, 200) 14 | self.assertIn('breed', response.json) 15 | 16 | 17 | if __name__ == '__main__': 18 | unittest.main() 19 | -------------------------------------------------------------------------------- /pfe-adv-sep/lab-03/allow-hostpath.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: constraints.gatekeeper.sh/v1beta1 2 | kind: K8sPSPHostFilesystem 3 | metadata: 4 | name: allow-hostpath 5 | spec: 6 | match: 7 | kinds: 8 | - apiGroups: 9 | - "" 10 | kinds: 11 | - Pod 12 | parameters: 13 | allowedHostPaths: 14 | - pathPrefix: /var/log 15 | readOnly: true -------------------------------------------------------------------------------- /pfe-adv-sep/lab-03/bad-pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | labels: 5 | app: bad-pod 6 | name: bad-pod 7 | spec: 8 | volumes: 9 | - name: host-fs 10 | hostPath: 11 | path: / 12 | containers: 13 | - image: ubuntu 14 | imagePullPolicy: Always 15 | name: bad-pod 16 | command: ["/bin/sh", "-c", "sleep infinity"] 17 | volumeMounts: 18 | - name: host-fs 19 | mountPath: /root 20 | restartPolicy: Never -------------------------------------------------------------------------------- /pfe-basic-sep/lab-02/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'gcr.io/cloud-builders/docker' 3 | args: ['build', '-t', 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/spring-gs:v1', '.'] 4 | 5 | - name: 'gcr.io/cloud-builders/docker' 6 | args: ['push', 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/spring-gs:v1'] 7 | 8 | - name: 'gcr.io/cloud-builders/gcloud' 9 | entrypoint: 'bash' 10 | args: 11 | - '-c' 12 | - | 13 | sed -i "s|image: .*|image: asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/spring-gs:v1|g" kubernetes-manifests/deployment.yaml 14 | 15 | - name: "gcr.io/cloud-builders/gke-deploy" 16 | args: 17 | - run 18 | - --filename=kubernetes-manifests/ 19 | - --image=asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/spring-gs:v1 20 | - --location=asia-northeast1 21 | - --cluster=dev-cluster -------------------------------------------------------------------------------- /pfe-basic-sep/lab-03/unschedulable-hello.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: unschedulable-hello 5 | labels: 6 | app: hello 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: hello 12 | template: 13 | metadata: 14 | labels: 15 | app: hello 16 | spec: 17 | containers: 18 | - name: unschedulable-hello 19 | image: us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0 20 | ports: 21 | - containerPort: 8080 22 | resources: 23 | requests: 24 | cpu: 5000m 25 | memory: 16Gi 26 | limits: 27 | cpu: 5000m 28 | memory: 16Gi -------------------------------------------------------------------------------- /pfe-cicd/.gitignore: -------------------------------------------------------------------------------- 1 | venv -------------------------------------------------------------------------------- /pfe-cicd/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.12-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY requirements.txt requirements.txt 6 | RUN pip install -r requirements.txt 7 | 8 | COPY app.py app.py 9 | 10 | EXPOSE 5000 11 | CMD ["python", "app.py"] -------------------------------------------------------------------------------- /pfe-cicd/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, jsonify 2 | import random 3 | 4 | app = Flask(__name__) 5 | 6 | breeds = [ 7 | "Labrador Retriever", 8 | "German Shepherd", 9 | "Golden Retriever", 10 | "French Bulldog", 11 | "Bulldog", 12 | "Poodle", 13 | "Beagle", 14 | "Rottweiler", 15 | "German Shorthaired Pointer", 16 | "Yorkshire Terrier" 17 | ] 18 | 19 | 20 | @app.route('/random-pets', methods=['GET']) 21 | def get_random_dog(): 22 | random_breed = random.choice(breeds) 23 | return jsonify({'breed': random_breed}) 24 | 25 | 26 | if __name__ == '__main__': 27 | app.run(host='0.0.0.0', port=5000) 28 | -------------------------------------------------------------------------------- /pfe-cicd/app.txt: -------------------------------------------------------------------------------- 1 | from flask import Flask, jsonify 2 | import random 3 | 4 | app = Flask(__name__) 5 | 6 | 7 | dog_breeds = [ 8 | "Labrador Retriever", 9 | "German Shepherd", 10 | "Golden Retriever", 11 | "French Bulldog", 12 | "Bulldog", 13 | "Poodle", 14 | "Beagle", 15 | "Rottweiler", 16 | "German Shorthaired Pointer", 17 | "Yorkshire Terrier" 18 | ] 19 | 20 | 21 | cat_breeds = [ 22 | "Persian Cat", 23 | "Maine Coon", 24 | "Ragdoll", 25 | "Siamese Cat", 26 | "British Shorthair", 27 | "Sphynx Cat", 28 | "Scottish Fold", 29 | "Abyssinian", 30 | "Bengal Cat", 31 | "Russian Blue" 32 | ] 33 | 34 | 35 | @app.route('/random-pets', methods=['GET']) 36 | def get_random_pet(): 37 | if random.choice([True, False]): 38 | random_breed = random.choice(dog_breeds) 39 | animal_type = "dog" 40 | else: 41 | random_breed = random.choice(cat_breeds) 42 | animal_type = "cat" 43 | return jsonify({"animal": animal_type, "breed": random_breed}) 44 | 45 | 46 | if __name__ == '__main__': 47 | app.run(host='0.0.0.0', port=5000) 48 | -------------------------------------------------------------------------------- /pfe-cicd/cloudbuild-2.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'python:3.12-alpine' 3 | entrypoint: 'sh' 4 | args: 5 | - '-c' 6 | - | 7 | pip install -r requirements.txt 8 | pip install flake8 9 | flake8 . 10 | 11 | - name: 'python:3.12-alpine' 12 | entrypoint: 'sh' 13 | args: 14 | - '-c' 15 | - | 16 | pip install -r requirements.txt 17 | python -m unittest discover 18 | 19 | - name: 'gcr.io/cloud-builders/docker' 20 | args: ['build', '-t', 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets:v2', '.'] 21 | 22 | - name: 'gcr.io/cloud-builders/docker' 23 | args: ['push', 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets:v2'] 24 | 25 | - name: 'gcr.io/cloud-builders/gcloud' 26 | entrypoint: 'bash' 27 | args: 28 | - '-c' 29 | - | 30 | sed -i "s|image: .*|image: asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets:v2|g" kubernetes-manifests/deployment.yaml 31 | 32 | - name: 'gcr.io/cloud-builders/gcloud' 33 | entrypoint: 'bash' 34 | args: 35 | - '-c' 36 | - | 37 | gcloud deploy releases create release-$(date +%Y%m%d%H%M%S) --delivery-pipeline=pfe-cicd --region=asia-northeast1 --source=./ --project=$PROJECT_ID 38 | 39 | images: 40 | - 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets:v2' 41 | -------------------------------------------------------------------------------- /pfe-cicd/cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'python:3.12-alpine' 3 | entrypoint: 'sh' 4 | args: 5 | - '-c' 6 | - | 7 | pip install -r requirements.txt 8 | pip install flake8 9 | flake8 . 10 | 11 | - name: 'python:3.12-alpine' 12 | entrypoint: 'sh' 13 | args: 14 | - '-c' 15 | - | 16 | pip install -r requirements.txt 17 | python -m unittest discover 18 | 19 | - name: 'gcr.io/cloud-builders/docker' 20 | args: ['build', '-t', 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets:v1', '.'] 21 | 22 | - name: 'gcr.io/cloud-builders/docker' 23 | args: ['push', 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets:v1'] 24 | 25 | images: 26 | - 'asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets:v1' 27 | 28 | -------------------------------------------------------------------------------- /pfe-cicd/clouddeploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: deploy.cloud.google.com/v1beta1 2 | kind: DeliveryPipeline 3 | metadata: 4 | name: pfe-cicd 5 | description: pfe-cicd 6 | serialPipeline: 7 | stages: 8 | - targetId: dev 9 | - targetId: prod 10 | --- 11 | apiVersion: deploy.cloud.google.com/v1beta1 12 | kind: Target 13 | metadata: 14 | name: dev 15 | description: Dev Environment 16 | gke: 17 | cluster: projects/${PROJECT_ID}/locations/asia-northeast1/clusters/dev-cluster 18 | --- 19 | apiVersion: deploy.cloud.google.com/v1beta1 20 | kind: Target 21 | metadata: 22 | name: prod 23 | description: Production Environment 24 | gke: 25 | cluster: projects/${PROJECT_ID}/locations/asia-northeast1/clusters/prod-cluster 26 | -------------------------------------------------------------------------------- /pfe-cicd/kubernetes-manifests/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: pets-deployment 5 | labels: 6 | app: pets 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: pets 12 | template: 13 | metadata: 14 | labels: 15 | app: pets 16 | spec: 17 | containers: 18 | - name: pets 19 | image: pets 20 | ports: 21 | - containerPort: 5000 22 | env: 23 | - name: FLASK_ENV 24 | value: production 25 | -------------------------------------------------------------------------------- /pfe-cicd/kubernetes-manifests/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: pets-service 5 | spec: 6 | selector: 7 | app: pets 8 | ports: 9 | - protocol: TCP 10 | port: 80 11 | targetPort: 5000 12 | type: LoadBalancer 13 | -------------------------------------------------------------------------------- /pfe-cicd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - deployment.yaml 3 | - service.yaml 4 | 5 | images: 6 | - name: asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets 7 | newName: asia-northeast1-docker.pkg.dev/$PROJECT_ID/app-repo/pets 8 | newTag: ${SHORT_SHA} 9 | -------------------------------------------------------------------------------- /pfe-cicd/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | flake8 3 | -------------------------------------------------------------------------------- /pfe-cicd/skaffold.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: skaffold/v2beta28 2 | kind: Config 3 | metadata: 4 | name: pets-app 5 | deploy: 6 | kubectl: 7 | manifests: ["kubernetes-manifests/*.yaml"] 8 | -------------------------------------------------------------------------------- /pfe-cicd/test_app.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from app import app 3 | 4 | 5 | class FlaskTestCase(unittest.TestCase): 6 | 7 | def setUp(self): 8 | self.app = app.test_client() 9 | self.app.testing = True 10 | 11 | def test_random_dog(self): 12 | response = self.app.get('/random-pets') 13 | self.assertEqual(response.status_code, 200) 14 | self.assertIn('breed', response.json) 15 | 16 | 17 | if __name__ == '__main__': 18 | unittest.main() 19 | -------------------------------------------------------------------------------- /workstations_with_generative_ai/images/reconnect_cloudshell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleCloudPlatform/gcp-getting-started-lab-jp/c5def029445a6474e5412c1bad1218c61573188c/workstations_with_generative_ai/images/reconnect_cloudshell.png --------------------------------------------------------------------------------