Compare commits
10 Commits
d569a15b54
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3d7a9ef15 | ||
|
|
4a9695ffbf | ||
|
|
61af5a1f05 | ||
|
|
db5db42181 | ||
|
|
9b295864fc | ||
|
|
00eec40fd2 | ||
|
|
30ee8949a3 | ||
|
|
33df40ded8 | ||
|
|
51f193b627 | ||
|
|
88befecac8 |
@@ -30,12 +30,14 @@ jobs:
|
||||
run: |
|
||||
mkdir -p build
|
||||
go env -w GOPROXY=https://go.hub.ipao.vip,direct
|
||||
go build -o build/exporter .
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o build/exporter .
|
||||
|
||||
- name: Build final Docker image
|
||||
run: |
|
||||
echo 'FROM docker.hub.ipao.vip/alpine:3.20' > Dockerfile
|
||||
echo 'COPY config.yml /root/.exporter.yml' >> Dockerfile
|
||||
echo 'COPY build/exporter /usr/local/exporter' >> Dockerfile
|
||||
docker login -u ${{ secrets.DOCKER_AF_USERNAME }} -p ${{ secrets.DOCKER_AF_PASSWORD }} docker.hub.ipao.vip
|
||||
docker login -u ${{ secrets.DOCKER_AF_USERNAME }} -p ${{ secrets.DOCKER_AF_PASSWORD }} docker-af.hub.ipao.vip
|
||||
docker build --push -t docker-af.hub.ipao.vip/rogeecn/tg-exporter:latest .
|
||||
|
||||
# curl -kL https://portainer.ipao.vip/api/stacks/webhooks/178f9e9d-6700-40b3-9f2f-364fd273deab --data {}
|
||||
- name: Notify Portainer to update stack
|
||||
run: |
|
||||
curl -kL https://portainer.ipao.vip/api/stacks/webhooks/178f9e9d-6700-40b3-9f2f-364fd273deab --data {}
|
||||
|
||||
17
Dockerfile
17
Dockerfile
@@ -1,23 +1,10 @@
|
||||
FROM docker.hub.ipao.vip/golang:1.22-alpine as builder
|
||||
|
||||
COPY . /app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN go env -w GOPROXY=https://go.hub.ipao.vip,direct && \
|
||||
go env -w GO111MODULE=on && \
|
||||
cd /app && \
|
||||
go mod tidy
|
||||
|
||||
RUN go build -o /app/exporter .
|
||||
|
||||
|
||||
FROM docker.hub.ipao.vip/alpine:3.20
|
||||
|
||||
COPY --from=builder /app/exporter /usr/local/bin/exporter
|
||||
COPY config.yml /root/.exporter.yml
|
||||
COPY build/exporter /usr/local/bin/exporter
|
||||
|
||||
WORKDIR /root
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/exporter"]
|
||||
|
||||
CMD [ "serve" ]
|
||||
246
frontend/package-lock.json
generated
246
frontend/package-lock.json
generated
@@ -20,6 +20,7 @@
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.4.47",
|
||||
"tailwindcss": "^3.4.12",
|
||||
"unplugin-auto-import": "^0.18.3",
|
||||
"vite": "^5.3.1"
|
||||
}
|
||||
},
|
||||
@@ -36,6 +37,16 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@antfu/utils": {
|
||||
"version": "0.7.10",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/@antfu/utils/-/utils-0.7.10.tgz",
|
||||
"integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.24.8",
|
||||
"resolved": "https://npm.hub.ipao.vip:88/repository/npm/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz",
|
||||
@@ -617,6 +628,29 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/@rollup/pluginutils/-/pluginutils-5.1.2.tgz",
|
||||
"integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
"picomatch": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.22.0",
|
||||
"resolved": "https://npm.hub.ipao.vip:88/repository/npm/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.0.tgz",
|
||||
@@ -994,6 +1028,19 @@
|
||||
"integrity": "sha512-eidH0HInnL39z6wAt6SFIwBrvGOpDWsDxlw3rCgo1B+CQ1781WzQUSU3YjxgdkcJo9Q8S6LmXTkvI+cLHGkQfA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.12.1",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/acorn/-/acorn-8.12.1.tgz",
|
||||
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://npm.hub.ipao.vip:88/repository/npm/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
||||
@@ -1290,6 +1337,13 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/confbox": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/confbox/-/confbox-0.1.7.tgz",
|
||||
"integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://npm.hub.ipao.vip:88/repository/npm/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@@ -1429,6 +1483,19 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
|
||||
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://npm.hub.ipao.vip:88/repository/npm/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||
@@ -1730,6 +1797,13 @@
|
||||
"jiti": "bin/jiti.js"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/js-tokens/-/js-tokens-9.0.0.tgz",
|
||||
"integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lilconfig": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://npm.hub.ipao.vip:88/repository/npm/lilconfig/-/lilconfig-2.1.0.tgz",
|
||||
@@ -1747,6 +1821,23 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/local-pkg": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/local-pkg/-/local-pkg-0.5.0.tgz",
|
||||
"integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mlly": "^1.4.2",
|
||||
"pkg-types": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "10.4.3",
|
||||
"resolved": "https://npm.hub.ipao.vip:88/repository/npm/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||
@@ -1834,6 +1925,19 @@
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/mlly": {
|
||||
"version": "1.7.1",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/mlly/-/mlly-1.7.1.tgz",
|
||||
"integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.11.3",
|
||||
"pathe": "^1.1.2",
|
||||
"pkg-types": "^1.1.1",
|
||||
"ufo": "^1.5.3"
|
||||
}
|
||||
},
|
||||
"node_modules/mz": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://npm.hub.ipao.vip:88/repository/npm/mz/-/mz-2.7.0.tgz",
|
||||
@@ -1952,6 +2056,13 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/pathe": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/pathe/-/pathe-1.1.2.tgz",
|
||||
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://npm.hub.ipao.vip:88/repository/npm/picocolors/-/picocolors-1.1.0.tgz",
|
||||
@@ -2043,6 +2154,18 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/pkg-types": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/pkg-types/-/pkg-types-1.2.0.tgz",
|
||||
"integrity": "sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"confbox": "^0.1.7",
|
||||
"mlly": "^1.7.1",
|
||||
"pathe": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.47",
|
||||
"resolved": "https://npm.hub.ipao.vip:88/repository/npm/postcss/-/postcss-8.4.47.tgz",
|
||||
@@ -2344,6 +2467,13 @@
|
||||
"queue-microtask": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/scule": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/scule/-/scule-1.3.0.tgz",
|
||||
"integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://npm.hub.ipao.vip:88/repository/npm/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
@@ -2493,6 +2623,19 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-literal": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/strip-literal/-/strip-literal-2.1.0.tgz",
|
||||
"integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-tokens": "^9.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
}
|
||||
},
|
||||
"node_modules/sucrase": {
|
||||
"version": "3.35.0",
|
||||
"resolved": "https://npm.hub.ipao.vip:88/repository/npm/sucrase/-/sucrase-3.35.0.tgz",
|
||||
@@ -2619,6 +2762,102 @@
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/ufo": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/ufo/-/ufo-1.5.4.tgz",
|
||||
"integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unimport": {
|
||||
"version": "3.12.0",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/unimport/-/unimport-3.12.0.tgz",
|
||||
"integrity": "sha512-5y8dSvNvyevsnw4TBQkIQR1Rjdbb+XjVSwQwxltpnVZrStBvvPkMPcZrh1kg5kY77kpx6+D4Ztd3W6FOBH/y2Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/pluginutils": "^5.1.0",
|
||||
"acorn": "^8.12.1",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"estree-walker": "^3.0.3",
|
||||
"fast-glob": "^3.3.2",
|
||||
"local-pkg": "^0.5.0",
|
||||
"magic-string": "^0.30.11",
|
||||
"mlly": "^1.7.1",
|
||||
"pathe": "^1.1.2",
|
||||
"pkg-types": "^1.2.0",
|
||||
"scule": "^1.3.0",
|
||||
"strip-literal": "^2.1.0",
|
||||
"unplugin": "^1.14.1"
|
||||
}
|
||||
},
|
||||
"node_modules/unimport/node_modules/estree-walker": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/estree-walker/-/estree-walker-3.0.3.tgz",
|
||||
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/unplugin/-/unplugin-1.14.1.tgz",
|
||||
"integrity": "sha512-lBlHbfSFPToDYp9pjXlUEFVxYLaue9f9T1HC+4OHlmj+HnMDdz9oZY+erXfoCe/5V/7gKUSY2jpXPb9S7f0f/w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn": "^8.12.1",
|
||||
"webpack-virtual-modules": "^0.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack-sources": "^3"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"webpack-sources": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/unplugin-auto-import": {
|
||||
"version": "0.18.3",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/unplugin-auto-import/-/unplugin-auto-import-0.18.3.tgz",
|
||||
"integrity": "sha512-q3FUtGQjYA2e+kb1WumyiQMjHM27MrTQ05QfVwtLRVhyYe+KF6TblBYaEX9L6Z0EibsqaXAiW+RFfkcQpfaXzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@antfu/utils": "^0.7.10",
|
||||
"@rollup/pluginutils": "^5.1.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"local-pkg": "^0.5.0",
|
||||
"magic-string": "^0.30.11",
|
||||
"minimatch": "^9.0.5",
|
||||
"unimport": "^3.12.0",
|
||||
"unplugin": "^1.14.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antfu"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nuxt/kit": "^3.2.2",
|
||||
"@vueuse/core": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@nuxt/kit": {
|
||||
"optional": true
|
||||
},
|
||||
"@vueuse/core": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://npm.hub.ipao.vip:88/repository/npm/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
|
||||
@@ -2753,6 +2992,13 @@
|
||||
"vue": "^3.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-virtual-modules": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://npm.hub.ipao.vip/repository/npm/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
|
||||
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://npm.hub.ipao.vip:88/repository/npm/which/-/which-2.0.2.tgz",
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"autoprefixer": "^10.4.20",
|
||||
"postcss": "^8.4.47",
|
||||
"tailwindcss": "^3.4.12",
|
||||
"unplugin-auto-import": "^0.18.3",
|
||||
"vite": "^5.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<div class="mb-4 md:mb-8 border rounded overflow-hidden" :id="`message-${item.ID}`">
|
||||
<div class="bg-slate-100 p-2 md:p-4">
|
||||
<div v-if="item.Content.length > 0" class="text-wrap font-sans" v-html="processedContent"></div>
|
||||
<div class="mb-2 font-semibold text-right text-gray-400">#{{ item.ID }}</div>
|
||||
|
||||
<MediaContent :content="item.Content" />
|
||||
|
||||
<div v-if="item.Media.length > 0" class="mt-2 md:mt-4">
|
||||
<div class="medias grid grid-cols-3 gap-2 md:gap-4">
|
||||
@@ -22,29 +24,20 @@
|
||||
|
||||
<script>
|
||||
import { deleteMessage, toggleFavorite } from "@/services/messages";
|
||||
import { computed, defineComponent } from "vue";
|
||||
import { defineComponent } from "vue";
|
||||
import MediaContent from "./MediaContent.vue";
|
||||
import MediaItem from "./MediaItem.vue";
|
||||
|
||||
function nl2br(str, is_xhtml) {
|
||||
var breakTag = is_xhtml || typeof is_xhtml === "undefined" ? "<br />" : "<br>";
|
||||
return (str + "").replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, "$1" + breakTag + "$2");
|
||||
}
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MediaItem,
|
||||
MediaContent,
|
||||
},
|
||||
name: "ListItem",
|
||||
props: {
|
||||
item: { type: Object, required: true },
|
||||
},
|
||||
setup(props) {
|
||||
const processedContent = computed(() => {
|
||||
let content = props.item.Content.trim();
|
||||
return nl2br(content, false);
|
||||
});
|
||||
|
||||
const toggleLike = async () => {
|
||||
const itemId = props.item.ID
|
||||
await toggleFavorite(itemId);
|
||||
|
||||
59
frontend/src/components/MediaContent.vue
Normal file
59
frontend/src/components/MediaContent.vue
Normal file
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div v-if="hasContent" class="text-wrap font-sans" v-html="processedContent"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
},
|
||||
name: "MediaContent",
|
||||
props: {
|
||||
content: { required: true },
|
||||
},
|
||||
setup(props) {
|
||||
const hasContent = () => {
|
||||
return props.content.trim().length > 0
|
||||
};
|
||||
|
||||
const nl2br = function (content) {
|
||||
var breakTag = "<br>";
|
||||
return (content + "").replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, "$1" + breakTag + "$2");
|
||||
}
|
||||
|
||||
|
||||
const replaceTags = (content) => {
|
||||
content = content.replace(/#(\S+)/g, '<a class="link" href="/search?tag=$1">#$1</a>');
|
||||
return content;
|
||||
}
|
||||
|
||||
const replaceLinks = (content) => {
|
||||
content = content.replace(/(https?:\/\/\S+)/g, '<a class="link" href="$1" target="_blank">$1</a>');
|
||||
return content;
|
||||
}
|
||||
|
||||
const processedContent = computed(() => {
|
||||
let content = props.content.trim();
|
||||
|
||||
content = replaceTags(content)
|
||||
content = replaceLinks(content)
|
||||
content = nl2br(content);
|
||||
|
||||
return content;
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
hasContent,
|
||||
processedContent,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.link {
|
||||
@apply hover:text-sky-300 text-sky-500;
|
||||
}
|
||||
</style>
|
||||
78
frontend/src/components/MessageList.vue
Normal file
78
frontend/src/components/MessageList.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<h1 class="mb-4 font-semibold text-xl">
|
||||
{{ title }}
|
||||
</h1>
|
||||
|
||||
|
||||
<div v-if="messages.length == 0">Empty...</div>
|
||||
<template v-else>
|
||||
<ListItem v-for="message in messages" :key="message.ID" :item="message" />
|
||||
|
||||
<div class="flex items-center justify-center mb-10">
|
||||
<button class="py-4 hover:bg-slate-100 rounded border text-xl font-bold w-full"
|
||||
@click="loadMore()">LoadMore</button>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, onMounted, ref } from 'vue';
|
||||
import ListItem from './ListItem.vue';
|
||||
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
ListItem,
|
||||
},
|
||||
name: 'MessageList',
|
||||
props: {
|
||||
getTitle: { type: String, required: true },
|
||||
routeName: { type: String, required: true },
|
||||
getMessages: { type: Function, required: true },
|
||||
},
|
||||
setup(props) {
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
const messages = ref([]);
|
||||
|
||||
const title = ref('');
|
||||
|
||||
onMounted(async () => {
|
||||
title.value = await props.getTitle()
|
||||
});
|
||||
|
||||
const loadData = async () => {
|
||||
messages.value = await props.getMessages(route.params?.channel, { offset: route.params.offset });
|
||||
}
|
||||
|
||||
const loadMore = async () => {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
|
||||
const offset = messages.value[messages.value.length - 1].ID
|
||||
|
||||
messages.value = [];
|
||||
|
||||
router.push({
|
||||
name: props.routeName,
|
||||
params: {
|
||||
channel: route.params.channel,
|
||||
offset: offset,
|
||||
},
|
||||
});
|
||||
|
||||
nextTick(loadData);
|
||||
}
|
||||
|
||||
|
||||
watch(route, loadData);
|
||||
onMounted(loadData);
|
||||
|
||||
return {
|
||||
messages,
|
||||
loadMore,
|
||||
title,
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -1,61 +1,17 @@
|
||||
<template>
|
||||
<h1 class="mb-4 font-semibold text-xl">{{ channel.Title }}</h1>
|
||||
<div v-if="messages.length == 0">Empty...</div>
|
||||
<template v-else>
|
||||
<ListItem v-for="message in messages" :key="message.ID" :item="message" />
|
||||
|
||||
<div class="flex items-center justify-center mb-10">
|
||||
<button class="py-2 hover:bg-slate-100 rounded border text-xl font-bold w-full"
|
||||
@click="loadMore">LoadMore</button>
|
||||
</div>
|
||||
</template>
|
||||
<MessageList :getTitle="getTitle" routeName="channel-messages" :getMessages="getChannelMessages" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import ListItem from "@/components/ListItem.vue";
|
||||
import MessageList from "@/components/MessageList.vue";
|
||||
import { getChannel } from "@/services/channels";
|
||||
import { getChannelMessages } from "@/services/messages";
|
||||
import { nextTick, onMounted, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
|
||||
const channel = ref({});
|
||||
const messages = ref([]);
|
||||
|
||||
const loadMore = async () => {
|
||||
// router goto next page
|
||||
// offset is last message ID
|
||||
|
||||
// page scroll to top with animation
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
const offset = messages.value[messages.value.length - 1].ID
|
||||
|
||||
messages.value = [];
|
||||
router.push({
|
||||
name: "channel-messages",
|
||||
params: {
|
||||
channel: route.params.channel,
|
||||
offset: offset,
|
||||
},
|
||||
});
|
||||
|
||||
// nextTick 干什么用的?
|
||||
nextTick(async () => {
|
||||
messages.value = await getChannelMessages(route.params.channel, { offset: offset });
|
||||
console.log("messages", messages.value);
|
||||
});
|
||||
const getTitle = async () => {
|
||||
const channel = await getChannel(route.params.channel);
|
||||
return channel.Title;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// get channel info
|
||||
channel.value = await getChannel(route.params.channel);
|
||||
console.log("channel", channel.value);
|
||||
|
||||
// get channel messages
|
||||
messages.value = await getChannelMessages(route.params.channel, { offset: route.params.offset });
|
||||
console.log("messages", messages.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,53 +1,10 @@
|
||||
<template>
|
||||
<h1 class="mb-4 font-semibold text-xl">Favorites</h1>
|
||||
|
||||
|
||||
<div v-if="messages.length == 0">Empty...</div>
|
||||
<template v-else>
|
||||
<ListItem v-for="message in messages" :key="message.ID" :item="message" />
|
||||
|
||||
<div class="flex items-center justify-center mb-10">
|
||||
<button class="py-4 hover:bg-slate-100 rounded border text-xl font-bold w-full"
|
||||
@click="loadMore">LoadMore</button>
|
||||
</div>
|
||||
</template>
|
||||
<MessageList :getTitle="async () => 'Favorite'" routeName="favorite-messages" :getMessages="getMessages" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import ListItem from "@/components/ListItem.vue";
|
||||
import MessageList from "@/components/MessageList.vue";
|
||||
import { getFavoriteMessages } from "@/services/messages";
|
||||
import { nextTick, onMounted, ref } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const messages = ref([]);
|
||||
|
||||
const loadMore = async () => {
|
||||
// router goto next page
|
||||
// offset is last message ID
|
||||
// page scroll to top with animation
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
|
||||
const offset = messages.value[messages.value.length - 1].ID
|
||||
|
||||
messages.value = [];
|
||||
|
||||
router.push({
|
||||
name: "favorite-messages",
|
||||
params: {
|
||||
channel: route.params.channel,
|
||||
offset: offset,
|
||||
},
|
||||
});
|
||||
|
||||
nextTick(async () => {
|
||||
messages.value = await getFavoriteMessages({ offset: offset });
|
||||
console.log("messages", messages.value);
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
messages.value = await getFavoriteMessages({ offset: route.params.offset });
|
||||
});
|
||||
const getMessages = async (ch, params) => await getFavoriteMessages(params)
|
||||
</script>
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
import { fileURLToPath, URL } from 'node:url';
|
||||
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import AutoImport from 'unplugin-auto-import/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
imports: [
|
||||
'vue',
|
||||
'vue-router',
|
||||
'pinia'
|
||||
],
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
|
||||
Reference in New Issue
Block a user