mirror of
https://github.com/muety/wakapi.git
synced 2023-08-10 21:12:56 +03:00
Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
3a4504d56a | |||
a018f70c3f | |||
a03e49e7f0 | |||
ec81d9fe5d | |||
b7a1e2d795 | |||
98b62b33c8 | |||
262bee9022 | |||
9766d8e903 | |||
39c4777fc8 | |||
143c80b7b4 | |||
72e42a9c42 | |||
439a87dec9 | |||
e8067bb13e | |||
219e969957 | |||
889edd7a33 | |||
4161623c24 |
@ -1 +1,10 @@
|
|||||||
.env
|
.env
|
||||||
|
config*.yml
|
||||||
|
!config.default.yml
|
||||||
|
*.db
|
||||||
|
*.exe
|
||||||
|
wakapi
|
||||||
|
Dockerfile
|
||||||
|
docker-compose.yml
|
||||||
|
.dockerignore
|
||||||
|
.git*
|
||||||
|
38
Dockerfile
38
Dockerfile
@ -1,26 +1,27 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
|
|
||||||
FROM golang:1.13 AS build-env
|
FROM golang:1.15 AS build-env
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
ADD ./go.mod .
|
ADD ./go.mod .
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
|
|
||||||
ADD . .
|
ADD . .
|
||||||
RUN go build -o wakapi
|
RUN go build -o wakapi
|
||||||
|
|
||||||
# Final Stage
|
WORKDIR /app
|
||||||
|
RUN cp /src/wakapi . && \
|
||||||
|
cp /src/config.default.yml config.yml && \
|
||||||
|
sed -i 's/listen_ipv6: ::1/listen_ipv6: /g' config.yml && \
|
||||||
|
cp /src/version.txt . && \
|
||||||
|
cp -r /src/static /src/data /src/migrations /src/views . && \
|
||||||
|
cp /src/wait-for-it.sh .
|
||||||
|
|
||||||
|
# Run Stage
|
||||||
|
|
||||||
# When running the application using `docker run`, you can pass environment variables
|
# When running the application using `docker run`, you can pass environment variables
|
||||||
# to override config values using `-e` syntax.
|
# to override config values using `-e` syntax.
|
||||||
# Available options are:
|
# Available options can be found in [README.md#-configuration](README.md#-configuration)
|
||||||
# – WAKAPI_DB_TYPE
|
|
||||||
# – WAKAPI_DB_USER
|
|
||||||
# – WAKAPI_DB_PASSWORD
|
|
||||||
# – WAKAPI_DB_HOST
|
|
||||||
# – WAKAPI_DB_PORT
|
|
||||||
# – WAKAPI_DB_NAME
|
|
||||||
# – WAKAPI_PASSWORD_SALT
|
|
||||||
# – WAKAPI_BASE_PATH
|
|
||||||
|
|
||||||
FROM debian
|
FROM debian
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
@ -32,19 +33,10 @@ ENV WAKAPI_DB_PASSWORD ''
|
|||||||
ENV WAKAPI_DB_HOST ''
|
ENV WAKAPI_DB_HOST ''
|
||||||
ENV WAKAPI_DB_NAME=/data/wakapi.db
|
ENV WAKAPI_DB_NAME=/data/wakapi.db
|
||||||
ENV WAKAPI_PASSWORD_SALT ''
|
ENV WAKAPI_PASSWORD_SALT ''
|
||||||
|
ENV WAKAPI_LISTEN_IPV4 '0.0.0.0'
|
||||||
|
ENV WAKAPI_INSECURE_COOKIES 'true'
|
||||||
|
|
||||||
COPY --from=build-env /src/wakapi /app/
|
COPY --from=build-env /app .
|
||||||
COPY --from=build-env /src/config.default.yml /app/config.yml
|
|
||||||
COPY --from=build-env /src/version.txt /app/
|
|
||||||
|
|
||||||
RUN sed -i 's/listen_ipv4: 127.0.0.1/listen_ipv4: 0.0.0.0/g' /app/config.yml
|
|
||||||
RUN sed -i 's/insecure_cookies: false/insecure_cookies: true/g' /app/config.yml
|
|
||||||
|
|
||||||
ADD static /app/static
|
|
||||||
ADD data /app/data
|
|
||||||
ADD migrations /app/migrations
|
|
||||||
ADD views /app/views
|
|
||||||
ADD wait-for-it.sh .
|
|
||||||
|
|
||||||
VOLUME /data
|
VOLUME /data
|
||||||
|
|
||||||
|
24
README.md
24
README.md
@ -21,9 +21,10 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
If you like this project, please consider supporting it 🙂. You can donate either through [buying me a coffee](https://buymeacoff.ee/n1try) or becoming a GitHub sponsor. Every little donation is highly appreciated and boosts the developers' motivation to keep improving Wakapi!
|
## 📬 **User Survey**
|
||||||
|
I'd love to get some community feedback from active Wakapi users. If you like, please participate in the recent [user survey](https://github.com/muety/wakapi/issues/82). Thanks a lot!
|
||||||
|
|
||||||
## 👀 Hosted Service
|
## 👀 Demo
|
||||||
🔥 **New:** Wakapi is available as a hosted service now. Check out **[wakapi.dev](https://wakapi.dev)**. Please use responsibly.
|
🔥 **New:** Wakapi is available as a hosted service now. Check out **[wakapi.dev](https://wakapi.dev)**. Please use responsibly.
|
||||||
|
|
||||||
To use the hosted version set `api_url = https://wakapi.dev/api/heartbeat`. However, we do not guarantee data persistence, so you might potentially lose your data if the service is taken down some day ❕
|
To use the hosted version set `api_url = https://wakapi.dev/api/heartbeat`. However, we do not guarantee data persistence, so you might potentially lose your data if the service is taken down some day ❕
|
||||||
@ -53,14 +54,14 @@ To use the hosted version set `api_url = https://wakapi.dev/api/heartbeat`. Howe
|
|||||||
|
|
||||||
### Run with Docker
|
### Run with Docker
|
||||||
```bash
|
```bash
|
||||||
docker run -d -p 3000:3000 --name wakapi n1try/wakapi
|
docker run -d -p 3000:3000 -e "WAKAPI_PASSWORD_SALT=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w ${1:-32} | head -n 1)" --name wakapi n1try/wakapi
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, SQLite is used as a database. To run Wakapi in Docker with MySQL or Postgres, see [Dockerfile](https://github.com/muety/wakapi/blob/master/Dockerfile) and [config.default.yml](https://github.com/muety/wakapi/blob/master/config.default.yml) for further options.
|
By default, SQLite is used as a database. To run Wakapi in Docker with MySQL or Postgres, see [Dockerfile](https://github.com/muety/wakapi/blob/master/Dockerfile) and [config.default.yml](https://github.com/muety/wakapi/blob/master/config.default.yml) for further options.
|
||||||
|
|
||||||
### Running tests
|
### Running tests
|
||||||
```bash
|
```bash
|
||||||
CGO_FLAGS="-g -O2 -Wno-return-local-addr" -coverprofile=coverage/coverage.out go test ./...
|
CGO_FLAGS="-g -O2 -Wno-return-local-addr" go test -json -coverprofile=coverage/coverage.out ./... -run ./...
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🔧 Configuration
|
## 🔧 Configuration
|
||||||
@ -71,12 +72,14 @@ You can specify configuration options either via a config file (default: `config
|
|||||||
| `env` | `ENVIRONMENT` | `dev` | Whether to use development- or production settings |
|
| `env` | `ENVIRONMENT` | `dev` | Whether to use development- or production settings |
|
||||||
| `app.custom_languages` | - | - | Map from file endings to language names |
|
| `app.custom_languages` | - | - | Map from file endings to language names |
|
||||||
| `server.port` | `WAKAPI_PORT` | `3000` | Port to listen on |
|
| `server.port` | `WAKAPI_PORT` | `3000` | Port to listen on |
|
||||||
| `server.listen_ipv4` | `WAKAPI_LISTEN_IPV4` | `127.0.0.1` | Network address to listen on |
|
| `server.listen_ipv4` | `WAKAPI_LISTEN_IPV4` | `127.0.0.1` | IPv4 network address to listen on (leave blank to disable IPv4) |
|
||||||
|
| `server.listen_ipv6` | `WAKAPI_LISTEN_IPV6` | `::1` | IPv6 network address to listen on (leave blank to disable IPv6) |
|
||||||
|
| `server.tls_cert_path` | `WAKAPI_TLS_CERT_PATH` | - | Path of SSL server certificate (leave blank to not use HTTPS) |
|
||||||
|
| `server.tls_key_path` | `WAKAPI_TLS_KEY_PATH` | - | Path of SSL server private key (leave blank to not use HTTPS) |
|
||||||
| `server.base_path` | `WAKAPI_BASE_PATH` | `/` | Web base path (change when running behind a proxy under a sub-path) |
|
| `server.base_path` | `WAKAPI_BASE_PATH` | `/` | Web base path (change when running behind a proxy under a sub-path) |
|
||||||
| `security.password_salt` | `WAKAPI_PASSWORD_SALT` | - | Pepper to use for password hashing |
|
| `security.password_salt` | `WAKAPI_PASSWORD_SALT` | - | Pepper to use for password hashing |
|
||||||
| `security.insecure_cookies` | `WAKAPI_INSECURE_COOKIES` | `false` | Whether or not to allow cookies over HTTP |
|
| `security.insecure_cookies` | `WAKAPI_INSECURE_COOKIES` | `false` | Whether or not to allow cookies over HTTP |
|
||||||
| `security.cookie_max_age` | `WAKAPI_COOKIE_MAX_AGE ` | `172800` | Lifetime of authentication cookies in seconds or `0` to use [Session](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Define_the_lifetime_of_a_cookie) cookies
|
| `security.cookie_max_age` | `WAKAPI_COOKIE_MAX_AGE ` | `172800` | Lifetime of authentication cookies in seconds or `0` to use [Session](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Define_the_lifetime_of_a_cookie) cookies |
|
||||||
|
|
|
||||||
| `db.host` | `WAKAPI_DB_HOST` | - | Database host |
|
| `db.host` | `WAKAPI_DB_HOST` | - | Database host |
|
||||||
| `db.port` | `WAKAPI_DB_PORT` | - | Database port |
|
| `db.port` | `WAKAPI_DB_PORT` | - | Database port |
|
||||||
| `db.user` | `WAKAPI_DB_USER` | - | Database user |
|
| `db.user` | `WAKAPI_DB_USER` | - | Database user |
|
||||||
@ -98,6 +101,10 @@ api_key = the_api_key_printed_to_the_console_after_starting_the_server`
|
|||||||
|
|
||||||
You can view your API Key after logging in to the web interface.
|
You can view your API Key after logging in to the web interface.
|
||||||
|
|
||||||
|
### Optional: Client-side proxy
|
||||||
|
|
||||||
|
See the [advanced setup instructions](docs/advanced_setup.md).
|
||||||
|
|
||||||
## 🔵 Customization
|
## 🔵 Customization
|
||||||
|
|
||||||
### Aliases
|
### Aliases
|
||||||
@ -142,6 +149,9 @@ We recently introduced support for [Shields.io](https://shields.io) badges (see
|
|||||||
It is recommended to use wakapi behind a **reverse proxy**, like [Caddy](https://caddyserver.com) or _nginx_ to enable **TLS encryption** (HTTPS).
|
It is recommended to use wakapi behind a **reverse proxy**, like [Caddy](https://caddyserver.com) or _nginx_ to enable **TLS encryption** (HTTPS).
|
||||||
However, if you want to expose your wakapi instance to the public anyway, you need to set `server.listen_ipv4` to `0.0.0.0` in `config.yml`
|
However, if you want to expose your wakapi instance to the public anyway, you need to set `server.listen_ipv4` to `0.0.0.0` in `config.yml`
|
||||||
|
|
||||||
|
## 🙏 Support
|
||||||
|
If you like this project, please consider supporting it 🙂. You can donate either through [buying me a coffee](https://buymeacoff.ee/n1try) or becoming a GitHub sponsor. Every little donation is highly appreciated and boosts the developers' motivation to keep improving Wakapi!
|
||||||
|
|
||||||
## ⚠️ Important Note
|
## ⚠️ Important Note
|
||||||
**This is not an alternative to using WakaTime.** It is just a custom, non-commercial, self-hosted application to collect coding statistics using the already existing editor plugins provided by the WakaTime community. It was created for personal use only and with the purpose of keeping the sovereignity of your own data. However, if you like the official product, **please support the authors and buy an official WakaTime subscription!**
|
**This is not an alternative to using WakaTime.** It is just a custom, non-commercial, self-hosted application to collect coding statistics using the already existing editor plugins provided by the WakaTime community. It was created for personal use only and with the purpose of keeping the sovereignity of your own data. However, if you like the official product, **please support the authors and buy an official WakaTime subscription!**
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
env: development
|
env: development
|
||||||
|
|
||||||
server:
|
server:
|
||||||
listen_ipv4: 127.0.0.1
|
listen_ipv4: 127.0.0.1 # leave blank to disable ipv4
|
||||||
|
listen_ipv6: ::1 # leave blank to disable ipv6
|
||||||
|
tls_cert_path: # leave blank to not use https
|
||||||
|
tls_key_path: # leave blank to not use https
|
||||||
port: 3000
|
port: 3000
|
||||||
base_path: /
|
base_path: /
|
||||||
|
|
||||||
|
@ -59,7 +59,10 @@ type dbConfig struct {
|
|||||||
type serverConfig struct {
|
type serverConfig struct {
|
||||||
Port int `default:"3000" env:"WAKAPI_PORT"`
|
Port int `default:"3000" env:"WAKAPI_PORT"`
|
||||||
ListenIpV4 string `yaml:"listen_ipv4" default:"127.0.0.1" env:"WAKAPI_LISTEN_IPV4"`
|
ListenIpV4 string `yaml:"listen_ipv4" default:"127.0.0.1" env:"WAKAPI_LISTEN_IPV4"`
|
||||||
|
ListenIpV6 string `yaml:"listen_ipv6" default:"::1" env:"WAKAPI_LISTEN_IPV6"`
|
||||||
BasePath string `yaml:"base_path" default:"/" env:"WAKAPI_BASE_PATH"`
|
BasePath string `yaml:"base_path" default:"/" env:"WAKAPI_BASE_PATH"`
|
||||||
|
TlsCertPath string `yaml:"tls_cert_path" default:"" env:"WAKAPI_TLS_CERT_PATH"`
|
||||||
|
TlsKeyPath string `yaml:"tls_key_path" default:"" env:"WAKAPI_TLS_KEY_PATH"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@ -95,6 +98,10 @@ func (c *Config) IsDev() bool {
|
|||||||
return IsDev(c.Env)
|
return IsDev(c.Env)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Config) UseTLS() bool {
|
||||||
|
return c.Server.TlsCertPath != "" && c.Server.TlsKeyPath != ""
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Config) GetMigrationFunc(dbDialect string) models.MigrationFunc {
|
func (c *Config) GetMigrationFunc(dbDialect string) models.MigrationFunc {
|
||||||
switch dbDialect {
|
switch dbDialect {
|
||||||
default:
|
default:
|
||||||
@ -172,6 +179,14 @@ func sqliteConnectionString(config *dbConfig) string {
|
|||||||
return config.Name
|
return config.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *appConfig) GetCustomLanguages() map[string]string {
|
||||||
|
return cloneStringMap(c.CustomLanguages)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *appConfig) GetLanguageColors() map[string]string {
|
||||||
|
return cloneStringMap(c.LanguageColors)
|
||||||
|
}
|
||||||
|
|
||||||
func IsDev(env string) bool {
|
func IsDev(env string) bool {
|
||||||
return env == "dev" || env == "development"
|
return env == "dev" || env == "development"
|
||||||
}
|
}
|
||||||
@ -260,6 +275,10 @@ func Load() *Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.Server.ListenIpV4 == "" && config.Server.ListenIpV6 == "" {
|
||||||
|
log.Fatalln("either of listen_ipv4 or listen_ipv6 must be set")
|
||||||
|
}
|
||||||
|
|
||||||
Set(config)
|
Set(config)
|
||||||
return Get()
|
return Get()
|
||||||
}
|
}
|
||||||
|
9
config/utils.go
Normal file
9
config/utils.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
func cloneStringMap(m map[string]string) map[string]string {
|
||||||
|
m2 := make(map[string]string)
|
||||||
|
for k, v := range m {
|
||||||
|
m2[k] = v
|
||||||
|
}
|
||||||
|
return m2
|
||||||
|
}
|
@ -1,4 +1,18 @@
|
|||||||
mode: set
|
mode: set
|
||||||
|
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/filters.go:16.56,17.16 1 0
|
github.com/muety/wakapi/models/filters.go:16.56,17.16 1 0
|
||||||
github.com/muety/wakapi/models/filters.go:29.2,29.19 1 0
|
github.com/muety/wakapi/models/filters.go:29.2,29.19 1 0
|
||||||
github.com/muety/wakapi/models/filters.go:18.22,19.32 1 0
|
github.com/muety/wakapi/models/filters.go:18.22,19.32 1 0
|
||||||
@ -28,6 +42,17 @@ github.com/muety/wakapi/models/filters.go:53.20,55.3 1 0
|
|||||||
github.com/muety/wakapi/models/filters.go:56.22,58.3 1 1
|
github.com/muety/wakapi/models/filters.go:56.22,58.3 1 1
|
||||||
github.com/muety/wakapi/models/filters.go:59.21,61.3 1 0
|
github.com/muety/wakapi/models/filters.go:59.21,61.3 1 0
|
||||||
github.com/muety/wakapi/models/filters.go:62.16,64.3 1 0
|
github.com/muety/wakapi/models/filters.go:62.16,64.3 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:15.36,17.2 1 0
|
||||||
|
github.com/muety/wakapi/models/heartbeats.go:19.43,22.2 2 0
|
||||||
|
github.com/muety/wakapi/models/heartbeats.go:24.41,26.18 1 0
|
||||||
|
github.com/muety/wakapi/models/heartbeats.go:29.2,29.16 1 0
|
||||||
|
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:37.2,37.24 1 0
|
||||||
|
github.com/muety/wakapi/models/heartbeats.go:34.18,36.3 1 0
|
||||||
|
github.com/muety/wakapi/models/models.go:3.14,5.2 0 1
|
||||||
github.com/muety/wakapi/models/heartbeat.go:26.34,28.2 1 1
|
github.com/muety/wakapi/models/heartbeat.go:26.34,28.2 1 1
|
||||||
github.com/muety/wakapi/models/heartbeat.go:30.65,31.28 1 1
|
github.com/muety/wakapi/models/heartbeat.go:30.65,31.28 1 1
|
||||||
github.com/muety/wakapi/models/heartbeat.go:34.2,35.45 2 1
|
github.com/muety/wakapi/models/heartbeat.go:34.2,35.45 2 1
|
||||||
@ -45,10 +70,12 @@ github.com/muety/wakapi/models/heartbeat.go:51.23,52.19 1 1
|
|||||||
github.com/muety/wakapi/models/heartbeat.go:53.17,54.26 1 1
|
github.com/muety/wakapi/models/heartbeat.go:53.17,54.26 1 1
|
||||||
github.com/muety/wakapi/models/heartbeat.go:55.22,56.18 1 1
|
github.com/muety/wakapi/models/heartbeat.go:55.22,56.18 1 1
|
||||||
github.com/muety/wakapi/models/heartbeat.go:59.15,61.3 1 1
|
github.com/muety/wakapi/models/heartbeat.go:59.15,61.3 1 1
|
||||||
github.com/muety/wakapi/models/models.go:3.14,5.2 0 1
|
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/summary.go:29.27,33.2 1 0
|
github.com/muety/wakapi/models/summary.go:29.27,33.2 1 0
|
||||||
github.com/muety/wakapi/models/summary.go:83.29,85.2 1 1
|
github.com/muety/wakapi/models/summary.go:83.29,85.2 1 1
|
||||||
github.com/muety/wakapi/models/summary.go:87.37,94.2 6 0
|
github.com/muety/wakapi/models/summary.go:87.37,94.2 6 1
|
||||||
github.com/muety/wakapi/models/summary.go:96.35,98.2 1 1
|
github.com/muety/wakapi/models/summary.go:96.35,98.2 1 1
|
||||||
github.com/muety/wakapi/models/summary.go:100.57,108.2 1 1
|
github.com/muety/wakapi/models/summary.go:100.57,108.2 1 1
|
||||||
github.com/muety/wakapi/models/summary.go:121.33,126.26 4 1
|
github.com/muety/wakapi/models/summary.go:121.33,126.26 4 1
|
||||||
@ -93,40 +120,100 @@ github.com/muety/wakapi/models/summary.go:216.31,218.60 1 1
|
|||||||
github.com/muety/wakapi/models/summary.go:218.60,219.55 1 1
|
github.com/muety/wakapi/models/summary.go:218.60,219.55 1 1
|
||||||
github.com/muety/wakapi/models/summary.go:219.55,221.6 1 1
|
github.com/muety/wakapi/models/summary.go:219.55,221.6 1 1
|
||||||
github.com/muety/wakapi/models/summary.go:221.11,229.6 1 1
|
github.com/muety/wakapi/models/summary.go:221.11,229.6 1 1
|
||||||
github.com/muety/wakapi/models/summary.go:246.33,248.2 1 0
|
github.com/muety/wakapi/models/summary.go:246.33,248.2 1 1
|
||||||
github.com/muety/wakapi/models/summary.go:250.43,252.2 1 0
|
github.com/muety/wakapi/models/summary.go:250.43,252.2 1 1
|
||||||
github.com/muety/wakapi/models/summary.go:254.38,256.2 1 0
|
github.com/muety/wakapi/models/summary.go:254.38,256.2 1 1
|
||||||
github.com/muety/wakapi/models/user.go:34.43,37.2 1 0
|
github.com/muety/wakapi/models/user.go:34.43,37.2 1 0
|
||||||
github.com/muety/wakapi/models/user.go:39.33,43.2 1 0
|
github.com/muety/wakapi/models/user.go:39.33,43.2 1 0
|
||||||
github.com/muety/wakapi/models/user.go:45.45,47.2 1 0
|
github.com/muety/wakapi/models/user.go:45.45,47.2 1 0
|
||||||
github.com/muety/wakapi/models/user.go:49.45,51.2 1 0
|
github.com/muety/wakapi/models/user.go:49.45,51.2 1 0
|
||||||
github.com/muety/wakapi/models/heartbeats.go:7.31,9.2 1 0
|
github.com/muety/wakapi/config/config.go:77.70,79.2 1 0
|
||||||
github.com/muety/wakapi/models/heartbeats.go:11.41,13.2 1 0
|
github.com/muety/wakapi/config/config.go:81.65,83.2 1 0
|
||||||
github.com/muety/wakapi/models/heartbeats.go:15.36,17.2 1 0
|
github.com/muety/wakapi/config/config.go:85.82,95.2 1 0
|
||||||
github.com/muety/wakapi/models/heartbeats.go:19.43,22.2 2 0
|
github.com/muety/wakapi/config/config.go:97.31,99.2 1 0
|
||||||
github.com/muety/wakapi/models/heartbeats.go:24.41,26.18 1 0
|
github.com/muety/wakapi/config/config.go:101.32,103.2 1 0
|
||||||
github.com/muety/wakapi/models/heartbeats.go:29.2,29.16 1 0
|
github.com/muety/wakapi/config/config.go:105.74,106.19 1 0
|
||||||
github.com/muety/wakapi/models/heartbeats.go:26.18,28.3 1 0
|
github.com/muety/wakapi/config/config.go:107.10,108.34 1 0
|
||||||
github.com/muety/wakapi/models/heartbeats.go:32.40,34.18 1 0
|
github.com/muety/wakapi/config/config.go:108.34,117.4 8 0
|
||||||
github.com/muety/wakapi/models/heartbeats.go:37.2,37.24 1 0
|
github.com/muety/wakapi/config/config.go:121.73,122.33 1 0
|
||||||
github.com/muety/wakapi/models/heartbeats.go:34.18,36.3 1 0
|
github.com/muety/wakapi/config/config.go:122.33,130.17 5 0
|
||||||
github.com/muety/wakapi/models/language_mapping.go:11.42,13.2 1 0
|
github.com/muety/wakapi/config/config.go:134.3,135.13 2 0
|
||||||
github.com/muety/wakapi/models/language_mapping.go:15.51,17.2 1 0
|
github.com/muety/wakapi/config/config.go:130.17,132.4 1 0
|
||||||
github.com/muety/wakapi/models/language_mapping.go:19.52,21.2 1 0
|
github.com/muety/wakapi/config/config.go:139.50,140.19 1 0
|
||||||
github.com/muety/wakapi/models/shared.go:34.52,37.16 3 0
|
github.com/muety/wakapi/config/config.go:153.2,153.12 1 0
|
||||||
github.com/muety/wakapi/models/shared.go:40.2,42.12 3 0
|
github.com/muety/wakapi/config/config.go:141.23,145.5 1 0
|
||||||
github.com/muety/wakapi/models/shared.go:37.16,39.3 1 0
|
github.com/muety/wakapi/config/config.go:146.26,149.5 1 0
|
||||||
github.com/muety/wakapi/models/shared.go:46.52,52.22 2 0
|
github.com/muety/wakapi/config/config.go:150.24,151.48 1 0
|
||||||
github.com/muety/wakapi/models/shared.go:68.2,71.12 3 0
|
github.com/muety/wakapi/config/config.go:156.53,166.2 1 1
|
||||||
github.com/muety/wakapi/models/shared.go:53.14,55.17 2 0
|
github.com/muety/wakapi/config/config.go:168.56,176.2 1 1
|
||||||
github.com/muety/wakapi/models/shared.go:58.13,60.8 2 0
|
github.com/muety/wakapi/config/config.go:178.54,180.2 1 1
|
||||||
github.com/muety/wakapi/models/shared.go:61.17,63.8 2 0
|
github.com/muety/wakapi/config/config.go:182.60,184.2 1 0
|
||||||
github.com/muety/wakapi/models/shared.go:64.10,65.64 1 0
|
github.com/muety/wakapi/config/config.go:186.59,188.2 1 0
|
||||||
github.com/muety/wakapi/models/shared.go:55.17,57.4 1 0
|
github.com/muety/wakapi/config/config.go:190.29,192.2 1 1
|
||||||
github.com/muety/wakapi/models/shared.go:74.51,77.2 2 0
|
github.com/muety/wakapi/config/config.go:194.27,196.16 2 0
|
||||||
github.com/muety/wakapi/models/shared.go:79.37,82.2 2 0
|
github.com/muety/wakapi/config/config.go:199.2,202.16 3 0
|
||||||
github.com/muety/wakapi/models/shared.go:84.35,86.2 1 0
|
github.com/muety/wakapi/config/config.go:206.2,206.22 1 0
|
||||||
github.com/muety/wakapi/models/shared.go:88.34,90.2 1 0
|
github.com/muety/wakapi/config/config.go:196.16,198.3 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:202.16,204.3 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:209.45,219.16 4 0
|
||||||
|
github.com/muety/wakapi/config/config.go:223.2,223.57 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:227.2,227.30 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:231.2,231.15 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:219.16,221.3 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:223.57,225.3 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:227.30,229.3 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:234.38,235.43 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:239.2,239.15 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:235.43,237.3 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:242.26,244.2 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:246.20,248.2 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:250.21,257.96 4 0
|
||||||
|
github.com/muety/wakapi/config/config.go:261.2,268.52 4 0
|
||||||
|
github.com/muety/wakapi/config/config.go:272.2,272.47 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:278.2,278.70 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:282.2,283.14 2 0
|
||||||
|
github.com/muety/wakapi/config/config.go:257.96,259.3 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:268.52,270.3 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:272.47,273.14 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:273.14,275.4 1 0
|
||||||
|
github.com/muety/wakapi/config/config.go:278.70,280.3 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:13.33,14.57 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:14.57,16.3 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:16.8,16.16 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:16.16,18.47 2 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:21.3,21.128 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:18.47,20.4 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:25.48,26.54 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:31.2,31.18 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:26.54,28.3 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:28.8,28.32 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:28.32,30.3 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:34.34,37.16 2 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:40.2,41.16 2 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:45.2,57.16 11 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:61.2,61.18 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:65.2,69.16 5 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:73.2,75.23 3 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:80.2,82.33 3 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:87.2,114.16 3 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:119.2,119.78 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:123.2,123.12 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:37.16,39.3 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:41.16,43.3 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:57.16,59.3 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:61.18,63.3 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:69.16,71.3 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:75.23,77.3 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:82.33,84.3 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:114.16,116.3 1 0
|
||||||
|
github.com/muety/wakapi/config/legacy.go:119.78,121.3 1 0
|
||||||
|
github.com/muety/wakapi/config/utils.go:3.60,5.22 2 0
|
||||||
|
github.com/muety/wakapi/config/utils.go:8.2,8.11 1 0
|
||||||
|
github.com/muety/wakapi/config/utils.go:5.22,7.3 1 0
|
||||||
|
github.com/muety/wakapi/utils/color.go:8.93,10.41 2 0
|
||||||
|
github.com/muety/wakapi/utils/color.go:15.2,15.15 1 0
|
||||||
|
github.com/muety/wakapi/utils/color.go:10.41,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: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: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:17.45,19.2 1 0
|
||||||
@ -202,85 +289,6 @@ github.com/muety/wakapi/utils/auth.go:77.55,80.16 3 0
|
|||||||
github.com/muety/wakapi/utils/auth.go:83.2,83.16 1 0
|
github.com/muety/wakapi/utils/auth.go:83.2,83.16 1 0
|
||||||
github.com/muety/wakapi/utils/auth.go:80.16,82.3 1 0
|
github.com/muety/wakapi/utils/auth.go:80.16,82.3 1 0
|
||||||
github.com/muety/wakapi/utils/auth.go:86.43,91.2 4 0
|
github.com/muety/wakapi/utils/auth.go:86.43,91.2 4 0
|
||||||
github.com/muety/wakapi/utils/color.go:8.93,10.41 2 0
|
|
||||||
github.com/muety/wakapi/utils/color.go:15.2,15.15 1 0
|
|
||||||
github.com/muety/wakapi/utils/color.go:10.41,11.50 1 0
|
|
||||||
github.com/muety/wakapi/utils/color.go:11.50,13.4 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:74.70,76.2 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:78.65,80.2 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:82.82,92.2 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:94.31,96.2 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:98.74,99.19 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:100.10,101.34 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:101.34,110.4 8 0
|
|
||||||
github.com/muety/wakapi/config/config.go:114.73,115.33 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:115.33,123.17 5 0
|
|
||||||
github.com/muety/wakapi/config/config.go:127.3,128.13 2 0
|
|
||||||
github.com/muety/wakapi/config/config.go:123.17,125.4 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:132.50,133.19 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:146.2,146.12 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:134.23,138.5 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:139.26,142.5 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:143.24,144.48 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:149.53,159.2 1 1
|
|
||||||
github.com/muety/wakapi/config/config.go:161.56,169.2 1 1
|
|
||||||
github.com/muety/wakapi/config/config.go:171.54,173.2 1 1
|
|
||||||
github.com/muety/wakapi/config/config.go:175.29,177.2 1 1
|
|
||||||
github.com/muety/wakapi/config/config.go:179.27,181.16 2 0
|
|
||||||
github.com/muety/wakapi/config/config.go:184.2,187.16 3 0
|
|
||||||
github.com/muety/wakapi/config/config.go:191.2,191.22 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:181.16,183.3 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:187.16,189.3 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:194.45,204.16 4 0
|
|
||||||
github.com/muety/wakapi/config/config.go:208.2,208.57 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:212.2,212.30 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:216.2,216.15 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:204.16,206.3 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:208.57,210.3 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:212.30,214.3 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:219.38,220.43 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:224.2,224.15 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:220.43,222.3 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:227.26,229.2 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:231.20,233.2 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:235.21,242.96 4 0
|
|
||||||
github.com/muety/wakapi/config/config.go:246.2,253.52 4 0
|
|
||||||
github.com/muety/wakapi/config/config.go:257.2,257.47 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:263.2,264.14 2 0
|
|
||||||
github.com/muety/wakapi/config/config.go:242.96,244.3 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:253.52,255.3 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:257.47,258.14 1 0
|
|
||||||
github.com/muety/wakapi/config/config.go:258.14,260.4 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:13.33,14.57 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:14.57,16.3 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:16.8,16.16 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:16.16,18.47 2 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:21.3,21.128 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:18.47,20.4 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:25.48,26.54 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:31.2,31.18 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:26.54,28.3 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:28.8,28.32 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:28.32,30.3 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:34.34,37.16 2 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:40.2,41.16 2 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:45.2,57.16 11 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:61.2,61.18 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:65.2,69.16 5 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:73.2,75.23 3 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:80.2,82.33 3 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:87.2,114.16 3 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:119.2,119.78 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:123.2,123.12 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:37.16,39.3 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:41.16,43.3 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:57.16,59.3 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:61.18,63.3 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:69.16,71.3 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:75.23,77.3 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:82.33,84.3 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:114.16,116.3 1 0
|
|
||||||
github.com/muety/wakapi/config/legacy.go:119.78,121.3 1 0
|
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:27.116,34.2 1 1
|
github.com/muety/wakapi/middlewares/authenticate.go:27.116,34.2 1 1
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:36.71,37.71 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:36.71,37.71 1 0
|
||||||
github.com/muety/wakapi/middlewares/authenticate.go:37.71,39.3 1 0
|
github.com/muety/wakapi/middlewares/authenticate.go:37.71,39.3 1 0
|
||||||
@ -320,44 +328,44 @@ github.com/muety/wakapi/middlewares/logging.go:11.48,13.2 1 0
|
|||||||
github.com/muety/wakapi/middlewares/logging.go:15.66,17.2 1 0
|
github.com/muety/wakapi/middlewares/logging.go:15.66,17.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,47.18 2 0
|
github.com/muety/wakapi/services/aggregation.go:46.2,48.19 3 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:42.37,44.3 1 0
|
github.com/muety/wakapi/services/aggregation.go:42.37,44.3 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:50.67,54.40 3 0
|
github.com/muety/wakapi/services/aggregation.go:51.67,55.40 3 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:58.2,58.50 1 0
|
github.com/muety/wakapi/services/aggregation.go:59.2,59.50 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:63.2,63.60 1 0
|
github.com/muety/wakapi/services/aggregation.go:64.2,64.60 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:69.2,69.35 1 0
|
github.com/muety/wakapi/services/aggregation.go:70.2,70.35 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:54.40,56.3 1 0
|
github.com/muety/wakapi/services/aggregation.go:55.40,57.3 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:58.50,60.3 1 0
|
github.com/muety/wakapi/services/aggregation.go:59.50,61.3 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:63.60,67.3 3 0
|
github.com/muety/wakapi/services/aggregation.go:64.60,68.3 3 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:72.109,73.24 1 0
|
github.com/muety/wakapi/services/aggregation.go:73.109,74.24 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:73.24,74.111 1 0
|
github.com/muety/wakapi/services/aggregation.go:74.24,75.111 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:74.111,76.4 1 0
|
github.com/muety/wakapi/services/aggregation.go:75.111,77.4 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:76.9,79.4 2 0
|
github.com/muety/wakapi/services/aggregation.go:77.9,80.4 2 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:83.80,84.33 1 0
|
github.com/muety/wakapi/services/aggregation.go:84.80,85.33 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:84.33,85.60 1 0
|
github.com/muety/wakapi/services/aggregation.go:85.33,86.60 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:85.60,87.4 1 0
|
github.com/muety/wakapi/services/aggregation.go:86.60,88.4 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:91.100,95.59 3 0
|
github.com/muety/wakapi/services/aggregation.go:92.100,96.59 3 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:110.2,111.16 2 0
|
github.com/muety/wakapi/services/aggregation.go:111.2,112.16 2 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:117.2,118.16 2 0
|
github.com/muety/wakapi/services/aggregation.go:118.2,119.16 2 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:124.2,125.44 2 0
|
github.com/muety/wakapi/services/aggregation.go:125.2,126.44 2 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:130.2,130.41 1 0
|
github.com/muety/wakapi/services/aggregation.go:131.2,131.41 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:144.2,144.12 1 0
|
github.com/muety/wakapi/services/aggregation.go:145.2,145.12 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:95.59,98.3 2 0
|
github.com/muety/wakapi/services/aggregation.go:96.59,99.3 2 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:98.8,98.47 1 0
|
github.com/muety/wakapi/services/aggregation.go:99.8,99.47 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:98.47,100.30 2 0
|
github.com/muety/wakapi/services/aggregation.go:99.47,101.30 2 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:100.30,101.43 1 0
|
github.com/muety/wakapi/services/aggregation.go:101.30,102.43 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:101.43,103.5 1 0
|
github.com/muety/wakapi/services/aggregation.go:102.43,104.5 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:105.8,107.3 1 0
|
github.com/muety/wakapi/services/aggregation.go:106.8,108.3 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:111.16,114.3 2 0
|
github.com/muety/wakapi/services/aggregation.go:112.16,115.3 2 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:118.16,121.3 2 0
|
github.com/muety/wakapi/services/aggregation.go:119.16,122.3 2 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:125.44,127.3 1 0
|
github.com/muety/wakapi/services/aggregation.go:126.44,128.3 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:130.41,131.21 1 0
|
github.com/muety/wakapi/services/aggregation.go:131.41,132.21 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:131.21,135.4 1 0
|
github.com/muety/wakapi/services/aggregation.go:132.21,136.4 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:135.9,135.62 1 0
|
github.com/muety/wakapi/services/aggregation.go:136.9,136.62 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:135.62,139.4 1 0
|
github.com/muety/wakapi/services/aggregation.go:136.62,140.4 1 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:147.83,162.41 5 0
|
github.com/muety/wakapi/services/aggregation.go:148.83,163.41 5 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:162.41,172.3 3 0
|
github.com/muety/wakapi/services/aggregation.go:163.41,173.3 3 0
|
||||||
github.com/muety/wakapi/services/aggregation.go:175.34,178.2 2 0
|
github.com/muety/wakapi/services/aggregation.go:176.34,179.2 2 0
|
||||||
github.com/muety/wakapi/services/alias.go:16.77,21.2 1 1
|
github.com/muety/wakapi/services/alias.go:16.77,21.2 1 1
|
||||||
github.com/muety/wakapi/services/alias.go:25.63,27.16 2 1
|
github.com/muety/wakapi/services/alias.go:25.63,27.16 2 1
|
||||||
github.com/muety/wakapi/services/alias.go:30.2,30.12 1 1
|
github.com/muety/wakapi/services/alias.go:30.2,30.12 1 1
|
||||||
@ -404,7 +412,7 @@ github.com/muety/wakapi/services/language_mapping.go:55.109,57.16 2 0
|
|||||||
github.com/muety/wakapi/services/language_mapping.go:61.2,62.20 2 0
|
github.com/muety/wakapi/services/language_mapping.go:61.2,62.20 2 0
|
||||||
github.com/muety/wakapi/services/language_mapping.go:57.16,59.3 1 0
|
github.com/muety/wakapi/services/language_mapping.go:57.16,59.3 1 0
|
||||||
github.com/muety/wakapi/services/language_mapping.go:65.82,69.2 3 0
|
github.com/muety/wakapi/services/language_mapping.go:65.82,69.2 3 0
|
||||||
github.com/muety/wakapi/services/language_mapping.go:71.73,73.2 1 0
|
github.com/muety/wakapi/services/language_mapping.go:71.74,74.2 1 0
|
||||||
github.com/muety/wakapi/services/summary.go:27.149,35.2 1 1
|
github.com/muety/wakapi/services/summary.go:27.149,35.2 1 1
|
||||||
github.com/muety/wakapi/services/summary.go:39.120,42.52 2 1
|
github.com/muety/wakapi/services/summary.go:39.120,42.52 2 1
|
||||||
github.com/muety/wakapi/services/summary.go:47.2,47.44 1 1
|
github.com/muety/wakapi/services/summary.go:47.2,47.44 1 1
|
||||||
|
29
docs/advanced_setup.md
Normal file
29
docs/advanced_setup.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Advanced Setup
|
||||||
|
## Optional: Client-side proxy
|
||||||
|
Most Wakatime plugins work in a way that, for every heartbeat to send, the plugin calls your local [wakatime-cli](https://github.com/wakatime/wakatime) (a small Python program that is automatically installed when installing a Wakatime plugin) with a few command-line arguments, which is then run as a new process. Inside that process, a heartbeat request is forged and sent to the backend API – Wakapi in this case.
|
||||||
|
|
||||||
|
While this is convenient for plugin developers, as they do not have to deal with sending HTTP requests, etc., it comes with a minor drawback. Because the CLI process shuts down after each request, its TCP connection is closed as well. Accordingly, **TCP connections cannot be re-used** and every single heartbeat request is inevitably preceded by the `SYN` + `SYN ACK` + `ACK` sequence for establishing a new TCP connection as well as a handshake for establishing a new TLS session.
|
||||||
|
|
||||||
|
While this certainly does not hurt, it is still a bit of overhead. You can avoid that by setting up a local reverse proxy on your machine, that keeps running as a daemon and can therefore keep a continuous connection.
|
||||||
|
|
||||||
|
In this example, [Caddy](https://caddyserver.com) is used as an easy-to-set-up webserver / reverse proxy.
|
||||||
|
|
||||||
|
1. [Install Caddy](https://caddyserver.com/)
|
||||||
|
* When installing manually, don't forget to set up a systemd service to start Caddy on system startup
|
||||||
|
1. Create a Caddyfile
|
||||||
|
```
|
||||||
|
# /etc/caddy/Caddyfile
|
||||||
|
|
||||||
|
http://localhost:8070 {
|
||||||
|
reverse_proxy * {
|
||||||
|
to https://wakapi.dev # <-- substitute your own Wakapi host here
|
||||||
|
header_up Host {http.reverse_proxy.upstream.host}
|
||||||
|
header_down -Server
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
1. Restart Caddy
|
||||||
|
1. Verify that you can access [`http://localhost:8070/api/health`](http://localhost:8070/api/health)
|
||||||
|
1. Update `~/.wakatime.cfg`
|
||||||
|
* Set `api_url = http://localhost:8070/api/heartbeat`
|
||||||
|
1. Done
|
2
go.mod
2
go.mod
@ -3,11 +3,11 @@ module github.com/muety/wakapi
|
|||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/go-co-op/gocron v0.3.3
|
||||||
github.com/gorilla/handlers v1.4.2
|
github.com/gorilla/handlers v1.4.2
|
||||||
github.com/gorilla/mux v1.7.3
|
github.com/gorilla/mux v1.7.3
|
||||||
github.com/gorilla/schema v1.1.0
|
github.com/gorilla/schema v1.1.0
|
||||||
github.com/gorilla/securecookie v1.1.1
|
github.com/gorilla/securecookie v1.1.1
|
||||||
github.com/jasonlvhit/gocron v0.0.0-20191106203602-f82992d443f4
|
|
||||||
github.com/jinzhu/configor v1.2.0
|
github.com/jinzhu/configor v1.2.0
|
||||||
github.com/joho/godotenv v1.3.0
|
github.com/joho/godotenv v1.3.0
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
5
go.sum
5
go.sum
@ -64,6 +64,8 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB
|
|||||||
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/go-co-op/gocron v0.3.3 h1:QnarcMZWWKrEP25uCbtDiLsnnGw+PhCjL3wNITdWJOs=
|
||||||
|
github.com/go-co-op/gocron v0.3.3/go.mod h1:Y9PWlYqDChf2Nbgg7kfS+ZsXHDTZbMZYPEQ0MILqH+M=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||||
@ -204,8 +206,6 @@ github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0f
|
|||||||
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.1.2/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jasonlvhit/gocron v0.0.0-20191106203602-f82992d443f4 h1:UbQcOUL8J8EpnhYmLa2v6y5PSOPEdRRSVQxh7imPjHg=
|
|
||||||
github.com/jasonlvhit/gocron v0.0.0-20191106203602-f82992d443f4/go.mod h1:1nXLkt6gXojCECs34KL3+LlZ3gTpZlkPUA8ejW3WeP0=
|
|
||||||
github.com/jinzhu/configor v1.2.0 h1:u78Jsrxw2+3sGbGMgpY64ObKU4xWCNmNRJIjGVqxYQA=
|
github.com/jinzhu/configor v1.2.0 h1:u78Jsrxw2+3sGbGMgpY64ObKU4xWCNmNRJIjGVqxYQA=
|
||||||
github.com/jinzhu/configor v1.2.0/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc=
|
github.com/jinzhu/configor v1.2.0/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc=
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
@ -455,6 +455,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR
|
|||||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
69
main.go
69
main.go
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
conf "github.com/muety/wakapi/config"
|
conf "github.com/muety/wakapi/config"
|
||||||
"github.com/muety/wakapi/migrations/common"
|
"github.com/muety/wakapi/migrations/common"
|
||||||
@ -178,15 +179,71 @@ func main() {
|
|||||||
router.PathPrefix("/assets").Handler(http.FileServer(http.Dir("./static")))
|
router.PathPrefix("/assets").Handler(http.FileServer(http.Dir("./static")))
|
||||||
|
|
||||||
// Listen HTTP
|
// Listen HTTP
|
||||||
portString := config.Server.ListenIpV4 + ":" + strconv.Itoa(config.Server.Port)
|
listen(router)
|
||||||
s := &http.Server{
|
}
|
||||||
Handler: router,
|
|
||||||
Addr: portString,
|
func listen(handler http.Handler) {
|
||||||
|
var s4, s6 *http.Server
|
||||||
|
|
||||||
|
// IPv4
|
||||||
|
if config.Server.ListenIpV4 != "" {
|
||||||
|
bindString4 := config.Server.ListenIpV4 + ":" + strconv.Itoa(config.Server.Port)
|
||||||
|
s4 = &http.Server{
|
||||||
|
Handler: handler,
|
||||||
|
Addr: bindString4,
|
||||||
ReadTimeout: 10 * time.Second,
|
ReadTimeout: 10 * time.Second,
|
||||||
WriteTimeout: 10 * time.Second,
|
WriteTimeout: 10 * time.Second,
|
||||||
}
|
}
|
||||||
log.Printf("Listening on %+s\n", portString)
|
}
|
||||||
s.ListenAndServe()
|
|
||||||
|
// IPv6
|
||||||
|
if config.Server.ListenIpV6 != "" {
|
||||||
|
bindString6 := "[" + config.Server.ListenIpV6 + "]:" + strconv.Itoa(config.Server.Port)
|
||||||
|
s6 = &http.Server{
|
||||||
|
Handler: handler,
|
||||||
|
Addr: bindString6,
|
||||||
|
ReadTimeout: 10 * time.Second,
|
||||||
|
WriteTimeout: 10 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.UseTLS() {
|
||||||
|
if s4 != nil {
|
||||||
|
fmt.Printf("Listening for HTTPS on %s.\n", s4.Addr)
|
||||||
|
go func() {
|
||||||
|
if err := s4.ListenAndServeTLS(config.Server.TlsCertPath, config.Server.TlsKeyPath); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
if s6 != nil {
|
||||||
|
fmt.Printf("Listening for HTTPS on %s.\n", s6.Addr)
|
||||||
|
go func() {
|
||||||
|
if err := s6.ListenAndServeTLS(config.Server.TlsCertPath, config.Server.TlsKeyPath); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if s4 != nil {
|
||||||
|
fmt.Printf("Listening for HTTP on %s.\n", s4.Addr)
|
||||||
|
go func() {
|
||||||
|
if err := s4.ListenAndServe(); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
if s6 != nil {
|
||||||
|
fmt.Printf("Listening for HTTP on %s.\n", s6.Addr)
|
||||||
|
go func() {
|
||||||
|
if err := s6.ListenAndServe(); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<-make(chan interface{}, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDatabaseMigrations() {
|
func runDatabaseMigrations() {
|
||||||
|
@ -43,7 +43,7 @@ func (s *Signup) IsValid() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func validateUsername(username string) bool {
|
func validateUsername(username string) bool {
|
||||||
return len(username) >= 3 && username != "current"
|
return len(username) >= 1 && username != "current"
|
||||||
}
|
}
|
||||||
|
|
||||||
func validatePassword(password string) bool {
|
func validatePassword(password string) bool {
|
||||||
|
@ -59,7 +59,7 @@ func (h *SummaryHandler) GetIndex(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
vm := models.SummaryViewModel{
|
vm := models.SummaryViewModel{
|
||||||
Summary: summary,
|
Summary: summary,
|
||||||
LanguageColors: utils.FilterLanguageColors(h.config.App.LanguageColors, summary),
|
LanguageColors: utils.FilterLanguageColors(h.config.App.GetLanguageColors(), summary),
|
||||||
ApiKey: user.ApiKey,
|
ApiKey: user.ApiKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jasonlvhit/gocron"
|
"github.com/go-co-op/gocron"
|
||||||
"github.com/muety/wakapi/models"
|
"github.com/muety/wakapi/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -43,8 +43,9 @@ func (srv *AggregationService) Schedule() {
|
|||||||
log.Fatalf("failed to run aggregation jobs: %v\n", err)
|
log.Fatalf("failed to run aggregation jobs: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gocron.Every(1).Day().At(srv.config.App.AggregationTime).Do(srv.Run, nil)
|
s := gocron.NewScheduler(time.Local)
|
||||||
<-gocron.Start()
|
s.Every(1).Day().At(srv.config.App.AggregationTime).Do(srv.Run, map[string]bool{})
|
||||||
|
s.StartBlocking()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *AggregationService) Run(userIds map[string]bool) error {
|
func (srv *AggregationService) Run(userIds map[string]bool) error {
|
||||||
|
@ -68,6 +68,7 @@ func (srv *LanguageMappingService) Delete(mapping *models.LanguageMapping) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv LanguageMappingService) getServerMappings() map[string]string {
|
func (srv *LanguageMappingService) getServerMappings() map[string]string {
|
||||||
return srv.config.App.CustomLanguages
|
// https://dave.cheney.net/2017/04/30/if-a-map-isnt-a-reference-variable-what-is-it
|
||||||
|
return srv.config.App.GetCustomLanguages()
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ func (srv *SummaryService) Aliased(from, to time.Time, user *models.User, f Summ
|
|||||||
|
|
||||||
func (srv *SummaryService) Retrieve(from, to time.Time, user *models.User) (*models.Summary, error) {
|
func (srv *SummaryService) Retrieve(from, to time.Time, user *models.User) (*models.Summary, error) {
|
||||||
// Check cache
|
// Check cache
|
||||||
cacheKey := srv.getHash(from.String(), to.String(), user.ID, "--aliased")
|
cacheKey := srv.getHash(from.String(), to.String(), user.ID)
|
||||||
if cacheResult, ok := srv.cache.Get(cacheKey); ok {
|
if cacheResult, ok := srv.cache.Get(cacheKey); ok {
|
||||||
return cacheResult.(*models.Summary), nil
|
return cacheResult.(*models.Summary), nil
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
const SHOW_TOP_N = 10
|
|
||||||
const CHART_TARGET_SIZE = 200
|
const CHART_TARGET_SIZE = 200
|
||||||
|
|
||||||
const projectsCanvas = document.getElementById('chart-projects')
|
const projectsCanvas = document.getElementById('chart-projects')
|
||||||
@ -17,7 +16,16 @@ const containers = [projectContainer, osContainer, editorContainer, languageCont
|
|||||||
const canvases = [projectsCanvas, osCanvas, editorsCanvas, languagesCanvas, machinesCanvas]
|
const canvases = [projectsCanvas, osCanvas, editorsCanvas, languagesCanvas, machinesCanvas]
|
||||||
const data = [wakapiData.projects, wakapiData.operatingSystems, wakapiData.editors, wakapiData.languages, wakapiData.machines]
|
const data = [wakapiData.projects, wakapiData.operatingSystems, wakapiData.editors, wakapiData.languages, wakapiData.machines]
|
||||||
|
|
||||||
|
let topNPickers = [...document.getElementsByClassName('top-picker')]
|
||||||
|
topNPickers.sort(((a, b) => parseInt(a.attributes['data-entity'].value) - parseInt(b.attributes['data-entity'].value)))
|
||||||
|
topNPickers.forEach(e => {
|
||||||
|
const idx = parseInt(e.attributes['data-entity'].value)
|
||||||
|
e.max = Math.min(data[idx].length, 10)
|
||||||
|
e.value = e.max
|
||||||
|
})
|
||||||
|
|
||||||
let charts = []
|
let charts = []
|
||||||
|
let showTopN = []
|
||||||
let resizeCount = 0
|
let resizeCount = 0
|
||||||
|
|
||||||
String.prototype.toHHMMSS = function () {
|
String.prototype.toHHMMSS = function () {
|
||||||
@ -38,7 +46,7 @@ String.prototype.toHHMMSS = function () {
|
|||||||
return hours + ':' + minutes + ':' + seconds
|
return hours + ':' + minutes + ':' + seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
function draw() {
|
function draw(subselection) {
|
||||||
function getTooltipOptions(key, type) {
|
function getTooltipOptions(key, type) {
|
||||||
return {
|
return {
|
||||||
mode: 'single',
|
mode: 'single',
|
||||||
@ -53,14 +61,20 @@ function draw() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
charts.forEach(c => c.destroy())
|
function shouldUpdate(index) {
|
||||||
|
return !subselection || (subselection.includes(index) && data[index].length >= showTopN[index])
|
||||||
|
}
|
||||||
|
|
||||||
let projectChart = !projectsCanvas.classList.contains('hidden')
|
charts
|
||||||
|
.filter((c, i) => shouldUpdate(i))
|
||||||
|
.forEach(c => c.destroy())
|
||||||
|
|
||||||
|
let projectChart = !projectsCanvas.classList.contains('hidden') && shouldUpdate(0)
|
||||||
? new Chart(projectsCanvas.getContext('2d'), {
|
? new Chart(projectsCanvas.getContext('2d'), {
|
||||||
type: 'horizontalBar',
|
type: 'horizontalBar',
|
||||||
data: {
|
data: {
|
||||||
datasets: wakapiData.projects
|
datasets: wakapiData.projects
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.projects.length))
|
.slice(0, Math.min(showTopN[0], wakapiData.projects.length))
|
||||||
.map(p => {
|
.map(p => {
|
||||||
return {
|
return {
|
||||||
label: p.key,
|
label: p.key,
|
||||||
@ -88,18 +102,18 @@ function draw() {
|
|||||||
})
|
})
|
||||||
: null
|
: null
|
||||||
|
|
||||||
let osChart = !osCanvas.classList.contains('hidden')
|
let osChart = !osCanvas.classList.contains('hidden') && shouldUpdate(1)
|
||||||
? new Chart(osCanvas.getContext('2d'), {
|
? new Chart(osCanvas.getContext('2d'), {
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
data: {
|
data: {
|
||||||
datasets: [{
|
datasets: [{
|
||||||
data: wakapiData.operatingSystems
|
data: wakapiData.operatingSystems
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.operatingSystems.length))
|
.slice(0, Math.min(showTopN[1], wakapiData.operatingSystems.length))
|
||||||
.map(p => parseInt(p.total)),
|
.map(p => parseInt(p.total)),
|
||||||
backgroundColor: wakapiData.operatingSystems.map(p => getRandomColor(p.key))
|
backgroundColor: wakapiData.operatingSystems.map(p => getRandomColor(p.key))
|
||||||
}],
|
}],
|
||||||
labels: wakapiData.operatingSystems
|
labels: wakapiData.operatingSystems
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.operatingSystems.length))
|
.slice(0, Math.min(showTopN[1], wakapiData.operatingSystems.length))
|
||||||
.map(p => p.key)
|
.map(p => p.key)
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
@ -110,18 +124,18 @@ function draw() {
|
|||||||
})
|
})
|
||||||
: null
|
: null
|
||||||
|
|
||||||
let editorChart = !editorsCanvas.classList.contains('hidden')
|
let editorChart = !editorsCanvas.classList.contains('hidden') && shouldUpdate(2)
|
||||||
? new Chart(editorsCanvas.getContext('2d'), {
|
? new Chart(editorsCanvas.getContext('2d'), {
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
data: {
|
data: {
|
||||||
datasets: [{
|
datasets: [{
|
||||||
data: wakapiData.editors
|
data: wakapiData.editors
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.editors.length))
|
.slice(0, Math.min(showTopN[2], wakapiData.editors.length))
|
||||||
.map(p => parseInt(p.total)),
|
.map(p => parseInt(p.total)),
|
||||||
backgroundColor: wakapiData.editors.map(p => getRandomColor(p.key))
|
backgroundColor: wakapiData.editors.map(p => getRandomColor(p.key))
|
||||||
}],
|
}],
|
||||||
labels: wakapiData.editors
|
labels: wakapiData.editors
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.editors.length))
|
.slice(0, Math.min(showTopN[2], wakapiData.editors.length))
|
||||||
.map(p => p.key)
|
.map(p => p.key)
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
@ -132,18 +146,18 @@ function draw() {
|
|||||||
})
|
})
|
||||||
: null
|
: null
|
||||||
|
|
||||||
let languageChart = !languagesCanvas.classList.contains('hidden')
|
let languageChart = !languagesCanvas.classList.contains('hidden') && shouldUpdate(3)
|
||||||
? new Chart(languagesCanvas.getContext('2d'), {
|
? new Chart(languagesCanvas.getContext('2d'), {
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
data: {
|
data: {
|
||||||
datasets: [{
|
datasets: [{
|
||||||
data: wakapiData.languages
|
data: wakapiData.languages
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.languages.length))
|
.slice(0, Math.min(showTopN[3], wakapiData.languages.length))
|
||||||
.map(p => parseInt(p.total)),
|
.map(p => parseInt(p.total)),
|
||||||
backgroundColor: wakapiData.languages.map(p => languageColors[p.key.toLowerCase()] || getRandomColor(p.key))
|
backgroundColor: wakapiData.languages.map(p => languageColors[p.key.toLowerCase()] || getRandomColor(p.key))
|
||||||
}],
|
}],
|
||||||
labels: wakapiData.languages
|
labels: wakapiData.languages
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.languages.length))
|
.slice(0, Math.min(showTopN[3], wakapiData.languages.length))
|
||||||
.map(p => p.key)
|
.map(p => p.key)
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
@ -154,18 +168,18 @@ function draw() {
|
|||||||
})
|
})
|
||||||
: null
|
: null
|
||||||
|
|
||||||
let machineChart = !machinesCanvas.classList.contains('hidden')
|
let machineChart = !machinesCanvas.classList.contains('hidden') && shouldUpdate(4)
|
||||||
? new Chart(machinesCanvas.getContext('2d'), {
|
? new Chart(machinesCanvas.getContext('2d'), {
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
data: {
|
data: {
|
||||||
datasets: [{
|
datasets: [{
|
||||||
data: wakapiData.machines
|
data: wakapiData.machines
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.machines.length))
|
.slice(0, Math.min(showTopN[4], wakapiData.machines.length))
|
||||||
.map(p => parseInt(p.total)),
|
.map(p => parseInt(p.total)),
|
||||||
backgroundColor: wakapiData.machines.map(p => getRandomColor(p.key))
|
backgroundColor: wakapiData.machines.map(p => getRandomColor(p.key))
|
||||||
}],
|
}],
|
||||||
labels: wakapiData.machines
|
labels: wakapiData.machines
|
||||||
.slice(0, Math.min(SHOW_TOP_N, wakapiData.machines.length))
|
.slice(0, Math.min(showTopN[4], wakapiData.machines.length))
|
||||||
.map(p => p.key)
|
.map(p => p.key)
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
@ -180,13 +194,14 @@ function draw() {
|
|||||||
|
|
||||||
charts = [projectChart, osChart, editorChart, languageChart, machineChart].filter(c => !!c)
|
charts = [projectChart, osChart, editorChart, languageChart, machineChart].filter(c => !!c)
|
||||||
|
|
||||||
|
if (!subselection) {
|
||||||
charts.forEach(c => c.options.onResize(c.chart))
|
charts.forEach(c => c.options.onResize(c.chart))
|
||||||
equalizeHeights()
|
equalizeHeights()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function setTopLabels() {
|
function parseTopN() {
|
||||||
[...document.getElementsByClassName('top-label')]
|
showTopN = topNPickers.map(e => parseInt(e.value))
|
||||||
.forEach(e => e.innerText = `(top ${SHOW_TOP_N})`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePlaceholders(mask) {
|
function togglePlaceholders(mask) {
|
||||||
@ -301,7 +316,12 @@ window.addEventListener('click', function (event) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
window.addEventListener('load', function () {
|
window.addEventListener('load', function () {
|
||||||
setTopLabels()
|
topNPickers.forEach(e => e.addEventListener('change', () => {
|
||||||
|
parseTopN()
|
||||||
|
draw([parseInt(e.attributes['data-entity'].value)])
|
||||||
|
}))
|
||||||
|
|
||||||
|
parseTopN()
|
||||||
togglePlaceholders(getPresentDataMask())
|
togglePlaceholders(getPresentDataMask())
|
||||||
draw()
|
draw()
|
||||||
})
|
})
|
||||||
|
@ -1 +1 @@
|
|||||||
1.17.2
|
1.18.2
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
<label class="inline-block text-sm mb-1 text-gray-500" for="username">Username</label>
|
<label class="inline-block text-sm mb-1 text-gray-500" for="username">Username</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"
|
||||||
type="text" id="username"
|
type="text" id="username"
|
||||||
name="username" placeholder="Enter your username" minlength="3" required autofocus>
|
name="username" placeholder="Enter your username" minlength="1" required autofocus>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<label class="inline-block text-sm mb-1 text-gray-500" for="password">Password</label>
|
<label class="inline-block text-sm mb-1 text-gray-500" for="password">Password</label>
|
||||||
|
@ -75,7 +75,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-gray-300 text-sm mb-4 mt-6">
|
<div class="text-gray-300 text-sm mb-4 mt-6">
|
||||||
You can specify custom mapping from file extensions to programming languages (e.g. a <span class="text-xs bg-gray-900 rounded py-1 px-2 font-mono">.jsx</span> file could be mapped to <span class="text-xs bg-gray-900 rounded py-1 px-2 font-mono">React</span>.
|
You can specify custom mapping from file extensions to programming languages (e.g. a <span class="text-xs bg-gray-900 rounded py-1 px-2 font-mono">.jsx</span> file could be mapped to <span class="text-xs bg-gray-900 rounded py-1 px-2 font-mono">React</span>.)
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ if .LanguageMappings }}
|
{{ if .LanguageMappings }}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="border-b border-green-700">WakaTime</a>
|
class="border-b border-green-700">WakaTime</a>
|
||||||
client tools.
|
client tools.
|
||||||
Please refer to <a href="https://github.com/muety/wakapi#client-setup" target="_blank"
|
Please refer to <a href="https://github.com/muety/wakapi#-client-setup" target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="border-b border-green-700">this readme section</a> for instructions.
|
class="border-b border-green-700">this readme section</a> for instructions.
|
||||||
You will be able to view you <strong>API Key</strong> once you log in.
|
You will be able to view you <strong>API Key</strong> once you log in.
|
||||||
@ -35,7 +35,7 @@
|
|||||||
<label class="inline-block text-sm mb-1 text-gray-500" for="username">Username</label>
|
<label class="inline-block text-sm mb-1 text-gray-500" for="username">Username</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"
|
||||||
type="text" id="username"
|
type="text" id="username"
|
||||||
name="username" placeholder="Choose a username" minlength="3" required autofocus>
|
name="username" placeholder="Choose a username" minlength="1" required autofocus>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<label class="inline-block text-sm mb-1 text-gray-500" for="password">Password</label>
|
<label class="inline-block text-sm mb-1 text-gray-500" for="password">Password</label>
|
||||||
|
@ -66,9 +66,13 @@
|
|||||||
<div class="flex flex-wrap justify-center">
|
<div class="flex flex-wrap justify-center">
|
||||||
<div class="w-full lg:w-1/2 p-1">
|
<div class="w-full lg:w-1/2 p-1">
|
||||||
<div class="p-4 pb-10 bg-white rounded shadow m-2 flex flex-col" id="project-container" style="height: 300px">
|
<div class="p-4 pb-10 bg-white rounded shadow m-2 flex flex-col" id="project-container" style="height: 300px">
|
||||||
<div class="self-center flex">
|
<div class="flex justify-between">
|
||||||
<span class="font-semibold mr-1">Projects</span>
|
<div class="w-1/4 flex-1"></div>
|
||||||
<span id="project-top-label" class="top-label"></span>
|
<span class="font-semibold w-1/2 text-center flex-1">Projects</span>
|
||||||
|
<div class="flex justify-end flex-1 text-xs items-center">
|
||||||
|
<label for="project-top-picker" class="mr-1">Show: </label>
|
||||||
|
<input type="number" min="1" id="project-top-picker" data-entity="0" class="w-1/4 top-picker bg-gray-200 rounded-md text-center" value="10">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<canvas id="chart-projects"></canvas>
|
<canvas id="chart-projects"></canvas>
|
||||||
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
|
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
|
||||||
@ -79,9 +83,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="w-full lg:w-1/2 p-1">
|
<div class="w-full lg:w-1/2 p-1">
|
||||||
<div class="p-4 pb-10 bg-white rounded shadow m-2 flex flex-col" id="os-container" style="height: 300px">
|
<div class="p-4 pb-10 bg-white rounded shadow m-2 flex flex-col" id="os-container" style="height: 300px">
|
||||||
<div class="self-center flex">
|
<div class="flex justify-between">
|
||||||
<span class="font-semibold mr-1">Operating Systems</span>
|
<div class="w-1/4 flex-1"></div>
|
||||||
<span id="os-top-label" class="top-label"></span>
|
<span class="font-semibold w-1/2 text-center flex-1">Operating Systems</span>
|
||||||
|
<div class="flex justify-end flex-1 text-xs items-center">
|
||||||
|
<label for="os-top-picker" class="mr-1">Show: </label>
|
||||||
|
<input type="number" min="1" id="os-top-picker" data-entity="1" class="w-1/4 top-picker bg-gray-200 rounded-md text-center" value="10">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<canvas id="chart-os"></canvas>
|
<canvas id="chart-os"></canvas>
|
||||||
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
|
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
|
||||||
@ -92,9 +100,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="w-full lg:w-1/2 p-1">
|
<div class="w-full lg:w-1/2 p-1">
|
||||||
<div class="p-4 pb-10 bg-white rounded shadow m-2 flex flex-col relative" id="language-container" style="height: 300px">
|
<div class="p-4 pb-10 bg-white rounded shadow m-2 flex flex-col relative" id="language-container" style="height: 300px">
|
||||||
<div class="self-center flex">
|
<div class="flex justify-between">
|
||||||
<span class="font-semibold mr-1">Languages</span>
|
<div class="w-1/4 flex-1"></div>
|
||||||
<span id="language-top-label" class="top-label"></span>
|
<span class="font-semibold w-1/2 text-center flex-1">Languages</span>
|
||||||
|
<div class="flex justify-end flex-1 text-xs items-center">
|
||||||
|
<label for="language-top-picker" class="mr-1">Show: </label>
|
||||||
|
<input type="number" min="1" id="language-top-picker" data-entity="3" class="w-1/4 top-picker bg-gray-200 rounded-md text-center" value="10">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<canvas id="chart-language"></canvas>
|
<canvas id="chart-language"></canvas>
|
||||||
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
|
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
|
||||||
@ -105,9 +117,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="w-full lg:w-1/2 p-1">
|
<div class="w-full lg:w-1/2 p-1">
|
||||||
<div class="p-4 pb-10 bg-white rounded shadow m-2 flex flex-col" id="editor-container" style="height: 300px">
|
<div class="p-4 pb-10 bg-white rounded shadow m-2 flex flex-col" id="editor-container" style="height: 300px">
|
||||||
<div class="self-center flex">
|
<div class="flex justify-between">
|
||||||
<span class="font-semibold mr-1">Editors</span>
|
<div class="w-1/4 flex-1"></div>
|
||||||
<span id="editor-top-label" class="top-label"></span>
|
<span class="font-semibold w-1/2 text-center flex-1">Editors</span>
|
||||||
|
<div class="flex justify-end flex-1 text-xs items-center">
|
||||||
|
<label for="editor-top-picker" class="mr-1">Show: </label>
|
||||||
|
<input type="number" min="1" id="editor-top-picker" data-entity="2" class="w-1/4 top-picker bg-gray-200 rounded-md text-center" value="10">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<canvas id="chart-editor"></canvas>
|
<canvas id="chart-editor"></canvas>
|
||||||
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
|
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
|
||||||
@ -118,9 +134,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="w-full lg:w-1/2 p-1">
|
<div class="w-full lg:w-1/2 p-1">
|
||||||
<div class="p-4 pb-10 bg-white rounded shadow m-2 flex flex-col" id="machine-container" style="height: 300px">
|
<div class="p-4 pb-10 bg-white rounded shadow m-2 flex flex-col" id="machine-container" style="height: 300px">
|
||||||
<div class="self-center flex">
|
<div class="flex justify-between">
|
||||||
<span class="font-semibold mr-1">Machines</span>
|
<div class="w-1/4 flex-1"></div>
|
||||||
<span id="machine-top-label" class="top-label"></span>
|
<span class="font-semibold w-1/2 text-center flex-1">Machines</span>
|
||||||
|
<div class="flex justify-end flex-1 text-xs items-center">
|
||||||
|
<label for="machine-top-picker" class="mr-1">Show: </label>
|
||||||
|
<input type="number" min="1" id="machine-top-picker" data-entity="4" class="w-1/4 top-picker bg-gray-200 rounded-md text-center" value="10">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<canvas id="chart-machine"></canvas>
|
<canvas id="chart-machine"></canvas>
|
||||||
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
|
<div class="hidden placeholder-container flex items-center justify-center h-full flex-col">
|
||||||
|
Reference in New Issue
Block a user