add mysql db #54
1
.gitignore
vendored
1
.gitignore
vendored
@ -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
11
Dockerfile
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
FROM node:14
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN yarn install
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
CMD ["yarn", "start"]
|
25
Readme.md
25
Readme.md
@ -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)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
app:
|
app:
|
||||||
|
# site: https://count.getloli.com # your website
|
||||||
port: 3000
|
port: 3000
|
||||||
|
|
||||||
db:
|
db:
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
51
db/sqlite.js
51
db/sqlite.js
@ -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
6
docker-compose.yml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
moe-counter:
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
46
index.js
46
index.js
@ -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 {
|
||||||
|
if (!(name in __cache_counter)) {
|
||||||
const counter = await db.getNum(name) || defaultCount
|
const counter = await db.getNum(name) || defaultCount
|
||||||
const num = counter.num + 1
|
__cache_counter[name] = counter.num + 1
|
||||||
db.setNum(counter.name, num)
|
} else {
|
||||||
return counter
|
__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)
|
||||||
|
@ -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",
|
||||||
|
@ -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}
|
||||||
|
@ -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 <img src="https://count.getloli.com/get/@:name" alt=":name" />
|
code <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
|
||||||
|
Loading…
Reference in New Issue
Block a user