Merge branch 'master' into master

This commit is contained in:
孟古一 2023-06-17 22:13:15 +08:00 committed by GitHub
commit 945dc231ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 143 additions and 61 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
count.db
/.history /.history
# dependencies # dependencies

11
Dockerfile Normal file
View File

@ -0,0 +1,11 @@
FROM node:14
WORKDIR /app
COPY . .
RUN yarn install
EXPOSE 3000
CMD ["yarn", "start"]

View File

@ -1,8 +1,8 @@
# Moe-counter # Moe-Counter
多种风格可选的萌萌计数器 多种风格可选的萌萌计数器
![Moe-counter](https://count.getloli.com/get/@Moe-counter.github) ![Moe-Counter](https://count.getloli.com/get/@Moe-counter.github)
<details> <details>
<summary>More theme</summary> <summary>More theme</summary>
@ -26,32 +26,33 @@
### Install ### Install
#### Run on Repl.it #### Run on Replit
- Open the url [https://repl.it/@journeyad/Moe-counter](https://repl.it/@journeyad/Moe-counter) - Open the url [https://replit.com/@journeyad/Moe-Counter](https://replit.com/@journeyad/Moe-Counter)
- Just hit the **Fork** button - Just hit the **Fork** button
- And hit the **Run** button - And hit the **Run** button
#### Deploying on your own server #### Deploying on your own server
```shell ```shell
$ git clone https://github.com/journey-ad/Moe-counter.git $ git clone https://github.com/journey-ad/Moe-Counter.git
$ cd Moe-counter $ cd Moe-Counter
$ yarn install $ yarn install
$ yarn start $ yarn start
``` ```
### Confignation ### Configuration
`config.yml` `config.yml`
```yaml ```yaml
app: app:
# site: https://count.getloli.com # your website
port: 3000 port: 3000
db: db:
type: mongodb # sqlite or mongodb type: sqlite # sqlite or mongodb
``` ```
If you use mongodb, you need to specify the environment variable `DB_URL` If you use mongodb, you need to specify the environment variable `DB_URL`
@ -61,7 +62,7 @@ If you use mongodb, you need to specify the environment variable `DB_URL`
export DB_URL=mongodb+srv://account:passwd@***.***.***.mongodb.net/db_count export DB_URL=mongodb+srv://account:passwd@***.***.***.mongodb.net/db_count
``` ```
repl.it can use `.env` file, [documentation](https://docs.repl.it/repls/secret-keys) replit can use Secrets, [documentation](https://docs.replit.com/programming-ide/storing-sensitive-information-environment-variables)
``` ```
DB_URL="mongodb+srv://account:passwd@***.***.***.mongodb.net/db_count" DB_URL="mongodb+srv://account:passwd@***.***.***.mongodb.net/db_count"
@ -69,8 +70,8 @@ DB_URL="mongodb+srv://account:passwd@***.***.***.mongodb.net/db_count"
## Credits ## Credits
* [repl.it](https://repl.it/) * [replit](https://replit.com/)
* [A-SOUL](https://www.asoulworld.com/) <sup>(非官方导航站)</sup> * [A-SOUL_Official](https://space.bilibili.com/703007996)
* [moebooru](https://github.com/moebooru/moebooru) * [moebooru](https://github.com/moebooru/moebooru)
* rule34.xxx NSFW * rule34.xxx NSFW
* gelbooru.com NSFW * gelbooru.com NSFW
@ -78,4 +79,4 @@ DB_URL="mongodb+srv://account:passwd@***.***.***.mongodb.net/db_count"
## License ## License
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fjourney-ad%2FMoe-counter.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fjourney-ad%2FMoe-counter?ref=badge_large) [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fjourney-ad%2FMoe-Counter.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fjourney-ad%2FMoe-Counter?ref=badge_large)

View File

@ -1,4 +1,5 @@
app: app:
# site: https://count.getloli.com # your website
port: 3000 port: 3000
db: db:

View File

@ -32,8 +32,24 @@ function setNum(name, num) {
.exec() .exec()
} }
function setNumMulti(counters) {
const bulkOps = counters.map(obj => {
const { name, num } = obj
return {
updateOne: {
filter: { name },
update: { name, num },
upsert: true
}
}
})
return Count.bulkWrite(bulkOps, { ordered : false })
}
module.exports = { module.exports = {
getNum, getNum,
getAll, getAll,
setNum setNum,
setNumMulti
} }

View File

@ -1,11 +1,11 @@
'use strict' 'use strict'
const path = require('path') const path = require('path')
const sqlite3 = require('sqlite3') const Database = require('better-sqlite3')
const db = new sqlite3.Database(path.resolve(__dirname, '../count.db')) const db = new Database(path.resolve(__dirname, '../count.db'))
db.run(`CREATE TABLE IF NOT EXISTS tb_count ( db.exec(`CREATE TABLE IF NOT EXISTS tb_count (
id INTEGER PRIMARY KEY AUTOINCREMENT id INTEGER PRIMARY KEY AUTOINCREMENT
NOT NULL NOT NULL
UNIQUE, UNIQUE,
@ -17,44 +17,53 @@ db.run(`CREATE TABLE IF NOT EXISTS tb_count (
function getNum(name) { function getNum(name) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
db.get('SELECT `name`, `num` from tb_count WHERE `name` = ?', name, (err, row) => { const stmt = db.prepare('SELECT `name`, `num` from tb_count WHERE `name` = ?')
if (err) reject(err) const row = stmt.get(name)
resolve(row || { name, num: 0 })
resolve(row || { name, num: 0 })
})
}) })
} }
function getAll(name) { function getAll(name) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
db.get('SELECT * from tb_count', (err, row) => { const stmt = db.prepare('SELECT * from tb_count')
if (err) reject(err) const rows = stmt.all()
resolve(rows)
resolve(row)
})
}) })
} }
function setNum(name, num) { function setNum(name, num) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
db.run(`INSERT INTO tb_count(\`name\`, \`num\`) db.exec(`INSERT INTO tb_count(\`name\`, \`num\`)
VALUES($name, $num) VALUES($name, $num)
ON CONFLICT(name) DO ON CONFLICT(name) DO
UPDATE SET \`num\` = $num;` UPDATE SET \`num\` = $num;`
, { ,
$name: name, { $name: name, $num: num }
$num: num )
}
, (err, row) => {
if (err) reject(err)
resolve(row) resolve()
}) })
}
function setNumMulti(counters) {
return new Promise((resolve, reject) => {
const stmt = db.prepare(`INSERT INTO tb_count(\`name\`, \`num\`)
VALUES($name, $num)
ON CONFLICT(name) DO
UPDATE SET \`num\` = $num;`)
const setMany = db.transaction((counters) => {
for (const counter of counters) stmt.run(counter)
})
setMany(counters)
resolve()
}) })
} }
module.exports = { module.exports = {
getNum, getNum,
getAll, getAll,
setNum setNum,
} setNumMulti
}

6
docker-compose.yml Normal file
View File

@ -0,0 +1,6 @@
version: '3'
services:
moe-counter:
build: .
ports:
- "3000:3000"

View File

@ -17,7 +17,8 @@ app.use(compression())
app.set('view engine', 'pug') app.set('view engine', 'pug')
app.get('/', (req, res) => { app.get('/', (req, res) => {
res.render('index') const site = config.app.site || `${req.protocol}://${req.get('host')}`
res.render('index', { site })
}); });
// get the image // get the image
@ -45,7 +46,7 @@ app.get('/get/@:name', async (req, res) => {
const renderSvg = themify.getCountImage({ count: data.num, theme, length }) const renderSvg = themify.getCountImage({ count: data.num, theme, length })
res.send(renderSvg) res.send(renderSvg)
console.log(data, `theme: ${theme}`) console.log(data, `theme: ${theme}`, `ref: ${req.get('Referrer') || null}`, `ua: ${req.get('User-Agent') || null}`)
}) })
// JSON record // JSON record
@ -70,16 +71,51 @@ const listener = app.listen(config.app.port || 3000, () => {
console.log('Your app is listening on port ' + listener.address().port) console.log('Your app is listening on port ' + listener.address().port)
}) })
let __cache_counter = {}, shouldPush = false
setInterval(() => {
shouldPush = true
}, 1000 * 60);
async function pushDB() {
if (!shouldPush) return
try {
shouldPush = false
if (Object.keys(__cache_counter).length === 0) return
console.log("pushDB", __cache_counter)
const counters = Object.keys(__cache_counter).map(key => {
return {
name: key,
num: __cache_counter[key]
}
})
await db.setNumMulti(counters)
__cache_counter = {}
} catch (error) {
console.log("pushDB is error: ", error)
}
}
async function getCountByName(name) { async function getCountByName(name) {
const defaultCount = { name, num: 0 } const defaultCount = { name, num: 0 }
if (name === 'demo') return { name, num: '0123456789' } if (name === 'demo') return { name, num: '0123456789' }
try { try {
const counter = await db.getNum(name) || defaultCount if (!(name in __cache_counter)) {
const num = counter.num + 1 const counter = await db.getNum(name) || defaultCount
db.setNum(counter.name, num) __cache_counter[name] = counter.num + 1
return counter } else {
__cache_counter[name]++
}
pushDB()
return { name, num: __cache_counter[name] }
} catch (error) { } catch (error) {
console.log("get count by name is error: ", error) console.log("get count by name is error: ", error)

View File

@ -1,5 +1,5 @@
{ {
"name": "kawaii-counter", "name": "moe-counter",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
@ -11,6 +11,7 @@
"author": "journey-ad", "author": "journey-ad",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"better-sqlite3": "^8.1.0",
"compression": "^1.7.4", "compression": "^1.7.4",
"config-yml": "^0.10.3", "config-yml": "^0.10.3",
"express": "^4.17.1", "express": "^4.17.1",

View File

@ -53,7 +53,7 @@ function getCountImage({ count, theme='moebooru', length=7 }) {
}, '') }, '')
return `<?xml version="1.0" encoding="UTF-8"?> return `<?xml version="1.0" encoding="UTF-8"?>
<svg width="${x}" height="${y}" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <svg width="${x}" height="${y}" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="image-rendering: pixelated;">
<title>Moe Count</title> <title>Moe Count</title>
<g> <g>
${parts} ${parts}
@ -64,4 +64,4 @@ function getCountImage({ count, theme='moebooru', length=7 }) {
module.exports = { module.exports = {
getCountImage getCountImage
} }

View File

@ -25,16 +25,16 @@ html
h3 How to use: h3 How to use:
h5 SVG address h5 SVG address
code https://count.getloli.com/get/@:name code #{site}/get/@:name
h5 Img tag h5 Img tag
code &lt;img src="https://count.getloli.com/get/@:name" alt=":name" /> code &lt;img src="#{site}/get/@:name" alt=":name" />
h5 Markdown h5 Markdown
code ![:name](https://count.getloli.com/get/@:name) code ![:name](#{site}/get/@:name)
h3 eg: h3 eg:
<img src="https://count.getloli.com/get/@index" alt="Moe Count!" /> <img src="#{site}/get/@index" alt="Moe Count!" />
i Data can access by anyone, please i Data can access by anyone, please
| <span style="color: #ff4500;"> DO NOT</span> | <span style="color: #ff4500;"> DO NOT</span>
@ -43,26 +43,26 @@ html
details details
summary(style='display: inline-block;', onclick='_evt_push("click", "normal", "more_theme")') summary(style='display: inline-block;', onclick='_evt_push("click", "normal", "more_theme")')
h3(style='display: inline-block; cursor: pointer;') More theme h3(style='display: inline-block; cursor: pointer;') More theme
p(style='margin: 0;') Just use the query parameters <code>theme</code>, like this: <code>https://count.getloli.com/get/@:name?theme=moebooru</code> p(style='margin: 0;') Just use the query parameters <code>theme</code>, like this: <code>#{site}/get/@:name?theme=moebooru</code>
h5 asoul h5 asoul
img(src='https://count.getloli.com/get/@demo?theme=asoul', alt='A-SOUL') img(src='#{site}/get/@demo?theme=asoul', alt='A-SOUL')
h5 moebooru h5 moebooru
img(src='https://count.getloli.com/get/@demo?theme=moebooru', alt='Moebooru') img(src='#{site}/get/@demo?theme=moebooru', alt='Moebooru')
h5 moebooru-h h5 moebooru-h
img(src='https://count.getloli.com/get/@demo?theme=moebooru-h', alt='Moebooru-Hentai') img(src='#{site}/get/@demo?theme=moebooru-h', alt='Moebooru-Hentai')
h5 rule34 h5 rule34
img(src='https://count.getloli.com/get/@demo?theme=rule34', alt='Rule34') img(src='#{site}/get/@demo?theme=rule34', alt='Rule34')
h5 gelbooru h5 gelbooru
img(src='https://count.getloli.com/get/@demo?theme=gelbooru', alt='Gelbooru') img(src='#{site}/get/@demo?theme=gelbooru', alt='Gelbooru')
h5 gelbooru-h h5 gelbooru-h
img(src='https://count.getloli.com/get/@demo?theme=gelbooru-h', alt='Gelbooru-Hentai') img(src='#{site}/get/@demo?theme=gelbooru-h', alt='Gelbooru-Hentai')
h3 Credits h3 Credits
ul ul
li li
a(href='https://repl.it/', target='_blank', rel='nofollow') repl.it a(href='https://repl.it/', target='_blank', rel='nofollow') repl.it
li li
a(href='https://www.asoulworld.com/', target='_blank', title='A-SOUL导航站(非官方)') A-SOUL a(href='https://space.bilibili.com/703007996', target='_blank', title='A-SOUL_Official') A-SOUL
li li
a(href='https://github.com/moebooru/moebooru', target='_blank', rel='nofollow') moebooru a(href='https://github.com/moebooru/moebooru', target='_blank', rel='nofollow') moebooru
li li
@ -76,7 +76,7 @@ html
h3 Tool h3 Tool
.tool .tool
code https://count.getloli.com/get/@ code #{site}/get/@
input#name(type='text', placeholder=':name', style='display: inline-block; width: 80px; height: 1.4em; line-height: 1.4em; margin: 0 4px; vertical-align: middle;') input#name(type='text', placeholder=':name', style='display: inline-block; width: 80px; height: 1.4em; line-height: 1.4em; margin: 0 4px; vertical-align: middle;')
code ?theme= code ?theme=
select#theme(style='display: inline-block; height: 1.6em; line-height: 1.6em; font-size: 14px; margin: 0 4px; padding: 0 4px; vertical-align: middle;') select#theme(style='display: inline-block; height: 1.6em; line-height: 1.6em; font-size: 14px; margin: 0 4px; padding: 0 4px; vertical-align: middle;')
@ -103,10 +103,10 @@ html
return return
} }
img.src = 'https://count.getloli.com/get/@' + text + '?theme=' + theme img.src = '#{site}/get/@' + text + '?theme=' + theme
}) })
iframe(src="https://chat.getloli.com/room/@Moe-counter?title=%E8%90%8C%E8%90%8C%E8%AE%A1%E6%95%B0%E5%99%A8%E7%9A%84%E7%95%99%E8%A8%80%E6%9D%BF", scrolling="no", frameborder="0", height="70%", width="26%", style="position: fixed;top: 2%;right: 5%;") iframe(src="https://chat.getloli.com/room/@Moe-counter?title=%E8%90%8C%E8%90%8C%E8%AE%A1%E6%95%B0%E5%99%A8%E7%9A%84%E7%95%99%E8%A8%80%E6%9D%BF", scrolling="no", frameborder="0", height="70%", width="26%", style="position: fixed;top: 2%;right: 5%;")
p.copy p.copy
a(href='https://github.com/journey-ad/Moe-counter', target='_blank', onclick='_evt_push("click", "normal", "go_github")') source code a(href='https://github.com/journey-ad/Moe-Counter', target='_blank', onclick='_evt_push("click", "normal", "go_github")') source code