@formixru/b2b-ui 1.9.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ services:
2
+ web:
3
+ image: gitea.optiweb.ru/public/nginx-static
4
+ user: ${DOCKER_USER}
5
+ restart: always
6
+ volumes:
7
+ - ${HOST_DEST}:/app
8
+ labels:
9
+ - consul.meta.ingress=true
10
+ - consul.tags="www"
11
+ ports:
12
+ - :80
@@ -0,0 +1,8 @@
1
+ services:
2
+ php:
3
+ image: gitea.optiweb.ru/public/nginx-static
4
+ user: ${DOCKER_USER}
5
+ ports:
6
+ - 8080:80
7
+ volumes:
8
+ - ../../../:/app
@@ -0,0 +1,237 @@
1
+ name: deploy
2
+ on:
3
+ push:
4
+ branches: ["**"]
5
+
6
+ jobs:
7
+ deploy:
8
+ runs-on: dev.optiweb
9
+ env:
10
+ NODE_CONTAINER_LABEL: "20.12-2.0.0"
11
+ steps:
12
+ - name: Checkout
13
+ uses: actions/checkout@v4
14
+
15
+ - name: Вычисление переменных
16
+ run: |
17
+ echo "BUILD_START=$(date +%s)" >> "$GITEA_ENV"
18
+
19
+ RAW="${{ gitea.ref_name }}"
20
+
21
+ if [[ "$RAW" =~ ^v[0-9]+(\.[0-9]+)+$ ]]; then
22
+ RAW="${RAW#v}"
23
+ fi
24
+
25
+ VERSION=$(echo "$RAW" | sed 's/[^a-zA-Z0-9]/-/g')
26
+ {
27
+ echo "VERSION=$VERSION"
28
+ echo "PROJECT=${{ gitea.event.repository.name }}"
29
+ echo "DEST=${DEPLOY_ROOT}/${{ gitea.event.repository.name }}/${VERSION}"
30
+ echo "HOST_DEST=${HOST_DEPLOY_ROOT}/${{ gitea.event.repository.name }}/${VERSION}"
31
+ echo "HOST_WORK_DIR=${HOST_WORK_ROOT}${{ gitea.workspace }}"
32
+ echo "COMPOSE_DIR=${DEPLOY_ROOT}/${{ gitea.event.repository.name }}/${VERSION}/.docker/${DEPLOY_TYPE}/compose"
33
+ } >> "$GITEA_ENV"
34
+
35
+ NODE_ADDR=$(curl -sS -H "X-Consul-Token: ${CONSUL_HTTP_TOKEN}" \
36
+ "${CONSUL_HTTP_ADDR}/v1/agent/self" | jq -r '.DebugConfig.AdvertiseAddrLAN')
37
+ if [ -z "$NODE_ADDR" ] || [ "$NODE_ADDR" = "null" ]; then
38
+ echo "::error::Не удалось получить адрес агента"
39
+ exit 1
40
+ fi
41
+ echo "NODE_ADDR=$NODE_ADDR" >> "$GITEA_ENV"
42
+
43
+ - name: Версии ПО и переменные с которыми выполняется сборка
44
+ run: |
45
+ echo 'PROJECT:' $PROJECT
46
+ echo 'VERSION:' $VERSION
47
+ echo 'DEST:' $DEST
48
+ echo 'HOST_DEST:' $HOST_DEST
49
+ echo 'HOST_WORK_DIR:' $HOST_WORK_DIR
50
+ echo 'PLACEHOLDER:' ${PLACEHOLDER}
51
+ echo 'BASE_DOMAIN:' ${BASE_DOMAIN}
52
+ echo 'DEPLOY_TYPE:' ${DEPLOY_TYPE}
53
+ echo 'COMPOSE_DIR:' ${COMPOSE_DIR}
54
+ echo 'NODE_ADDR:' ${NODE_ADDR}
55
+
56
+ - name: Запуск сборки
57
+ run: |
58
+ docker run --rm -i \
59
+ -e YARN_CACHE_FOLDER=/.yarn-cache \
60
+ -v $HOST_WORK_DIR:/app \
61
+ -v yarn-cache:/.yarn-cache \
62
+ --pull always gitea.optiweb.ru/public/node:${NODE_CONTAINER_LABEL} \
63
+ sh -c "yarn install && yarn build && yarn lint"
64
+
65
+ - name: Копирование собранных файлов
66
+ run: |
67
+ mkdir -p "$DEST"
68
+ rsync -a --info=copy,del --delete --exclude '.git' ./ "$DEST/"
69
+
70
+ - name: Запуск контейнеров
71
+ run: docker compose -f ${COMPOSE_DIR}/docker-compose.yaml -p "${PROJECT}_${VERSION}" up -d --pull always --remove-orphans
72
+
73
+ - name: Дерегистрация прежних записей версии
74
+ run: |
75
+ SVC_NAME="${VERSION}-${PROJECT}-${PLACEHOLDER}"
76
+ curl -sS -H "X-Consul-Token: ${CONSUL_HTTP_TOKEN}" \
77
+ "${CONSUL_HTTP_ADDR}/v1/agent/services" | \
78
+ jq -r --arg name "$SVC_NAME" \
79
+ 'to_entries[] | select(.value.Service == $name) | .key' | \
80
+ while read -r sid; do
81
+ echo "Дерегистрирую прежнюю запись: $sid"
82
+ curl -sS -H "X-Consul-Token: ${CONSUL_HTTP_TOKEN}" \
83
+ -X PUT "${CONSUL_HTTP_ADDR}/v1/agent/service/deregister/${sid}"
84
+ done
85
+
86
+ - name: Регистрация сервисов в consul
87
+ run: |
88
+ cd "${COMPOSE_DIR}"
89
+ COMPOSE="docker compose -f docker-compose.yaml -p ${PROJECT}_${VERSION}"
90
+ SVC_NAME="${VERSION}-${PROJECT}-${PLACEHOLDER}"
91
+
92
+ ALL_TAGS="" # накопитель всех тегов ingress-сервисов
93
+
94
+ for service in $($COMPOSE ps --services); do
95
+ for cid in $($COMPOSE ps -q "$service"); do
96
+ [ -z "$cid" ] && continue
97
+
98
+ tags=""; additional_meta=""; ingress=""; first=true
99
+ while IFS='=' read -r key value; do
100
+ key=$(echo "$key" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')
101
+ value=$(echo "$value" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//; s/^"\(.*\)"$/\1/')
102
+ case "$key" in
103
+ consul.meta.ingress)
104
+ ingress="$value" ;;
105
+ consul.meta.*)
106
+ mkey=$(echo "$key" | sed 's/^consul\.meta\.//')
107
+ $first && first=false || additional_meta="${additional_meta},"
108
+ additional_meta="${additional_meta}\"$mkey\":\"$value\"" ;;
109
+ consul.tags)
110
+ tags=$(echo "$value" | sed 's/[^,]*/"&"/g') ;;
111
+ esac
112
+ done < <(docker inspect --format='{{range $k,$v := .Config.Labels}}{{$k}}={{$v}}{{"\n"}}{{end}}' "$cid" | grep '^consul')
113
+
114
+ if [ -z "$tags" ]; then
115
+ echo "$service: без consul.tags, пропуск"; continue
116
+ fi
117
+ if [ "$ingress" != "true" ]; then
118
+ echo "$service: ingress не true (='$ingress'), пропуск роутинга"; continue
119
+ fi
120
+
121
+ pnum=$(docker inspect --format='{{range $p,$c := .NetworkSettings.Ports}}{{$p}} {{end}}' "$cid" | awk '{print $1}' | cut -d/ -f1)
122
+ [ -z "$pnum" ] && { echo "$service: нет портов, пропуск"; continue; }
123
+ pext=$($COMPOSE port "$service" "$pnum" | cut -d: -f2)
124
+ [ -z "$pext" ] && { echo "$service: порт не опубликован, пропуск"; continue; }
125
+
126
+ # накапливаем теги этого сервиса (снимаем кавычки, что sed навесил для JSON)
127
+ plain_tags=$(echo "$tags" | sed 's/"//g; s/,/ /g')
128
+ ALL_TAGS="${ALL_TAGS} ${plain_tags}"
129
+
130
+ meta="\"project\":\"${PROJECT}\",\"version\":\"${VERSION}\",\"placeholder\":\"${PLACEHOLDER}\",\"ingress\":\"${ingress}\""
131
+ [ -n "$additional_meta" ] && meta="${meta},${additional_meta}"
132
+
133
+ BODY="{
134
+ \"ID\": \"${cid}\",
135
+ \"Name\": \"${SVC_NAME}\",
136
+ \"Address\": \"${NODE_ADDR}\",
137
+ \"Port\": ${pext},
138
+ \"Tags\": [${tags}],
139
+ \"Meta\": {${meta}},
140
+ \"Check\": {
141
+ \"TCP\": \"127.0.0.1:${pext}\",
142
+ \"Interval\": \"10s\"
143
+ }
144
+ }"
145
+
146
+ echo "Регистрирую $service cid=${cid:0:12} порт=$pext теги=[$tags] ingress=$ingress"
147
+ RESP=$(curl -sS -w "\n%{http_code}" \
148
+ -H "X-Consul-Token: ${CONSUL_HTTP_TOKEN}" \
149
+ -H 'Content-Type: application/json' \
150
+ -X PUT --data "$BODY" \
151
+ "${CONSUL_HTTP_ADDR}/v1/agent/service/register")
152
+ CODE=$(echo "$RESP" | tail -n1)
153
+ if [ "$CODE" != "200" ]; then
154
+ echo "::warning::Регистрация $service вернула $CODE: $(echo "$RESP" | head -n-1)"
155
+ fi
156
+ done
157
+ done
158
+
159
+ # нормализуем список тегов (убираем лишние пробелы) и сохраняем для шага уведомления
160
+ ALL_TAGS=$(echo "$ALL_TAGS" | tr -s ' ' | sed 's/^ //; s/ $//')
161
+ echo "ALL_TAGS=$ALL_TAGS" >> "$GITEA_ENV"
162
+ echo "Собранные теги для уведомления: $ALL_TAGS"
163
+
164
+ - name: Уведомление в Telegram
165
+ if: always()
166
+ run: |
167
+ STATUS="${{ job.status }}"
168
+ case "$STATUS" in
169
+ success) ICON="✅"; WORD="успешно" ;;
170
+ failure) ICON="❌"; WORD="с ошибкой" ;;
171
+ *) ICON="⚠️"; WORD="$STATUS" ;;
172
+ esac
173
+
174
+ # длительность сборки
175
+ if [ -n "$BUILD_START" ]; then
176
+ ELAPSED=$(( $(date +%s) - BUILD_START ))
177
+ MIN=$(( ELAPSED / 60 ))
178
+ SEC=$(( ELAPSED % 60 ))
179
+ if [ "$MIN" -gt 0 ]; then
180
+ DURATION="${MIN} м ${SEC} с"
181
+ else
182
+ DURATION="${SEC} с"
183
+ fi
184
+ else
185
+ DURATION="—"
186
+ fi
187
+
188
+ md_escape() {
189
+ printf '%s' "$1" | sed 's/[\\_*`\[]/\\&/g'
190
+ }
191
+
192
+ COMMIT_MSG_RAW=$(git log -1 --pretty=%s 2>/dev/null || echo "—")
193
+ COMMIT_MSG=$(md_escape "$COMMIT_MSG_RAW")
194
+ REF=$(md_escape "${{ gitea.ref_name }}")
195
+ ACTOR=$(md_escape "${{ gitea.actor }}")
196
+ PROJECT_ESC=$(md_escape "$PROJECT")
197
+
198
+ SHA_FULL="${{ gitea.sha }}"
199
+ SHA_SHORT="${SHA_FULL:0:8}"
200
+ COMMIT_URL="${{ gitea.server_url }}/${{ gitea.repository }}/commit/${SHA_FULL}"
201
+
202
+ DOMAINS=""
203
+ for tag in $ALL_TAGS; do
204
+ DOMAINS="${DOMAINS}\nhttp://${tag}.${VERSION}.${PROJECT}.${PLACEHOLDER}.${BASE_DOMAIN}"
205
+ done
206
+ [ -z "$ALL_TAGS" ] && DOMAINS="\n(нет ingress-сервисов)"
207
+
208
+ MESSAGE=$(printf '%s Деплой %s / %s завершён %s\nВетка: %s\nКоммит: [%s](%s)\nСообщение: %s\nАвтор: %s\nВремя сборки: %s\nПлощадка: %s.%s\nАдреса:%b' \
209
+ "$ICON" "$PROJECT_ESC" "$VERSION" "$WORD" \
210
+ "$REF" "$SHA_SHORT" "$COMMIT_URL" "$COMMIT_MSG" "$ACTOR" \
211
+ "$DURATION" "$PLACEHOLDER" "$BASE_DOMAIN" "$DOMAINS")
212
+
213
+ HTTP_BODY=$(curl -sS --max-time 15 \
214
+ -U "${TELEGRAM_PROXY_LOGIN}:${TELEGRAM_PROXY_PASS}" \
215
+ -x "${TELEGRAM_PROXY_ADDRESS}" \
216
+ -w "\nHTTP_CODE=%{http_code}" \
217
+ -X POST "https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage" \
218
+ --data-urlencode "chat_id=${TELEGRAM_CHAT_ID}" \
219
+ --data-urlencode "text=${MESSAGE}" \
220
+ --data-urlencode "parse_mode=Markdown" \
221
+ --data-urlencode "disable_web_page_preview=true" 2>&1)
222
+ RC=$?
223
+ echo "curl exit=$RC"
224
+ echo "response: $HTTP_BODY"
225
+ if [ $RC -ne 0 ]; then
226
+ echo "::warning::curl завершился с кодом $RC — $HTTP_BODY"
227
+ elif ! echo "$HTTP_BODY" | grep -q 'HTTP_CODE=200'; then
228
+ echo "::warning::Telegram API вернул не 200 — $HTTP_BODY"
229
+ else
230
+ echo "Уведомление отправлено"
231
+ fi
232
+ env:
233
+ TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }}
234
+ TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
235
+ TELEGRAM_PROXY_ADDRESS: ${{ secrets.TELEGRAM_PROXY_ADDRESS }}
236
+ TELEGRAM_PROXY_LOGIN: ${{ secrets.TELEGRAM_PROXY_LOGIN }}
237
+ TELEGRAM_PROXY_PASS: ${{ secrets.TELEGRAM_PROXY_PASS }}
@@ -0,0 +1,47 @@
1
+ name: teardown
2
+ on:
3
+ workflow_dispatch:
4
+
5
+ jobs:
6
+ teardown:
7
+ runs-on: optiweb
8
+ steps:
9
+ - name: Вычисление переменных
10
+ run: |
11
+ RAW="${{ gitea.ref_name }}"
12
+
13
+ if [[ "$RAW" =~ ^v[0-9]+(\.[0-9]+)+$ ]]; then
14
+ RAW="${RAW#v}"
15
+ fi
16
+
17
+ VERSION=$(echo "$RAW" | sed 's/[^a-zA-Z0-9]/-/g')
18
+ {
19
+ echo "VERSION=$VERSION"
20
+ echo "PROJECT=${{ gitea.event.repository.name }}"
21
+ echo "DEST=${DEPLOY_ROOT}/${{ gitea.event.repository.name }}/${VERSION}"
22
+ echo "HOST_DEST=${HOST_DEPLOY_ROOT}/${{ gitea.event.repository.name }}/${VERSION}"
23
+ echo "COMPOSE_DIR=${DEPLOY_ROOT}/${{ gitea.event.repository.name }}/${VERSION}/.docker/${DEPLOY_TYPE}/compose"
24
+ } >> "$GITEA_ENV"
25
+
26
+ - name: Дерегистрация записей версии в consul
27
+ run: |
28
+ SVC_NAME="${VERSION}-${PROJECT}-${PLACEHOLDER}"
29
+ FOUND=0
30
+ curl -sS -H "X-Consul-Token: ${CONSUL_HTTP_TOKEN}" \
31
+ "${CONSUL_HTTP_ADDR}/v1/agent/services" | jq -r --arg name "$SVC_NAME" \
32
+ 'to_entries[] | select(.value.Service == $name) | .key' | \
33
+ while read -r sid; do
34
+ echo "Дерегистрирую: $sid"
35
+ curl -sS -H "X-Consul-Token: ${CONSUL_HTTP_TOKEN}" \
36
+ -X PUT "${CONSUL_HTTP_ADDR}/v1/agent/service/deregister/${sid}"
37
+ done
38
+
39
+ - name: Остановка контейнеров
40
+ run: |
41
+ if [ -d "$COMPOSE_DIR" ]; then
42
+ docker compose -f "$COMPOSE_DIR/docker-compose.yaml" -p "${PROJECT}_${VERSION}" down --remove-orphans
43
+ else
44
+ echo "compose-каталог не найден, останавливаю по метке проекта"
45
+ docker ps -q --filter "label=com.docker.compose.project=${PROJECT}_${VERSION}" \
46
+ | xargs -r docker rm -f
47
+ fi
package/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## v1.10.0
2
+
3
+ - Табы: атрибут data-tabs-native для нативных ссылок
4
+
1
5
  ## v1.9.0
2
6
 
3
7
  - Табы: поддержка тегов кроме `button`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formixru/b2b-ui",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -66,6 +66,9 @@ export const init = async ($b) => {
66
66
  const $el = e.currentTarget;
67
67
  const index = [...$item].indexOf($el);
68
68
 
69
+ // Ссылка с нативным поведением — отдаём клик браузеру
70
+ if ($el.dataset.tabsNative !== undefined) return;
71
+
69
72
  if ($el.tagName === 'A') {
70
73
  e.preventDefault();
71
74
  }