2024-05-27 22:20:58 +00:00
|
|
|
# Tiltfile for working with Next and Strapi locally
|
|
|
|
|
2024-06-27 04:41:16 +00:00
|
|
|
## cert-manager slows down Tilt updates so I prefer to keep it commented unless I specifically need to test certs
|
2024-05-27 22:20:58 +00:00
|
|
|
# load('ext://cert_manager', 'deploy_cert_manager')
|
2024-06-27 04:41:16 +00:00
|
|
|
# deploy_cert_manager(
|
|
|
|
# load_to_kind=True,
|
|
|
|
# # registry='localhost:5001'
|
|
|
|
# )
|
|
|
|
|
2024-06-13 05:17:44 +00:00
|
|
|
default_registry('localhost:5001')
|
2024-06-04 23:06:47 +00:00
|
|
|
load('ext://helm_remote', 'helm_remote')
|
2024-06-27 04:41:16 +00:00
|
|
|
# load('ext://dotenv', 'dotenv')
|
|
|
|
# dotenv(fn='.env')
|
2024-06-12 04:28:36 +00:00
|
|
|
|
2024-06-19 16:46:23 +00:00
|
|
|
# allow_k8s_contexts('vke-e41885d3-7f93-4f01-bfaa-426f20bf9f3f')
|
|
|
|
|
2024-06-04 23:06:47 +00:00
|
|
|
|
2024-06-18 02:21:08 +00:00
|
|
|
# helm_remote(
|
|
|
|
# 'velero',
|
|
|
|
# repo_name='velero',
|
|
|
|
# repo_url='https://vmware-tanzu.github.io/helm-charts',
|
|
|
|
# namespace='futureporn',
|
|
|
|
# version='6.6.0',
|
|
|
|
# set=[
|
|
|
|
# 'configuration.backupStorageLocation[0].name=dev',
|
|
|
|
# 'configuration.backupStorageLocation[0].provider=aws',
|
|
|
|
# 'configuration.backupStorageLocation[0].bucket=futureporn-db-backup-dev',
|
|
|
|
# 'configuration.backupStorageLocation[0].config.region=us-west-000',
|
|
|
|
# 'configuration.backupStorageLocation[0].config.s3ForcePathStyle=true',
|
|
|
|
# 'configuration.backupStorageLocation[0].config.s3Url=https://s3.us-west-000.backblazeb2.com',
|
|
|
|
# 'credentials.secretContents=cloud\n[default]\naws_access_key_id=AAAA\naws_secret_access_key=BBBB',
|
|
|
|
# 'snapshotsEnabled=false',
|
|
|
|
# # --set configuration.backupStorageLocation[0].name=<BACKUP STORAGE LOCATION NAME> \
|
|
|
|
# # --set configuration.backupStorageLocation[0].provider=<PROVIDER NAME> \
|
|
|
|
# # --set configuration.backupStorageLocation[0].bucket=<BUCKET NAME> \
|
|
|
|
# # --set configuration.backupStorageLocation[0].config.region=<REGION> \
|
|
|
|
# # --set configuration.volumeSnapshotLocation[0].name=<VOLUME SNAPSHOT LOCATION NAME> \
|
|
|
|
# # --set configuration.volumeSnapshotLocation[0].provider=<PROVIDER NAME> \
|
|
|
|
# # --set configuration.volumeSnapshotLocation[0].config.region=<REGION> \
|
|
|
|
# # --set initContainers[0].name=velero-plugin-for-<PROVIDER NAME> \
|
|
|
|
# # --set initContainers[0].image=velero/velero-plugin-for-<PROVIDER NAME>:<PROVIDER PLUGIN TAG> \
|
|
|
|
# # --set initContainers[0].volumeMounts[0].mountPath=/target \
|
|
|
|
# # --set initContainers[0].volumeMounts[0].name=plugins
|
|
|
|
# ]
|
|
|
|
# )
|
|
|
|
|
2024-06-12 04:28:36 +00:00
|
|
|
helm_remote(
|
2024-06-27 04:41:16 +00:00
|
|
|
'traefik',
|
|
|
|
repo_name='traefik',
|
|
|
|
repo_url='https://traefik.github.io/charts',
|
2024-06-12 04:28:36 +00:00
|
|
|
namespace='futureporn',
|
2024-06-27 04:41:16 +00:00
|
|
|
version='28.3.0',
|
2024-06-12 04:28:36 +00:00
|
|
|
set=[
|
2024-06-27 04:41:16 +00:00
|
|
|
'globalArguments[0]=--global.sendanonymoususage=false',
|
|
|
|
'globalArguments[1]=--certificatesresolvers.le.acme.email=cj@futureporn.net',
|
|
|
|
'globalArguments[2]=--certificatesresolvers.le.acme.storage=/data/acme.json',
|
|
|
|
'globalArguments[3]=--certificatesresolvers.le.acme.tlschallenge=true',
|
|
|
|
'globalArguments[4]=--certificatesresolvers.le.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory',
|
|
|
|
'service.enabled=false',
|
|
|
|
'logs.access.enabled=true'
|
2024-06-12 04:28:36 +00:00
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2024-06-27 04:41:16 +00:00
|
|
|
|
2024-06-04 23:06:47 +00:00
|
|
|
# helm_remote(
|
|
|
|
# 'nitter',
|
|
|
|
# repo_name='truecharts',
|
|
|
|
# repo_url='https://charts.truecharts.org',
|
|
|
|
# namespace='futureporn',
|
|
|
|
# version='7.1.4',
|
|
|
|
# )
|
2024-05-27 22:20:58 +00:00
|
|
|
# helm_remote(
|
|
|
|
# 'frp-operator',
|
|
|
|
# repo_name='frp-operator',
|
|
|
|
# repo_url='https://zufardhiyaulhaq.com/frp-operator/charts/releases/',
|
|
|
|
# namespace='futureporn',
|
|
|
|
# version='1.0.0'
|
|
|
|
# )
|
|
|
|
# helm_remote(
|
|
|
|
# 'kubernetes-ingress-controller',
|
|
|
|
# repo_name='kubernetes-ingress-controller',
|
|
|
|
# repo_url='https://ngrok.github.io/kubernetes-ingress-controller',
|
|
|
|
# namespace='futureporn',
|
|
|
|
# create_namespace='false',
|
|
|
|
# set=[
|
|
|
|
# 'credentials.apiKey=%s' % os.getenv('NGROK_API_KEY'),
|
|
|
|
# 'credentials.authtoken=%s' % os.getenv('NGROK_AUTHTOKEN')
|
|
|
|
# ]
|
|
|
|
# )
|
|
|
|
|
2024-05-29 23:43:47 +00:00
|
|
|
|
2024-06-04 23:06:47 +00:00
|
|
|
# k8s_yaml(helm(
|
|
|
|
# './charts/nitter',
|
|
|
|
# values=['./charts/nitter/values.yaml'],
|
|
|
|
# ))
|
|
|
|
|
2024-05-27 22:20:58 +00:00
|
|
|
k8s_yaml(helm(
|
|
|
|
'./charts/fp',
|
|
|
|
values=['./charts/fp/values-dev.yaml'],
|
|
|
|
))
|
2024-06-12 04:28:36 +00:00
|
|
|
# k8s_yaml(helm(
|
|
|
|
# './charts/trigger',
|
|
|
|
# set=[
|
|
|
|
# 'trigger.name=trigger',
|
|
|
|
# 'trigger.replicaCount=2',
|
|
|
|
# 'trigger.image.tag=self-host-rc.2',
|
|
|
|
# 'trigger.image.pullPolicy=IfNotPresent',
|
|
|
|
# 'trigger.env.ENCRYPTION_KEY=%s' % os.getenv('TRIGGER_ENCRYPTION_KEY'),
|
|
|
|
# 'trigger.env.MAGIC_LINK_SECRET=%s' % os.getenv('TRIGGER_MAGIC_LINK_SECRET'),
|
|
|
|
# 'trigger.env.DATABASE_URL=%s' % os.getenv('TRIGGER_DATABASE_URL'),
|
|
|
|
# 'trigger.env.LOGIN_ORIGIN=%s' % os.getenv('TRIGGER_LOGIN_ORIGIN'),
|
|
|
|
# 'trigger.env.APP_ORIGIN=%s' % os.getenv('TRIGGER_APP_ORIGIN'),
|
|
|
|
# 'trigger.env.PORT=%s' % os.getenv('TRIGGER_PORT'),
|
|
|
|
# 'trigger.env.REMIX_APP_PORT=%s' % os.getenv('TRIGGER_REMIX_APP_PORT'),
|
|
|
|
# 'trigger.env.REDIS_HOST=redis-master.futureporn.svc.cluster.local',
|
|
|
|
# 'trigger.env.REDIS_PORT=6379',
|
|
|
|
# 'trigger.ingress.nginx.enabled=false',
|
|
|
|
# 'trigger.ingress.enabled=false',
|
|
|
|
# 'postgres.enabled=false'
|
|
|
|
# ]
|
|
|
|
# ))
|
|
|
|
# k8s_resource(
|
|
|
|
# workload='trigger',
|
|
|
|
# port_forwards=['3030'],
|
|
|
|
# )
|
2024-05-27 22:20:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# docker_build('fp/link2cid', './packages/link2cid')
|
|
|
|
docker_build(
|
|
|
|
'fp/strapi',
|
|
|
|
'.',
|
|
|
|
build_args={
|
|
|
|
'NODE_ENV': 'development',
|
|
|
|
},
|
|
|
|
only=['./packages/strapi'],
|
|
|
|
dockerfile='./d.strapi.dockerfile',
|
|
|
|
live_update=[
|
|
|
|
sync('./packages/strapi', '/app')
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# docker_build(
|
|
|
|
# 'fp/strapi-app',
|
|
|
|
# '.',
|
|
|
|
# only=["./packages/strapi-app"],
|
|
|
|
# dockerfile='d.strapi-app.dockerfile',
|
|
|
|
# live_update=[
|
|
|
|
# sync('./packages/strapi-app', '/app')
|
|
|
|
# ]
|
|
|
|
# )
|
|
|
|
|
|
|
|
load('ext://uibutton', 'cmd_button')
|
2024-06-12 04:28:36 +00:00
|
|
|
cmd_button('postgres:create',
|
|
|
|
argv=['sh', './scripts/postgres-create.sh'],
|
|
|
|
resource='postgres',
|
|
|
|
icon_name='dataset',
|
|
|
|
text='create (empty) databases',
|
|
|
|
)
|
2024-05-27 22:20:58 +00:00
|
|
|
cmd_button('postgres:restore',
|
|
|
|
argv=['sh', './scripts/postgres-restore.sh'],
|
|
|
|
resource='postgres',
|
2024-06-12 04:28:36 +00:00
|
|
|
icon_name='upload',
|
2024-05-27 22:20:58 +00:00
|
|
|
text='restore db from backup',
|
|
|
|
)
|
|
|
|
cmd_button('postgres:drop',
|
|
|
|
argv=['sh', './scripts/postgres-drop.sh'],
|
|
|
|
resource='postgres',
|
|
|
|
icon_name='delete',
|
2024-06-12 04:28:36 +00:00
|
|
|
text='DROP futureporn_db'
|
|
|
|
)
|
|
|
|
cmd_button('postgres:drop_temporal',
|
|
|
|
argv=['sh', './scripts/postgres-drop-temporal.sh'],
|
|
|
|
resource='postgres',
|
|
|
|
icon_name='delete',
|
|
|
|
text='DROP temporal'
|
2024-05-27 22:20:58 +00:00
|
|
|
)
|
2024-05-29 23:43:47 +00:00
|
|
|
cmd_button('postgres:backup',
|
|
|
|
argv=['sh', './scripts/postgres-backup.sh'],
|
|
|
|
resource='postgres',
|
|
|
|
icon_name='download',
|
|
|
|
text='backup the database'
|
|
|
|
)
|
2024-06-12 04:28:36 +00:00
|
|
|
cmd_button('temporal-web:namespace',
|
|
|
|
argv=['sh', './scripts/temporal-namespaces.sh'],
|
|
|
|
resource='temporal-web',
|
|
|
|
icon_name='badge',
|
|
|
|
text='create futureporn namespace',
|
|
|
|
)
|
2024-05-27 22:20:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
## Uncomment the following for fp/next in dev mode
|
|
|
|
## this is useful for changing the UI and seeing results
|
|
|
|
docker_build(
|
|
|
|
'fp/next',
|
|
|
|
'.',
|
|
|
|
only=['./pnpm-lock.yaml', './package.json', './packages/next'],
|
|
|
|
dockerfile='d.next.dockerfile',
|
|
|
|
target='dev',
|
|
|
|
build_args={
|
2024-06-27 04:41:16 +00:00
|
|
|
'NEXT_PUBLIC_STRAPI_URL': 'https://strapi.fp.sbtp.xyz'
|
2024-05-27 22:20:58 +00:00
|
|
|
},
|
|
|
|
live_update=[
|
|
|
|
sync('./packages/next', '/app')
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
docker_build(
|
2024-06-12 04:28:36 +00:00
|
|
|
'fp/scout-manager',
|
2024-05-27 22:20:58 +00:00
|
|
|
'.',
|
2024-06-12 04:28:36 +00:00
|
|
|
only=['./pnpm-lock.yaml', './package.json', './packages/scout', './packages/next'],
|
2024-05-27 22:20:58 +00:00
|
|
|
dockerfile='d.scout.dockerfile',
|
2024-06-12 04:28:36 +00:00
|
|
|
target='manager',
|
2024-05-27 22:20:58 +00:00
|
|
|
live_update=[
|
2024-06-12 04:28:36 +00:00
|
|
|
sync('./packages/scout', '/app'),
|
|
|
|
run('cd /app && pnpm i', trigger=['./packages/scout/package.json', './packages/scout/pnpm-lock.yaml']),
|
|
|
|
],
|
|
|
|
entrypoint='pnpm nodemon --ext js,ts,json,yaml --exec node --no-warnings=ExperimentalWarning --loader ts-node/esm ./src/index.ts'
|
|
|
|
# entrypoint='pnpm tsx watch ./src/index.ts'
|
|
|
|
)
|
|
|
|
|
|
|
|
docker_build(
|
|
|
|
'fp/scout-worker',
|
|
|
|
'.',
|
|
|
|
only=['./pnpm-lock.yaml', './package.json', './packages/scout', './packages/next'],
|
2024-06-13 22:25:12 +00:00
|
|
|
# ignore=['./packages/next'], # I wish I could use this ignore to ignore file changes in this dir, but that's not how it works
|
2024-06-12 04:28:36 +00:00
|
|
|
dockerfile='d.scout.dockerfile',
|
|
|
|
target='worker',
|
|
|
|
live_update=[
|
2024-06-13 22:25:12 +00:00
|
|
|
# idk if this run() is effective
|
|
|
|
# run('cd /app && pnpm i', trigger=['./packages/scout/package.json', './packages/scout/pnpm-lock.yaml']),
|
2024-06-12 04:28:36 +00:00
|
|
|
sync('./packages/scout', '/app'),
|
2024-06-13 05:17:44 +00:00
|
|
|
## this is a hack to avoid complete scout image rebuilds when src in ./packages/next is updated
|
|
|
|
## ./packages/next needs to be in the build context because scout depends on types exported from next module (a feature of pnpm workspaces)
|
|
|
|
## instead of a full rebuild, we put ./packages/next in the live_update spec so the changed files get shoved into /ignore-me
|
|
|
|
## ideally, I would like to include ./packages/next in the build context, but ignore file changes there for rebuilds.
|
|
|
|
## however, I don't think tilt has this capability.
|
|
|
|
sync('./packages/next', '/ignore-me'),
|
2024-06-12 04:28:36 +00:00
|
|
|
],
|
2024-06-13 22:51:25 +00:00
|
|
|
# this entrypoint is a godsend. It lets me restart the node app (fast) without having to rebuild the docker container (slow)
|
2024-06-12 04:28:36 +00:00
|
|
|
entrypoint='pnpm nodemon --ext js,ts,json,yaml --exec node --no-warnings=ExperimentalWarning --loader ts-node/esm ./src/temporal/worker.ts'
|
2024-05-27 22:20:58 +00:00
|
|
|
)
|
|
|
|
# k8s_resource(
|
|
|
|
# workload='kubernetes-ingress-controller-manager',
|
|
|
|
# links=[
|
|
|
|
# link(os.getenv('NGROK_URL'), 'Endpoint')
|
|
|
|
# ],
|
|
|
|
# labels='ngrok'
|
|
|
|
# )
|
|
|
|
# k8s_resource(
|
|
|
|
# workload='frp-operator-controller-manager',
|
|
|
|
# labels='tunnel'
|
|
|
|
# )
|
2024-06-27 04:41:16 +00:00
|
|
|
k8s_resource(
|
|
|
|
workload='echo',
|
|
|
|
port_forwards=['8080'],
|
|
|
|
links=[
|
|
|
|
link('https://echo.fp.sbtp.xyz'),
|
|
|
|
link('http://echo.futureporn.svc.cluster.local:8001')
|
|
|
|
],
|
|
|
|
labels='debug'
|
|
|
|
)
|
2024-05-27 22:20:58 +00:00
|
|
|
# k8s_resource(
|
|
|
|
# workload='snake',
|
|
|
|
# port_forwards=['8080'],
|
|
|
|
# labels='debug'
|
|
|
|
# )
|
|
|
|
# k8s_resource(
|
|
|
|
# workload='game-2048',
|
|
|
|
# port_forwards=['8081:8080'],
|
|
|
|
# labels='debug'
|
|
|
|
# )
|
|
|
|
|
|
|
|
k8s_resource(
|
|
|
|
workload='next',
|
|
|
|
port_forwards=['3000'],
|
|
|
|
links=[
|
2024-06-27 04:41:16 +00:00
|
|
|
link('https://next.fp.sbtp.xyz'),
|
2024-06-12 04:28:36 +00:00
|
|
|
],
|
|
|
|
resource_deps=['strapi', 'postgres']
|
2024-05-27 22:20:58 +00:00
|
|
|
)
|
|
|
|
k8s_resource(
|
|
|
|
workload='strapi',
|
|
|
|
port_forwards=['1339'],
|
|
|
|
links=[
|
|
|
|
link('http://localhost:1339/admin'),
|
2024-06-27 04:41:16 +00:00
|
|
|
link('https://strapi.fp.sbtp.xyz'),
|
2024-06-12 04:28:36 +00:00
|
|
|
],
|
|
|
|
resource_deps=['postgres']
|
2024-05-27 22:20:58 +00:00
|
|
|
)
|
2024-06-01 02:43:44 +00:00
|
|
|
|
2024-05-27 22:20:58 +00:00
|
|
|
k8s_resource(
|
|
|
|
workload='postgres',
|
|
|
|
port_forwards=['5432']
|
|
|
|
)
|
|
|
|
|
2024-06-27 04:41:16 +00:00
|
|
|
# k8s_resource(
|
|
|
|
# workload='scout-worker',
|
|
|
|
# resource_deps=['postgres', 'strapi', 'temporal-frontend', 'scout-manager']
|
|
|
|
# )
|
2024-06-12 04:28:36 +00:00
|
|
|
|
2024-06-27 04:41:16 +00:00
|
|
|
# k8s_resource(
|
|
|
|
# workload='scout-manager',
|
|
|
|
# resource_deps=['postgres', 'strapi', 'temporal-frontend']
|
|
|
|
# )
|
2024-06-12 04:28:36 +00:00
|
|
|
|
2024-06-04 23:06:47 +00:00
|
|
|
# k8s_resource(
|
|
|
|
# workload='pgadmin',
|
2024-06-12 04:28:36 +00:00
|
|
|
# port_forwards=['5050'],
|
|
|
|
# resource_deps=['postgres']
|
2024-06-04 23:06:47 +00:00
|
|
|
# )
|
2024-05-29 23:43:47 +00:00
|
|
|
|
2024-06-04 23:06:47 +00:00
|
|
|
# k8s_resource(
|
|
|
|
# workload='nitter',
|
|
|
|
# port_forwards=['6060:10606'],
|
2024-06-12 04:28:36 +00:00
|
|
|
# )
|
|
|
|
|
|
|
|
|
2024-06-27 04:41:16 +00:00
|
|
|
# helm_remote(
|
|
|
|
# 'temporal',
|
|
|
|
# repo_name='temporal',
|
|
|
|
# repo_url='https://charts.lemontech.engineering',
|
|
|
|
# namespace='futureporn',
|
|
|
|
# version='0.37.0',
|
|
|
|
# set=[
|
|
|
|
# 'admintools.image.tag=1.24.1-tctl-1.18.1-cli-0.12.0',
|
|
|
|
# 'web.image.tag=2.27.2',
|
|
|
|
# 'prometheus.enabled=false',
|
|
|
|
# 'grafana.enabled=false',
|
|
|
|
# 'elasticsearch.enabled=false',
|
|
|
|
# 'web.config.auth.enabled=true',
|
|
|
|
# 'cassandra.enabled=false',
|
|
|
|
# 'server.config.persistence.default.driver=sql',
|
|
|
|
# 'server.config.persistence.default.sql.driver=postgres12',
|
|
|
|
# 'server.config.persistence.default.sql.host=%s' % os.getenv('POSTGRES_HOST'),
|
|
|
|
# 'server.config.persistence.default.sql.port=5432',
|
|
|
|
# 'server.config.persistence.default.sql.user=%s' % os.getenv('POSTGRES_USER'),
|
|
|
|
# 'server.config.persistence.default.sql.password=%s' % os.getenv('POSTGRES_PASSWORD'),
|
|
|
|
# 'server.config.persistence.visibility.driver=sql',
|
|
|
|
# 'server.config.persistence.visibility.sql.driver=postgres12',
|
|
|
|
# 'server.config.persistence.visibility.sql.host=%s' % os.getenv('POSTGRES_HOST'),
|
|
|
|
# 'server.config.persistence.visibility.sql.port=5432',
|
|
|
|
# 'server.config.persistence.visibility.sql.user=%s' % os.getenv('POSTGRES_USER'),
|
|
|
|
# 'server.config.persistence.visibility.sql.password=%s' % os.getenv('POSTGRES_PASSWORD'),
|
|
|
|
# ]
|
|
|
|
# )
|
|
|
|
# k8s_resource(
|
|
|
|
# workload='temporal-admintools',
|
|
|
|
# labels='temporal',
|
|
|
|
# resource_deps=[
|
|
|
|
# 'postgres',
|
|
|
|
# 'strapi'
|
|
|
|
# ])
|
|
|
|
# k8s_resource(
|
|
|
|
# workload='temporal-frontend',
|
|
|
|
# labels='temporal', port_forwards=['7233'],
|
|
|
|
# resource_deps=[
|
|
|
|
# 'postgres',
|
|
|
|
# 'strapi'
|
|
|
|
# ])
|
|
|
|
# k8s_resource(
|
|
|
|
# workload='temporal-history',
|
|
|
|
# labels='temporal',
|
|
|
|
# resource_deps=[
|
|
|
|
# 'postgres',
|
|
|
|
# 'strapi'
|
|
|
|
# ])
|
|
|
|
# k8s_resource(
|
|
|
|
# workload='temporal-worker',
|
|
|
|
# labels='temporal',
|
|
|
|
# resource_deps=[
|
|
|
|
# 'postgres',
|
|
|
|
# 'strapi'
|
|
|
|
# ])
|
|
|
|
# k8s_resource(
|
|
|
|
# workload='temporal-web',
|
|
|
|
# labels='temporal', port_forwards=['8080'],
|
|
|
|
# resource_deps=[
|
|
|
|
# 'postgres',
|
|
|
|
# 'strapi'
|
|
|
|
# ])
|
|
|
|
# k8s_resource(
|
|
|
|
# workload='temporal-schema-setup',
|
|
|
|
# labels='temporal',
|
|
|
|
# resource_deps=[
|
|
|
|
# 'postgres',
|
|
|
|
# 'strapi'
|
|
|
|
# ])
|
|
|
|
# k8s_resource(
|
|
|
|
# workload='temporal-schema-update',
|
|
|
|
# labels='temporal',
|
|
|
|
# resource_deps=[
|
|
|
|
# 'postgres',
|
|
|
|
# 'strapi'
|
|
|
|
# ])
|
|
|
|
# k8s_resource(
|
|
|
|
# workload='temporal-matching',
|
|
|
|
# labels='temporal',
|
|
|
|
# resource_deps=[
|
|
|
|
# 'postgres',
|
|
|
|
# 'strapi'
|
|
|
|
# ])
|