mirror of
https://github.com/muety/wakapi.git
synced 2023-08-10 21:12:56 +03:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
aef0c929df | |||
9cb9747e2e | |||
68a17950ef | |||
a2368ff76a | |||
4838300086 | |||
a60c725d38 | |||
8ceef42ad4 | |||
8bed266110 | |||
a7afd73e62 | |||
1dc5be4784 | |||
b6812ddc3a |
42
.github/workflows/docker.yml
vendored
Normal file
42
.github/workflows/docker.yml
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
name: Publish Docker Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*.*.*'
|
||||||
|
- '!*.*.*-*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker-publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
# https://stackoverflow.com/questions/58177786
|
||||||
|
- name: Get version
|
||||||
|
run: echo "GIT_TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
|
||||||
|
- name: Cache Docker layers
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: /tmp/.buildx-cache
|
||||||
|
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-buildx-
|
||||||
|
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push to Docker Hub
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
n1try/wakapi:${{ env.GIT_TAG }}
|
||||||
|
n1try/wakapi:latest
|
||||||
|
cache-from: type=local,src=/tmp/.buildx-cache
|
||||||
|
cache-to: type=local,dest=/tmp/.buildx-cache
|
3
.github/workflows/linux-build-on-release.yml
vendored
3
.github/workflows/linux-build-on-release.yml
vendored
@ -2,10 +2,11 @@ name: Build Wakapi on Linux
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
branches:
|
||||||
pull_request:
|
pull_request:
|
||||||
release:
|
release:
|
||||||
types:
|
types:
|
||||||
- created
|
- published
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-release:
|
build-and-release:
|
||||||
|
3
.github/workflows/win-build-on-release.yml
vendored
3
.github/workflows/win-build-on-release.yml
vendored
@ -2,10 +2,11 @@ name: Build Wakapi on Windows
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
branches:
|
||||||
pull_request:
|
pull_request:
|
||||||
release:
|
release:
|
||||||
types:
|
types:
|
||||||
- created
|
- published
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-release:
|
build-and-release:
|
||||||
|
@ -312,6 +312,10 @@ func Load() *Config {
|
|||||||
logbuch.Fatal("either of listen_ipv4 or listen_ipv6 must be set")
|
logbuch.Fatal("either of listen_ipv4 or listen_ipv6 must be set")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.Db.MaxConn <= 0 {
|
||||||
|
logbuch.Fatal("you must allow at least one database connection")
|
||||||
|
}
|
||||||
|
|
||||||
Set(config)
|
Set(config)
|
||||||
return Get()
|
return Get()
|
||||||
}
|
}
|
||||||
|
@ -22,27 +22,6 @@ github.com/muety/wakapi/models/filters.go:39.8,39.27 1 1
|
|||||||
github.com/muety/wakapi/models/filters.go:39.27,41.3 1 0
|
github.com/muety/wakapi/models/filters.go:39.27,41.3 1 0
|
||||||
github.com/muety/wakapi/models/filters.go:41.8,41.28 1 1
|
github.com/muety/wakapi/models/filters.go:41.8,41.28 1 1
|
||||||
github.com/muety/wakapi/models/filters.go:41.28,43.3 1 0
|
github.com/muety/wakapi/models/filters.go:41.28,43.3 1 0
|
||||||
github.com/muety/wakapi/models/language_mapping.go:11.42,13.2 1 0
|
|
||||||
github.com/muety/wakapi/models/language_mapping.go:15.51,17.2 1 0
|
|
||||||
github.com/muety/wakapi/models/language_mapping.go:19.52,21.2 1 0
|
|
||||||
github.com/muety/wakapi/models/shared.go:34.52,37.16 3 0
|
|
||||||
github.com/muety/wakapi/models/shared.go:40.2,42.12 3 0
|
|
||||||
github.com/muety/wakapi/models/shared.go:37.16,39.3 1 0
|
|
||||||
github.com/muety/wakapi/models/shared.go:46.52,52.22 2 0
|
|
||||||
github.com/muety/wakapi/models/shared.go:68.2,71.12 3 0
|
|
||||||
github.com/muety/wakapi/models/shared.go:53.14,55.17 2 0
|
|
||||||
github.com/muety/wakapi/models/shared.go:58.13,60.8 2 0
|
|
||||||
github.com/muety/wakapi/models/shared.go:61.17,63.8 2 0
|
|
||||||
github.com/muety/wakapi/models/shared.go:64.10,65.64 1 0
|
|
||||||
github.com/muety/wakapi/models/shared.go:55.17,57.4 1 0
|
|
||||||
github.com/muety/wakapi/models/shared.go:74.51,77.2 2 0
|
|
||||||
github.com/muety/wakapi/models/shared.go:79.37,82.2 2 0
|
|
||||||
github.com/muety/wakapi/models/shared.go:84.35,86.2 1 0
|
|
||||||
github.com/muety/wakapi/models/shared.go:88.34,90.2 1 0
|
|
||||||
github.com/muety/wakapi/models/user.go:35.43,38.2 1 0
|
|
||||||
github.com/muety/wakapi/models/user.go:40.33,44.2 1 0
|
|
||||||
github.com/muety/wakapi/models/user.go:46.45,48.2 1 0
|
|
||||||
github.com/muety/wakapi/models/user.go:50.45,52.2 1 0
|
|
||||||
github.com/muety/wakapi/models/heartbeat.go:30.34,32.2 1 1
|
github.com/muety/wakapi/models/heartbeat.go:30.34,32.2 1 1
|
||||||
github.com/muety/wakapi/models/heartbeat.go:34.65,35.28 1 1
|
github.com/muety/wakapi/models/heartbeat.go:34.65,35.28 1 1
|
||||||
github.com/muety/wakapi/models/heartbeat.go:38.2,39.45 2 1
|
github.com/muety/wakapi/models/heartbeat.go:38.2,39.45 2 1
|
||||||
@ -60,9 +39,14 @@ github.com/muety/wakapi/models/heartbeat.go:55.23,56.19 1 1
|
|||||||
github.com/muety/wakapi/models/heartbeat.go:57.17,58.26 1 1
|
github.com/muety/wakapi/models/heartbeat.go:57.17,58.26 1 1
|
||||||
github.com/muety/wakapi/models/heartbeat.go:59.22,60.18 1 1
|
github.com/muety/wakapi/models/heartbeat.go:59.22,60.18 1 1
|
||||||
github.com/muety/wakapi/models/heartbeat.go:63.15,65.3 1 1
|
github.com/muety/wakapi/models/heartbeat.go:63.15,65.3 1 1
|
||||||
github.com/muety/wakapi/models/heartbeat.go:76.41,78.16 2 0
|
github.com/muety/wakapi/models/heartbeat.go:70.37,86.2 1 0
|
||||||
github.com/muety/wakapi/models/heartbeat.go:81.2,82.10 2 0
|
github.com/muety/wakapi/models/heartbeat.go:94.41,96.16 2 0
|
||||||
github.com/muety/wakapi/models/heartbeat.go:78.16,80.3 1 0
|
github.com/muety/wakapi/models/heartbeat.go:99.2,100.10 2 0
|
||||||
|
github.com/muety/wakapi/models/heartbeat.go:96.16,98.3 1 0
|
||||||
|
github.com/muety/wakapi/models/user.go:35.43,38.2 1 0
|
||||||
|
github.com/muety/wakapi/models/user.go:40.33,44.2 1 0
|
||||||
|
github.com/muety/wakapi/models/user.go:46.45,48.2 1 0
|
||||||
|
github.com/muety/wakapi/models/user.go:50.45,52.2 1 0
|
||||||
github.com/muety/wakapi/models/heartbeats.go:7.31,9.2 1 0
|
github.com/muety/wakapi/models/heartbeats.go:7.31,9.2 1 0
|
||||||
github.com/muety/wakapi/models/heartbeats.go:11.41,13.2 1 0
|
github.com/muety/wakapi/models/heartbeats.go:11.41,13.2 1 0
|
||||||
github.com/muety/wakapi/models/heartbeats.go:15.36,17.2 1 0
|
github.com/muety/wakapi/models/heartbeats.go:15.36,17.2 1 0
|
||||||
@ -73,7 +57,25 @@ github.com/muety/wakapi/models/heartbeats.go:26.18,28.3 1 0
|
|||||||
github.com/muety/wakapi/models/heartbeats.go:32.40,34.18 1 0
|
github.com/muety/wakapi/models/heartbeats.go:32.40,34.18 1 0
|
||||||
github.com/muety/wakapi/models/heartbeats.go:37.2,37.24 1 0
|
github.com/muety/wakapi/models/heartbeats.go:37.2,37.24 1 0
|
||||||
github.com/muety/wakapi/models/heartbeats.go:34.18,36.3 1 0
|
github.com/muety/wakapi/models/heartbeats.go:34.18,36.3 1 0
|
||||||
|
github.com/muety/wakapi/models/language_mapping.go:11.42,13.2 1 0
|
||||||
|
github.com/muety/wakapi/models/language_mapping.go:15.51,17.2 1 0
|
||||||
|
github.com/muety/wakapi/models/language_mapping.go:19.52,21.2 1 0
|
||||||
github.com/muety/wakapi/models/models.go:3.14,5.2 0 1
|
github.com/muety/wakapi/models/models.go:3.14,5.2 0 1
|
||||||
|
github.com/muety/wakapi/models/shared.go:34.52,37.16 3 0
|
||||||
|
github.com/muety/wakapi/models/shared.go:40.2,42.12 3 0
|
||||||
|
github.com/muety/wakapi/models/shared.go:37.16,39.3 1 0
|
||||||
|
github.com/muety/wakapi/models/shared.go:46.52,52.22 2 0
|
||||||
|
github.com/muety/wakapi/models/shared.go:68.2,71.12 3 0
|
||||||
|
github.com/muety/wakapi/models/shared.go:53.14,55.17 2 0
|
||||||
|
github.com/muety/wakapi/models/shared.go:58.13,60.8 2 0
|
||||||
|
github.com/muety/wakapi/models/shared.go:61.17,63.8 2 0
|
||||||
|
github.com/muety/wakapi/models/shared.go:64.10,65.64 1 0
|
||||||
|
github.com/muety/wakapi/models/shared.go:55.17,57.4 1 0
|
||||||
|
github.com/muety/wakapi/models/shared.go:74.45,76.2 1 0
|
||||||
|
github.com/muety/wakapi/models/shared.go:78.51,81.2 2 0
|
||||||
|
github.com/muety/wakapi/models/shared.go:83.37,86.2 2 0
|
||||||
|
github.com/muety/wakapi/models/shared.go:88.35,90.2 1 0
|
||||||
|
github.com/muety/wakapi/models/shared.go:92.34,94.2 1 0
|
||||||
github.com/muety/wakapi/models/summary.go:41.27,45.2 1 0
|
github.com/muety/wakapi/models/summary.go:41.27,45.2 1 0
|
||||||
github.com/muety/wakapi/models/summary.go:97.29,99.2 1 1
|
github.com/muety/wakapi/models/summary.go:97.29,99.2 1 1
|
||||||
github.com/muety/wakapi/models/summary.go:101.37,108.2 6 1
|
github.com/muety/wakapi/models/summary.go:101.37,108.2 6 1
|
||||||
@ -124,90 +126,51 @@ github.com/muety/wakapi/models/summary.go:236.11,244.6 1 1
|
|||||||
github.com/muety/wakapi/models/summary.go:261.33,263.2 1 1
|
github.com/muety/wakapi/models/summary.go:261.33,263.2 1 1
|
||||||
github.com/muety/wakapi/models/summary.go:265.43,267.2 1 1
|
github.com/muety/wakapi/models/summary.go:265.43,267.2 1 1
|
||||||
github.com/muety/wakapi/models/summary.go:269.38,271.2 1 1
|
github.com/muety/wakapi/models/summary.go:269.38,271.2 1 1
|
||||||
github.com/muety/wakapi/utils/strings.go:8.34,10.2 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:20.116,26.2 1 1
|
||||||
github.com/muety/wakapi/utils/strings.go:12.77,13.29 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:28.71,29.71 1 0
|
||||||
github.com/muety/wakapi/utils/strings.go:18.2,18.19 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:29.71,31.3 1 0
|
||||||
github.com/muety/wakapi/utils/strings.go:13.29,14.18 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:34.107,35.37 1 0
|
||||||
github.com/muety/wakapi/utils/strings.go:14.18,16.4 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:42.2,45.16 3 0
|
||||||
github.com/muety/wakapi/utils/summary.go:10.71,13.18 2 0
|
github.com/muety/wakapi/middlewares/authenticate.go:49.2,49.16 1 0
|
||||||
github.com/muety/wakapi/utils/summary.go:49.2,49.22 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:59.2,60.29 2 0
|
||||||
github.com/muety/wakapi/utils/summary.go:14.58,15.24 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:35.37,36.58 1 0
|
||||||
github.com/muety/wakapi/utils/summary.go:16.66,18.22 2 0
|
github.com/muety/wakapi/middlewares/authenticate.go:36.58,39.4 2 0
|
||||||
github.com/muety/wakapi/utils/summary.go:19.64,20.23 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:45.16,47.3 1 0
|
||||||
github.com/muety/wakapi/utils/summary.go:21.39,23.21 2 0
|
github.com/muety/wakapi/middlewares/authenticate.go:49.16,50.44 1 0
|
||||||
github.com/muety/wakapi/utils/summary.go:24.66,25.24 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:56.3,56.9 1 0
|
||||||
github.com/muety/wakapi/utils/summary.go:26.40,28.22 2 0
|
github.com/muety/wakapi/middlewares/authenticate.go:50.44,52.4 1 0
|
||||||
github.com/muety/wakapi/utils/summary.go:29.31,30.23 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:52.9,55.4 2 0
|
||||||
github.com/muety/wakapi/utils/summary.go:31.66,32.42 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:63.92,65.16 2 1
|
||||||
github.com/muety/wakapi/utils/summary.go:33.49,35.40 2 0
|
github.com/muety/wakapi/middlewares/authenticate.go:69.2,72.16 4 1
|
||||||
github.com/muety/wakapi/utils/summary.go:36.41,37.43 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:75.2,75.18 1 1
|
||||||
github.com/muety/wakapi/utils/summary.go:38.68,40.42 2 0
|
github.com/muety/wakapi/middlewares/authenticate.go:65.16,67.3 1 1
|
||||||
github.com/muety/wakapi/utils/summary.go:41.35,42.43 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:72.16,74.3 1 0
|
||||||
github.com/muety/wakapi/utils/summary.go:43.26,44.21 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:78.92,80.16 2 0
|
||||||
github.com/muety/wakapi/utils/summary.go:45.10,46.39 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:84.2,85.16 2 0
|
||||||
github.com/muety/wakapi/utils/summary.go:52.73,59.56 5 0
|
github.com/muety/wakapi/middlewares/authenticate.go:92.2,92.18 1 0
|
||||||
github.com/muety/wakapi/utils/summary.go:73.2,80.8 2 0
|
github.com/muety/wakapi/middlewares/authenticate.go:80.16,82.3 1 0
|
||||||
github.com/muety/wakapi/utils/summary.go:59.56,61.3 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:85.16,87.3 1 0
|
||||||
github.com/muety/wakapi/utils/summary.go:61.8,63.17 2 0
|
github.com/muety/wakapi/middlewares/logging.go:17.79,18.43 1 0
|
||||||
github.com/muety/wakapi/utils/summary.go:67.3,68.17 2 0
|
github.com/muety/wakapi/middlewares/logging.go:18.43,23.3 1 0
|
||||||
github.com/muety/wakapi/utils/summary.go:63.17,65.4 1 0
|
github.com/muety/wakapi/middlewares/logging.go:26.80,44.2 6 0
|
||||||
github.com/muety/wakapi/utils/summary.go:68.17,70.4 1 0
|
github.com/muety/wakapi/middlewares/logging.go:46.41,48.14 2 0
|
||||||
github.com/muety/wakapi/utils/template.go:8.41,10.16 2 0
|
github.com/muety/wakapi/middlewares/logging.go:51.2,51.14 1 0
|
||||||
github.com/muety/wakapi/utils/template.go:13.2,13.23 1 0
|
github.com/muety/wakapi/middlewares/logging.go:54.2,54.11 1 0
|
||||||
github.com/muety/wakapi/utils/template.go:10.16,12.3 1 0
|
github.com/muety/wakapi/middlewares/logging.go:48.14,50.3 1 0
|
||||||
github.com/muety/wakapi/utils/template.go:16.37,17.30 1 0
|
github.com/muety/wakapi/middlewares/logging.go:51.14,53.3 1 0
|
||||||
github.com/muety/wakapi/utils/template.go:20.2,20.10 1 0
|
github.com/muety/wakapi/middlewares/logging.go:85.52,87.2 1 0
|
||||||
github.com/muety/wakapi/utils/template.go:17.30,19.3 1 0
|
github.com/muety/wakapi/middlewares/logging.go:99.45,100.20 1 0
|
||||||
github.com/muety/wakapi/utils/auth.go:16.79,18.54 2 0
|
github.com/muety/wakapi/middlewares/logging.go:100.20,104.3 3 0
|
||||||
github.com/muety/wakapi/utils/auth.go:22.2,24.16 3 0
|
github.com/muety/wakapi/middlewares/logging.go:106.54,109.18 3 0
|
||||||
github.com/muety/wakapi/utils/auth.go:28.2,30.45 3 0
|
github.com/muety/wakapi/middlewares/logging.go:116.2,117.15 2 0
|
||||||
github.com/muety/wakapi/utils/auth.go:33.2,34.32 2 0
|
github.com/muety/wakapi/middlewares/logging.go:109.18,112.17 2 0
|
||||||
github.com/muety/wakapi/utils/auth.go:18.54,20.3 1 0
|
github.com/muety/wakapi/middlewares/logging.go:112.17,114.4 1 0
|
||||||
github.com/muety/wakapi/utils/auth.go:24.16,26.3 1 0
|
github.com/muety/wakapi/middlewares/logging.go:119.42,120.20 1 0
|
||||||
github.com/muety/wakapi/utils/auth.go:30.45,32.3 1 0
|
github.com/muety/wakapi/middlewares/logging.go:120.20,122.3 1 0
|
||||||
github.com/muety/wakapi/utils/auth.go:37.65,39.54 2 0
|
github.com/muety/wakapi/middlewares/logging.go:124.36,126.2 1 0
|
||||||
github.com/muety/wakapi/utils/auth.go:43.2,44.30 2 0
|
github.com/muety/wakapi/middlewares/logging.go:127.42,129.2 1 0
|
||||||
github.com/muety/wakapi/utils/auth.go:39.54,41.3 1 0
|
github.com/muety/wakapi/middlewares/logging.go:130.40,132.2 1 0
|
||||||
github.com/muety/wakapi/utils/auth.go:47.94,49.16 2 0
|
github.com/muety/wakapi/middlewares/logging.go:133.52,135.2 1 0
|
||||||
github.com/muety/wakapi/utils/auth.go:53.2,53.107 1 0
|
|
||||||
github.com/muety/wakapi/utils/auth.go:57.2,57.22 1 0
|
|
||||||
github.com/muety/wakapi/utils/auth.go:49.16,51.3 1 0
|
|
||||||
github.com/muety/wakapi/utils/auth.go:53.107,55.3 1 0
|
|
||||||
github.com/muety/wakapi/utils/auth.go:60.56,64.2 3 0
|
|
||||||
github.com/muety/wakapi/utils/auth.go:66.55,69.16 3 0
|
|
||||||
github.com/muety/wakapi/utils/auth.go:72.2,72.16 1 0
|
|
||||||
github.com/muety/wakapi/utils/auth.go:69.16,71.3 1 0
|
|
||||||
github.com/muety/wakapi/utils/color.go:8.90,10.32 2 0
|
|
||||||
github.com/muety/wakapi/utils/color.go:15.2,15.15 1 0
|
|
||||||
github.com/muety/wakapi/utils/color.go:10.32,11.50 1 0
|
|
||||||
github.com/muety/wakapi/utils/color.go:11.50,13.4 1 0
|
|
||||||
github.com/muety/wakapi/utils/common.go:9.48,11.2 1 0
|
|
||||||
github.com/muety/wakapi/utils/common.go:13.40,15.2 1 0
|
|
||||||
github.com/muety/wakapi/utils/common.go:17.45,19.2 1 0
|
|
||||||
github.com/muety/wakapi/utils/common.go:21.24,23.2 1 0
|
|
||||||
github.com/muety/wakapi/utils/common.go:25.56,28.45 3 1
|
|
||||||
github.com/muety/wakapi/utils/common.go:31.2,31.40 1 1
|
|
||||||
github.com/muety/wakapi/utils/common.go:28.45,30.3 1 1
|
|
||||||
github.com/muety/wakapi/utils/date.go:8.31,10.2 1 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:12.43,14.2 1 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:16.30,20.2 3 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:22.31,25.2 2 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:27.30,30.2 2 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:32.67,35.33 2 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:44.2,44.18 1 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:35.33,37.19 2 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:40.3,41.10 2 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:37.19,39.4 1 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:47.50,53.2 5 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:56.79,59.36 3 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:63.2,63.21 1 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:67.2,67.21 1 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:71.2,71.13 1 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:59.36,62.3 2 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:63.21,66.3 2 0
|
|
||||||
github.com/muety/wakapi/utils/date.go:67.21,70.3 2 0
|
|
||||||
github.com/muety/wakapi/utils/http.go:9.73,12.58 3 0
|
|
||||||
github.com/muety/wakapi/utils/http.go:12.58,14.3 1 0
|
|
||||||
github.com/muety/wakapi/config/utils.go:5.78,7.22 2 0
|
github.com/muety/wakapi/config/utils.go:5.78,7.22 2 0
|
||||||
github.com/muety/wakapi/config/utils.go:13.2,13.11 1 0
|
github.com/muety/wakapi/config/utils.go:13.2,13.11 1 0
|
||||||
github.com/muety/wakapi/config/utils.go:7.22,8.18 1 0
|
github.com/muety/wakapi/config/utils.go:7.22,8.18 1 0
|
||||||
@ -264,52 +227,33 @@ github.com/muety/wakapi/config/config.go:284.21,289.96 3 0
|
|||||||
github.com/muety/wakapi/config/config.go:293.2,301.52 5 0
|
github.com/muety/wakapi/config/config.go:293.2,301.52 5 0
|
||||||
github.com/muety/wakapi/config/config.go:305.2,305.47 1 0
|
github.com/muety/wakapi/config/config.go:305.2,305.47 1 0
|
||||||
github.com/muety/wakapi/config/config.go:311.2,311.70 1 0
|
github.com/muety/wakapi/config/config.go:311.2,311.70 1 0
|
||||||
github.com/muety/wakapi/config/config.go:315.2,316.14 2 0
|
github.com/muety/wakapi/config/config.go:315.2,315.28 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:319.2,320.14 2 0
|
||||||
github.com/muety/wakapi/config/config.go:289.96,291.3 1 0
|
github.com/muety/wakapi/config/config.go:289.96,291.3 1 0
|
||||||
github.com/muety/wakapi/config/config.go:301.52,303.3 1 0
|
github.com/muety/wakapi/config/config.go:301.52,303.3 1 0
|
||||||
github.com/muety/wakapi/config/config.go:305.47,306.14 1 0
|
github.com/muety/wakapi/config/config.go:305.47,306.14 1 0
|
||||||
github.com/muety/wakapi/config/config.go:306.14,308.4 1 0
|
github.com/muety/wakapi/config/config.go:306.14,308.4 1 0
|
||||||
github.com/muety/wakapi/config/config.go:311.70,313.3 1 0
|
github.com/muety/wakapi/config/config.go:311.70,313.3 1 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:20.116,26.2 1 1
|
github.com/muety/wakapi/config/config.go:315.28,317.3 1 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:28.71,29.71 1 0
|
github.com/muety/wakapi/services/language_mapping.go:18.118,24.2 1 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:29.71,31.3 1 0
|
github.com/muety/wakapi/services/language_mapping.go:26.86,28.2 1 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:34.107,35.37 1 0
|
github.com/muety/wakapi/services/language_mapping.go:30.96,31.53 1 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:42.2,45.16 3 0
|
github.com/muety/wakapi/services/language_mapping.go:35.2,36.16 2 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:49.2,49.16 1 0
|
github.com/muety/wakapi/services/language_mapping.go:39.2,40.22 2 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:59.2,60.29 2 0
|
github.com/muety/wakapi/services/language_mapping.go:31.53,33.3 1 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:35.37,36.58 1 0
|
github.com/muety/wakapi/services/language_mapping.go:36.16,38.3 1 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:36.58,39.4 2 0
|
github.com/muety/wakapi/services/language_mapping.go:43.92,46.16 3 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:45.16,47.3 1 0
|
github.com/muety/wakapi/services/language_mapping.go:50.2,50.33 1 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:49.16,50.44 1 0
|
github.com/muety/wakapi/services/language_mapping.go:53.2,53.22 1 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:56.3,56.9 1 0
|
github.com/muety/wakapi/services/language_mapping.go:46.16,48.3 1 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:50.44,52.4 1 0
|
github.com/muety/wakapi/services/language_mapping.go:50.33,52.3 1 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:52.9,55.4 2 0
|
github.com/muety/wakapi/services/language_mapping.go:56.109,58.16 2 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:63.92,65.16 2 1
|
github.com/muety/wakapi/services/language_mapping.go:62.2,63.20 2 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:69.2,72.16 4 1
|
github.com/muety/wakapi/services/language_mapping.go:58.16,60.3 1 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:75.2,75.18 1 1
|
github.com/muety/wakapi/services/language_mapping.go:66.82,67.26 1 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:65.16,67.3 1 1
|
github.com/muety/wakapi/services/language_mapping.go:70.2,72.12 3 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:72.16,74.3 1 0
|
github.com/muety/wakapi/services/language_mapping.go:67.26,69.3 1 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:78.92,80.16 2 0
|
github.com/muety/wakapi/services/language_mapping.go:75.74,78.2 1 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:84.2,85.16 2 0
|
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:92.2,92.18 1 0
|
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:80.16,82.3 1 0
|
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:85.16,87.3 1 0
|
|
||||||
github.com/muety/wakapi/middlewares/logging.go:17.79,18.43 1 0
|
|
||||||
github.com/muety/wakapi/middlewares/logging.go:18.43,23.3 1 0
|
|
||||||
github.com/muety/wakapi/middlewares/logging.go:26.80,44.2 6 0
|
|
||||||
github.com/muety/wakapi/middlewares/logging.go:74.52,76.2 1 0
|
|
||||||
github.com/muety/wakapi/middlewares/logging.go:88.45,89.20 1 0
|
|
||||||
github.com/muety/wakapi/middlewares/logging.go:89.20,93.3 3 0
|
|
||||||
github.com/muety/wakapi/middlewares/logging.go:95.54,98.18 3 0
|
|
||||||
github.com/muety/wakapi/middlewares/logging.go:105.2,106.15 2 0
|
|
||||||
github.com/muety/wakapi/middlewares/logging.go:98.18,101.17 2 0
|
|
||||||
github.com/muety/wakapi/middlewares/logging.go:101.17,103.4 1 0
|
|
||||||
github.com/muety/wakapi/middlewares/logging.go:108.42,109.20 1 0
|
|
||||||
github.com/muety/wakapi/middlewares/logging.go:109.20,111.3 1 0
|
|
||||||
github.com/muety/wakapi/middlewares/logging.go:113.36,115.2 1 0
|
|
||||||
github.com/muety/wakapi/middlewares/logging.go:116.42,118.2 1 0
|
|
||||||
github.com/muety/wakapi/middlewares/logging.go:119.40,121.2 1 0
|
|
||||||
github.com/muety/wakapi/middlewares/logging.go:122.52,124.2 1 0
|
|
||||||
github.com/muety/wakapi/services/misc.go:23.126,30.2 1 0
|
github.com/muety/wakapi/services/misc.go:23.126,30.2 1 0
|
||||||
github.com/muety/wakapi/services/misc.go:42.50,44.48 1 0
|
github.com/muety/wakapi/services/misc.go:42.50,44.48 1 0
|
||||||
github.com/muety/wakapi/services/misc.go:48.2,50.19 3 0
|
github.com/muety/wakapi/services/misc.go:48.2,50.19 3 0
|
||||||
@ -422,6 +366,41 @@ github.com/muety/wakapi/services/summary.go:324.54,326.3 1 1
|
|||||||
github.com/muety/wakapi/services/summary.go:331.59,333.25 2 1
|
github.com/muety/wakapi/services/summary.go:331.59,333.25 2 1
|
||||||
github.com/muety/wakapi/services/summary.go:336.2,336.32 1 1
|
github.com/muety/wakapi/services/summary.go:336.2,336.32 1 1
|
||||||
github.com/muety/wakapi/services/summary.go:333.25,335.3 1 1
|
github.com/muety/wakapi/services/summary.go:333.25,335.3 1 1
|
||||||
|
github.com/muety/wakapi/services/alias.go:17.77,22.2 1 1
|
||||||
|
github.com/muety/wakapi/services/alias.go:26.60,27.43 1 1
|
||||||
|
github.com/muety/wakapi/services/alias.go:30.2,30.14 1 1
|
||||||
|
github.com/muety/wakapi/services/alias.go:27.43,29.3 1 1
|
||||||
|
github.com/muety/wakapi/services/alias.go:33.62,35.16 2 1
|
||||||
|
github.com/muety/wakapi/services/alias.go:38.2,38.12 1 1
|
||||||
|
github.com/muety/wakapi/services/alias.go:35.16,37.3 1 1
|
||||||
|
github.com/muety/wakapi/services/alias.go:41.76,43.16 2 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:46.2,46.21 1 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:43.16,45.3 1 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:49.113,51.16 2 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:54.2,54.21 1 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:51.16,53.3 1 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:57.108,58.32 1 1
|
||||||
|
github.com/muety/wakapi/services/alias.go:64.2,65.46 2 1
|
||||||
|
github.com/muety/wakapi/services/alias.go:70.2,70.19 1 1
|
||||||
|
github.com/muety/wakapi/services/alias.go:58.32,59.52 1 1
|
||||||
|
github.com/muety/wakapi/services/alias.go:59.52,61.4 1 1
|
||||||
|
github.com/muety/wakapi/services/alias.go:65.46,66.48 1 1
|
||||||
|
github.com/muety/wakapi/services/alias.go:66.48,68.4 1 1
|
||||||
|
github.com/muety/wakapi/services/alias.go:73.77,75.16 2 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:78.2,79.20 2 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:75.16,77.3 1 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:82.60,83.24 1 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:86.2,88.12 3 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:83.24,85.3 1 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:91.69,94.28 3 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:102.2,104.31 2 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:108.2,108.12 1 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:94.28,95.21 1 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:98.3,99.16 2 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:95.21,97.4 1 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:104.31,106.3 1 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:111.52,112.51 1 0
|
||||||
|
github.com/muety/wakapi/services/alias.go:112.51,114.3 1 0
|
||||||
github.com/muety/wakapi/services/heartbeat.go:17.141,23.2 1 0
|
github.com/muety/wakapi/services/heartbeat.go:17.141,23.2 1 0
|
||||||
github.com/muety/wakapi/services/heartbeat.go:25.80,27.2 1 0
|
github.com/muety/wakapi/services/heartbeat.go:25.80,27.2 1 0
|
||||||
github.com/muety/wakapi/services/heartbeat.go:29.111,31.16 2 0
|
github.com/muety/wakapi/services/heartbeat.go:29.111,31.16 2 0
|
||||||
@ -438,25 +417,6 @@ github.com/muety/wakapi/services/key_value.go:14.89,19.2 1 0
|
|||||||
github.com/muety/wakapi/services/key_value.go:21.83,23.2 1 0
|
github.com/muety/wakapi/services/key_value.go:21.83,23.2 1 0
|
||||||
github.com/muety/wakapi/services/key_value.go:25.72,27.2 1 0
|
github.com/muety/wakapi/services/key_value.go:25.72,27.2 1 0
|
||||||
github.com/muety/wakapi/services/key_value.go:29.60,31.2 1 0
|
github.com/muety/wakapi/services/key_value.go:29.60,31.2 1 0
|
||||||
github.com/muety/wakapi/services/language_mapping.go:18.118,24.2 1 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:26.86,28.2 1 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:30.96,31.53 1 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:35.2,36.16 2 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:39.2,40.22 2 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:31.53,33.3 1 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:36.16,38.3 1 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:43.92,46.16 3 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:50.2,50.33 1 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:53.2,53.22 1 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:46.16,48.3 1 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:50.33,52.3 1 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:56.109,58.16 2 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:62.2,63.20 2 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:58.16,60.3 1 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:66.82,67.26 1 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:70.2,72.12 3 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:67.26,69.3 1 0
|
|
||||||
github.com/muety/wakapi/services/language_mapping.go:75.74,78.2 1 0
|
|
||||||
github.com/muety/wakapi/services/aggregation.go:24.142,31.2 1 0
|
github.com/muety/wakapi/services/aggregation.go:24.142,31.2 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:40.43,42.37 1 0
|
github.com/muety/wakapi/services/aggregation.go:40.43,42.37 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:46.2,48.19 3 0
|
github.com/muety/wakapi/services/aggregation.go:46.2,48.19 3 0
|
||||||
@ -497,41 +457,6 @@ github.com/muety/wakapi/services/aggregation.go:136.62,140.4 1 0
|
|||||||
github.com/muety/wakapi/services/aggregation.go:148.83,163.41 5 0
|
github.com/muety/wakapi/services/aggregation.go:148.83,163.41 5 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:163.41,173.3 3 0
|
github.com/muety/wakapi/services/aggregation.go:163.41,173.3 3 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:176.34,179.2 2 0
|
github.com/muety/wakapi/services/aggregation.go:176.34,179.2 2 0
|
||||||
github.com/muety/wakapi/services/alias.go:17.77,22.2 1 1
|
|
||||||
github.com/muety/wakapi/services/alias.go:26.60,27.43 1 1
|
|
||||||
github.com/muety/wakapi/services/alias.go:30.2,30.14 1 1
|
|
||||||
github.com/muety/wakapi/services/alias.go:27.43,29.3 1 1
|
|
||||||
github.com/muety/wakapi/services/alias.go:33.62,35.16 2 1
|
|
||||||
github.com/muety/wakapi/services/alias.go:38.2,38.12 1 1
|
|
||||||
github.com/muety/wakapi/services/alias.go:35.16,37.3 1 1
|
|
||||||
github.com/muety/wakapi/services/alias.go:41.76,43.16 2 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:46.2,46.21 1 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:43.16,45.3 1 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:49.113,51.16 2 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:54.2,54.21 1 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:51.16,53.3 1 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:57.108,58.32 1 1
|
|
||||||
github.com/muety/wakapi/services/alias.go:64.2,65.46 2 1
|
|
||||||
github.com/muety/wakapi/services/alias.go:70.2,70.19 1 1
|
|
||||||
github.com/muety/wakapi/services/alias.go:58.32,59.52 1 1
|
|
||||||
github.com/muety/wakapi/services/alias.go:59.52,61.4 1 1
|
|
||||||
github.com/muety/wakapi/services/alias.go:65.46,66.48 1 1
|
|
||||||
github.com/muety/wakapi/services/alias.go:66.48,68.4 1 1
|
|
||||||
github.com/muety/wakapi/services/alias.go:73.77,75.16 2 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:78.2,79.20 2 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:75.16,77.3 1 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:82.60,83.24 1 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:86.2,88.12 3 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:83.24,85.3 1 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:91.69,94.28 3 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:102.2,104.31 2 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:108.2,108.12 1 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:94.28,95.21 1 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:98.3,99.16 2 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:95.21,97.4 1 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:104.31,106.3 1 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:111.52,112.51 1 0
|
|
||||||
github.com/muety/wakapi/services/alias.go:112.51,114.3 1 0
|
|
||||||
github.com/muety/wakapi/services/user.go:19.73,25.2 1 0
|
github.com/muety/wakapi/services/user.go:19.73,25.2 1 0
|
||||||
github.com/muety/wakapi/services/user.go:27.74,28.40 1 0
|
github.com/muety/wakapi/services/user.go:27.74,28.40 1 0
|
||||||
github.com/muety/wakapi/services/user.go:32.2,33.16 2 0
|
github.com/muety/wakapi/services/user.go:32.2,33.16 2 0
|
||||||
@ -556,3 +481,88 @@ github.com/muety/wakapi/services/user.go:96.106,99.96 3 0
|
|||||||
github.com/muety/wakapi/services/user.go:104.2,104.68 1 0
|
github.com/muety/wakapi/services/user.go:104.2,104.68 1 0
|
||||||
github.com/muety/wakapi/services/user.go:99.96,101.3 1 0
|
github.com/muety/wakapi/services/user.go:99.96,101.3 1 0
|
||||||
github.com/muety/wakapi/services/user.go:101.8,103.3 1 0
|
github.com/muety/wakapi/services/user.go:101.8,103.3 1 0
|
||||||
|
github.com/muety/wakapi/services/user.go:107.57,110.2 2 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:16.79,18.54 2 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:22.2,24.16 3 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:28.2,30.45 3 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:33.2,34.32 2 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:18.54,20.3 1 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:24.16,26.3 1 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:30.45,32.3 1 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:37.65,39.54 2 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:43.2,44.30 2 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:39.54,41.3 1 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:47.94,49.16 2 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:53.2,53.107 1 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:57.2,57.22 1 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:49.16,51.3 1 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:53.107,55.3 1 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:60.56,64.2 3 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:66.55,69.16 3 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:72.2,72.16 1 0
|
||||||
|
github.com/muety/wakapi/utils/auth.go:69.16,71.3 1 0
|
||||||
|
github.com/muety/wakapi/utils/color.go:8.90,10.32 2 0
|
||||||
|
github.com/muety/wakapi/utils/color.go:15.2,15.15 1 0
|
||||||
|
github.com/muety/wakapi/utils/color.go:10.32,11.50 1 0
|
||||||
|
github.com/muety/wakapi/utils/color.go:11.50,13.4 1 0
|
||||||
|
github.com/muety/wakapi/utils/common.go:9.48,11.2 1 0
|
||||||
|
github.com/muety/wakapi/utils/common.go:13.40,15.2 1 0
|
||||||
|
github.com/muety/wakapi/utils/common.go:17.45,19.2 1 0
|
||||||
|
github.com/muety/wakapi/utils/common.go:21.24,23.2 1 0
|
||||||
|
github.com/muety/wakapi/utils/common.go:25.56,28.45 3 1
|
||||||
|
github.com/muety/wakapi/utils/common.go:31.2,31.40 1 1
|
||||||
|
github.com/muety/wakapi/utils/common.go:28.45,30.3 1 1
|
||||||
|
github.com/muety/wakapi/utils/date.go:8.31,10.2 1 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:12.43,14.2 1 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:16.30,20.2 3 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:22.31,25.2 2 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:27.30,30.2 2 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:32.67,35.33 2 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:44.2,44.18 1 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:35.33,37.19 2 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:40.3,41.10 2 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:37.19,39.4 1 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:47.50,53.2 5 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:56.79,59.36 3 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:63.2,63.21 1 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:67.2,67.21 1 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:71.2,71.13 1 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:59.36,62.3 2 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:63.21,66.3 2 0
|
||||||
|
github.com/muety/wakapi/utils/date.go:67.21,70.3 2 0
|
||||||
|
github.com/muety/wakapi/utils/http.go:9.73,12.58 3 0
|
||||||
|
github.com/muety/wakapi/utils/http.go:12.58,14.3 1 0
|
||||||
|
github.com/muety/wakapi/utils/strings.go:8.34,10.2 1 0
|
||||||
|
github.com/muety/wakapi/utils/strings.go:12.77,13.29 1 0
|
||||||
|
github.com/muety/wakapi/utils/strings.go:18.2,18.19 1 0
|
||||||
|
github.com/muety/wakapi/utils/strings.go:13.29,14.18 1 0
|
||||||
|
github.com/muety/wakapi/utils/strings.go:14.18,16.4 1 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:10.71,13.18 2 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:49.2,49.22 1 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:14.58,15.24 1 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:16.66,18.22 2 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:19.64,20.23 1 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:21.39,23.21 2 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:24.66,25.24 1 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:26.40,28.22 2 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:29.31,30.23 1 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:31.66,32.42 1 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:33.49,35.40 2 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:36.41,37.43 1 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:38.68,40.42 2 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:41.35,42.43 1 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:43.26,44.21 1 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:45.10,46.39 1 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:52.73,59.56 5 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:73.2,80.8 2 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:59.56,61.3 1 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:61.8,63.17 2 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:67.3,68.17 2 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:63.17,65.4 1 0
|
||||||
|
github.com/muety/wakapi/utils/summary.go:68.17,70.4 1 0
|
||||||
|
github.com/muety/wakapi/utils/template.go:8.41,10.16 2 0
|
||||||
|
github.com/muety/wakapi/utils/template.go:13.2,13.23 1 0
|
||||||
|
github.com/muety/wakapi/utils/template.go:10.16,12.3 1 0
|
||||||
|
github.com/muety/wakapi/utils/template.go:16.37,17.30 1 0
|
||||||
|
github.com/muety/wakapi/utils/template.go:20.2,20.10 1 0
|
||||||
|
github.com/muety/wakapi/utils/template.go:17.30,19.3 1 0
|
||||||
|
1
go.sum
1
go.sum
@ -273,7 +273,6 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
|
|||||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0=
|
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.1 h1:L60q1+q7cXE4JeEJJKMnh2brFIe3rZxCihYAB61ypAY=
|
github.com/mitchellh/hashstructure/v2 v2.0.1 h1:L60q1+q7cXE4JeEJJKMnh2brFIe3rZxCihYAB61ypAY=
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.1/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
github.com/mitchellh/hashstructure/v2 v2.0.1/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
|
84
main.go
84
main.go
@ -7,8 +7,9 @@ import (
|
|||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
"github.com/markbates/pkger"
|
"github.com/markbates/pkger"
|
||||||
conf "github.com/muety/wakapi/config"
|
conf "github.com/muety/wakapi/config"
|
||||||
"github.com/muety/wakapi/migrations/common"
|
"github.com/muety/wakapi/migrations"
|
||||||
"github.com/muety/wakapi/repositories"
|
"github.com/muety/wakapi/repositories"
|
||||||
|
"github.com/muety/wakapi/routes/api"
|
||||||
"gorm.io/gorm/logger"
|
"gorm.io/gorm/logger"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -18,7 +19,6 @@ import (
|
|||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/muety/wakapi/middlewares"
|
"github.com/muety/wakapi/middlewares"
|
||||||
customMiddleware "github.com/muety/wakapi/middlewares/custom"
|
|
||||||
"github.com/muety/wakapi/routes"
|
"github.com/muety/wakapi/routes"
|
||||||
shieldsV1Routes "github.com/muety/wakapi/routes/compat/shields/v1"
|
shieldsV1Routes "github.com/muety/wakapi/routes/compat/shields/v1"
|
||||||
wtV1Routes "github.com/muety/wakapi/routes/compat/wakatime/v1"
|
wtV1Routes "github.com/muety/wakapi/routes/compat/wakatime/v1"
|
||||||
@ -96,9 +96,9 @@ func main() {
|
|||||||
defer sqlDb.Close()
|
defer sqlDb.Close()
|
||||||
|
|
||||||
// Migrate database schema
|
// Migrate database schema
|
||||||
common.RunCustomPreMigrations(db, config)
|
migrations.RunPreMigrations(db, config)
|
||||||
runDatabaseMigrations()
|
runDatabaseMigrations()
|
||||||
common.RunCustomPostMigrations(db, config)
|
migrations.RunCustomPostMigrations(db, config)
|
||||||
|
|
||||||
// Repositories
|
// Repositories
|
||||||
aliasRepository = repositories.NewAliasRepository(db)
|
aliasRepository = repositories.NewAliasRepository(db)
|
||||||
@ -122,69 +122,57 @@ func main() {
|
|||||||
go aggregationService.Schedule()
|
go aggregationService.Schedule()
|
||||||
go miscService.ScheduleCountTotalTime()
|
go miscService.ScheduleCountTotalTime()
|
||||||
|
|
||||||
// TODO: move endpoint registration to the respective routes files
|
|
||||||
|
|
||||||
routes.Init()
|
routes.Init()
|
||||||
|
|
||||||
// Handlers
|
// API Handlers
|
||||||
summaryHandler := routes.NewSummaryHandler(summaryService)
|
healthApiHandler := api.NewHealthApiHandler(db)
|
||||||
healthHandler := routes.NewHealthHandler(db)
|
heartbeatApiHandler := api.NewHeartbeatApiHandler(heartbeatService, languageMappingService)
|
||||||
heartbeatHandler := routes.NewHeartbeatHandler(heartbeatService, languageMappingService)
|
summaryApiHandler := api.NewSummaryApiHandler(summaryService)
|
||||||
settingsHandler := routes.NewSettingsHandler(userService, summaryService, aliasService, aggregationService, languageMappingService)
|
|
||||||
homeHandler := routes.NewHomeHandler(keyValueService)
|
// Compat Handlers
|
||||||
loginHandler := routes.NewLoginHandler(userService)
|
|
||||||
imprintHandler := routes.NewImprintHandler(keyValueService)
|
|
||||||
wakatimeV1AllHandler := wtV1Routes.NewAllTimeHandler(summaryService)
|
wakatimeV1AllHandler := wtV1Routes.NewAllTimeHandler(summaryService)
|
||||||
wakatimeV1SummariesHandler := wtV1Routes.NewSummariesHandler(summaryService)
|
wakatimeV1SummariesHandler := wtV1Routes.NewSummariesHandler(summaryService)
|
||||||
shieldV1BadgeHandler := shieldsV1Routes.NewBadgeHandler(summaryService, userService)
|
shieldV1BadgeHandler := shieldsV1Routes.NewBadgeHandler(summaryService, userService)
|
||||||
|
|
||||||
|
// MVC Handlers
|
||||||
|
summaryHandler := routes.NewSummaryHandler(summaryService, userService)
|
||||||
|
settingsHandler := routes.NewSettingsHandler(userService, summaryService, aliasService, aggregationService, languageMappingService)
|
||||||
|
homeHandler := routes.NewHomeHandler(keyValueService)
|
||||||
|
loginHandler := routes.NewLoginHandler(userService)
|
||||||
|
imprintHandler := routes.NewImprintHandler(keyValueService)
|
||||||
|
|
||||||
// Setup Routers
|
// Setup Routers
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
publicRouter := router.PathPrefix("/").Subrouter()
|
rootRouter := router.PathPrefix("/").Subrouter()
|
||||||
settingsRouter := publicRouter.PathPrefix("/settings").Subrouter()
|
|
||||||
summaryRouter := publicRouter.PathPrefix("/summary").Subrouter()
|
|
||||||
apiRouter := router.PathPrefix("/api").Subrouter()
|
apiRouter := router.PathPrefix("/api").Subrouter()
|
||||||
summaryApiRouter := apiRouter.PathPrefix("/summary").Subrouter()
|
compatApiRouter := apiRouter.PathPrefix("/compat").Subrouter()
|
||||||
heartbeatApiRouter := apiRouter.PathPrefix("/heartbeat").Subrouter()
|
|
||||||
healthApiRouter := apiRouter.PathPrefix("/health").Subrouter()
|
|
||||||
compatRouter := apiRouter.PathPrefix("/compat").Subrouter()
|
|
||||||
wakatimeV1Router := compatRouter.PathPrefix("/wakatime/v1/users/{user}").Subrouter()
|
|
||||||
shieldsV1Router := compatRouter.PathPrefix("/shields/v1/{user}").Subrouter()
|
|
||||||
|
|
||||||
// Middlewares
|
// Globally used middlewares
|
||||||
recoveryMiddleware := handlers.RecoveryHandler()
|
recoveryMiddleware := handlers.RecoveryHandler()
|
||||||
loggingMiddleware := middlewares.NewLoggingMiddleware(
|
loggingMiddleware := middlewares.NewLoggingMiddleware(log.New(os.Stdout, "", log.LstdFlags))
|
||||||
// Use logbuch here once https://github.com/emvi/logbuch/issues/4 is realized
|
|
||||||
log.New(os.Stdout, "", log.LstdFlags),
|
|
||||||
)
|
|
||||||
corsMiddleware := handlers.CORS()
|
corsMiddleware := handlers.CORS()
|
||||||
authenticateMiddleware := middlewares.NewAuthenticateMiddleware(
|
authenticateMiddleware := middlewares.NewAuthenticateMiddleware(userService, []string{"/api/health", "/api/compat/shields/v1"}).Handler
|
||||||
userService,
|
|
||||||
[]string{"/api/health", "/api/compat/shields/v1"},
|
|
||||||
).Handler
|
|
||||||
wakatimeRelayMiddleware := customMiddleware.NewWakatimeRelayMiddleware().Handler
|
|
||||||
|
|
||||||
// Router configs
|
// Router configs
|
||||||
router.Use(loggingMiddleware, recoveryMiddleware)
|
router.Use(loggingMiddleware, recoveryMiddleware)
|
||||||
summaryRouter.Use(authenticateMiddleware)
|
|
||||||
settingsRouter.Use(authenticateMiddleware)
|
|
||||||
apiRouter.Use(corsMiddleware, authenticateMiddleware)
|
apiRouter.Use(corsMiddleware, authenticateMiddleware)
|
||||||
heartbeatApiRouter.Use(wakatimeRelayMiddleware)
|
|
||||||
|
|
||||||
// Route registrations
|
// Route registrations
|
||||||
homeHandler.RegisterRoutes(publicRouter)
|
homeHandler.RegisterRoutes(rootRouter)
|
||||||
loginHandler.RegisterRoutes(publicRouter)
|
loginHandler.RegisterRoutes(rootRouter)
|
||||||
imprintHandler.RegisterRoutes(publicRouter)
|
imprintHandler.RegisterRoutes(rootRouter)
|
||||||
summaryHandler.RegisterRoutes(summaryRouter)
|
summaryHandler.RegisterRoutes(rootRouter)
|
||||||
settingsHandler.RegisterRoutes(settingsRouter)
|
settingsHandler.RegisterRoutes(rootRouter)
|
||||||
|
|
||||||
// API Route registrations
|
// API route registrations
|
||||||
summaryHandler.RegisterAPIRoutes(summaryApiRouter)
|
summaryApiHandler.RegisterRoutes(apiRouter)
|
||||||
healthHandler.RegisterAPIRoutes(healthApiRouter)
|
healthApiHandler.RegisterRoutes(apiRouter)
|
||||||
heartbeatHandler.RegisterAPIRoutes(heartbeatApiRouter)
|
heartbeatApiHandler.RegisterRoutes(apiRouter)
|
||||||
wakatimeV1AllHandler.RegisterAPIRoutes(wakatimeV1Router)
|
|
||||||
wakatimeV1SummariesHandler.RegisterAPIRoutes(wakatimeV1Router)
|
// Compat route registrations
|
||||||
shieldV1BadgeHandler.RegisterAPIRoutes(shieldsV1Router)
|
wakatimeV1AllHandler.RegisterRoutes(compatApiRouter)
|
||||||
|
wakatimeV1SummariesHandler.RegisterRoutes(compatApiRouter)
|
||||||
|
shieldV1BadgeHandler.RegisterRoutes(compatApiRouter)
|
||||||
|
|
||||||
// Static Routes
|
// Static Routes
|
||||||
router.PathPrefix("/assets").Handler(http.FileServer(pkger.Dir("/static")))
|
router.PathPrefix("/assets").Handler(http.FileServer(pkger.Dir("/static")))
|
||||||
|
17
migrations/00000000_apply_fixtures.go
Normal file
17
migrations/00000000_apply_fixtures.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/muety/wakapi/config"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
f := migrationFunc{
|
||||||
|
name: "000-apply_fixtures",
|
||||||
|
f: func(db *gorm.DB, cfg *config.Config) error {
|
||||||
|
return cfg.GetFixturesFunc(cfg.Db.Dialect)(db)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
registerPostMigration(f)
|
||||||
|
}
|
32
migrations/20201103_rename_language_mappings_table.go
Normal file
32
migrations/20201103_rename_language_mappings_table.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/emvi/logbuch"
|
||||||
|
"github.com/muety/wakapi/config"
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
f := migrationFunc{
|
||||||
|
name: "20201103-rename_language_mappings_table",
|
||||||
|
f: func(db *gorm.DB, cfg *config.Config) error {
|
||||||
|
migrator := db.Migrator()
|
||||||
|
oldTableName, newTableName := "custom_rules", "language_mappings"
|
||||||
|
oldIndexName, newIndexName := "idx_customrule_user", "idx_language_mapping_user"
|
||||||
|
|
||||||
|
if migrator.HasTable(oldTableName) {
|
||||||
|
logbuch.Info("renaming '%s' table to '%s'", oldTableName, newTableName)
|
||||||
|
if err := migrator.RenameTable(oldTableName, &models.LanguageMapping{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logbuch.Info("renaming '%s' index to '%s'", oldIndexName, newIndexName)
|
||||||
|
return migrator.RenameIndex(&models.LanguageMapping{}, oldIndexName, newIndexName)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
registerPreMigration(f)
|
||||||
|
}
|
79
migrations/20201106_migration_cascade_constraints.go
Normal file
79
migrations/20201106_migration_cascade_constraints.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/emvi/logbuch"
|
||||||
|
"github.com/muety/wakapi/config"
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
const name = "20201106-migration_cascade_constraints"
|
||||||
|
|
||||||
|
f := migrationFunc{
|
||||||
|
name: name,
|
||||||
|
f: func(db *gorm.DB, cfg *config.Config) error {
|
||||||
|
// drop all already existing foreign key constraints
|
||||||
|
// afterwards let them be re-created by auto migrate with the newly introduced cascade settings,
|
||||||
|
|
||||||
|
migrator := db.Migrator()
|
||||||
|
|
||||||
|
if cfg.Db.Dialect == config.SQLDialectSqlite {
|
||||||
|
// https://stackoverflow.com/a/1884893/3112139
|
||||||
|
// unfortunately, we can't migrate existing sqlite databases to the newly introduced cascade settings
|
||||||
|
// things like deleting all summaries won't work in those cases unless an entirely new db is created
|
||||||
|
logbuch.Info("not attempting to drop and regenerate constraints on sqlite")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !migrator.HasTable(&models.KeyStringValue{}) {
|
||||||
|
logbuch.Info("key-value table not yet existing")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
condition := "key = ?"
|
||||||
|
if cfg.Db.Dialect == config.SQLDialectMysql {
|
||||||
|
condition = "`key` = ?"
|
||||||
|
}
|
||||||
|
lookupResult := db.Where(condition, name).First(&models.KeyStringValue{})
|
||||||
|
if lookupResult.Error == nil && lookupResult.RowsAffected > 0 {
|
||||||
|
logbuch.Info("no need to migrate '%s'", name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SELECT * FROM INFORMATION_SCHEMA.table_constraints;
|
||||||
|
constraints := map[string]interface{}{
|
||||||
|
"fk_summaries_editors": &models.SummaryItem{},
|
||||||
|
"fk_summaries_languages": &models.SummaryItem{},
|
||||||
|
"fk_summaries_machines": &models.SummaryItem{},
|
||||||
|
"fk_summaries_operating_systems": &models.SummaryItem{},
|
||||||
|
"fk_summaries_projects": &models.SummaryItem{},
|
||||||
|
"fk_summary_items_summary": &models.SummaryItem{},
|
||||||
|
"fk_summaries_user": &models.Summary{},
|
||||||
|
"fk_language_mappings_user": &models.LanguageMapping{},
|
||||||
|
"fk_heartbeats_user": &models.Heartbeat{},
|
||||||
|
"fk_aliases_user": &models.Alias{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, table := range constraints {
|
||||||
|
if migrator.HasConstraint(table, name) {
|
||||||
|
logbuch.Info("dropping constraint '%s'", name)
|
||||||
|
if err := migrator.DropConstraint(table, name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Create(&models.KeyStringValue{
|
||||||
|
Key: name,
|
||||||
|
Value: "done",
|
||||||
|
}).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
registerPreMigration(f)
|
||||||
|
}
|
58
migrations/20210202_fix_cascade_for_alias_user_constraint.go
Normal file
58
migrations/20210202_fix_cascade_for_alias_user_constraint.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/emvi/logbuch"
|
||||||
|
"github.com/muety/wakapi/config"
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
const name = "20210202-fix_cascade_for_alias_user_constraint"
|
||||||
|
|
||||||
|
f := migrationFunc{
|
||||||
|
name: name,
|
||||||
|
f: func(db *gorm.DB, cfg *config.Config) error {
|
||||||
|
migrator := db.Migrator()
|
||||||
|
|
||||||
|
if cfg.Db.Dialect == config.SQLDialectSqlite {
|
||||||
|
// see 20201106_migration_cascade_constraints
|
||||||
|
logbuch.Info("not attempting to drop and regenerate constraints on sqlite")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !migrator.HasTable(&models.KeyStringValue{}) {
|
||||||
|
logbuch.Info("key-value table not yet existing")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
condition := "key = ?"
|
||||||
|
if cfg.Db.Dialect == config.SQLDialectMysql {
|
||||||
|
condition = "`key` = ?"
|
||||||
|
}
|
||||||
|
lookupResult := db.Where(condition, name).First(&models.KeyStringValue{})
|
||||||
|
if lookupResult.Error == nil && lookupResult.RowsAffected > 0 {
|
||||||
|
logbuch.Info("no need to migrate '%s'", name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if migrator.HasConstraint(&models.Alias{}, "fk_aliases_user") {
|
||||||
|
logbuch.Info("dropping constraint 'fk_aliases_user'")
|
||||||
|
if err := migrator.DropConstraint(&models.Alias{}, "fk_aliases_user"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.Create(&models.KeyStringValue{
|
||||||
|
Key: name,
|
||||||
|
Value: "done",
|
||||||
|
}).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
registerPreMigration(f)
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/muety/wakapi/config"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
type migrationFunc struct {
|
|
||||||
f func(db *gorm.DB, cfg *config.Config) error
|
|
||||||
name string
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/emvi/logbuch"
|
|
||||||
"github.com/muety/wakapi/config"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
var customPostMigrations []migrationFunc
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
customPostMigrations = []migrationFunc{
|
|
||||||
{
|
|
||||||
f: func(db *gorm.DB, cfg *config.Config) error {
|
|
||||||
return cfg.GetFixturesFunc(cfg.Db.Dialect)(db)
|
|
||||||
},
|
|
||||||
name: "apply fixtures",
|
|
||||||
},
|
|
||||||
// TODO: add function to modify aggregated summaries according to configured custom language mappings
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunCustomPostMigrations(db *gorm.DB, cfg *config.Config) {
|
|
||||||
for _, m := range customPostMigrations {
|
|
||||||
logbuch.Info("potentially running migration '%s'", m.name)
|
|
||||||
if err := m.f(db, cfg); err != nil {
|
|
||||||
logbuch.Fatal("migration '%s' failed – %v", m.name, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,108 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/emvi/logbuch"
|
|
||||||
"github.com/muety/wakapi/config"
|
|
||||||
"github.com/muety/wakapi/models"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
var customPreMigrations []migrationFunc
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
customPreMigrations = []migrationFunc{
|
|
||||||
{
|
|
||||||
f: func(db *gorm.DB, cfg *config.Config) error {
|
|
||||||
migrator := db.Migrator()
|
|
||||||
oldTableName, newTableName := "custom_rules", "language_mappings"
|
|
||||||
oldIndexName, newIndexName := "idx_customrule_user", "idx_language_mapping_user"
|
|
||||||
|
|
||||||
if migrator.HasTable(oldTableName) {
|
|
||||||
logbuch.Info("renaming '%s' table to '%s'", oldTableName, newTableName)
|
|
||||||
if err := migrator.RenameTable(oldTableName, &models.LanguageMapping{}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
logbuch.Info("renaming '%s' index to '%s'", oldIndexName, newIndexName)
|
|
||||||
return migrator.RenameIndex(&models.LanguageMapping{}, oldIndexName, newIndexName)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
name: "rename language mappings table",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
f: func(db *gorm.DB, cfg *config.Config) error {
|
|
||||||
// drop all already existing foreign key constraints
|
|
||||||
// afterwards let them be re-created by auto migrate with the newly introduced cascade settings,
|
|
||||||
|
|
||||||
migrator := db.Migrator()
|
|
||||||
const lookupKey = "20201106-migration_cascade_constraints"
|
|
||||||
|
|
||||||
if cfg.Db.Dialect == config.SQLDialectSqlite {
|
|
||||||
// https://stackoverflow.com/a/1884893/3112139
|
|
||||||
// unfortunately, we can't migrate existing sqlite databases to the newly introduced cascade settings
|
|
||||||
// things like deleting all summaries won't work in those cases unless an entirely new db is created
|
|
||||||
logbuch.Info("not attempting to drop and regenerate constraints on sqlite")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !migrator.HasTable(&models.KeyStringValue{}) {
|
|
||||||
logbuch.Info("key-value table not yet existing")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
condition := "key = ?"
|
|
||||||
if cfg.Db.Dialect == config.SQLDialectMysql {
|
|
||||||
condition = "`key` = ?"
|
|
||||||
}
|
|
||||||
lookupResult := db.Where(condition, lookupKey).First(&models.KeyStringValue{})
|
|
||||||
if lookupResult.Error == nil && lookupResult.RowsAffected > 0 {
|
|
||||||
logbuch.Info("no need to migrate cascade constraints")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SELECT * FROM INFORMATION_SCHEMA.table_constraints;
|
|
||||||
constraints := map[string]interface{}{
|
|
||||||
"fk_summaries_editors": &models.SummaryItem{},
|
|
||||||
"fk_summaries_languages": &models.SummaryItem{},
|
|
||||||
"fk_summaries_machines": &models.SummaryItem{},
|
|
||||||
"fk_summaries_operating_systems": &models.SummaryItem{},
|
|
||||||
"fk_summaries_projects": &models.SummaryItem{},
|
|
||||||
"fk_summary_items_summary": &models.SummaryItem{},
|
|
||||||
"fk_summaries_user": &models.Summary{},
|
|
||||||
"fk_language_mappings_user": &models.LanguageMapping{},
|
|
||||||
"fk_heartbeats_user": &models.Heartbeat{},
|
|
||||||
"fk_aliases_user": &models.Alias{},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, table := range constraints {
|
|
||||||
if migrator.HasConstraint(table, name) {
|
|
||||||
logbuch.Info("dropping constraint '%s'", name)
|
|
||||||
if err := migrator.DropConstraint(table, name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := db.Create(&models.KeyStringValue{
|
|
||||||
Key: lookupKey,
|
|
||||||
Value: "done",
|
|
||||||
}).Error; err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
name: "add cascade constraints",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunCustomPreMigrations(db *gorm.DB, cfg *config.Config) {
|
|
||||||
for _, m := range customPreMigrations {
|
|
||||||
logbuch.Info("potentially running migration '%s'", m.name)
|
|
||||||
if err := m.f(db, cfg); err != nil {
|
|
||||||
logbuch.Fatal("migration '%s' failed – %v", m.name, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/emvi/logbuch"
|
|
||||||
"github.com/muety/wakapi/config"
|
|
||||||
"github.com/muety/wakapi/models"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func MigrateLanguages(db *gorm.DB) {
|
|
||||||
cfg := config.Get()
|
|
||||||
|
|
||||||
for k, v := range cfg.App.CustomLanguages {
|
|
||||||
result := db.Model(models.Heartbeat{}).
|
|
||||||
Where("language = ?", "").
|
|
||||||
Where("entity LIKE ?", "%."+k).
|
|
||||||
Updates(models.Heartbeat{Language: v})
|
|
||||||
if result.Error != nil {
|
|
||||||
logbuch.Fatal(result.Error.Error())
|
|
||||||
}
|
|
||||||
if result.RowsAffected > 0 {
|
|
||||||
logbuch.Info("migrated %+v rows for custom language %+s", result.RowsAffected, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
67
migrations/migrations.go
Normal file
67
migrations/migrations.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/emvi/logbuch"
|
||||||
|
"github.com/muety/wakapi/config"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type migrationFunc struct {
|
||||||
|
f func(db *gorm.DB, cfg *config.Config) error
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type migrationFuncs []migrationFunc
|
||||||
|
|
||||||
|
var (
|
||||||
|
preMigrations migrationFuncs
|
||||||
|
postMigrations migrationFuncs
|
||||||
|
)
|
||||||
|
|
||||||
|
func registerPreMigration(f migrationFunc) {
|
||||||
|
preMigrations = append(preMigrations, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerPostMigration(f migrationFunc) {
|
||||||
|
postMigrations = append(postMigrations, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Currently, migrations themselves keep track
|
||||||
|
// of whether they have run, yet or not, because some
|
||||||
|
// simply run on every start.
|
||||||
|
|
||||||
|
func RunPreMigrations(db *gorm.DB, cfg *config.Config) {
|
||||||
|
sort.Sort(preMigrations)
|
||||||
|
|
||||||
|
for _, m := range preMigrations {
|
||||||
|
logbuch.Info("potentially running migration '%s'", m.name)
|
||||||
|
if err := m.f(db, cfg); err != nil {
|
||||||
|
logbuch.Fatal("migration '%s' failed – %v", m.name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunCustomPostMigrations(db *gorm.DB, cfg *config.Config) {
|
||||||
|
sort.Sort(postMigrations)
|
||||||
|
|
||||||
|
for _, m := range postMigrations {
|
||||||
|
logbuch.Info("potentially running migration '%s'", m.name)
|
||||||
|
if err := m.f(db, cfg); err != nil {
|
||||||
|
logbuch.Fatal("migration '%s' failed – %v", m.name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m migrationFuncs) Len() int {
|
||||||
|
return len(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m migrationFuncs) Less(i, j int) bool {
|
||||||
|
return strings.Compare(m[i].name, m[j].name) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m migrationFuncs) Swap(i, j int) {
|
||||||
|
m[i], m[j] = m[j], m[i]
|
||||||
|
}
|
@ -34,6 +34,11 @@ func (m *UserServiceMock) Update(user *models.User) (*models.User, error) {
|
|||||||
return args.Get(0).(*models.User), args.Error(1)
|
return args.Get(0).(*models.User), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *UserServiceMock) Delete(user *models.User) error {
|
||||||
|
args := m.Called(user)
|
||||||
|
return args.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
func (m *UserServiceMock) ResetApiKey(user *models.User) (*models.User, error) {
|
func (m *UserServiceMock) ResetApiKey(user *models.User) (*models.User, error) {
|
||||||
args := m.Called(user)
|
args := m.Called(user)
|
||||||
return args.Get(0).(*models.User), args.Error(1)
|
return args.Get(0).(*models.User), args.Error(1)
|
||||||
|
@ -2,8 +2,8 @@ package models
|
|||||||
|
|
||||||
type Alias struct {
|
type Alias struct {
|
||||||
ID uint `gorm:"primary_key"`
|
ID uint `gorm:"primary_key"`
|
||||||
Type uint8 `gorm:"not null; index:idx_alias_type_key; constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
|
Type uint8 `gorm:"not null; index:idx_alias_type_key"`
|
||||||
User *User `json:"-" gorm:"not null"`
|
User *User `json:"-" gorm:"not null; constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
|
||||||
UserID string `gorm:"not null; index:idx_alias_user"`
|
UserID string `gorm:"not null; index:idx_alias_user"`
|
||||||
Key string `gorm:"not null; index:idx_alias_type_key"`
|
Key string `gorm:"not null; index:idx_alias_type_key"`
|
||||||
Value string `gorm:"not null"`
|
Value string `gorm:"not null"`
|
||||||
|
@ -49,4 +49,5 @@ type IUserRepository interface {
|
|||||||
InsertOrGet(*models.User) (*models.User, bool, error)
|
InsertOrGet(*models.User) (*models.User, bool, error)
|
||||||
Update(*models.User) (*models.User, error)
|
Update(*models.User) (*models.User, error)
|
||||||
UpdateField(*models.User, string, interface{}) (*models.User, error)
|
UpdateField(*models.User, string, interface{}) (*models.User, error)
|
||||||
|
Delete(*models.User) error
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ func (r *UserRepository) GetByApiKey(key string) (*models.User, error) {
|
|||||||
func (r *UserRepository) GetAll() ([]*models.User, error) {
|
func (r *UserRepository) GetAll() ([]*models.User, error) {
|
||||||
var users []*models.User
|
var users []*models.User
|
||||||
if err := r.db.
|
if err := r.db.
|
||||||
Table("users").
|
Where(&models.User{}).
|
||||||
Find(&users).Error; err != nil {
|
Find(&users).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -78,3 +78,7 @@ func (r *UserRepository) UpdateField(user *models.User, key string, value interf
|
|||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *UserRepository) Delete(user *models.User) error {
|
||||||
|
return r.db.Delete(user).Error
|
||||||
|
}
|
||||||
|
33
routes/api/health.go
Normal file
33
routes/api/health.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HealthApiHandler struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHealthApiHandler(db *gorm.DB) *HealthApiHandler {
|
||||||
|
return &HealthApiHandler{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HealthApiHandler) RegisterRoutes(router *mux.Router) {
|
||||||
|
r := router.PathPrefix("/health").Subrouter()
|
||||||
|
r.Methods(http.MethodGet).HandlerFunc(h.Get)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HealthApiHandler) Get(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var dbStatus int
|
||||||
|
if sqlDb, err := h.db.DB(); err == nil {
|
||||||
|
if err := sqlDb.Ping(); err == nil {
|
||||||
|
dbStatus = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
w.Write([]byte(fmt.Sprintf("app=1\ndb=%d", dbStatus)))
|
||||||
|
}
|
@ -1,10 +1,11 @@
|
|||||||
package routes
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/emvi/logbuch"
|
"github.com/emvi/logbuch"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
conf "github.com/muety/wakapi/config"
|
conf "github.com/muety/wakapi/config"
|
||||||
|
customMiddleware "github.com/muety/wakapi/middlewares/custom"
|
||||||
"github.com/muety/wakapi/services"
|
"github.com/muety/wakapi/services"
|
||||||
"github.com/muety/wakapi/utils"
|
"github.com/muety/wakapi/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -12,14 +13,14 @@ import (
|
|||||||
"github.com/muety/wakapi/models"
|
"github.com/muety/wakapi/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HeartbeatHandler struct {
|
type HeartbeatApiHandler struct {
|
||||||
config *conf.Config
|
config *conf.Config
|
||||||
heartbeatSrvc services.IHeartbeatService
|
heartbeatSrvc services.IHeartbeatService
|
||||||
languageMappingSrvc services.ILanguageMappingService
|
languageMappingSrvc services.ILanguageMappingService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHeartbeatHandler(heartbeatService services.IHeartbeatService, languageMappingService services.ILanguageMappingService) *HeartbeatHandler {
|
func NewHeartbeatApiHandler(heartbeatService services.IHeartbeatService, languageMappingService services.ILanguageMappingService) *HeartbeatApiHandler {
|
||||||
return &HeartbeatHandler{
|
return &HeartbeatApiHandler{
|
||||||
config: conf.Get(),
|
config: conf.Get(),
|
||||||
heartbeatSrvc: heartbeatService,
|
heartbeatSrvc: heartbeatService,
|
||||||
languageMappingSrvc: languageMappingService,
|
languageMappingSrvc: languageMappingService,
|
||||||
@ -30,13 +31,15 @@ type heartbeatResponseVm struct {
|
|||||||
Responses [][]interface{} `json:"responses"`
|
Responses [][]interface{} `json:"responses"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HeartbeatHandler) RegisterRoutes(router *mux.Router) {}
|
func (h *HeartbeatApiHandler) RegisterRoutes(router *mux.Router) {
|
||||||
|
r := router.PathPrefix("/heartbeat").Subrouter()
|
||||||
func (h *HeartbeatHandler) RegisterAPIRoutes(router *mux.Router) {
|
r.Use(
|
||||||
router.Methods(http.MethodPost).HandlerFunc(h.ApiPost)
|
customMiddleware.NewWakatimeRelayMiddleware().Handler,
|
||||||
|
)
|
||||||
|
r.Methods(http.MethodPost).HandlerFunc(h.Post)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HeartbeatHandler) ApiPost(w http.ResponseWriter, r *http.Request) {
|
func (h *HeartbeatApiHandler) Post(w http.ResponseWriter, r *http.Request) {
|
||||||
var heartbeats []*models.Heartbeat
|
var heartbeats []*models.Heartbeat
|
||||||
user := r.Context().Value(models.UserKey).(*models.User)
|
user := r.Context().Value(models.UserKey).(*models.User)
|
||||||
opSys, editor, _ := utils.ParseUserAgent(r.Header.Get("User-Agent"))
|
opSys, editor, _ := utils.ParseUserAgent(r.Header.Get("User-Agent"))
|
38
routes/api/summary.go
Normal file
38
routes/api/summary.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
conf "github.com/muety/wakapi/config"
|
||||||
|
su "github.com/muety/wakapi/routes/utils"
|
||||||
|
"github.com/muety/wakapi/services"
|
||||||
|
"github.com/muety/wakapi/utils"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SummaryApiHandler struct {
|
||||||
|
config *conf.Config
|
||||||
|
summarySrvc services.ISummaryService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSummaryApiHandler(summaryService services.ISummaryService) *SummaryApiHandler {
|
||||||
|
return &SummaryApiHandler{
|
||||||
|
summarySrvc: summaryService,
|
||||||
|
config: conf.Get(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SummaryApiHandler) RegisterRoutes(router *mux.Router) {
|
||||||
|
r := router.PathPrefix("/summary").Subrouter()
|
||||||
|
r.Methods(http.MethodGet).HandlerFunc(h.Get)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SummaryApiHandler) Get(w http.ResponseWriter, r *http.Request) {
|
||||||
|
summary, err, status := su.LoadUserSummary(h.summarySrvc, r)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(status)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.RespondJSON(w, http.StatusOK, summary)
|
||||||
|
}
|
@ -31,13 +31,12 @@ func NewBadgeHandler(summaryService services.ISummaryService, userService servic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BadgeHandler) RegisterRoutes(router *mux.Router) {}
|
func (h *BadgeHandler) RegisterRoutes(router *mux.Router) {
|
||||||
|
r := router.PathPrefix("/shields/v1/{user}").Subrouter()
|
||||||
func (h *BadgeHandler) RegisterAPIRoutes(router *mux.Router) {
|
r.Methods(http.MethodGet).HandlerFunc(h.Get)
|
||||||
router.Methods(http.MethodGet).HandlerFunc(h.ApiGet)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *BadgeHandler) ApiGet(w http.ResponseWriter, r *http.Request) {
|
func (h *BadgeHandler) Get(w http.ResponseWriter, r *http.Request) {
|
||||||
intervalReg := regexp.MustCompile(intervalPattern)
|
intervalReg := regexp.MustCompile(intervalPattern)
|
||||||
entityFilterReg := regexp.MustCompile(entityFilterPattern)
|
entityFilterReg := regexp.MustCompile(entityFilterPattern)
|
||||||
|
|
||||||
|
@ -24,13 +24,11 @@ func NewAllTimeHandler(summaryService services.ISummaryService) *AllTimeHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *AllTimeHandler) RegisterRoutes(router *mux.Router) {}
|
func (h *AllTimeHandler) RegisterRoutes(router *mux.Router) {
|
||||||
|
router.Path("/wakatime/v1/users/{user}/all_time_since_today").Methods(http.MethodGet).HandlerFunc(h.Get)
|
||||||
func (h *AllTimeHandler) RegisterAPIRoutes(router *mux.Router) {
|
|
||||||
router.Path("/all_time_since_today").Methods(http.MethodGet).HandlerFunc(h.ApiGet)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *AllTimeHandler) ApiGet(w http.ResponseWriter, r *http.Request) {
|
func (h *AllTimeHandler) Get(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
values, _ := url.ParseQuery(r.URL.RawQuery)
|
values, _ := url.ParseQuery(r.URL.RawQuery)
|
||||||
|
|
||||||
|
@ -25,10 +25,8 @@ func NewSummariesHandler(summaryService services.ISummaryService) *SummariesHand
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *SummariesHandler) RegisterRoutes(router *mux.Router) {}
|
func (h *SummariesHandler) RegisterRoutes(router *mux.Router) {
|
||||||
|
router.Path("/wakatime/v1/users/{user}/summaries").Methods(http.MethodGet).HandlerFunc(h.Get)
|
||||||
func (h *SummariesHandler) RegisterAPIRoutes(router *mux.Router) {
|
|
||||||
router.Path("/summaries").Methods(http.MethodGet).HandlerFunc(h.ApiGet)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Support parameters: project, branches, timeout, writes_only, timezone
|
// TODO: Support parameters: project, branches, timeout, writes_only, timezone
|
||||||
@ -36,7 +34,7 @@ func (h *SummariesHandler) RegisterAPIRoutes(router *mux.Router) {
|
|||||||
// Timezone can be specified via an offset suffix (e.g. +02:00) in date strings.
|
// Timezone can be specified via an offset suffix (e.g. +02:00) in date strings.
|
||||||
// Requires https://github.com/muety/wakapi/issues/108.
|
// Requires https://github.com/muety/wakapi/issues/108.
|
||||||
|
|
||||||
func (h *SummariesHandler) ApiGet(w http.ResponseWriter, r *http.Request) {
|
func (h *SummariesHandler) Get(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
requestedUser := vars["user"]
|
requestedUser := vars["user"]
|
||||||
authorizedUser := r.Context().Value(models.UserKey).(*models.User)
|
authorizedUser := r.Context().Value(models.UserKey).(*models.User)
|
||||||
|
@ -4,5 +4,4 @@ import "github.com/gorilla/mux"
|
|||||||
|
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
RegisterRoutes(router *mux.Router)
|
RegisterRoutes(router *mux.Router)
|
||||||
RegisterAPIRoutes(router *mux.Router)
|
|
||||||
}
|
}
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
package routes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/gorilla/mux"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HealthHandler struct {
|
|
||||||
db *gorm.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHealthHandler(db *gorm.DB) *HealthHandler {
|
|
||||||
return &HealthHandler{db: db}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HealthHandler) RegisterRoutes(router *mux.Router) {}
|
|
||||||
|
|
||||||
func (h *HealthHandler) RegisterAPIRoutes(router *mux.Router) {
|
|
||||||
router.Methods(http.MethodGet).HandlerFunc(h.ApiGet)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HealthHandler) ApiGet(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var dbStatus int
|
|
||||||
if sqlDb, err := h.db.DB(); err == nil {
|
|
||||||
if err := sqlDb.Ping(); err == nil {
|
|
||||||
dbStatus = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
|
||||||
w.Write([]byte(fmt.Sprintf("app=1\ndb=%d", dbStatus)))
|
|
||||||
}
|
|
@ -32,8 +32,6 @@ func (h *HomeHandler) RegisterRoutes(router *mux.Router) {
|
|||||||
router.Path("/").Methods(http.MethodGet).HandlerFunc(h.GetIndex)
|
router.Path("/").Methods(http.MethodGet).HandlerFunc(h.GetIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HomeHandler) RegisterAPIRoutes(router *mux.Router) {}
|
|
||||||
|
|
||||||
func (h *HomeHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
|
func (h *HomeHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
|
||||||
if h.config.IsDev() {
|
if h.config.IsDev() {
|
||||||
loadTemplates()
|
loadTemplates()
|
||||||
|
@ -25,8 +25,6 @@ func (h *ImprintHandler) RegisterRoutes(router *mux.Router) {
|
|||||||
router.Path("/imprint").Methods(http.MethodGet).HandlerFunc(h.GetImprint)
|
router.Path("/imprint").Methods(http.MethodGet).HandlerFunc(h.GetImprint)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ImprintHandler) RegisterAPIRoutes(router *mux.Router) {}
|
|
||||||
|
|
||||||
func (h *ImprintHandler) GetImprint(w http.ResponseWriter, r *http.Request) {
|
func (h *ImprintHandler) GetImprint(w http.ResponseWriter, r *http.Request) {
|
||||||
if h.config.IsDev() {
|
if h.config.IsDev() {
|
||||||
loadTemplates()
|
loadTemplates()
|
||||||
|
@ -32,8 +32,6 @@ func (h *LoginHandler) RegisterRoutes(router *mux.Router) {
|
|||||||
router.Path("/signup").Methods(http.MethodPost).HandlerFunc(h.PostSignup)
|
router.Path("/signup").Methods(http.MethodPost).HandlerFunc(h.PostSignup)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *LoginHandler) RegisterAPIRoutes(router *mux.Router) {}
|
|
||||||
|
|
||||||
func (h *LoginHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
|
func (h *LoginHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
|
||||||
if h.config.IsDev() {
|
if h.config.IsDev() {
|
||||||
loadTemplates()
|
loadTemplates()
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/muety/wakapi/utils"
|
"github.com/muety/wakapi/utils"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -16,6 +17,8 @@ func Init() {
|
|||||||
loadTemplates()
|
loadTemplates()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type action func(w http.ResponseWriter, r *http.Request) (int, string, string)
|
||||||
|
|
||||||
var templates map[string]*template.Template
|
var templates map[string]*template.Template
|
||||||
|
|
||||||
func loadTemplates() {
|
func loadTemplates() {
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/gorilla/schema"
|
"github.com/gorilla/schema"
|
||||||
conf "github.com/muety/wakapi/config"
|
conf "github.com/muety/wakapi/config"
|
||||||
|
"github.com/muety/wakapi/middlewares"
|
||||||
"github.com/muety/wakapi/models"
|
"github.com/muety/wakapi/models"
|
||||||
"github.com/muety/wakapi/models/view"
|
"github.com/muety/wakapi/models/view"
|
||||||
"github.com/muety/wakapi/services"
|
"github.com/muety/wakapi/services"
|
||||||
@ -41,20 +42,14 @@ func NewSettingsHandler(userService services.IUserService, summaryService servic
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *SettingsHandler) RegisterRoutes(router *mux.Router) {
|
func (h *SettingsHandler) RegisterRoutes(router *mux.Router) {
|
||||||
router.Methods(http.MethodGet).HandlerFunc(h.GetIndex)
|
r := router.PathPrefix("/settings").Subrouter()
|
||||||
router.Path("/credentials").Methods(http.MethodPost).HandlerFunc(h.PostCredentials)
|
r.Use(
|
||||||
router.Path("/aliases").Methods(http.MethodPost).HandlerFunc(h.PostAlias)
|
middlewares.NewAuthenticateMiddleware(h.userSrvc, []string{}).Handler,
|
||||||
router.Path("/aliases/delete").Methods(http.MethodPost).HandlerFunc(h.DeleteAlias)
|
)
|
||||||
router.Path("/language_mappings").Methods(http.MethodPost).HandlerFunc(h.PostLanguageMapping)
|
r.Methods(http.MethodGet).HandlerFunc(h.GetIndex)
|
||||||
router.Path("/language_mappings/delete").Methods(http.MethodPost).HandlerFunc(h.DeleteLanguageMapping)
|
r.Methods(http.MethodPost).HandlerFunc(h.PostIndex)
|
||||||
router.Path("/reset").Methods(http.MethodPost).HandlerFunc(h.PostResetApiKey)
|
|
||||||
router.Path("/badges").Methods(http.MethodPost).HandlerFunc(h.PostToggleBadges)
|
|
||||||
router.Path("/wakatime_integration").Methods(http.MethodPost).HandlerFunc(h.PostSetWakatimeApiKey)
|
|
||||||
router.Path("/regenerate").Methods(http.MethodPost).HandlerFunc(h.PostRegenerateSummaries)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *SettingsHandler) RegisterAPIRoutes(router *mux.Router) {}
|
|
||||||
|
|
||||||
func (h *SettingsHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
|
func (h *SettingsHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
|
||||||
if h.config.IsDev() {
|
if h.config.IsDev() {
|
||||||
loadTemplates()
|
loadTemplates()
|
||||||
@ -63,7 +58,73 @@ func (h *SettingsHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
|
|||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r))
|
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *SettingsHandler) PostCredentials(w http.ResponseWriter, r *http.Request) {
|
func (h *SettingsHandler) PostIndex(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if h.config.IsDev() {
|
||||||
|
loadTemplates()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("missing form values"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
action := r.PostForm.Get("action")
|
||||||
|
r.PostForm.Del("action")
|
||||||
|
|
||||||
|
actionFunc := h.dispatchAction(action)
|
||||||
|
if actionFunc == nil {
|
||||||
|
logbuch.Warn("failed to dispatch action '%s'", action)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("unknown action requests"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
status, successMsg, errorMsg := actionFunc(w, r)
|
||||||
|
|
||||||
|
// action responded itself
|
||||||
|
if status == -1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if errorMsg != "" {
|
||||||
|
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError(errorMsg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if successMsg != "" {
|
||||||
|
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithSuccess(successMsg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SettingsHandler) dispatchAction(action string) action {
|
||||||
|
switch action {
|
||||||
|
case "change_password":
|
||||||
|
return h.actionChangePassword
|
||||||
|
case "reset_apikey":
|
||||||
|
return h.actionResetApiKey
|
||||||
|
case "delete_alias":
|
||||||
|
return h.actionDeleteAlias
|
||||||
|
case "add_alias":
|
||||||
|
return h.actionAddAlias
|
||||||
|
case "delete_mapping":
|
||||||
|
return h.actionDeleteLanguageMapping
|
||||||
|
case "add_mapping":
|
||||||
|
return h.actionAddLanguageMapping
|
||||||
|
case "toggle_badges":
|
||||||
|
return h.actionToggleBadges
|
||||||
|
case "toggle_wakatime":
|
||||||
|
return h.actionSetWakatimeApiKey
|
||||||
|
case "regenerate_summaries":
|
||||||
|
return h.actionRegenerateSummaries
|
||||||
|
case "delete_account":
|
||||||
|
return h.actionDeleteUser
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SettingsHandler) actionChangePassword(w http.ResponseWriter, r *http.Request) (int, string, string) {
|
||||||
if h.config.IsDev() {
|
if h.config.IsDev() {
|
||||||
loadTemplates()
|
loadTemplates()
|
||||||
}
|
}
|
||||||
@ -72,41 +133,29 @@ func (h *SettingsHandler) PostCredentials(w http.ResponseWriter, r *http.Request
|
|||||||
|
|
||||||
var credentials models.CredentialsReset
|
var credentials models.CredentialsReset
|
||||||
if err := r.ParseForm(); err != nil {
|
if err := r.ParseForm(); err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
return http.StatusBadRequest, "", "missing parameters"
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("missing parameters"))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if err := credentialsDecoder.Decode(&credentials, r.PostForm); err != nil {
|
if err := credentialsDecoder.Decode(&credentials, r.PostForm); err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
return http.StatusBadRequest, "", "missing parameters"
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("missing parameters"))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !utils.CompareBcrypt(user.Password, credentials.PasswordOld, h.config.Security.PasswordSalt) {
|
if !utils.CompareBcrypt(user.Password, credentials.PasswordOld, h.config.Security.PasswordSalt) {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
return http.StatusUnauthorized, "", "invalid credentials"
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("invalid credentials"))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !credentials.IsValid() {
|
if !credentials.IsValid() {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
return http.StatusBadRequest, "", "invalid parameters"
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("invalid parameters"))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Password = credentials.PasswordNew
|
user.Password = credentials.PasswordNew
|
||||||
if hash, err := utils.HashBcrypt(user.Password, h.config.Security.PasswordSalt); err != nil {
|
if hash, err := utils.HashBcrypt(user.Password, h.config.Security.PasswordSalt); err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
return http.StatusInternalServerError, "", "internal server error"
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("internal server error"))
|
|
||||||
return
|
|
||||||
} else {
|
} else {
|
||||||
user.Password = hash
|
user.Password = hash
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := h.userSrvc.Update(user); err != nil {
|
if _, err := h.userSrvc.Update(user); err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
return http.StatusInternalServerError, "", "internal server error"
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("internal server error"))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
login := &models.Login{
|
login := &models.Login{
|
||||||
@ -115,75 +164,28 @@ func (h *SettingsHandler) PostCredentials(w http.ResponseWriter, r *http.Request
|
|||||||
}
|
}
|
||||||
encoded, err := h.config.Security.SecureCookie.Encode(models.AuthCookieKey, login.Username)
|
encoded, err := h.config.Security.SecureCookie.Encode(models.AuthCookieKey, login.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
return http.StatusInternalServerError, "", "internal server error"
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("internal server error"))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
http.SetCookie(w, h.config.CreateCookie(models.AuthCookieKey, encoded, "/"))
|
http.SetCookie(w, h.config.CreateCookie(models.AuthCookieKey, encoded, "/"))
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithSuccess("password was updated successfully"))
|
return http.StatusOK, "password was updated successfully", ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *SettingsHandler) DeleteLanguageMapping(w http.ResponseWriter, r *http.Request) {
|
func (h *SettingsHandler) actionResetApiKey(w http.ResponseWriter, r *http.Request) (int, string, string) {
|
||||||
if h.config.IsDev() {
|
if h.config.IsDev() {
|
||||||
loadTemplates()
|
loadTemplates()
|
||||||
}
|
}
|
||||||
|
|
||||||
user := r.Context().Value(models.UserKey).(*models.User)
|
user := r.Context().Value(models.UserKey).(*models.User)
|
||||||
id, err := strconv.Atoi(r.PostFormValue("mapping_id"))
|
if _, err := h.userSrvc.ResetApiKey(user); err != nil {
|
||||||
if err != nil {
|
return http.StatusInternalServerError, "", "internal server error"
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("could not delete mapping"))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if mapping, err := h.languageMappingSrvc.GetById(uint(id)); err != nil || mapping == nil {
|
msg := fmt.Sprintf("your new api key is: %s", user.ApiKey)
|
||||||
w.WriteHeader(http.StatusNotFound)
|
return http.StatusOK, msg, ""
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("mapping not found"))
|
|
||||||
return
|
|
||||||
} else if mapping.UserID != user.ID {
|
|
||||||
w.WriteHeader(http.StatusForbidden)
|
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("not allowed to delete mapping"))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.languageMappingSrvc.Delete(&models.LanguageMapping{ID: uint(id)}); err != nil {
|
func (h *SettingsHandler) actionDeleteAlias(w http.ResponseWriter, r *http.Request) (int, string, string) {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("could not delete mapping"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithSuccess("mapping deleted successfully"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *SettingsHandler) PostLanguageMapping(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if h.config.IsDev() {
|
|
||||||
loadTemplates()
|
|
||||||
}
|
|
||||||
user := r.Context().Value(models.UserKey).(*models.User)
|
|
||||||
extension := r.PostFormValue("extension")
|
|
||||||
language := r.PostFormValue("language")
|
|
||||||
|
|
||||||
if extension[0] == '.' {
|
|
||||||
extension = extension[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
mapping := &models.LanguageMapping{
|
|
||||||
UserID: user.ID,
|
|
||||||
Extension: extension,
|
|
||||||
Language: language,
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := h.languageMappingSrvc.Create(mapping); err != nil {
|
|
||||||
w.WriteHeader(http.StatusConflict)
|
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("mapping already exists"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithSuccess("mapping added successfully"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *SettingsHandler) DeleteAlias(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if h.config.IsDev() {
|
if h.config.IsDev() {
|
||||||
loadTemplates()
|
loadTemplates()
|
||||||
}
|
}
|
||||||
@ -196,19 +198,15 @@ func (h *SettingsHandler) DeleteAlias(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if aliases, err := h.aliasSrvc.GetByUserAndKeyAndType(user.ID, aliasKey, uint8(aliasType)); err != nil {
|
if aliases, err := h.aliasSrvc.GetByUserAndKeyAndType(user.ID, aliasKey, uint8(aliasType)); err != nil {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
return http.StatusNotFound, "", "aliases not found"
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("aliases not found"))
|
|
||||||
return
|
|
||||||
} else if err := h.aliasSrvc.DeleteMulti(aliases); err != nil {
|
} else if err := h.aliasSrvc.DeleteMulti(aliases); err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
return http.StatusInternalServerError, "", "could not delete aliases"
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("could not delete aliases"))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithSuccess("aliases deleted successfully"))
|
return http.StatusOK, "aliases deleted successfully", ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *SettingsHandler) PostAlias(w http.ResponseWriter, r *http.Request) {
|
func (h *SettingsHandler) actionAddAlias(w http.ResponseWriter, r *http.Request) (int, string, string) {
|
||||||
if h.config.IsDev() {
|
if h.config.IsDev() {
|
||||||
loadTemplates()
|
loadTemplates()
|
||||||
}
|
}
|
||||||
@ -228,32 +226,76 @@ func (h *SettingsHandler) PostAlias(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, err := h.aliasSrvc.Create(alias); err != nil {
|
if _, err := h.aliasSrvc.Create(alias); err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
|
||||||
// TODO: distinguish between bad request, conflict and server error
|
// TODO: distinguish between bad request, conflict and server error
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("invalid input"))
|
return http.StatusBadRequest, "", "invalid input"
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithSuccess("alias added successfully"))
|
return http.StatusOK, "alias added successfully", ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *SettingsHandler) PostResetApiKey(w http.ResponseWriter, r *http.Request) {
|
func (h *SettingsHandler) actionDeleteLanguageMapping(w http.ResponseWriter, r *http.Request) (int, string, string) {
|
||||||
if h.config.IsDev() {
|
if h.config.IsDev() {
|
||||||
loadTemplates()
|
loadTemplates()
|
||||||
}
|
}
|
||||||
|
|
||||||
user := r.Context().Value(models.UserKey).(*models.User)
|
user := r.Context().Value(models.UserKey).(*models.User)
|
||||||
if _, err := h.userSrvc.ResetApiKey(user); err != nil {
|
id, err := strconv.Atoi(r.PostFormValue("mapping_id"))
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
if err != nil {
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("internal server error"))
|
return http.StatusInternalServerError, "", "could not delete mapping"
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := fmt.Sprintf("your new api key is: %s", user.ApiKey)
|
if mapping, err := h.languageMappingSrvc.GetById(uint(id)); err != nil || mapping == nil {
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithSuccess(msg))
|
return http.StatusNotFound, "", "mapping not found"
|
||||||
|
} else if mapping.UserID != user.ID {
|
||||||
|
return http.StatusForbidden, "", "not allowed to delete mapping"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *SettingsHandler) PostSetWakatimeApiKey(w http.ResponseWriter, r *http.Request) {
|
if err := h.languageMappingSrvc.Delete(&models.LanguageMapping{ID: uint(id)}); err != nil {
|
||||||
|
return http.StatusInternalServerError, "", "could not delete mapping"
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.StatusOK, "mapping deleted successfully", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SettingsHandler) actionAddLanguageMapping(w http.ResponseWriter, r *http.Request) (int, string, string) {
|
||||||
|
if h.config.IsDev() {
|
||||||
|
loadTemplates()
|
||||||
|
}
|
||||||
|
user := r.Context().Value(models.UserKey).(*models.User)
|
||||||
|
extension := r.PostFormValue("extension")
|
||||||
|
language := r.PostFormValue("language")
|
||||||
|
|
||||||
|
if extension[0] == '.' {
|
||||||
|
extension = extension[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping := &models.LanguageMapping{
|
||||||
|
UserID: user.ID,
|
||||||
|
Extension: extension,
|
||||||
|
Language: language,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := h.languageMappingSrvc.Create(mapping); err != nil {
|
||||||
|
return http.StatusConflict, "", "mapping already exists"
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.StatusOK, "mapping added successfully", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SettingsHandler) actionToggleBadges(w http.ResponseWriter, r *http.Request) (int, string, string) {
|
||||||
|
if h.config.IsDev() {
|
||||||
|
loadTemplates()
|
||||||
|
}
|
||||||
|
|
||||||
|
user := r.Context().Value(models.UserKey).(*models.User)
|
||||||
|
if _, err := h.userSrvc.ToggleBadges(user); err != nil {
|
||||||
|
return http.StatusInternalServerError, "", "internal server error"
|
||||||
|
}
|
||||||
|
|
||||||
|
return http.StatusOK, "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SettingsHandler) actionSetWakatimeApiKey(w http.ResponseWriter, r *http.Request) (int, string, string) {
|
||||||
if h.config.IsDev() {
|
if h.config.IsDev() {
|
||||||
loadTemplates()
|
loadTemplates()
|
||||||
}
|
}
|
||||||
@ -263,35 +305,17 @@ func (h *SettingsHandler) PostSetWakatimeApiKey(w http.ResponseWriter, r *http.R
|
|||||||
|
|
||||||
// Healthcheck, if a new API key is set, i.e. the feature is activated
|
// Healthcheck, if a new API key is set, i.e. the feature is activated
|
||||||
if (user.WakatimeApiKey == "" && apiKey != "") && !h.validateWakatimeKey(apiKey) {
|
if (user.WakatimeApiKey == "" && apiKey != "") && !h.validateWakatimeKey(apiKey) {
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("failed to connect to WakaTime, API key invalid?"))
|
return http.StatusBadRequest, "", "failed to connect to WakaTime, API key invalid?"
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := h.userSrvc.SetWakatimeApiKey(user, apiKey); err != nil {
|
if _, err := h.userSrvc.SetWakatimeApiKey(user, apiKey); err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
return http.StatusInternalServerError, "", "internal server error"
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("internal server error"))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithSuccess("Wakatime API Key updated successfully"))
|
return http.StatusOK, "Wakatime API Key updated successfully", ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *SettingsHandler) PostToggleBadges(w http.ResponseWriter, r *http.Request) {
|
func (h *SettingsHandler) actionRegenerateSummaries(w http.ResponseWriter, r *http.Request) (int, string, string) {
|
||||||
if h.config.IsDev() {
|
|
||||||
loadTemplates()
|
|
||||||
}
|
|
||||||
|
|
||||||
user := r.Context().Value(models.UserKey).(*models.User)
|
|
||||||
if _, err := h.userSrvc.ToggleBadges(user); err != nil {
|
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("internal server error"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *SettingsHandler) PostRegenerateSummaries(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if h.config.IsDev() {
|
if h.config.IsDev() {
|
||||||
loadTemplates()
|
loadTemplates()
|
||||||
}
|
}
|
||||||
@ -301,19 +325,37 @@ func (h *SettingsHandler) PostRegenerateSummaries(w http.ResponseWriter, r *http
|
|||||||
logbuch.Info("clearing summaries for user '%s'", user.ID)
|
logbuch.Info("clearing summaries for user '%s'", user.ID)
|
||||||
if err := h.summarySrvc.DeleteByUser(user.ID); err != nil {
|
if err := h.summarySrvc.DeleteByUser(user.ID); err != nil {
|
||||||
logbuch.Error("failed to clear summaries: %v", err)
|
logbuch.Error("failed to clear summaries: %v", err)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
return http.StatusInternalServerError, "", "failed to delete old summaries"
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("failed to delete old summaries"))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.aggregationSrvc.Run(map[string]bool{user.ID: true}); err != nil {
|
if err := h.aggregationSrvc.Run(map[string]bool{user.ID: true}); err != nil {
|
||||||
logbuch.Error("failed to regenerate summaries: %v", err)
|
logbuch.Error("failed to regenerate summaries: %v", err)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
return http.StatusInternalServerError, "", "failed to generate aggregations"
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithError("failed to generate aggregations"))
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templates[conf.SettingsTemplate].Execute(w, h.buildViewModel(r).WithSuccess("summaries are being regenerated – this may take a few seconds"))
|
return http.StatusOK, "summaries are being regenerated – this may take a few seconds", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SettingsHandler) actionDeleteUser(w http.ResponseWriter, r *http.Request) (int, string, string) {
|
||||||
|
if h.config.IsDev() {
|
||||||
|
loadTemplates()
|
||||||
|
}
|
||||||
|
|
||||||
|
user := r.Context().Value(models.UserKey).(*models.User)
|
||||||
|
go func(user *models.User) {
|
||||||
|
logbuch.Info("deleting user '%s' shortly", user.ID)
|
||||||
|
time.Sleep(5 * time.Minute)
|
||||||
|
if err := h.userSrvc.Delete(user); err != nil {
|
||||||
|
logbuch.Error("failed to delete user '%s' – %v", user.ID, err)
|
||||||
|
} else {
|
||||||
|
logbuch.Info("successfully deleted user '%s'", user.ID)
|
||||||
|
}
|
||||||
|
}(user)
|
||||||
|
|
||||||
|
http.SetCookie(w, h.config.GetClearCookie(models.AuthCookieKey, "/"))
|
||||||
|
http.Redirect(w, r, fmt.Sprintf("%s/?success=%s", h.config.Server.BasePath, "Your account will be deleted in a few minutes. Sorry to you go."), http.StatusFound)
|
||||||
|
return -1, "", ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *SettingsHandler) validateWakatimeKey(apiKey string) bool {
|
func (h *SettingsHandler) validateWakatimeKey(apiKey string) bool {
|
||||||
|
@ -3,8 +3,10 @@ package routes
|
|||||||
import (
|
import (
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
conf "github.com/muety/wakapi/config"
|
conf "github.com/muety/wakapi/config"
|
||||||
|
"github.com/muety/wakapi/middlewares"
|
||||||
"github.com/muety/wakapi/models"
|
"github.com/muety/wakapi/models"
|
||||||
"github.com/muety/wakapi/models/view"
|
"github.com/muety/wakapi/models/view"
|
||||||
|
su "github.com/muety/wakapi/routes/utils"
|
||||||
"github.com/muety/wakapi/services"
|
"github.com/muety/wakapi/services"
|
||||||
"github.com/muety/wakapi/utils"
|
"github.com/muety/wakapi/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -12,33 +14,24 @@ import (
|
|||||||
|
|
||||||
type SummaryHandler struct {
|
type SummaryHandler struct {
|
||||||
config *conf.Config
|
config *conf.Config
|
||||||
|
userSrvc services.IUserService
|
||||||
summarySrvc services.ISummaryService
|
summarySrvc services.ISummaryService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSummaryHandler(summaryService services.ISummaryService) *SummaryHandler {
|
func NewSummaryHandler(summaryService services.ISummaryService, userService services.IUserService) *SummaryHandler {
|
||||||
return &SummaryHandler{
|
return &SummaryHandler{
|
||||||
summarySrvc: summaryService,
|
summarySrvc: summaryService,
|
||||||
|
userSrvc: userService,
|
||||||
config: conf.Get(),
|
config: conf.Get(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *SummaryHandler) RegisterRoutes(router *mux.Router) {
|
func (h *SummaryHandler) RegisterRoutes(router *mux.Router) {
|
||||||
router.Methods(http.MethodGet).HandlerFunc(h.GetIndex)
|
r := router.PathPrefix("/summary").Subrouter()
|
||||||
}
|
r.Use(
|
||||||
|
middlewares.NewAuthenticateMiddleware(h.userSrvc, []string{}).Handler,
|
||||||
func (h *SummaryHandler) RegisterAPIRoutes(router *mux.Router) {
|
)
|
||||||
router.Methods(http.MethodGet).HandlerFunc(h.ApiGet)
|
r.Methods(http.MethodGet).HandlerFunc(h.GetIndex)
|
||||||
}
|
|
||||||
|
|
||||||
func (h *SummaryHandler) ApiGet(w http.ResponseWriter, r *http.Request) {
|
|
||||||
summary, err, status := h.loadUserSummary(r)
|
|
||||||
if err != nil {
|
|
||||||
w.WriteHeader(status)
|
|
||||||
w.Write([]byte(err.Error()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.RespondJSON(w, http.StatusOK, summary)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *SummaryHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
|
func (h *SummaryHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -52,7 +45,7 @@ func (h *SummaryHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
|
|||||||
r.URL.RawQuery = q.Encode()
|
r.URL.RawQuery = q.Encode()
|
||||||
}
|
}
|
||||||
|
|
||||||
summary, err, status := h.loadUserSummary(r)
|
summary, err, status := su.LoadUserSummary(h.summarySrvc, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(status)
|
w.WriteHeader(status)
|
||||||
templates[conf.SummaryTemplate].Execute(w, h.buildViewModel(r).WithError(err.Error()))
|
templates[conf.SummaryTemplate].Execute(w, h.buildViewModel(r).WithError(err.Error()))
|
||||||
@ -77,25 +70,6 @@ func (h *SummaryHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
|
|||||||
templates[conf.SummaryTemplate].Execute(w, vm)
|
templates[conf.SummaryTemplate].Execute(w, vm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *SummaryHandler) loadUserSummary(r *http.Request) (*models.Summary, error, int) {
|
|
||||||
summaryParams, err := utils.ParseSummaryParams(r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err, http.StatusBadRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
var retrieveSummary services.SummaryRetriever = h.summarySrvc.Retrieve
|
|
||||||
if summaryParams.Recompute {
|
|
||||||
retrieveSummary = h.summarySrvc.Summarize
|
|
||||||
}
|
|
||||||
|
|
||||||
summary, err := h.summarySrvc.Aliased(summaryParams.From, summaryParams.To, summaryParams.User, retrieveSummary)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err, http.StatusInternalServerError
|
|
||||||
}
|
|
||||||
|
|
||||||
return summary, nil, http.StatusOK
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *SummaryHandler) buildViewModel(r *http.Request) *view.SummaryViewModel {
|
func (h *SummaryHandler) buildViewModel(r *http.Request) *view.SummaryViewModel {
|
||||||
return &view.SummaryViewModel{
|
return &view.SummaryViewModel{
|
||||||
Success: r.URL.Query().Get("success"),
|
Success: r.URL.Query().Get("success"),
|
||||||
|
27
routes/utils/summary_utils.go
Normal file
27
routes/utils/summary_utils.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/muety/wakapi/models"
|
||||||
|
"github.com/muety/wakapi/services"
|
||||||
|
"github.com/muety/wakapi/utils"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func LoadUserSummary(ss services.ISummaryService, r *http.Request) (*models.Summary, error, int) {
|
||||||
|
summaryParams, err := utils.ParseSummaryParams(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err, http.StatusBadRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
var retrieveSummary services.SummaryRetriever = ss.Retrieve
|
||||||
|
if summaryParams.Recompute {
|
||||||
|
retrieveSummary = ss.Summarize
|
||||||
|
}
|
||||||
|
|
||||||
|
summary, err := ss.Aliased(summaryParams.From, summaryParams.To, summaryParams.User, retrieveSummary)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err, http.StatusInternalServerError
|
||||||
|
}
|
||||||
|
|
||||||
|
return summary, nil, http.StatusOK
|
||||||
|
}
|
@ -97,10 +97,10 @@ func (srv *AggregationService) trigger(jobs chan<- *AggregationJob, userIds map[
|
|||||||
logbuch.Error(err.Error())
|
logbuch.Error(err.Error())
|
||||||
return err
|
return err
|
||||||
} else if userIds != nil && len(userIds) > 0 {
|
} else if userIds != nil && len(userIds) > 0 {
|
||||||
users = make([]*models.User, len(userIds))
|
users = make([]*models.User, 0)
|
||||||
for i, u := range allUsers {
|
for _, u := range allUsers {
|
||||||
if yes, ok := userIds[u.ID]; yes && ok {
|
if yes, ok := userIds[u.ID]; yes && ok {
|
||||||
users[i] = u
|
users = append(users, u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -61,6 +61,7 @@ type IUserService interface {
|
|||||||
GetAll() ([]*models.User, error)
|
GetAll() ([]*models.User, error)
|
||||||
CreateOrGet(*models.Signup) (*models.User, bool, error)
|
CreateOrGet(*models.Signup) (*models.User, bool, error)
|
||||||
Update(*models.User) (*models.User, error)
|
Update(*models.User) (*models.User, error)
|
||||||
|
Delete(*models.User) error
|
||||||
ResetApiKey(*models.User) (*models.User, error)
|
ResetApiKey(*models.User) (*models.User, error)
|
||||||
ToggleBadges(*models.User) (*models.User, error)
|
ToggleBadges(*models.User) (*models.User, error)
|
||||||
SetWakatimeApiKey(*models.User, string) (*models.User, error)
|
SetWakatimeApiKey(*models.User, string) (*models.User, error)
|
||||||
|
@ -103,3 +103,8 @@ func (srv *UserService) MigrateMd5Password(user *models.User, login *models.Logi
|
|||||||
}
|
}
|
||||||
return srv.repository.UpdateField(user, "password", user.Password)
|
return srv.repository.UpdateField(user, "password", user.Password)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (srv *UserService) Delete(user *models.User) error {
|
||||||
|
srv.cache.Flush()
|
||||||
|
return srv.repository.Delete(user)
|
||||||
|
}
|
||||||
|
@ -1 +1 @@
|
|||||||
1.21.0
|
1.22.2
|
@ -64,7 +64,8 @@
|
|||||||
Change Password
|
Change Password
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<form class="mt-10" action="settings/credentials" method="post">
|
<form class="mt-10" action="" method="post">
|
||||||
|
<input type="hidden" name="action" value="change_password">
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<label class="inline-block text-sm mb-1 text-gray-500" for="password_old">Current Password</label>
|
<label class="inline-block text-sm mb-1 text-gray-500" for="password_old">Current Password</label>
|
||||||
<input class="shadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded w-full py-1 px-3"
|
<input class="shadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded w-full py-1 px-3"
|
||||||
@ -96,7 +97,8 @@
|
|||||||
Reset API Key
|
Reset API Key
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<form class="mt-6" action="settings/reset" method="post">
|
<form class="mt-6" action="" method="post">
|
||||||
|
<input type="hidden" name="action" value="reset_apikey">
|
||||||
<div class="text-gray-300 text-sm mb-4">
|
<div class="text-gray-300 text-sm mb-4">
|
||||||
<strong>⚠️ Caution:</strong> Resetting your API key requires you to update your <span
|
<strong>⚠️ Caution:</strong> Resetting your API key requires you to update your <span
|
||||||
class="font-mono">.wakatime.cfg</span> files on all of your computers to make the WakaTime
|
class="font-mono">.wakatime.cfg</span> files on all of your computers to make the WakaTime
|
||||||
@ -141,7 +143,8 @@
|
|||||||
are mapped to <span class="underline">{{ $alias.Type | typeName }}</span> <span
|
are mapped to <span class="underline">{{ $alias.Type | typeName }}</span> <span
|
||||||
class="text-white text-xs bg-gray-900 rounded py-1 px-2 font-mono">{{ $alias.Key }}</span>.
|
class="text-white text-xs bg-gray-900 rounded py-1 px-2 font-mono">{{ $alias.Key }}</span>.
|
||||||
</div>
|
</div>
|
||||||
<form class="float-right" action="settings/aliases/delete" method="post">
|
<form class="float-right" action="" method="post">
|
||||||
|
<input type="hidden" name="action" value="delete_alias">
|
||||||
<input type="hidden" id="delete_alias_key" name="key" required value="{{ $alias.Key }}">
|
<input type="hidden" id="delete_alias_key" name="key" required value="{{ $alias.Key }}">
|
||||||
<input type="hidden" id="delete_alias_type" name="type" required value="{{ $alias.Type }}">
|
<input type="hidden" id="delete_alias_type" name="type" required value="{{ $alias.Type }}">
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
@ -155,7 +158,8 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<h3 class="inline-block font-semibold text-md border-b border-green-700 text-white mb-2">Add Rule</h3>
|
<h3 class="inline-block font-semibold text-md border-b border-green-700 text-white mb-2">Add Rule</h3>
|
||||||
<form action="settings/aliases" method="post">
|
<form action="" method="post">
|
||||||
|
<input type="hidden" name="action" value="add_alias">
|
||||||
<div class="flex items-center mt-2 w-full text-gray-500 text-sm">
|
<div class="flex items-center mt-2 w-full text-gray-500 text-sm">
|
||||||
<span class="mr-2">Map</span>
|
<span class="mr-2">Map</span>
|
||||||
<select name="type" id="select-type"
|
<select name="type" id="select-type"
|
||||||
@ -203,7 +207,8 @@
|
|||||||
then change the <span class="underline">language</span> to <span
|
then change the <span class="underline">language</span> to <span
|
||||||
class="text-white text-xs bg-gray-900 rounded py-1 px-2 font-mono mr-1">{{ $mapping.Language }}</span>
|
class="text-white text-xs bg-gray-900 rounded py-1 px-2 font-mono mr-1">{{ $mapping.Language }}</span>
|
||||||
</div>
|
</div>
|
||||||
<form class="float-right" action="settings/language_mappings/delete" method="post">
|
<form class="float-right" action="" method="post">
|
||||||
|
<input type="hidden" name="action" value="delete_mapping">
|
||||||
<input type="hidden" id="mapping_id" name="mapping_id" required value="{{ $mapping.ID }}">
|
<input type="hidden" id="mapping_id" name="mapping_id" required value="{{ $mapping.ID }}">
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
class="py-1 px-3 rounded border border-red-500 hover:border-red-600 text-gray-400 text-sm">
|
class="py-1 px-3 rounded border border-red-500 hover:border-red-600 text-gray-400 text-sm">
|
||||||
@ -216,7 +221,8 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<h3 class="inline-block font-semibold text-md border-b border-green-700 text-white">Add Rule</h3>
|
<h3 class="inline-block font-semibold text-md border-b border-green-700 text-white">Add Rule</h3>
|
||||||
<form action="settings/language_mappings" method="post">
|
<form action="" method="post">
|
||||||
|
<input type="hidden" name="action" value="add_mapping">
|
||||||
<div class="flex items-center w-full text-gray-500 text-sm">
|
<div class="flex items-center w-full text-gray-500 text-sm">
|
||||||
<span class="mr-2">When filename ends in</span>
|
<span class="mr-2">When filename ends in</span>
|
||||||
<input class="shadow appearance-nonshadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded py-1 px-3"
|
<input class="shadow appearance-nonshadow appearance-none bg-gray-800 focus:bg-gray-700 text-gray-300 border-green-700 focus:border-gray-500 border rounded py-1 px-3"
|
||||||
@ -241,7 +247,8 @@
|
|||||||
Badges
|
Badges
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form class="mt-6" action="settings/badges" method="post">
|
<form class="mt-6" action="" method="post">
|
||||||
|
<input type="hidden" name="action" value="toggle_badges">
|
||||||
<div class="text-gray-300 text-sm mb-4">
|
<div class="text-gray-300 text-sm mb-4">
|
||||||
{{ if .User.BadgesEnabled }}
|
{{ if .User.BadgesEnabled }}
|
||||||
<p>Badges are currently enabled. You can disable the feature by deactivating the respective API
|
<p>Badges are currently enabled. You can disable the feature by deactivating the respective API
|
||||||
@ -328,7 +335,8 @@
|
|||||||
target="_blank">get your API key</a> and paste it here.</p>
|
target="_blank">get your API key</a> and paste it here.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form action="settings/wakatime_integration" method="post">
|
<form action="" method="post">
|
||||||
|
<input type="hidden" name="action" value="toggle_wakatime">
|
||||||
|
|
||||||
{{ $placeholderText := "Paste your WakaTime API key here ..." }}
|
{{ $placeholderText := "Paste your WakaTime API key here ..." }}
|
||||||
{{ if .User.WakatimeApiKey }}
|
{{ if .User.WakatimeApiKey }}
|
||||||
@ -389,14 +397,34 @@
|
|||||||
case heartbeats were deleted after the respective summaries had been generated.
|
case heartbeats were deleted after the respective summaries had been generated.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-10 flex justify-center">
|
<div class="mt-6 flex justify-center">
|
||||||
<form action="settings/regenerate" method="post" id="form-regenerate-summaries">
|
<form action="" method="post" id="form-regenerate-summaries">
|
||||||
|
<input type="hidden" name="action" value="regenerate_summaries">
|
||||||
<button type="button" class="py-1 px-3 rounded bg-red-500 hover:bg-red-600 text-white text-sm"
|
<button type="button" class="py-1 px-3 rounded bg-red-500 hover:bg-red-600 text-white text-sm"
|
||||||
id="btn-regenerate-summaries">
|
id="btn-regenerate-summaries">
|
||||||
Clear & Regenerate
|
Clear & Regenerate
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-10 text-gray-300 text-sm">
|
||||||
|
<h3 class="font-semibold text-md mb-4 border-b border-green-700 inline-block">
|
||||||
|
Delete Account
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
Deleting your account will cause all data, including all your heartbeats, to be erased from the
|
||||||
|
server immediately. This action is irreversible. <strong>Be careful!</strong>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="mt-6 flex justify-center">
|
||||||
|
<form action="" method="post" id="form-delete-user">
|
||||||
|
<input type="hidden" name="action" value="delete_account">
|
||||||
|
<button type="button" class="py-1 px-3 rounded bg-red-500 hover:bg-red-600 text-white text-sm"
|
||||||
|
id="btn-confirm-delete-user">
|
||||||
|
Delete my Account
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
@ -419,6 +447,14 @@
|
|||||||
formRegenerate.submit()
|
formRegenerate.submit()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const btnDelete = document.querySelector('#btn-confirm-delete-user')
|
||||||
|
const formDelete = document.querySelector('#form-delete-user')
|
||||||
|
btnDelete.addEventListener('click', () => {
|
||||||
|
if (confirm('Are you sure? This can not be undone!')) {
|
||||||
|
formDelete.submit()
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{{ template "footer.tpl.html" . }}
|
{{ template "footer.tpl.html" . }}
|
||||||
|
Reference in New Issue
Block a user