Compare commits
116 commits
bump-dep-1
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f34a96abbe |
||
|
|
9fa312e3fc |
||
|
|
8f37d34df6 |
||
|
|
5c416e3a32 |
||
|
|
d5e11c23f6 |
||
|
|
f1d7123596 |
||
|
|
ce5c9b242a |
||
|
|
e72322f847 |
||
|
|
e0c4cb1545 |
||
|
|
6314749263 |
||
|
|
ae290a226f |
||
|
|
a46afb821f |
||
|
|
f6a4c6344c |
||
|
|
e3380a4dfa |
||
|
|
0a5728faf3 |
||
|
|
b03ca999a5 |
||
|
|
83ac43b737 |
||
|
|
505fc67966 |
||
|
|
7d44c541a4 |
||
|
|
850f9cc6c9 |
||
|
|
41f25514f0 |
||
|
|
03889a3d7e |
||
|
|
c8238aa327 |
||
|
|
346bd9afb1 |
||
|
|
74119b1d0b |
||
|
|
24491bc68a |
||
|
|
e0f68fc8d8 |
||
|
|
fd7b2a78b2 |
||
|
|
ca5af5e34a |
||
|
|
162ceb4ad1 |
||
|
|
637d4c6861 |
||
|
|
a62c7f9e93 |
||
|
|
5df3e7af70 |
||
|
|
b141b677bb |
||
|
|
61162e2add |
||
|
|
5928f9619f |
||
|
|
a26037f83a |
||
|
|
b37820da1f |
||
|
|
8ffd44f362 |
||
|
|
895dcaa59f |
||
|
|
1ff4c07528 |
||
|
|
bdc980e5ce |
||
|
|
f1c0d0bc04 |
||
|
|
f0ba8a1fa6 |
||
|
|
4dded18395 |
||
|
|
818ebcce69 |
||
|
|
94ad949453 |
||
|
|
5eae1299f7 |
||
|
|
2de1798fdd |
||
|
|
558bf8dbe1 |
||
|
|
664520e027 |
||
|
|
f18677e498 |
||
|
|
b47c792d5f |
||
|
|
489d4f407a |
||
|
|
17b4c7d6cf |
||
|
|
fd5f92c247 |
||
|
|
0ef9e20e8d |
||
|
|
c3214f12f0 |
||
|
|
4258ea1715 |
||
|
|
d52a5bdf48 |
||
|
|
dd6f8c7f65 |
||
|
|
b07858a3df |
||
|
|
420f30fb92 |
||
|
|
4978e57fbe |
||
|
|
86bfc44f47 |
||
|
|
577a6e8a57 |
||
|
|
9a83ac3c51 |
||
|
|
ff0ae9ac29 |
||
|
|
4a780574eb |
||
|
|
4007647c34 |
||
|
|
01b70bdcaa |
||
|
|
5e6b96f0d6 |
||
|
|
ee45144cd7 |
||
|
|
62fe48e451 |
||
|
|
8e5c051437 |
||
|
|
14284057ea |
||
|
|
5bba57fd29 |
||
|
|
f649b5225b |
||
|
|
26e8da8f0a |
||
|
|
f7ac7f5ab6 |
||
|
|
a4b7a07bff |
||
|
|
01a378c5b1 |
||
|
|
172f608b66 |
||
|
|
9a054e9ff6 |
||
|
|
504ba44852 |
||
|
|
6eb68d1817 |
||
|
|
fe32dc4f2d |
||
|
|
a4640977b5 |
||
|
|
e9f3b080d5 |
||
|
|
1b7473149c |
||
|
|
a9fe1e987e |
||
|
|
258fd0995f |
||
|
|
cba1df56cc |
||
|
|
32c13b3b53 |
||
|
|
0d44112904 |
||
|
|
2ca6818b9a |
||
|
|
a060176cbb |
||
|
|
6acc2936e3 |
||
|
|
91d5c710ed |
||
|
|
cee2db2510 |
||
|
|
1a0361922b |
||
|
|
6515aacf42 |
||
|
|
fa9f43167d |
||
|
|
5864e34bdd |
||
|
|
ebe360d7ff |
||
|
|
4b700f5e66 |
||
|
|
1c96a2bbaa |
||
|
|
ae8531fe11 |
||
|
|
96d3c50780 |
||
|
|
cabe02aa71 |
||
|
|
5f77d92076 |
||
|
|
6e690ea4da |
||
|
|
0d9a1810bb |
||
|
|
1e3efa9bb2 |
||
|
|
a9f052b19c |
||
|
|
b2da22acae |
73
.eslintrc
|
|
@ -1,73 +0,0 @@
|
|||
{ "extends": ["eslint-config-airbnb", "prettier"],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"jest": true
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"rules": {
|
||||
"camelcase": 0,
|
||||
"strict": 0,
|
||||
"react/no-multi-comp": 0,
|
||||
"import/default": 0,
|
||||
"import/no-duplicates": 0,
|
||||
"import/named": 0,
|
||||
"import/namespace": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/no-named-as-default": 2,
|
||||
"import/prefer-default-export": 0,
|
||||
"comma-dangle": 0, // not sure why airbnb turned this on. gross!
|
||||
"indent": [2, 2, {"SwitchCase": 1}],
|
||||
"no-console": 0,
|
||||
"no-alert": 0,
|
||||
"no-shadow": 2,
|
||||
"arrow-body-style": 0,
|
||||
"react/prop-types": 0,
|
||||
"react/jsx-filename-extension": 0,
|
||||
"react/prefer-stateless-function": 0,
|
||||
"jsx-a11y/anchor-is-valid": 0,
|
||||
"jsx-a11y/tabindex-no-positive": 0,
|
||||
"no-mixed-operators": 0,
|
||||
"no-plusplus": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"prettier/prettier": "error",
|
||||
"jsx-a11y/no-autofocus": 0,
|
||||
"jsx-a11y/label-has-for": 0,
|
||||
"prefer-destructuring": 0,
|
||||
"react-hooks/rules-of-hooks": "error",
|
||||
"react-hooks/exhaustive-deps": "warn",
|
||||
"react/jsx-wrap-multilines": ["error", {"declaration": false, "assignment": false}],
|
||||
"react/jsx-one-expression-per-line": 0,
|
||||
"@typescript-eslint/no-unused-vars": 1,
|
||||
"import/no-extraneous-dependencies": ["error", {"devDependencies": ["**/*_test.ts"]}],
|
||||
"lines-between-class-members": 0,
|
||||
"react/jsx-fragments": 0,
|
||||
"jsx-a11y/label-has-associated-control": 0,
|
||||
"no-empty": 0,
|
||||
"import/extensions": 0
|
||||
},
|
||||
"plugins": [
|
||||
"react", "react-hooks", "import", "prettier", "@typescript-eslint"
|
||||
],
|
||||
"globals": {
|
||||
"__DEVELOPMENT__": true,
|
||||
"__PRODUCTION__": true,
|
||||
"__DISABLE_SSR__": true,
|
||||
"__DEVTOOLS__": true,
|
||||
"__DOMAIN__": true,
|
||||
"__BASE_URL__": true,
|
||||
"__BASE_NAME__": true,
|
||||
"__STRIPE_PUBLIC_KEY__": true,
|
||||
"__ROOT_URL__": true,
|
||||
"__CDN_URL__": true,
|
||||
"socket": true,
|
||||
"webpackIsomorphicTools": true,
|
||||
"StripeCheckout": true,
|
||||
|
||||
"browser": true,
|
||||
"chrome": true,
|
||||
__WEB_URL__: true,
|
||||
__API_ENDPOINT__: true,
|
||||
__VERSION__: true
|
||||
}
|
||||
}
|
||||
34
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '>=1.25.0'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
make install
|
||||
|
||||
- name: Test cli
|
||||
run: |
|
||||
make test-cli
|
||||
|
||||
- name: Test app
|
||||
run: |
|
||||
make test-api
|
||||
|
||||
- name: Test e2e
|
||||
run: |
|
||||
make test-e2e
|
||||
77
.github/workflows/release-cli.yml
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
name: Release CLI
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'cli-v*'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '>=1.25.0'
|
||||
|
||||
- name: Extract version from tag
|
||||
id: version
|
||||
run: |
|
||||
TAG=${GITHUB_REF#refs/tags/cli-v}
|
||||
echo "version=$TAG" >> $GITHUB_OUTPUT
|
||||
echo "Releasing version: $TAG"
|
||||
|
||||
- name: Install dependencies
|
||||
run: make install
|
||||
|
||||
- name: Run CLI tests
|
||||
run: make test-cli
|
||||
|
||||
- name: Run E2E tests
|
||||
run: make test-e2e
|
||||
|
||||
- name: Build CLI
|
||||
run: make version=${{ steps.version.outputs.version }} build-cli
|
||||
|
||||
- name: Generate changelog
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
TAG="cli-v${VERSION}"
|
||||
|
||||
# Find previous CLI tag
|
||||
PREV_TAG=$(git tag --sort=-version:refname | grep "^cli-" | grep -v "^${TAG}$" | head -n 1)
|
||||
|
||||
if [ -z "$PREV_TAG" ]; then
|
||||
echo "Error: No previous CLI tag found"
|
||||
echo "This appears to be the first release."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
./scripts/generate-changelog.sh cli "$TAG" "$PREV_TAG" > /tmp/changelog.txt
|
||||
cat /tmp/changelog.txt
|
||||
|
||||
- name: Create GitHub release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
TAG="cli-v${VERSION}"
|
||||
|
||||
# Determine if prerelease (version not matching major.minor.patch)
|
||||
FLAGS=""
|
||||
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
FLAGS="--prerelease"
|
||||
fi
|
||||
|
||||
gh release create "$TAG" \
|
||||
build/cli/*.tar.gz \
|
||||
build/cli/*_checksums.txt \
|
||||
$FLAGS \
|
||||
--title="$TAG" \
|
||||
--notes-file=/tmp/changelog.txt \
|
||||
--draft
|
||||
109
.github/workflows/release-server.yml
vendored
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
name: Release Server
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'server-v*'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: '>=1.25.0'
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Extract version from tag
|
||||
id: version
|
||||
run: |
|
||||
TAG=${GITHUB_REF#refs/tags/server-v}
|
||||
echo "version=$TAG" >> $GITHUB_OUTPUT
|
||||
echo "Releasing version: $TAG"
|
||||
|
||||
- name: Install dependencies
|
||||
run: make install
|
||||
|
||||
- name: Run tests
|
||||
run: make test
|
||||
|
||||
- name: Build server
|
||||
run: make version=${{ steps.version.outputs.version }} build-server
|
||||
|
||||
- name: Generate changelog
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
TAG="server-v${VERSION}"
|
||||
|
||||
# Find previous server tag
|
||||
PREV_TAG=$(git tag --sort=-version:refname | grep "^server-" | grep -v "^${TAG}$" | head -n 1)
|
||||
|
||||
if [ -z "$PREV_TAG" ]; then
|
||||
echo "Error: No previous server tag found"
|
||||
echo "This appears to be the first release."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
./scripts/generate-changelog.sh server "$TAG" "$PREV_TAG" > /tmp/changelog.txt
|
||||
cat /tmp/changelog.txt
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Prepare Docker build context
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
cp build/server/dnote_server_${VERSION}_linux_amd64.tar.gz host/docker/
|
||||
cp build/server/dnote_server_${VERSION}_linux_arm64.tar.gz host/docker/
|
||||
cp build/server/dnote_server_${VERSION}_linux_arm.tar.gz host/docker/
|
||||
cp build/server/dnote_server_${VERSION}_linux_386.tar.gz host/docker/
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./host/docker
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386
|
||||
tags: |
|
||||
dnote/dnote:${{ steps.version.outputs.version }}
|
||||
dnote/dnote:latest
|
||||
build-args: |
|
||||
version=${{ steps.version.outputs.version }}
|
||||
|
||||
- name: Create GitHub release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
TAG="server-v${VERSION}"
|
||||
|
||||
# Determine if prerelease (version not matching major.minor.patch)
|
||||
FLAGS=""
|
||||
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
FLAGS="--prerelease"
|
||||
fi
|
||||
|
||||
gh release create "$TAG" \
|
||||
build/server/*.tar.gz \
|
||||
build/server/*_checksums.txt \
|
||||
$FLAGS \
|
||||
--title="$TAG" \
|
||||
--notes-file=/tmp/changelog.txt \
|
||||
--draft
|
||||
4
.gitignore
vendored
|
|
@ -3,3 +3,7 @@
|
|||
.vagrant
|
||||
*.log
|
||||
node_modules
|
||||
/test
|
||||
tmp
|
||||
*.db
|
||||
/server
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"singleQuote": true
|
||||
}
|
||||
|
||||
31
.travis.yml
|
|
@ -1,31 +0,0 @@
|
|||
language: go
|
||||
dist: xenial
|
||||
|
||||
go:
|
||||
- 1.13
|
||||
|
||||
env:
|
||||
- NODE_VERSION=10.15.0
|
||||
|
||||
before_install:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get --yes remove postgresql\*
|
||||
- sudo apt-get install -y postgresql-11 postgresql-client-11
|
||||
- sudo cp /etc/postgresql/{9.6,11}/main/pg_hba.conf
|
||||
- sudo service postgresql restart 11
|
||||
|
||||
before_script:
|
||||
- nvm install "$NODE_VERSION"
|
||||
- nvm use "$NODE_VERSION"
|
||||
- node --version
|
||||
- psql -c "CREATE DATABASE dnote_test;" -U postgres
|
||||
|
||||
install:
|
||||
- make install
|
||||
|
||||
script:
|
||||
- make lint
|
||||
- make test-cli
|
||||
- make test-api
|
||||
- make test-web
|
||||
- make test-jslib
|
||||
178
CHANGELOG.md
|
|
@ -1,178 +0,0 @@
|
|||
# CHANAGELOG
|
||||
|
||||
All notable changes to the projects under this repository will be documented in this file.
|
||||
|
||||
* [Server](#server)
|
||||
* [CLI](#cli)
|
||||
* [Browser Extensions](#browser-extensions)
|
||||
|
||||
## Server
|
||||
|
||||
The following log documents the history of the server project.
|
||||
|
||||
### 0.5.0 - 2020-02-06
|
||||
|
||||
#### Changed
|
||||
|
||||
- **Deprecated** the digest and digest emails (#397)
|
||||
- **Deprecated** the repetition rules (#397)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Fix refocusing to the end of the textarea input (#405)
|
||||
|
||||
### 0.4.0 - 2020-01-09
|
||||
|
||||
#### Added
|
||||
|
||||
- A web-based digest (#380)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Send inactive reminders with a correct email type (#385)
|
||||
- Wrap words in note content (#389)
|
||||
|
||||
### 0.3.4 - 2019-12-24
|
||||
|
||||
#### Added
|
||||
|
||||
- Remind when the knowledge base stops growing (#375)
|
||||
- Alert when a password is changed (#375)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Implement syntax highlighting for code blocks ($377)
|
||||
|
||||
### 0.3.3 - 2019-12-17
|
||||
|
||||
#### Added
|
||||
|
||||
- Send welcome email with login instructions upon reigstering (#352)
|
||||
- Add an option to disable registration (#365)
|
||||
|
||||
#### Changed
|
||||
|
||||
- Send emails from the domain that hosts the application for on premise installations (#355)
|
||||
- For on premise installations, automatically upgrade user accounts (#361)
|
||||
|
||||
### 0.3.2 - 2019-11-20
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Fix server crash upon landing on a note page (#324).
|
||||
- Allow to synchronize a large number of records (#321)
|
||||
|
||||
### 0.3.1 - 2019-11-12
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Fix static files not being embedded in the binary. (#309)
|
||||
- Fix mobile menu not covering the whole screen. (#308)
|
||||
|
||||
### 0.3.0 - 2019-11-12
|
||||
|
||||
#### Added
|
||||
|
||||
- Share notes (#300)
|
||||
- Allow to recover from a missed repetition processing (#305)
|
||||
|
||||
### 0.2.1 - 2019-11-04
|
||||
|
||||
#### Upgrade Guide
|
||||
|
||||
* Please define the follwoing new environment variables:
|
||||
|
||||
- `WebURL`: the URL to your Dnote server, without the trailing slash. (e.g. `https://my-server.com`) (Please see #290)
|
||||
- `SmtpPort`: the SMTP port. (e.g. `465`) optional - required *if you want to configure email*
|
||||
|
||||
#### Added
|
||||
|
||||
- Display version number in the settings (#293)
|
||||
- Allow unsecure database connection in production (#276)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Allow to customize the app URL in the emails (#290)
|
||||
- Allow to customize the SMTP port (#292)
|
||||
|
||||
### 0.2.0 - 2019-10-28
|
||||
|
||||
#### Added
|
||||
|
||||
- Specify spaced repetition rule (#280)
|
||||
|
||||
#### Changed
|
||||
|
||||
- Treat a linebreak as a new line in the preview (#261)
|
||||
- Allow to have multiple editor states for adding and editing notes (#260)
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Fix jumping focus on editor (#265)
|
||||
|
||||
### 0.1.1 - 2019-09-30
|
||||
|
||||
#### Fixed
|
||||
|
||||
- Fix asset loading (#257)
|
||||
|
||||
|
||||
### 0.1.0 - 2019-09-30
|
||||
|
||||
#### Added
|
||||
|
||||
- Full-text search (#254)
|
||||
- Password recovery (#254)
|
||||
- Embedded notes in the digest emails (#254)
|
||||
|
||||
#### Removed
|
||||
|
||||
- **Breaking Change**: End-to-end encryption was removed. Existing users need to go to `/classic` and follow the automated migration steps. (#254)
|
||||
- **Breaking Change**: `v1` and `v2` API endpoints were removed, and `v3` API was added as a replacement.
|
||||
|
||||
#### Migration guide
|
||||
|
||||
- In your application, navigate to `/classic` and follow the automated migration steps.
|
||||
|
||||
|
||||
## CLI
|
||||
|
||||
The following log documentes the history of the CLI project
|
||||
|
||||
### 0.11.0 - 2020-02-05
|
||||
|
||||
#### Added
|
||||
|
||||
- Allow to pass credentials through flags while logging in (#403)
|
||||
|
||||
### 0.10.0 - 2019-09-30
|
||||
|
||||
#### Removed
|
||||
|
||||
- **Breaking Change**: End-to-end encryption was removed. Previous versions will no longer be able to interact with the web API, because `v1` and `v2` endpoints were replaced by a new `v3` endpoint to remove encryption.
|
||||
|
||||
#### Migration guide
|
||||
|
||||
- If you are using Dnote Pro, change the value of `apiEndpoint` in `~/.dnote/dnoterc` to `https://api.getdnote.com`.
|
||||
|
||||
## Browser Extensions
|
||||
|
||||
The following log documentes the history of the browser extensions project
|
||||
|
||||
### [Unreleased]
|
||||
|
||||
N/A
|
||||
|
||||
### 2.0.0 - 2019-10-29
|
||||
|
||||
- Allow to customize API and web URLs (#285)
|
||||
|
||||
### 1.1.1 - 2019-10-02
|
||||
|
||||
- Fix failing requests (#263)
|
||||
|
||||
### 1.1.0 - 2019-09-30
|
||||
|
||||
#### Removed
|
||||
|
||||
- **Breaking Change**: End-to-end encryption was removed. Previous versions will no longer be able to interact with the web API, because `v1` and `v2` endpoints were replaced by a new `v3` endpoint to remove encryption.
|
||||
|
|
@ -8,95 +8,61 @@ Dnote is an open source project.
|
|||
|
||||
## Setting up
|
||||
|
||||
Dnote uses [Vagrant](https://github.com/hashicorp/vagrant) to provision a consistent development environment.
|
||||
The CLI and server are single single binary files with SQLite embedded - no databases to install, no containers to run, no VMs required.
|
||||
|
||||
*Prerequisites*
|
||||
**Prerequisites**
|
||||
|
||||
* Vagrant ([Download](https://www.vagrantup.com/downloads.html))
|
||||
* VirtualBox ([Download](https://www.virtualbox.org/))
|
||||
* Go 1.25+ ([Download](https://go.dev/dl/))
|
||||
* Node.js 18+ ([Download](https://nodejs.org/) - only needed for building frontend assets)
|
||||
|
||||
Run the following command from the project root. It starts the virtual machine and bootstraps the project.
|
||||
**Quick Start**
|
||||
|
||||
```
|
||||
vagrant up
|
||||
```
|
||||
1. Clone the repository
|
||||
2. Install dependencies:
|
||||
```bash
|
||||
make install
|
||||
```
|
||||
3. Start developing! Run tests:
|
||||
```bash
|
||||
make test
|
||||
```
|
||||
Or start the dev server:
|
||||
```bash
|
||||
make dev-server
|
||||
```
|
||||
|
||||
*Workflow*
|
||||
|
||||
* You can make changes to the source code from the host machine.
|
||||
* Any commands need to be run inside the virtual machine. You can connect to it by running `vagrant ssh`.
|
||||
* If you want to run tests in a WATCH mode, please do so from the host machine. We cannot watch file changes due to the limitation of file system used in a virtual machine.
|
||||
That's it. You're ready to contribute.
|
||||
|
||||
## Server
|
||||
|
||||
The server consists of the frontend web application and a web server.
|
||||
|
||||
### Development
|
||||
|
||||
* Run `make dev-server` to start a local server.
|
||||
* You can access the server on `localhost:3000` on your machine.
|
||||
|
||||
### Test
|
||||
|
||||
```bash
|
||||
# Run tests for the frontend web application
|
||||
make test-web
|
||||
# Start dev server (runs on localhost:3001)
|
||||
make dev-server
|
||||
|
||||
# Run in watch mode
|
||||
WATCH=true make test-web
|
||||
|
||||
# Run tests for API
|
||||
# Run tests
|
||||
make test-api
|
||||
|
||||
# Run in watch mode
|
||||
# Run tests in watch mode
|
||||
WATCH=true make test-api
|
||||
```
|
||||
|
||||
|
||||
## Command Line Interface
|
||||
|
||||
### Build
|
||||
```bash
|
||||
# Run tests
|
||||
make test-cli
|
||||
|
||||
You can build either a development version or a production version:
|
||||
|
||||
```
|
||||
# Build a development version for your platform and place it in your `PATH`.
|
||||
# Build dev version (places in your PATH)
|
||||
make debug=true build-cli
|
||||
|
||||
# Build a production version for all platforms
|
||||
# Build production version for all platforms
|
||||
make version=v0.1.0 build-cli
|
||||
|
||||
# Build a production version for a specific platform
|
||||
# Build for a specific platform
|
||||
# Note: You cannot cross-compile using this method because Dnote uses CGO
|
||||
# and requires the OS specific headers.
|
||||
GOOS=[insert OS] GOARCH=[insert arch] make version=v0.1.0 build-cli
|
||||
```
|
||||
|
||||
### Test
|
||||
|
||||
* Run all tests for the command line interface:
|
||||
|
||||
```
|
||||
make test-cli
|
||||
```
|
||||
|
||||
### Debug
|
||||
|
||||
Run Dnote with `DNOTE_DEBUG=1` to print debugging statements. For instance:
|
||||
|
||||
```
|
||||
# Debug mode
|
||||
DNOTE_DEBUG=1 dnote sync
|
||||
```
|
||||
|
||||
### Release
|
||||
|
||||
* Run `make version=v0.1.0 release-cli` to achieve the following:
|
||||
* Build for all target platforms, create a git tag, push all tags to the repository
|
||||
* Create a release on GitHub and [Dnote Homebrew tap](https://github.com/dnote/homebrew-dnote).
|
||||
|
||||
**Note**
|
||||
|
||||
- If a release is not stable,
|
||||
- disable the homebrew release by commenting out relevant code in the release script.
|
||||
- mark release as pre-release on GitHub release
|
||||
|
||||
|
|
|
|||
209
LICENSE
|
|
@ -1,8 +1,203 @@
|
|||
Source code in this repository is variously licensed under the GNU Affero General Public
|
||||
License v3.0 (GNU AGPLv3), and GNU General Public License v3.0 (GNU GPLv3). A copy of each
|
||||
license can be found in the licenses directory. The Source code for the cli is licensed under
|
||||
GNU GPLv3. The source code for the server and the web is licensed under GNU AGPLv3. Unless
|
||||
otherwise noted, source code in a given file is licensed under the GNU AGPLv3.
|
||||
|
||||
Unless otherwise noted at the beginning of the file, the copyright belongs to
|
||||
Monomax Software Pty Ltd.
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
|
|
|||
146
Makefile
|
|
@ -1,6 +1,5 @@
|
|||
PACKR2 := $(shell command -v packr2 2> /dev/null)
|
||||
NPM := $(shell command -v npm 2> /dev/null)
|
||||
HUB := $(shell command -v hub 2> /dev/null)
|
||||
GH := $(shell command -v gh 2> /dev/null)
|
||||
|
||||
currentDir = $(shell pwd)
|
||||
serverOutputDir = ${currentDir}/build/server
|
||||
|
|
@ -12,11 +11,6 @@ install: install-go install-js
|
|||
.PHONY: install
|
||||
|
||||
install-go:
|
||||
ifndef PACKR2
|
||||
@echo "==> installing packr2"
|
||||
@go get -u github.com/gobuffalo/packr/v2/packr2
|
||||
endif
|
||||
|
||||
@echo "==> installing go dependencies"
|
||||
@go mod download
|
||||
.PHONY: install-go
|
||||
|
|
@ -29,29 +23,17 @@ endif
|
|||
@echo "==> installing js dependencies"
|
||||
|
||||
ifeq ($(CI), true)
|
||||
@(cd ${currentDir} && npm install --unsafe-perm=true)
|
||||
@(cd ${currentDir}/web && npm install --unsafe-perm=true)
|
||||
@(cd ${currentDir}/browser && npm install --unsafe-perm=true)
|
||||
@(cd ${currentDir}/jslib && npm install --unsafe-perm=true)
|
||||
@(cd ${currentDir}/pkg/server/assets && npm ci --cache $(NPM_CACHE_DIR) --prefer-offline --unsafe-perm=true)
|
||||
else
|
||||
@(cd ${currentDir} && npm install)
|
||||
@(cd ${currentDir}/web && npm install)
|
||||
@(cd ${currentDir}/browser && npm install)
|
||||
@(cd ${currentDir}/jslib && npm install)
|
||||
@(cd ${currentDir}/pkg/server/assets && npm install)
|
||||
endif
|
||||
.PHONY: install-js
|
||||
|
||||
lint:
|
||||
@(cd ${currentDir}/web && npm run lint)
|
||||
@(cd ${currentDir}/jslib && npm run lint)
|
||||
@(cd ${currentDir}/browser && npm run lint)
|
||||
.PHONY: lint
|
||||
|
||||
## test
|
||||
test: test-cli test-api test-web test-jslib
|
||||
test: test-cli test-api test-e2e
|
||||
.PHONY: test
|
||||
|
||||
test-cli:
|
||||
test-cli: generate-cli-schema
|
||||
@echo "==> running CLI test"
|
||||
@(${currentDir}/scripts/cli/test.sh)
|
||||
.PHONY: test-cli
|
||||
|
|
@ -61,57 +43,47 @@ test-api:
|
|||
@(${currentDir}/scripts/server/test-local.sh)
|
||||
.PHONY: test-api
|
||||
|
||||
test-web:
|
||||
@echo "==> running web test"
|
||||
|
||||
ifeq ($(WATCH), true)
|
||||
@(cd ${currentDir}/web && npm run test:watch)
|
||||
else
|
||||
@(cd ${currentDir}/web && npm run test)
|
||||
endif
|
||||
.PHONY: test-web
|
||||
|
||||
test-jslib:
|
||||
@echo "==> running jslib test"
|
||||
|
||||
ifeq ($(WATCH), true)
|
||||
@(cd ${currentDir}/jslib && npm run test:watch)
|
||||
else
|
||||
@(cd ${currentDir}/jslib && npm run test)
|
||||
endif
|
||||
.PHONY: test-jslib
|
||||
|
||||
test-selfhost:
|
||||
@echo "==> running a smoke test for self-hosting"
|
||||
|
||||
@${currentDir}/host/smoketest/run_test.sh ${tarballPath}
|
||||
.PHONY: test-jslib
|
||||
test-e2e:
|
||||
@echo "==> running E2E test"
|
||||
@(${currentDir}/scripts/e2e/test.sh)
|
||||
.PHONY: test-e2e
|
||||
|
||||
# development
|
||||
dev-server:
|
||||
@echo "==> running dev environment"
|
||||
@VERSION=master ${currentDir}/scripts/web/dev.sh
|
||||
@VERSION=master ${currentDir}/scripts/server/dev.sh
|
||||
.PHONY: dev-server
|
||||
|
||||
## build
|
||||
build-web:
|
||||
ifndef version
|
||||
$(error version is required. Usage: make version=0.1.0 build-web)
|
||||
endif
|
||||
@echo "==> building web"
|
||||
@VERSION=${version} ${currentDir}/scripts/web/build-prod.sh
|
||||
.PHONY: build-web
|
||||
|
||||
build-server: build-web
|
||||
build-server:
|
||||
ifndef version
|
||||
$(error version is required. Usage: make version=0.1.0 build-server)
|
||||
endif
|
||||
|
||||
@echo "==> building server assets"
|
||||
@(cd "${currentDir}/pkg/server/assets/" && ./styles/build.sh)
|
||||
@(cd "${currentDir}/pkg/server/assets/" && ./js/build.sh)
|
||||
|
||||
@echo "==> building server"
|
||||
@${currentDir}/scripts/server/build.sh $(version)
|
||||
.PHONY: build-server
|
||||
|
||||
build-cli:
|
||||
build-server-docker: build-server
|
||||
ifndef version
|
||||
$(error version is required. Usage: make version=0.1.0 [platform=linux/amd64] build-server-docker)
|
||||
endif
|
||||
|
||||
@echo "==> building Docker image"
|
||||
@(cd ${currentDir}/host/docker && ./build.sh $(version) $(platform))
|
||||
.PHONY: build-server-docker
|
||||
|
||||
generate-cli-schema:
|
||||
@echo "==> generating CLI database schema"
|
||||
@mkdir -p pkg/cli/database
|
||||
@touch pkg/cli/database/schema.sql
|
||||
@go run -tags fts5 ./pkg/cli/database/schema
|
||||
.PHONY: generate-cli-schema
|
||||
|
||||
build-cli: generate-cli-schema
|
||||
ifeq ($(debug), true)
|
||||
@echo "==> building cli in dev mode"
|
||||
@${currentDir}/scripts/cli/dev.sh
|
||||
|
|
@ -126,61 +98,7 @@ endif
|
|||
endif
|
||||
.PHONY: build-cli
|
||||
|
||||
## release
|
||||
release-cli: clean build-cli
|
||||
ifndef version
|
||||
$(error version is required. Usage: make version=0.1.0 release-cli)
|
||||
endif
|
||||
ifndef HUB
|
||||
$(error please install hub)
|
||||
endif
|
||||
|
||||
if [ ! -d ${cliHomebrewDir} ]; then \
|
||||
@echo "homebrew-dnote not found locally. did you clone it?"; \
|
||||
@exit 1; \
|
||||
fi
|
||||
|
||||
@echo "==> releasing cli"
|
||||
@${currentDir}/scripts/release.sh cli $(version) ${cliOutputDir}
|
||||
|
||||
@echo "===> releading on Homebrew"
|
||||
@(cd "${cliHomebrewDir}" && \
|
||||
./release.sh "$(version)" "${cliOutputDir}/dnote_$(version)_darwin_amd64.tar.gz")
|
||||
.PHONY: release-cli
|
||||
|
||||
release-server:
|
||||
ifndef version
|
||||
$(error version is required. Usage: make version=0.1.0 release-server)
|
||||
endif
|
||||
ifndef HUB
|
||||
$(error please install hub)
|
||||
endif
|
||||
|
||||
@echo "==> releasing server"
|
||||
@${currentDir}/scripts/release.sh server $(version) ${serverOutputDir}
|
||||
|
||||
@echo "==> building and releasing docker image"
|
||||
@(cd ${currentDir}/host/docker && ./build.sh $(version))
|
||||
@(cd ${currentDir}/host/docker && ./release.sh $(version))
|
||||
.PHONY: release-server
|
||||
|
||||
# migrations
|
||||
create-migration:
|
||||
ifndef filename
|
||||
$(error filename is required. Usage: make filename=your-filename create-migration)
|
||||
endif
|
||||
|
||||
@(cd ${currentDir}/pkg/server/database && ./scripts/create-migration.sh $(filename))
|
||||
.PHONY: create-migration
|
||||
|
||||
clean:
|
||||
@git clean -f
|
||||
@rm -rf build
|
||||
@rm -rf web/public
|
||||
.PHONY: clean
|
||||
|
||||
clean-dep:
|
||||
@rm -rf ${currentDir}/web/node_modules
|
||||
@rm -rf ${currentDir}/jslib/node_modules
|
||||
@rm -rf ${currentDir}/browser/node_modules
|
||||
.PHONY: clean-dep
|
||||
|
|
|
|||
61
README.md
|
|
@ -1,37 +1,64 @@
|
|||

|
||||
=========================
|
||||
|
||||
[](https://travis-ci.org/dnote/dnote)
|
||||

|
||||
|
||||
Dnote is a simple command line notebook for programmers.
|
||||
Dnote is a simple command line notebook. Single binary, no dependencies. Since 2017.
|
||||
|
||||
It **keeps you focused** by providing a way of effortlessly capturing and retrieving information **without leaving your terminal**. It also offers a seamless **multi-device sync** and a **web interface**.
|
||||
Your notes are stored in **one SQLite file** - portable, searchable, and completely under your control. Optional sync between devices via a self-hosted server with REST API access.
|
||||
|
||||

|
||||
```sh
|
||||
# Add a note (or omit -c to launch your editor)
|
||||
dnote add linux -c "Check disk usage with df -h"
|
||||
|
||||
# View notes in a book
|
||||
dnote view linux
|
||||
|
||||
# Full-text search
|
||||
dnote find "disk usage"
|
||||
|
||||
# Sync notes
|
||||
dnote sync
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
On macOS, you can install using Homebrew:
|
||||
```bash
|
||||
# Linux, macOS, FreeBSD, Windows
|
||||
curl -s https://www.getdnote.com/install | sh
|
||||
|
||||
```sh
|
||||
brew tap dnote/dnote
|
||||
# macOS with Homebrew
|
||||
brew install dnote
|
||||
```
|
||||
|
||||
On Linux or macOS, you can use the installation script:
|
||||
Or [download binary](https://github.com/dnote/dnote/releases).
|
||||
|
||||
curl -s https://www.getdnote.com/install | sh
|
||||
## Server (Optional)
|
||||
|
||||
Otherwise, you can download the binary for your platform manually from the [releases page](https://github.com/dnote/dnote/releases).
|
||||
Server is a binary with SQLite embedded. No database setup is required.
|
||||
|
||||
## Server
|
||||
If using docker, create a compose.yml:
|
||||
|
||||
The quickest way to experience the Dnote server is to use [Dnote Cloud](https://app.getdnote.com).
|
||||
```yaml
|
||||
services:
|
||||
dnote:
|
||||
image: dnote/dnote:latest
|
||||
container_name: dnote
|
||||
ports:
|
||||
- 3001:3001
|
||||
volumes:
|
||||
- ./dnote_data:/data
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
Or you can install it on your server by [using Docker](https://github.com/dnote/dnote/blob/master/host/docker/README.md), or [using a binary](https://github.com/dnote/dnote/blob/master/SELF_HOSTING.md).
|
||||
Then run:
|
||||
|
||||
## See Also
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
- [Homepage](https://www.getdnote.com)
|
||||
- [Forum](https://forum.getdnote.com)
|
||||
- [I Wrote Down Everything I Learned While Programming for a Month](https://www.getdnote.com/blog/writing-everything-i-learn-coding-for-a-month/)
|
||||
Or see the [guide](https://www.getdnote.com/docs/server/manual) for binary installation.
|
||||
|
||||
## Documentation
|
||||
|
||||
See the [Dnote doc](https://www.getdnote.com/docs).
|
||||
|
|
|
|||
176
SELF_HOSTING.md
|
|
@ -1,157 +1,43 @@
|
|||
# Installing Dnote Server
|
||||
# Self-Hosting Dnote Server
|
||||
|
||||
This guide documents the steps for installing the Dnote server on your own machine. If you prefer Docker, please see [the Docker guide](https://github.com/dnote/dnote/blob/master/host/docker/README.md).
|
||||
Please see the [doc](https://www.getdnote.com/docs/server) for more.
|
||||
|
||||
## Overview
|
||||
## Docker Installation
|
||||
|
||||
Dnote server comes as a single binary file that you can simply download and run. It uses Postgres as the database.
|
||||
1. Install [Docker](https://docs.docker.com/install/).
|
||||
2. Install Docker [Compose plugin](https://docs.docker.com/compose/install/linux/).
|
||||
3. Create a `compose.yml` file with the following content:
|
||||
|
||||
## Installation
|
||||
```yaml
|
||||
services:
|
||||
dnote:
|
||||
image: dnote/dnote:latest
|
||||
container_name: dnote
|
||||
ports:
|
||||
- 3001:3001
|
||||
volumes:
|
||||
- ./dnote_data:/data
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
1. Install Postgres 11+.
|
||||
2. Create a `dnote` database by running `createdb dnote`
|
||||
3. Download the official Dnote server release from the [release page](https://github.com/dnote/dnote/releases).
|
||||
4. Extract the archive and move the `dnote-server` executable to `/usr/local/bin`.
|
||||
4. Run the following to download the image and start the container
|
||||
|
||||
```
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Visit http://localhost:3001 in your browser to see Dnote running.
|
||||
|
||||
## Manual Installation
|
||||
|
||||
Download from [releases](https://github.com/dnote/dnote/releases), extract, and run:
|
||||
|
||||
```bash
|
||||
tar -xzf dnote-server-$version-$os.tar.gz
|
||||
mv ./dnote-server /usr/local/bin
|
||||
dnote-server start --baseUrl=https://your.server
|
||||
```
|
||||
|
||||
4. Run Dnote
|
||||
You're up and running. Database: `~/.local/share/dnote/server.db` (customize with `--dbPath`). Run `dnote-server start --help` for options.
|
||||
|
||||
```bash
|
||||
GO_ENV=PRODUCTION \
|
||||
DBHost=localhost \
|
||||
DBPort=5432 \
|
||||
DBName=dnote \
|
||||
DBUser=$user \
|
||||
DBPassword=$password \
|
||||
WebURL=$webURL \
|
||||
SmtpHost=$SmtpHost \
|
||||
SmtpPort=$SmtpPort \
|
||||
SmtpUsername=$SmtpUsername \
|
||||
SmtpPassword=$SmtpPassword \
|
||||
DisableRegistration=false
|
||||
dnote-server start
|
||||
```
|
||||
|
||||
Replace `$user`, `$password` with the credentials of the Postgres user that owns the `dnote` database.
|
||||
|
||||
Replace `$webURL` with the full URL to your server, without a trailing slash (e.g. `https://your.server`).
|
||||
|
||||
Replace `$SmtpHost`, `SmtpPort`, `$SmtpUsername`, `$SmtpPassword` with actual values, if you would like to receive spaced repetition through email.
|
||||
|
||||
Replace `DisableRegistration` to `true` if you would like to disable user registrations.
|
||||
|
||||
By default, dnote server will run on the port 3000.
|
||||
|
||||
## Configuration
|
||||
|
||||
By now, Dnote is fully functional in your machine. The API, frontend app, and the background tasks are all in the single binary. Let's take a few more steps to configure Dnote.
|
||||
|
||||
### Configure Nginx
|
||||
|
||||
To make it accessible from the Internet, you need to configure Nginx.
|
||||
|
||||
1. Install nginx.
|
||||
2. Create a new file in `/etc/nginx/sites-enabled/dnote` with the following contents:
|
||||
|
||||
```
|
||||
server {
|
||||
server_name my-dnote-server.com;
|
||||
|
||||
location / {
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
}
|
||||
}
|
||||
```
|
||||
3. Replace `my-dnote-server.com` with the URL for your server.
|
||||
4. Reload the nginx configuration by running the following:
|
||||
|
||||
```
|
||||
sudo service nginx reload
|
||||
```
|
||||
|
||||
Now you can access the Dnote frontend application on `/`, and the API on `/api`.
|
||||
|
||||
### Configure TLS by using LetsEncrypt
|
||||
|
||||
It is recommended to use HTTPS. Obtain a certificate using LetsEncrypt and configure TLS in Nginx.
|
||||
|
||||
In the future versions of the Dnote Server, HTTPS will be required at all times.
|
||||
|
||||
### Run Dnote As a Daemon
|
||||
|
||||
We can use `systemd` to run Dnote in the background as a Daemon, and automatically start it on system reboot.
|
||||
|
||||
1. Create a new file at `/etc/systemd/system/dnote.service` with the following content:
|
||||
|
||||
```
|
||||
[Unit]
|
||||
Description=Starts the dnote server
|
||||
Requires=network.target
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=$user
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
WorkingDirectory=/home/$user
|
||||
ExecStart=/usr/local/bin/dnote-server start
|
||||
Environment=GO_ENV=PRODUCTION
|
||||
Environment=DBHost=localhost
|
||||
Environment=DBPort=5432
|
||||
Environment=DBName=dnote
|
||||
Environment=DBUser=$DBUser
|
||||
Environment=DBPassword=$DBPassword
|
||||
Environment=DBSkipSSL=true
|
||||
Environment=WebURL=$WebURL
|
||||
Environment=SmtpHost=
|
||||
Environment=SmtpPort=
|
||||
Environment=SmtpUsername=
|
||||
Environment=SmtpPassword=
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Replace `$user`, `$WebURL`, `$DBUser`, and `$DBPassword` with the actual values.
|
||||
|
||||
Optionally, if you would like to send spaced repetitions throught email, populate `SmtpHost`, `SmtpPort`, `SmtpUsername`, and `SmtpPassword`.
|
||||
|
||||
2. Reload the change by running `sudo systemctl daemon-reload`.
|
||||
3. Enable the Daemon by running `sudo systemctl enable dnote`.`
|
||||
4. Start the Daemon by running `sudo systemctl start dnote`
|
||||
|
||||
### Configure clients
|
||||
|
||||
Let's configure Dnote clients to connect to the self-hosted web API endpoint.
|
||||
|
||||
#### CLI
|
||||
|
||||
We need to modify the configuration file for the CLI. It should have been generated at `~/.dnote/dnoterc` upon running the CLI for the first time.
|
||||
|
||||
The following is an example configuration:
|
||||
|
||||
```yaml
|
||||
editor: nvim
|
||||
apiEndpoint: https://api.getdnote.com
|
||||
```
|
||||
|
||||
Simply change the value for `apiEndpoint` to a full URL to the self-hosted instance, followed by '/api', and save the configuration file.
|
||||
|
||||
e.g.
|
||||
|
||||
```yaml
|
||||
editor: nvim
|
||||
apiEndpoint: my-dnote-server.com/api
|
||||
```
|
||||
|
||||
#### Browser extension
|
||||
|
||||
Navigate into the 'Settings' tab and set the values for 'API URL', and 'Web URL'.
|
||||
Set `apiEndpoint: https://your.server/api` in `~/.config/dnote/dnoterc` to connect your CLI to the server.
|
||||
|
|
|
|||
20
Vagrantfile
vendored
|
|
@ -1,20 +0,0 @@
|
|||
# -*- mode: ruby -*-
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "ubuntu/bionic64"
|
||||
config.vm.synced_folder '.', '/go/src/github.com/dnote/dnote'
|
||||
config.vm.network "forwarded_port", guest: 3000, host: 3000
|
||||
config.vm.network "forwarded_port", guest: 8080, host: 8080
|
||||
config.vm.network "forwarded_port", guest: 5432, host: 5433
|
||||
|
||||
config.vm.provision 'shell', path: './scripts/vagrant/install_utils.sh'
|
||||
config.vm.provision 'shell', path: './scripts/vagrant/install_go.sh', privileged: false
|
||||
config.vm.provision 'shell', path: './scripts/vagrant/install_node.sh', privileged: false
|
||||
config.vm.provision 'shell', path: './scripts/vagrant/install_postgres.sh', privileged: false
|
||||
config.vm.provision 'shell', path: './scripts/vagrant/bootstrap.sh', privileged: false
|
||||
|
||||
config.vm.provider "virtualbox" do |v|
|
||||
v.memory = 4000
|
||||
v.cpus = 2
|
||||
end
|
||||
end
|
||||
BIN
assets/cli.gif
|
Before Width: | Height: | Size: 245 KiB |
|
Before Width: | Height: | Size: 37 KiB |
5
browser/.gitignore
vendored
|
|
@ -1,5 +0,0 @@
|
|||
/dist
|
||||
/package
|
||||
/node_modules
|
||||
.DS_Store
|
||||
extension.tar.gz
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
# Contributing
|
||||
|
||||
Use the following commands to set up, build, and release.
|
||||
|
||||
## Set up
|
||||
|
||||
* `npm install` to install dependencies.
|
||||
|
||||
## Developing locally
|
||||
|
||||
* `npm run watch:firefox`
|
||||
* `npm run watch:chrome`
|
||||
|
||||
## Releasing
|
||||
|
||||
* Set a new version in `package.json`
|
||||
* Run `./scripts/build_prod.sh`
|
||||
* A gulp task `manifest` will copy the version from `package.json` to `manifest.json`
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
# Note to reviewer
|
||||
|
||||
This document contains instructions about how to reproduce the final build of this extension.
|
||||
|
||||
All releases are tagged and pushed to [the GitHub repository](https://github.com/dnote/dnote).
|
||||
|
||||
## Steps
|
||||
|
||||
To reproduce the obfuscated code for Firefox, please follow the steps below.
|
||||
|
||||
1. Run `npm install` to install dependencies
|
||||
2. Run `./scripts/build_prod.sh` to build for Firefox and Chrome.
|
||||
|
||||
The obfuscated code will be under `/dist/firefox` and `/dist/chrome`.
|
||||
|
||||
## Further questions
|
||||
|
||||
Please contact sung@dnote.io
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
# Dnote Browser Extension
|
||||
|
||||
Dnote browser extension for Chrome and Firefox. Capture new information without opening a new tab or leaving your browser.
|
||||
|
||||

|
||||
|
||||
## Installation
|
||||
|
||||
1. Install the extension
|
||||
|
||||
* Firefox - https://addons.mozilla.org/addon/dnote
|
||||
* Chrome - https://chrome.google.com/webstore/detail/dnote/mcfbfmihbijfaambfbbfcdcfibcjcahi
|
||||
|
||||
2. Login with your API key from https://dnote.io
|
||||
|
||||
## Overview
|
||||
|
||||
We learn many things while reading technical articles, or browsing StackOverflow. Unless we write them down we forget most of them exponentially.
|
||||
|
||||
This extension integrates seamlessly with [Dnote CLI](https://github.com/dnote/dnote/cli) and requires [Dnote Cloud](https://www.getdnote.com/pricing) account.
|
||||
|
||||
## Hotkeys
|
||||
|
||||
Write new notes without even moving your hands to the mouse.
|
||||
|
||||
* **Ctrl + d** - Open the extension (**Ctrl + Shift + v** on Firefox on Linux).
|
||||
* **Shift + Enter** - Save the current note
|
||||
* **b** - Open the saved note in the browser
|
||||
|
Before Width: | Height: | Size: 519 KiB |
|
|
@ -1,100 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const gulp = require('gulp');
|
||||
const del = require('del');
|
||||
const replace = require('gulp-replace');
|
||||
const gulpif = require('gulp-if');
|
||||
const imagemin = require('gulp-imagemin');
|
||||
const livereload = require('gulp-livereload');
|
||||
const zip = require('gulp-zip');
|
||||
|
||||
const target = process.env.TARGET;
|
||||
|
||||
gulp.task('manifest', () => {
|
||||
const pkg = require('./package.json');
|
||||
|
||||
return gulp
|
||||
.src(`manifests/${target}/manifest.json`)
|
||||
.pipe(replace('__VERSION__', pkg.version))
|
||||
.pipe(gulp.dest(`dist/${target}`));
|
||||
});
|
||||
|
||||
gulp.task('styles', () => {
|
||||
return gulp.src('src/styles/*.css').pipe(gulp.dest(`dist/${target}/styles`));
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
'html',
|
||||
gulp.series('styles', () => {
|
||||
return gulp.src('src/*.html').pipe(gulp.dest(`dist/${target}`));
|
||||
})
|
||||
);
|
||||
|
||||
gulp.task('images', () => {
|
||||
return gulp
|
||||
.src('src/images/**/*')
|
||||
.pipe(
|
||||
gulpif(
|
||||
gulpif.isFile,
|
||||
imagemin({
|
||||
progressive: true,
|
||||
interlaced: true,
|
||||
svgoPlugins: [{ cleanupIDs: false }]
|
||||
})
|
||||
)
|
||||
)
|
||||
.pipe(gulp.dest(`dist/${target}/images`));
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
'clean',
|
||||
del.bind(null, ['.tmp', `dist/${target}`, `package/${target}`])
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
'watch',
|
||||
gulp.series('manifest', 'html', 'styles', 'images', () => {
|
||||
livereload.listen();
|
||||
|
||||
gulp
|
||||
.watch([
|
||||
'src/*.html',
|
||||
'src/scripts/**/*',
|
||||
'src/images/**/*',
|
||||
'src/styles/**/*'
|
||||
])
|
||||
.on('change', livereload.reload);
|
||||
|
||||
gulp.watch('src/*.html', gulp.parallel('html'));
|
||||
gulp.watch('manifests/**/*.json', gulp.parallel('manifest'));
|
||||
})
|
||||
);
|
||||
|
||||
gulp.task('package', function() {
|
||||
const manifest = require(`./dist/${target}/manifest.json`);
|
||||
|
||||
return gulp
|
||||
.src(`dist/${target}/**`)
|
||||
.pipe(zip('dnote-' + manifest.version + '.zip'))
|
||||
.pipe(gulp.dest(`package/${target}`));
|
||||
});
|
||||
|
||||
gulp.task('build', gulp.series('manifest', gulp.parallel('html', 'images')));
|
||||
|
||||
gulp.task('default', gulp.series('clean', 'build'));
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
{
|
||||
"name": "Dnote",
|
||||
"version": "__VERSION__",
|
||||
"description": "Capture your microlessons without leaving the browser.",
|
||||
"icons": {
|
||||
"16": "images/iconx16.png",
|
||||
"48": "images/iconx48.png",
|
||||
"128": "images/iconx128.png"
|
||||
},
|
||||
"manifest_version": 2,
|
||||
"browser_action": {
|
||||
"default_icon": {
|
||||
"16": "images/iconx16.png",
|
||||
"32": "images/iconx32.png"
|
||||
},
|
||||
"default_popup": "popup.html"
|
||||
},
|
||||
"background": {
|
||||
"scripts": []
|
||||
},
|
||||
"content_scripts": [],
|
||||
"permissions": ["storage"],
|
||||
"commands": {
|
||||
"_execute_browser_action": {
|
||||
"suggested_key": {
|
||||
"default": "Ctrl+D",
|
||||
"mac": "MacCtrl+D"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"name": "Dnote",
|
||||
"version": "__VERSION__",
|
||||
"description": "Capture your microlessons without leaving the browser.",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "sung@dnote.io",
|
||||
"strict_min_version": "42.0"
|
||||
}
|
||||
},
|
||||
"icons": {
|
||||
"16": "images/iconx16.png",
|
||||
"48": "images/iconx48.png",
|
||||
"128": "images/iconx128.png"
|
||||
},
|
||||
"manifest_version": 2,
|
||||
"browser_action": {
|
||||
"default_icon": {
|
||||
"16": "images/iconx16.png",
|
||||
"32": "images/iconx32.png"
|
||||
},
|
||||
"default_popup": "popup.html"
|
||||
},
|
||||
"background": {
|
||||
"scripts": []
|
||||
},
|
||||
"content_scripts": [],
|
||||
"permissions": ["storage"],
|
||||
"commands": {
|
||||
"_execute_browser_action": {
|
||||
"suggested_key": {
|
||||
"default": "Ctrl+D",
|
||||
"linux": "Ctrl+Shift+V",
|
||||
"mac": "MacCtrl+D"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10511
browser/package-lock.json
generated
|
|
@ -1,49 +0,0 @@
|
|||
{
|
||||
"name": "dnote-extension",
|
||||
"repository": "https://github.com/dnote/dnote",
|
||||
"description": "Dnote browser extension for Chrome and Firefox",
|
||||
"scripts": {
|
||||
"clean": "TARGET=firefox gulp clean && TARGET=chrome gulp clean",
|
||||
"build:chrome": "TARGET=chrome NODE_ENV=production concurrently webpack \"gulp build\"",
|
||||
"build:firefox": "TARGET=firefox NODE_ENV=production concurrently webpack \"gulp build\"",
|
||||
"package:chrome": "TARGET=chrome NODE_ENV=production gulp package",
|
||||
"package:firefox": "TARGET=firefox NODE_ENV=production gulp package",
|
||||
"watch:chrome": "TARGET=chrome NODE_ENV=development concurrently \"webpack --watch\" \"gulp watch\" ",
|
||||
"watch:firefox": "TARGET=firefox NODE_ENV=development concurrently \"webpack --watch\" \"gulp watch\" ",
|
||||
"lint": "../node_modules/.bin/eslint ./src --ext .ts,.tsx,.js"
|
||||
},
|
||||
"author": "Monomax Software Pty Ltd",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"classnames": "^2.2.5",
|
||||
"lodash": "^4.17.15",
|
||||
"qs": "^6.9.1",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"react-redux": "^7.1.3",
|
||||
"react-select": "^3.0.8",
|
||||
"redux": "^4.0.4",
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-thunk": "^2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.7.5",
|
||||
"@babel/preset-env": "^7.7.6",
|
||||
"@types/react": "^16.9.16",
|
||||
"@types/react-dom": "^16.9.4",
|
||||
"concurrently": "^5.0.1",
|
||||
"del": "^5.0.0",
|
||||
"gulp": "^4.0.0",
|
||||
"gulp-if": "^3.0.0",
|
||||
"gulp-imagemin": "^6.2.0",
|
||||
"gulp-livereload": "^4.0.2",
|
||||
"gulp-replace": "^1.0.0",
|
||||
"gulp-zip": "^5.0.1",
|
||||
"prettier": "^1.19.1",
|
||||
"ts-loader": "^6.2.1",
|
||||
"typescript": "^3.7.3",
|
||||
"webpack": "^4.41.3",
|
||||
"webpack-cli": "^3.3.10"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#!/bin/bash
|
||||
# build_prod.sh builds distributable archive for the addon
|
||||
# remember to bump version in package.json
|
||||
set -eux
|
||||
|
||||
# clean
|
||||
npm run clean
|
||||
|
||||
# chrome
|
||||
npm run build:chrome
|
||||
npm run package:chrome
|
||||
# firefox
|
||||
npm run build:firefox
|
||||
npm run package:firefox
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
tar --exclude='./node_modules' --exclude='./package' --exclude='./dist' -zcvf extension.tar.gz * .eslintrc
|
||||
21
browser/src/browser.d.ts
vendored
|
|
@ -1,21 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// browser.d.ts
|
||||
declare let browser: any;
|
||||
declare let chrome: any;
|
||||
24
browser/src/global.d.ts
vendored
|
|
@ -1,24 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// global.d.ts
|
||||
|
||||
// defined by webpack-define-plugin
|
||||
declare let __API_ENDPOINT__: string;
|
||||
declare let __WEB_URL__: string;
|
||||
declare let __VERSION__: string;
|
||||
|
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 100 100" id="Layer_1" version="1.1" viewBox="0 0 100 100" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><polygon fill="#010101" points="77.6,21.1 49.6,49.2 21.5,21.1 19.6,23 47.6,51.1 19.6,79.2 21.5,81.1 49.6,53 77.6,81.1 79.6,79.2 51.5,51.1 79.6,23 "/></svg>
|
||||
|
Before Width: | Height: | Size: 468 B |
|
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN' 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'><svg enable-background="new 0 0 24 24" id="Layer_1" version="1.0" viewBox="0 0 24 24" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><line fill="none" stroke="#000000" stroke-miterlimit="10" stroke-width="2" x1="2" x2="22" y1="12" y2="12"/><line fill="none" stroke="#000000" stroke-miterlimit="10" stroke-width="2" x1="2" x2="22" y1="6" y2="6"/><line fill="none" stroke="#000000" stroke-miterlimit="10" stroke-width="2" x1="2" x2="22" y1="18" y2="18"/></svg>
|
||||
|
Before Width: | Height: | Size: 641 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 327 B |
|
Before Width: | Height: | Size: 517 B |
|
Before Width: | Height: | Size: 631 B |
|
Before Width: | Height: | Size: 1 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
|
@ -1,15 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Dnote browser extension</title>
|
||||
<meta charset="utf-8" />
|
||||
<link href="styles/popup.css" rel="stylesheet" />
|
||||
<link href="styles/select.css" rel="stylesheet" />
|
||||
<link href="https://fonts.googleapis.com/css?family=Lato:400,700" rel="stylesheet" />
|
||||
</head>
|
||||
<body class="pending">
|
||||
<div id="app"></div>
|
||||
|
||||
<script src="scripts/popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import initServices from '../utils/services';
|
||||
import { logout } from '../store/auth/actions';
|
||||
import { AuthState } from '../store/auth/types';
|
||||
import { useSelector, useDispatch } from '../store/hooks';
|
||||
import Header from './Header';
|
||||
import Home from './Home';
|
||||
import Menu from './Menu';
|
||||
import Success from './Success';
|
||||
import Settings from './Settings';
|
||||
import Composer from './Composer';
|
||||
|
||||
interface Props {}
|
||||
|
||||
function renderRoutes(path: string, isLoggedIn: boolean) {
|
||||
switch (path) {
|
||||
case '/success':
|
||||
return <Success />;
|
||||
case '/': {
|
||||
if (isLoggedIn) {
|
||||
return <Composer />;
|
||||
}
|
||||
|
||||
return <Home />;
|
||||
}
|
||||
case '/settings': {
|
||||
return <Settings />;
|
||||
}
|
||||
default:
|
||||
return <div>Not found</div>;
|
||||
}
|
||||
}
|
||||
|
||||
// useCheckSessionValid ensures that the current session is valid
|
||||
function useCheckSessionValid(auth: AuthState) {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
// if session is expired, clear it
|
||||
const now = Math.round(new Date().getTime() / 1000);
|
||||
if (auth.sessionKey && auth.sessionKeyExpiry < now) {
|
||||
dispatch(logout());
|
||||
}
|
||||
}, [dispatch, auth.sessionKey, auth.sessionKeyExpiry]);
|
||||
}
|
||||
|
||||
const App: React.FunctionComponent<Props> = () => {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const [errMsg, setErrMsg] = useState('');
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const { path, auth, settings } = useSelector(state => ({
|
||||
path: state.location.path,
|
||||
auth: state.auth,
|
||||
settings: state.settings
|
||||
}));
|
||||
|
||||
useCheckSessionValid(auth);
|
||||
|
||||
const isLoggedIn = Boolean(auth.sessionKey);
|
||||
const toggleMenu = () => {
|
||||
setIsMenuOpen(!isMenuOpen);
|
||||
};
|
||||
|
||||
const handleLogout = async (done?: Function) => {
|
||||
try {
|
||||
await initServices(settings.apiUrl).users.signout();
|
||||
dispatch(logout());
|
||||
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
} catch (e) {
|
||||
setErrMsg(e.message);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<Header toggleMenu={toggleMenu} isShowingMenu={isMenuOpen} />
|
||||
|
||||
{isMenuOpen && (
|
||||
<Menu
|
||||
toggleMenu={toggleMenu}
|
||||
loggedIn={isLoggedIn}
|
||||
onLogout={handleLogout}
|
||||
/>
|
||||
)}
|
||||
|
||||
<main>
|
||||
{errMsg && <div className="alert error">{errMsg}</div>}
|
||||
|
||||
{renderRoutes(path, isLoggedIn)}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
const Icon = ({ fill, width, height, className }) => {
|
||||
const h = `${height}px`;
|
||||
const w = `${width}px`;
|
||||
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 32 32"
|
||||
height={h}
|
||||
width={w}
|
||||
fill={fill}
|
||||
className={className}
|
||||
>
|
||||
<g transform="translate(240 0)">
|
||||
<path d="M-211,4v26h-24c-1.104,0-2-0.895-2-2s0.896-2,2-2h22V0h-22c-2.209,0-4,1.791-4,4v24c0,2.209,1.791,4,4,4h26V4H-211z M-235,8V2h20v22h-20V8z M-219,6h-12V4h12V6z M-223,10h-8V8h8V10z M-227,14h-4v-2h4V14z" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
Icon.defaultProps = {
|
||||
fill: '#000',
|
||||
width: 32,
|
||||
height: 32
|
||||
};
|
||||
|
||||
export default Icon;
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import CreatableSelect from 'react-select/creatable';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { useSelector, useDispatch } from '../store/hooks';
|
||||
import { updateBook, resetBook } from '../store/composer/actions';
|
||||
|
||||
interface Props {
|
||||
selectorRef: React.Dispatch<any>;
|
||||
onAfterChange: () => void;
|
||||
}
|
||||
|
||||
function useCurrentOptions(options) {
|
||||
const currentValue = useSelector(state => state.composer.bookUUID);
|
||||
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
const option = options[i];
|
||||
|
||||
if (option.value === currentValue) {
|
||||
return option;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function useOptions() {
|
||||
const { books, composer } = useSelector(state => ({
|
||||
books: state.books,
|
||||
composer: state.composer
|
||||
}));
|
||||
|
||||
const opts = books.items.map(book => ({
|
||||
label: book.label,
|
||||
value: book.uuid
|
||||
}));
|
||||
|
||||
if (composer.bookLabel !== '' && composer.bookUUID === '') {
|
||||
opts.push({
|
||||
label: composer.bookLabel,
|
||||
value: ''
|
||||
});
|
||||
}
|
||||
|
||||
// clone the array so as not to mutate Redux state manually
|
||||
// e.g. react-select mutates options prop internally upon adding a new option
|
||||
return cloneDeep(opts);
|
||||
}
|
||||
|
||||
const BookSelector: React.FunctionComponent<Props> = ({
|
||||
selectorRef,
|
||||
onAfterChange
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const { books } = useSelector(state => ({
|
||||
books: state.books
|
||||
}));
|
||||
const options = useOptions();
|
||||
const currentOption = useCurrentOptions(options);
|
||||
|
||||
let placeholder: string;
|
||||
if (books.isFetched) {
|
||||
placeholder = 'Choose a book';
|
||||
} else {
|
||||
placeholder = 'Loading books...';
|
||||
}
|
||||
|
||||
return (
|
||||
<CreatableSelect
|
||||
ref={el => {
|
||||
selectorRef(el);
|
||||
}}
|
||||
multi={false}
|
||||
isClearable
|
||||
placeholder={placeholder}
|
||||
options={options}
|
||||
value={currentOption}
|
||||
onChange={(option, meta) => {
|
||||
if (meta.action === 'clear') {
|
||||
dispatch(resetBook());
|
||||
} else {
|
||||
let uuid: string;
|
||||
if (meta.action === 'create-option') {
|
||||
uuid = '';
|
||||
} else {
|
||||
uuid = option.value;
|
||||
}
|
||||
|
||||
dispatch(updateBook({ uuid, label: option.label }));
|
||||
}
|
||||
|
||||
onAfterChange();
|
||||
}}
|
||||
formatCreateLabel={label => `Add a new book ${label}`}
|
||||
isDisabled={!books.isFetched}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default BookSelector;
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export default () => (
|
||||
<svg height="20" viewBox="0 0 48 48" width="20">
|
||||
<path
|
||||
fill="#ffffff"
|
||||
d="M38 12.83l-2.83-2.83-11.17 11.17-11.17-11.17-2.83 2.83 11.17 11.17-11.17 11.17 2.83 2.83 11.17-11.17 11.17 11.17 2.83-2.83-11.17-11.17z"
|
||||
/>
|
||||
<path fill="none" d="M0 0h48v48h-48z" />
|
||||
</svg>
|
||||
);
|
||||
|
|
@ -1,239 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { KEYCODE_ENTER } from 'jslib/helpers/keyboard';
|
||||
import initServices from '../utils/services';
|
||||
import BookSelector from './BookSelector';
|
||||
import Flash from './Flash';
|
||||
import { useSelector, useDispatch } from '../store/hooks';
|
||||
import { updateContent, resetComposer } from '../store/composer/actions';
|
||||
import { fetchBooks } from '../store/books/actions';
|
||||
import { navigate } from '../store/location/actions';
|
||||
|
||||
interface Props {}
|
||||
|
||||
// focusBookSelectorInput focuses on the input element of the book selector.
|
||||
// It needs to traverse the tree returned by the ref API of the 'react-select' library,
|
||||
// and to guard against possible breaking changes, if the path does not exist, it noops.
|
||||
function focusBookSelectorInput(bookSelectorRef) {
|
||||
return (
|
||||
bookSelectorRef.select &&
|
||||
bookSelectorRef.select.select &&
|
||||
bookSelectorRef.select.select.inputRef &&
|
||||
bookSelectorRef.select.select.inputRef.focus()
|
||||
);
|
||||
}
|
||||
|
||||
function useFetchData() {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { books } = useSelector(state => ({
|
||||
books: state.books
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (!books.isFetched) {
|
||||
dispatch(fetchBooks());
|
||||
}
|
||||
}, [dispatch, books.isFetched]);
|
||||
}
|
||||
|
||||
function useInitFocus(contentRef, bookSelectorRef) {
|
||||
const { composer, books } = useSelector(state => ({
|
||||
composer: state.composer,
|
||||
books: state.books
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (!books.isFetched) {
|
||||
return () => null;
|
||||
}
|
||||
|
||||
if (bookSelectorRef && contentRef) {
|
||||
if (composer.bookLabel === '') {
|
||||
focusBookSelectorInput(bookSelectorRef);
|
||||
} else {
|
||||
contentRef.focus();
|
||||
}
|
||||
}
|
||||
|
||||
return () => null;
|
||||
}, [contentRef, bookSelectorRef, books.isFetched, composer.bookLabel]);
|
||||
}
|
||||
|
||||
const Composer: React.FunctionComponent<Props> = () => {
|
||||
useFetchData();
|
||||
const [contentFocused, setContentFocused] = useState(false);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const [errMsg, setErrMsg] = useState('');
|
||||
const dispatch = useDispatch();
|
||||
const [contentRef, setContentEl] = useState(null);
|
||||
const [bookSelectorRef, setBookSelectorEl] = useState(null);
|
||||
|
||||
const { composer, settings, auth } = useSelector(state => ({
|
||||
composer: state.composer,
|
||||
settings: state.settings,
|
||||
auth: state.auth
|
||||
}));
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
async e => {
|
||||
e.preventDefault();
|
||||
|
||||
const services = initServices(settings.apiUrl);
|
||||
|
||||
setSubmitting(true);
|
||||
|
||||
try {
|
||||
let bookUUID;
|
||||
if (composer.bookUUID === '') {
|
||||
const resp = await services.books.create(
|
||||
{
|
||||
name: composer.bookLabel
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${auth.sessionKey}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
bookUUID = resp.book.uuid;
|
||||
} else {
|
||||
bookUUID = composer.bookUUID;
|
||||
}
|
||||
|
||||
const resp = await services.notes.create(
|
||||
{
|
||||
book_uuid: bookUUID,
|
||||
content: composer.content
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${auth.sessionKey}`
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// clear the composer state
|
||||
setErrMsg('');
|
||||
setSubmitting(false);
|
||||
|
||||
dispatch(resetComposer());
|
||||
|
||||
// navigate
|
||||
dispatch(
|
||||
navigate('/success', {
|
||||
bookName: composer.bookLabel,
|
||||
noteUUID: resp.result.uuid
|
||||
})
|
||||
);
|
||||
} catch (err) {
|
||||
setErrMsg(err.message);
|
||||
setSubmitting(false);
|
||||
}
|
||||
},
|
||||
[
|
||||
settings.apiUrl,
|
||||
composer.bookUUID,
|
||||
composer.content,
|
||||
composer.bookLabel,
|
||||
auth.sessionKey,
|
||||
dispatch
|
||||
]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const handleSubmitShortcut = e => {
|
||||
// Shift + Enter
|
||||
if (e.shiftKey && e.keyCode === KEYCODE_ENTER) {
|
||||
handleSubmit(e);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleSubmitShortcut);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleSubmitShortcut);
|
||||
};
|
||||
}, [composer, handleSubmit]);
|
||||
|
||||
let submitBtnText: string;
|
||||
if (submitting) {
|
||||
submitBtnText = 'Saving...';
|
||||
} else {
|
||||
submitBtnText = 'Save';
|
||||
}
|
||||
|
||||
useInitFocus(contentRef, bookSelectorRef);
|
||||
|
||||
return (
|
||||
<div className="composer">
|
||||
<Flash kind="error" when={errMsg !== ''} message={errMsg} />
|
||||
|
||||
<form onSubmit={handleSubmit} className="form">
|
||||
<BookSelector
|
||||
selectorRef={setBookSelectorEl}
|
||||
onAfterChange={() => {
|
||||
contentRef.focus();
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="content-container">
|
||||
<textarea
|
||||
className="content"
|
||||
placeholder="What did you learn?"
|
||||
onChange={e => {
|
||||
const val = e.target.value;
|
||||
|
||||
dispatch(updateContent(val));
|
||||
}}
|
||||
value={composer.content}
|
||||
ref={el => {
|
||||
setContentEl(el);
|
||||
}}
|
||||
onFocus={() => {
|
||||
setContentFocused(true);
|
||||
}}
|
||||
onBlur={() => {
|
||||
setContentFocused(false);
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
className={classnames('shortcut-hint', { shown: contentFocused })}
|
||||
>
|
||||
Shift + Enter to save
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="submit"
|
||||
value={submitBtnText}
|
||||
className="submit-button"
|
||||
disabled={submitting}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Composer;
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
type Kind = 'error' | 'info';
|
||||
|
||||
interface Props {
|
||||
message: string;
|
||||
when: boolean;
|
||||
kind: Kind;
|
||||
}
|
||||
|
||||
const Flash: React.FunctionComponent<Props> = ({ message, when, kind }) => {
|
||||
if (when) {
|
||||
return <div className={`alert alert-${kind}`}>{message}</div>;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default Flash;
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Link from './Link';
|
||||
import MenuToggleIcon from './MenuToggleIcon';
|
||||
import CloseIcon from './CloseIcon';
|
||||
|
||||
interface Props {
|
||||
toggleMenu: () => void;
|
||||
isShowingMenu: boolean;
|
||||
}
|
||||
|
||||
const Header: React.FunctionComponent<Props> = ({
|
||||
toggleMenu,
|
||||
isShowingMenu
|
||||
}) => (
|
||||
<header className="header">
|
||||
<Link to="/" className="logo-link" tabIndex={-1}>
|
||||
<img src="images/logo-circle.png" alt="dnote" className="logo" />
|
||||
</Link>
|
||||
|
||||
<a
|
||||
href="#toggle"
|
||||
className="menu-toggle"
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
|
||||
toggleMenu();
|
||||
}}
|
||||
tabIndex={-1}
|
||||
>
|
||||
{isShowingMenu ? <CloseIcon /> : <MenuToggleIcon />}
|
||||
</a>
|
||||
</header>
|
||||
);
|
||||
|
||||
export default Header;
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { login } from '../store/auth/actions';
|
||||
import { useDispatch } from '../store/hooks';
|
||||
import Flash from '../components/Flash';
|
||||
|
||||
interface Props {}
|
||||
|
||||
const Home: React.FunctionComponent<Props> = () => {
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [errMsg, setErrMsg] = useState('');
|
||||
const [loggingIn, setLoggingIn] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleLogin = async e => {
|
||||
e.preventDefault();
|
||||
|
||||
setErrMsg('');
|
||||
setLoggingIn(true);
|
||||
|
||||
try {
|
||||
await dispatch(login({ email, password }));
|
||||
} catch (err) {
|
||||
console.log('error while logging in', err);
|
||||
|
||||
setErrMsg(e.message);
|
||||
setLoggingIn(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="home page">
|
||||
<h1 className="heading">Welcome to Dnote</h1>
|
||||
|
||||
<p className="lead">A simple personal knowledge base</p>
|
||||
|
||||
<Flash kind="error" when={errMsg !== ''} message={errMsg} />
|
||||
|
||||
<form id="login-form" onSubmit={handleLogin}>
|
||||
<label htmlFor="email-input">Email</label>
|
||||
|
||||
<input
|
||||
type="email"
|
||||
placeholder="your@email.com"
|
||||
className="input login-input"
|
||||
id="email-input"
|
||||
value={email}
|
||||
onChange={e => {
|
||||
setEmail(e.target.value);
|
||||
}}
|
||||
/>
|
||||
|
||||
<label htmlFor="password-input">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="●●●●●●●●"
|
||||
className="input login-input"
|
||||
id="password-input"
|
||||
value={password}
|
||||
onChange={e => {
|
||||
setPassword(e.target.value);
|
||||
}}
|
||||
/>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="button button-first button-small login-btn"
|
||||
disabled={loggingIn}
|
||||
>
|
||||
{loggingIn ? 'Signing in...' : 'Sign in'}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div className="actions">
|
||||
Don't have an account?{' '}
|
||||
<a
|
||||
href="https://app.getdnote.com/join"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="signup"
|
||||
>
|
||||
Sign Up
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { useDispatch } from '../store/hooks';
|
||||
import { navigate } from '../store/location/actions';
|
||||
|
||||
interface Props {
|
||||
to: string;
|
||||
className: string;
|
||||
tabIndex?: number;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
const Link: React.FunctionComponent<Props> = ({
|
||||
to,
|
||||
children,
|
||||
onClick,
|
||||
...restProps
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
return (
|
||||
<a
|
||||
href={`${to}`}
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
|
||||
dispatch(navigate(to));
|
||||
|
||||
if (onClick) {
|
||||
onClick();
|
||||
}
|
||||
}}
|
||||
{...restProps}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
export default Link;
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { Fragment } from 'react';
|
||||
|
||||
import Link from './Link';
|
||||
|
||||
export default ({ toggleMenu, loggedIn, onLogout }) => (
|
||||
<Fragment>
|
||||
<ul className="menu">
|
||||
<li>
|
||||
<Link to="/" onClick={toggleMenu} className="menu-link">
|
||||
Home
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/settings" onClick={toggleMenu} className="menu-link">
|
||||
Settings
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
{loggedIn && (
|
||||
<li>
|
||||
<form
|
||||
onSubmit={e => {
|
||||
e.preventDefault();
|
||||
|
||||
onLogout(toggleMenu);
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="submit"
|
||||
value="Logout"
|
||||
className="menu-link logout-button"
|
||||
/>
|
||||
</form>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
|
||||
<div
|
||||
className="menu-overlay"
|
||||
onClick={toggleMenu}
|
||||
onKeyDown={() => {}}
|
||||
role="none"
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export default () => (
|
||||
<svg
|
||||
enableBackground="new 0 0 24 24"
|
||||
id="Layer_1"
|
||||
version="1.0"
|
||||
viewBox="0 0 24 24"
|
||||
width="20"
|
||||
height="20"
|
||||
>
|
||||
<line
|
||||
fill="none"
|
||||
stroke="#ffffff"
|
||||
strokeMiterlimit="10"
|
||||
strokeWidth="2"
|
||||
x1="2"
|
||||
x2="22"
|
||||
y1="12"
|
||||
y2="12"
|
||||
/>
|
||||
<line
|
||||
fill="none"
|
||||
stroke="#ffffff"
|
||||
strokeMiterlimit="10"
|
||||
strokeWidth="2"
|
||||
x1="2"
|
||||
x2="22"
|
||||
y1="6"
|
||||
y2="6"
|
||||
/>
|
||||
<line
|
||||
fill="none"
|
||||
stroke="#ffffff"
|
||||
strokeMiterlimit="10"
|
||||
strokeWidth="2"
|
||||
x1="2"
|
||||
x2="22"
|
||||
y1="18"
|
||||
y2="18"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import Flash from './Flash';
|
||||
import { updateSettings, resetSettings } from '../store/settings/actions';
|
||||
import { useDispatch, useSelector, useStore } from '../store/hooks';
|
||||
|
||||
interface Props {}
|
||||
|
||||
// isValidURL checks if the given string is a valid URL
|
||||
function isValidURL(url: string): boolean {
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
return a.host && a.host !== window.location.host;
|
||||
}
|
||||
|
||||
// validateFormState validates the given form state. If any input is
|
||||
// invalid, it throws an error.
|
||||
function validateFormState({ apiUrl, webUrl }) {
|
||||
if (!isValidURL(apiUrl)) {
|
||||
throw new Error('Invalid URL for the API URL');
|
||||
}
|
||||
|
||||
if (!isValidURL(webUrl)) {
|
||||
throw new Error('Invalid URL for the web URL');
|
||||
}
|
||||
}
|
||||
|
||||
const Settings: React.FunctionComponent<Props> = () => {
|
||||
const { settings } = useSelector(state => ({
|
||||
settings: state.settings
|
||||
}));
|
||||
const store = useStore();
|
||||
|
||||
const [apiUrl, setAPIUrl] = useState(settings.apiUrl);
|
||||
const [webUrl, setWebUrl] = useState(settings.webUrl);
|
||||
const [errMsg, setErrMsg] = useState('');
|
||||
const [successMsg, setSuccessMsg] = useState('');
|
||||
const dispatch = useDispatch();
|
||||
|
||||
function handleRestore() {
|
||||
dispatch(resetSettings());
|
||||
setSuccessMsg('Restored the default settings');
|
||||
|
||||
const { settings: settingsState } = store.getState();
|
||||
|
||||
setAPIUrl(settingsState.apiUrl);
|
||||
setWebUrl(settingsState.webUrl);
|
||||
}
|
||||
|
||||
function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
setSuccessMsg('');
|
||||
setErrMsg('');
|
||||
|
||||
try {
|
||||
validateFormState({ apiUrl, webUrl });
|
||||
} catch (err) {
|
||||
setErrMsg(err.message);
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(
|
||||
updateSettings({
|
||||
apiUrl,
|
||||
webUrl
|
||||
})
|
||||
);
|
||||
setSuccessMsg('Succesfully updated the settings.');
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Flash kind="error" when={errMsg !== ''} message={errMsg} />
|
||||
<Flash kind="info" when={successMsg !== ''} message={successMsg} />
|
||||
|
||||
<div className="settings page">
|
||||
<h1 className="heading">Settings</h1>
|
||||
|
||||
<p className="lead">Customize your Dnote extension</p>
|
||||
|
||||
<form id="settings-form" onSubmit={handleSubmit}>
|
||||
<div className="input-row">
|
||||
<label htmlFor="api-url-input" className="label">
|
||||
API URL
|
||||
</label>
|
||||
|
||||
<input
|
||||
type="api-url"
|
||||
placeholder="https://api.getdnote.com"
|
||||
className="input"
|
||||
id="api-url-input"
|
||||
value={apiUrl}
|
||||
onChange={e => {
|
||||
setAPIUrl(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="input-row">
|
||||
<label htmlFor="web-url-input" className="label">
|
||||
Web URL
|
||||
</label>
|
||||
|
||||
<input
|
||||
type="web-url"
|
||||
placeholder="https://app.getdnote.com"
|
||||
className="input"
|
||||
id="web-url-input"
|
||||
value={webUrl}
|
||||
onChange={e => {
|
||||
setWebUrl(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="actions">
|
||||
<button
|
||||
type="submit"
|
||||
className="button button-first button-small button-stretch"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleRestore}
|
||||
className="restore button-no-ui"
|
||||
>
|
||||
Restore default
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useState, Fragment } from 'react';
|
||||
|
||||
import {
|
||||
KEYCODE_ENTER,
|
||||
KEYCODE_ESC,
|
||||
KEYCODE_LOWERCASE_B
|
||||
} from 'jslib/helpers/keyboard';
|
||||
import Flash from './Flash';
|
||||
import ext from '../utils/ext';
|
||||
import BookIcon from './BookIcon';
|
||||
import { navigate } from '../store/location/actions';
|
||||
import { useSelector, useDispatch } from '../store/hooks';
|
||||
|
||||
const Success: React.FunctionComponent = () => {
|
||||
const [errorMsg, setErrorMsg] = useState('');
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const { location, settings } = useSelector(state => ({
|
||||
location: state.location,
|
||||
settings: state.settings
|
||||
}));
|
||||
|
||||
const { bookName, noteUUID } = location.state;
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeydown = e => {
|
||||
e.preventDefault();
|
||||
|
||||
if (e.keyCode === KEYCODE_ENTER) {
|
||||
dispatch(navigate('/'));
|
||||
} else if (e.keyCode === KEYCODE_ESC) {
|
||||
window.close();
|
||||
} else if (e.keyCode === KEYCODE_LOWERCASE_B) {
|
||||
const url = `${settings.webUrl}/notes/${noteUUID}`;
|
||||
|
||||
ext.tabs
|
||||
.create({ url })
|
||||
.then(() => {
|
||||
window.close();
|
||||
})
|
||||
.catch(err => {
|
||||
setErrorMsg(err.message);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeydown);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeydown);
|
||||
};
|
||||
}, [dispatch, noteUUID, settings.webUrl]);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Flash kind="error" when={errorMsg !== ''} message={errorMsg} />
|
||||
|
||||
<div className="success-page">
|
||||
<div>
|
||||
<BookIcon width={20} height={20} className="book-icon" />
|
||||
|
||||
<h1 className="heading">
|
||||
Saved to
|
||||
{bookName}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<ul className="key-list">
|
||||
<li className="key-item">
|
||||
<kbd className="key">Enter</kbd>{' '}
|
||||
<div className="key-desc">Go back</div>
|
||||
</li>
|
||||
<li className="key-item">
|
||||
<kbd className="key">b</kbd>{' '}
|
||||
<div className="key-desc">Open in browser</div>
|
||||
</li>
|
||||
<li className="key-item">
|
||||
<kbd className="key">ESC</kbd>
|
||||
<div className="key-desc">Close</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default Success;
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import { debounce } from 'jslib/helpers/perf';
|
||||
import configureStore from './store';
|
||||
import { loadState, saveState } from './utils/storage';
|
||||
import App from './components/App';
|
||||
import ext from './utils/ext';
|
||||
|
||||
const appContainer = document.getElementById('app');
|
||||
|
||||
loadState(items => {
|
||||
if (ext.runtime.lastError) {
|
||||
appContainer.innerText = `Failed to retrieve previous app state ${ext.runtime.lastError.message}`;
|
||||
return;
|
||||
}
|
||||
|
||||
let initialState;
|
||||
const prevState = items.state;
|
||||
if (prevState) {
|
||||
// rehydrate
|
||||
initialState = prevState;
|
||||
}
|
||||
|
||||
const store = configureStore(initialState);
|
||||
|
||||
store.subscribe(
|
||||
debounce(() => {
|
||||
const state = store.getState();
|
||||
|
||||
saveState(state);
|
||||
}, 100)
|
||||
);
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>,
|
||||
appContainer,
|
||||
() => {
|
||||
// On Chrome, popup window size is kept at minimum if app render is delayed
|
||||
// Therefore add minimum dimension to body until app is rendered
|
||||
document.getElementsByTagName('body')[0].className = '';
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { LOGIN, LOGOUT, LogoutAction } from './types';
|
||||
import { ThunkAction } from '../types';
|
||||
import initServices from '../../utils/services';
|
||||
|
||||
export function login({ email, password }): ThunkAction<void> {
|
||||
return (dispatch, getState) => {
|
||||
const { settings } = getState();
|
||||
const { apiUrl } = settings;
|
||||
|
||||
return initServices(apiUrl)
|
||||
.users.signin({ email, password })
|
||||
.then(resp => {
|
||||
dispatch({
|
||||
type: LOGIN,
|
||||
data: {
|
||||
sessionKey: resp.key,
|
||||
sessionKeyExpiry: resp.expiresAt
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function logout(): LogoutAction {
|
||||
return {
|
||||
type: LOGOUT
|
||||
};
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { LOGIN, LOGOUT, AuthState, AuthActionType } from './types';
|
||||
|
||||
const initialState: AuthState = {
|
||||
sessionKey: '',
|
||||
sessionKeyExpiry: 0
|
||||
};
|
||||
|
||||
export default function(
|
||||
state = initialState,
|
||||
action: AuthActionType
|
||||
): AuthState {
|
||||
switch (action.type) {
|
||||
case LOGIN: {
|
||||
const { sessionKey, sessionKeyExpiry } = action.data;
|
||||
|
||||
return {
|
||||
...state,
|
||||
sessionKey,
|
||||
sessionKeyExpiry
|
||||
};
|
||||
}
|
||||
case LOGOUT:
|
||||
return initialState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export interface AuthState {
|
||||
sessionKey: string;
|
||||
sessionKeyExpiry: number;
|
||||
}
|
||||
|
||||
export const LOGIN = 'auth/LOGIN';
|
||||
export const LOGOUT = 'auth/LOGOUT';
|
||||
|
||||
export interface LoginAction {
|
||||
type: typeof LOGIN;
|
||||
data: {
|
||||
sessionKey: string;
|
||||
sessionKeyExpiry: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface LogoutAction {
|
||||
type: typeof LOGOUT;
|
||||
}
|
||||
|
||||
export type AuthActionType = LogoutAction | LoginAction;
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import initServices from '../../utils/services';
|
||||
|
||||
import {
|
||||
START_FETCHING,
|
||||
RECEIVE,
|
||||
RECEIVE_ERROR,
|
||||
StartFetchingAction,
|
||||
ReceiveAction,
|
||||
ReceiveErrorAction
|
||||
} from './types';
|
||||
|
||||
function startFetchingBooks(): StartFetchingAction {
|
||||
return {
|
||||
type: START_FETCHING
|
||||
};
|
||||
}
|
||||
|
||||
function receiveBooks(books): ReceiveAction {
|
||||
return {
|
||||
type: RECEIVE,
|
||||
data: {
|
||||
books
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function receiveBooksError(error: string): ReceiveErrorAction {
|
||||
return {
|
||||
type: RECEIVE_ERROR,
|
||||
data: {
|
||||
error
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchBooks() {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(startFetchingBooks());
|
||||
|
||||
const { settings, auth } = getState();
|
||||
const services = initServices(settings.apiUrl);
|
||||
|
||||
services.books
|
||||
.fetch(
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${auth.sessionKey}`
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(books => {
|
||||
dispatch(receiveBooks(books));
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('error fetching books', err);
|
||||
dispatch(receiveBooksError(err));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
START_FETCHING,
|
||||
RECEIVE,
|
||||
RECEIVE_ERROR,
|
||||
BooksState,
|
||||
BooksActionType
|
||||
} from './types';
|
||||
|
||||
const initialState = {
|
||||
items: [],
|
||||
isFetching: false,
|
||||
isFetched: false,
|
||||
error: null
|
||||
};
|
||||
|
||||
export default function(
|
||||
state = initialState,
|
||||
action: BooksActionType
|
||||
): BooksState {
|
||||
switch (action.type) {
|
||||
case START_FETCHING:
|
||||
return {
|
||||
...state,
|
||||
isFetching: true,
|
||||
isFetched: false
|
||||
};
|
||||
case RECEIVE: {
|
||||
const { books } = action.data;
|
||||
|
||||
// get uuids of deleted books and that of a currently selected book
|
||||
return {
|
||||
...state,
|
||||
isFetching: false,
|
||||
isFetched: true,
|
||||
items: [...state.items, ...books]
|
||||
};
|
||||
}
|
||||
case RECEIVE_ERROR:
|
||||
return {
|
||||
...state,
|
||||
isFetching: false,
|
||||
isFetched: true,
|
||||
error: action.data.error
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export type BookData = any;
|
||||
|
||||
export interface BooksState {
|
||||
items: BookData[];
|
||||
isFetching: boolean;
|
||||
isFetched: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
export const START_FETCHING = 'books/START_FETCHING';
|
||||
export const RECEIVE = 'books/RECEIVE';
|
||||
export const RECEIVE_ERROR = 'books/RECEIVE_ERROR';
|
||||
|
||||
export interface StartFetchingAction {
|
||||
type: typeof START_FETCHING;
|
||||
}
|
||||
|
||||
export interface ReceiveAction {
|
||||
type: typeof RECEIVE;
|
||||
data: {
|
||||
books: BookData[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ReceiveErrorAction {
|
||||
type: typeof RECEIVE_ERROR;
|
||||
data: {
|
||||
error: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type BooksActionType =
|
||||
| StartFetchingAction
|
||||
| ReceiveAction
|
||||
| ReceiveErrorAction;
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
UPDATE_CONTENT,
|
||||
UPDATE_BOOK,
|
||||
RESET,
|
||||
RESET_BOOK,
|
||||
UpdateContentAction,
|
||||
UpdateBookAction,
|
||||
ResetBookAction,
|
||||
ResetAction
|
||||
} from './types';
|
||||
|
||||
export function updateContent(content: string): UpdateContentAction {
|
||||
return {
|
||||
type: UPDATE_CONTENT,
|
||||
data: { content }
|
||||
};
|
||||
}
|
||||
|
||||
export interface UpdateBookActionParam {
|
||||
uuid: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export function updateBook({
|
||||
uuid,
|
||||
label
|
||||
}: UpdateBookActionParam): UpdateBookAction {
|
||||
return {
|
||||
type: UPDATE_BOOK,
|
||||
data: {
|
||||
uuid,
|
||||
label
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function resetBook(): ResetBookAction {
|
||||
return {
|
||||
type: RESET_BOOK
|
||||
};
|
||||
}
|
||||
|
||||
export function resetComposer(): ResetAction {
|
||||
return {
|
||||
type: RESET
|
||||
};
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
UPDATE_CONTENT,
|
||||
UPDATE_BOOK,
|
||||
RESET,
|
||||
RESET_BOOK,
|
||||
ComposerActionType,
|
||||
ComposerState
|
||||
} from './types';
|
||||
|
||||
const initialState: ComposerState = {
|
||||
content: '',
|
||||
bookUUID: '',
|
||||
bookLabel: ''
|
||||
};
|
||||
|
||||
export default function(
|
||||
state = initialState,
|
||||
action: ComposerActionType
|
||||
): ComposerState {
|
||||
switch (action.type) {
|
||||
case UPDATE_CONTENT: {
|
||||
return {
|
||||
...state,
|
||||
content: action.data.content
|
||||
};
|
||||
}
|
||||
case UPDATE_BOOK: {
|
||||
return {
|
||||
...state,
|
||||
bookUUID: action.data.uuid,
|
||||
bookLabel: action.data.label
|
||||
};
|
||||
}
|
||||
case RESET_BOOK: {
|
||||
return {
|
||||
...state,
|
||||
bookUUID: '',
|
||||
bookLabel: ''
|
||||
};
|
||||
}
|
||||
case RESET: {
|
||||
return initialState;
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export interface ComposerState {
|
||||
content: string;
|
||||
bookUUID: string;
|
||||
bookLabel: string;
|
||||
}
|
||||
|
||||
export const UPDATE_CONTENT = 'composer/UPDATE_CONTENT';
|
||||
export const UPDATE_BOOK = 'composer/UPDATE_BOOK';
|
||||
export const RESET = 'composer/RESET';
|
||||
export const RESET_BOOK = 'composer/RESET_BOOK';
|
||||
|
||||
export interface UpdateContentAction {
|
||||
type: typeof UPDATE_CONTENT;
|
||||
data: {
|
||||
content: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface UpdateBookAction {
|
||||
type: typeof UPDATE_BOOK;
|
||||
data: {
|
||||
uuid: string;
|
||||
label: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ResetAction {
|
||||
type: typeof RESET;
|
||||
}
|
||||
|
||||
export interface ResetBookAction {
|
||||
type: typeof RESET_BOOK;
|
||||
}
|
||||
|
||||
export type ComposerActionType =
|
||||
| UpdateContentAction
|
||||
| UpdateBookAction
|
||||
| ResetAction
|
||||
| ResetBookAction;
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Store, Action } from 'redux';
|
||||
import {
|
||||
useDispatch as useReduxDispatch,
|
||||
useStore as useReduxStore,
|
||||
useSelector as useReduxSelector
|
||||
} from 'react-redux';
|
||||
import { ThunkDispatch } from 'redux-thunk';
|
||||
|
||||
import { AppState } from './types';
|
||||
|
||||
type ReduxDispatch = ThunkDispatch<AppState, any, Action>;
|
||||
|
||||
export function useDispatch(): ReduxDispatch {
|
||||
return useReduxDispatch<ReduxDispatch>();
|
||||
}
|
||||
|
||||
export function useStore(): Store<AppState> {
|
||||
return useReduxStore<AppState>();
|
||||
}
|
||||
|
||||
export function useSelector<TSelected>(
|
||||
selector: (state: AppState) => TSelected
|
||||
) {
|
||||
return useReduxSelector<AppState, TSelected>(selector);
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { combineReducers, createStore, applyMiddleware, compose } from 'redux';
|
||||
import thunkMiddleware from 'redux-thunk';
|
||||
import createLogger from 'redux-logger';
|
||||
|
||||
import location from './location/reducers';
|
||||
import settings from './settings/reducers';
|
||||
import books from './books/reducers';
|
||||
import composer from './composer/reducers';
|
||||
import auth from './auth/reducers';
|
||||
import { AppState } from './types';
|
||||
import config from '../utils/config';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
auth,
|
||||
location,
|
||||
settings,
|
||||
books,
|
||||
composer
|
||||
});
|
||||
|
||||
// initState returns a new state with any missing values populated
|
||||
// if a state is given.
|
||||
function initState(s: AppState | undefined): AppState {
|
||||
if (s === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { settings: settingsState } = s;
|
||||
|
||||
return {
|
||||
...s,
|
||||
settings: {
|
||||
...settingsState,
|
||||
apiUrl: settingsState.apiUrl || config.defaultApiEndpoint,
|
||||
webUrl: settingsState.webUrl || config.defaultWebUrl
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// configureStore returns a new store that contains the appliation state
|
||||
export default function configureStore(state: AppState | undefined) {
|
||||
const typedWindow = window as any;
|
||||
|
||||
const composeEnhancers =
|
||||
typedWindow.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
|
||||
|
||||
return createStore(
|
||||
rootReducer,
|
||||
initState(state),
|
||||
composeEnhancers(applyMiddleware(createLogger, thunkMiddleware))
|
||||
);
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { NAVIGATE, NavigateAction } from './types';
|
||||
|
||||
export function navigate(path: string, state?): NavigateAction {
|
||||
return {
|
||||
type: NAVIGATE,
|
||||
data: { path, state }
|
||||
};
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { NAVIGATE, LocationState, LocationActionType } from './types';
|
||||
|
||||
const initialState: LocationState = {
|
||||
path: '/',
|
||||
state: {}
|
||||
};
|
||||
|
||||
export default function(
|
||||
state = initialState,
|
||||
action: LocationActionType
|
||||
): LocationState {
|
||||
switch (action.type) {
|
||||
case NAVIGATE:
|
||||
return {
|
||||
...state,
|
||||
path: action.data.path,
|
||||
state: action.data.state || {}
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export interface LocationState {
|
||||
path: string;
|
||||
state: any;
|
||||
}
|
||||
|
||||
export const NAVIGATE = 'location/NAVIGATE';
|
||||
|
||||
export interface NavigateAction {
|
||||
type: typeof NAVIGATE;
|
||||
data: {
|
||||
path: string;
|
||||
state: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type LocationActionType = NavigateAction;
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { UPDATE, RESET, UpdateAction, ResetAction } from './types';
|
||||
|
||||
export function updateSettings(settings): UpdateAction {
|
||||
return {
|
||||
type: UPDATE,
|
||||
data: { settings }
|
||||
};
|
||||
}
|
||||
|
||||
export function resetSettings(): ResetAction {
|
||||
return {
|
||||
type: RESET
|
||||
};
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { UPDATE, RESET, SettingsState, SettingsActionType } from './types';
|
||||
import config from '../../utils/config';
|
||||
|
||||
const initialState: SettingsState = {
|
||||
apiUrl: config.defaultApiEndpoint,
|
||||
webUrl: config.defaultWebUrl
|
||||
};
|
||||
|
||||
export default function(
|
||||
state = initialState,
|
||||
action: SettingsActionType
|
||||
): SettingsState {
|
||||
switch (action.type) {
|
||||
case UPDATE:
|
||||
return {
|
||||
...state,
|
||||
...action.data.settings
|
||||
};
|
||||
case RESET:
|
||||
return initialState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export interface SettingsState {
|
||||
apiUrl: string;
|
||||
webUrl: string;
|
||||
}
|
||||
|
||||
export const UPDATE = 'settings/UPDATE';
|
||||
export const RESET = 'settings/RESET';
|
||||
|
||||
export interface UpdateAction {
|
||||
type: typeof UPDATE;
|
||||
data: {
|
||||
settings: Partial<SettingsState>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ResetAction {
|
||||
type: typeof RESET;
|
||||
}
|
||||
|
||||
export type SettingsActionType = UpdateAction | ResetAction;
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Action } from 'redux';
|
||||
import { ThunkAction as ReduxThunkAction } from 'redux-thunk';
|
||||
|
||||
import { AuthState } from './auth/types';
|
||||
import { ComposerState } from './composer/types';
|
||||
import { LocationState } from './location/types';
|
||||
import { SettingsState } from './settings/types';
|
||||
import { BooksState } from './books/types';
|
||||
|
||||
// AppState represents the application state
|
||||
export interface AppState {
|
||||
auth: AuthState;
|
||||
composer: ComposerState;
|
||||
location: LocationState;
|
||||
settings: SettingsState;
|
||||
books: BooksState;
|
||||
}
|
||||
|
||||
// ThunkAction is a thunk action type
|
||||
export type ThunkAction<T = void> = ReduxThunkAction<
|
||||
Promise<T>,
|
||||
AppState,
|
||||
void,
|
||||
Action
|
||||
>;
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export default {
|
||||
defaultWebUrl: __WEB_URL__,
|
||||
defaultApiEndpoint: __API_ENDPOINT__,
|
||||
version: __VERSION__
|
||||
};
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// module ext provides a cross-browser interface to access extension APIs
|
||||
// by using WebExtensions API if available, and using Chrome as a fallback.
|
||||
const ext: any = {};
|
||||
|
||||
const apis = ['tabs', 'storage', 'runtime'];
|
||||
|
||||
for (let i = 0; i < apis.length; i++) {
|
||||
const api = apis[i];
|
||||
|
||||
try {
|
||||
if (browser[api]) {
|
||||
ext[api] = browser[api];
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
try {
|
||||
if (chrome[api] && !ext[api]) {
|
||||
ext[api] = chrome[api];
|
||||
|
||||
// Standardize the signature to conform to WebExtensions API
|
||||
if (api === 'tabs') {
|
||||
const fn = ext[api].create;
|
||||
|
||||
// Promisify chrome.tabs.create
|
||||
ext[api].create = obj => {
|
||||
return new Promise(resolve => {
|
||||
fn(obj, tab => {
|
||||
resolve(tab);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
export default ext;
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import qs from 'qs';
|
||||
|
||||
function checkStatus(response) {
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return response;
|
||||
}
|
||||
return response.text().then(body => {
|
||||
const error = new Error(body);
|
||||
error.response = response;
|
||||
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
function parseJSON(response) {
|
||||
if (response.headers.get('Content-Type') === 'application/json') {
|
||||
return response.json();
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function request(url, options) {
|
||||
return fetch(url, options)
|
||||
.then(checkStatus)
|
||||
.then(parseJSON);
|
||||
}
|
||||
|
||||
export function post(url, data, options = {}) {
|
||||
return request(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
export function get(url, options = {}) {
|
||||
let endpoint = url;
|
||||
|
||||
if (options.params) {
|
||||
endpoint = `${endpoint}?${qs.stringify(options.params)}`;
|
||||
}
|
||||
|
||||
return request(endpoint, {
|
||||
method: 'GET',
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import init from 'jslib/services';
|
||||
|
||||
const initServices = (baseUrl: string) =>
|
||||
init({
|
||||
baseUrl,
|
||||
pathPrefix: ''
|
||||
});
|
||||
|
||||
export default initServices;
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import ext from './ext';
|
||||
|
||||
const stateKey = 'state';
|
||||
|
||||
// filterState filters the given state to be suitable for reuse upon next app
|
||||
// load
|
||||
function filterState(state) {
|
||||
return {
|
||||
...state,
|
||||
location: {
|
||||
...state.location,
|
||||
path: '/'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function parseStorageItem(item) {
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return JSON.parse(item);
|
||||
}
|
||||
|
||||
// saveState writes the given state to storage
|
||||
export function saveState(state) {
|
||||
const filtered = filterState(state);
|
||||
const serialized = JSON.stringify(filtered);
|
||||
|
||||
ext.storage.local.set({ [stateKey]: serialized }, () => {
|
||||
console.log('synced state');
|
||||
});
|
||||
}
|
||||
|
||||
// loadState loads and parses serialized state stored in ext.storage
|
||||
export function loadState(done) {
|
||||
ext.storage.local.get('state', items => {
|
||||
const parsed = {
|
||||
...items,
|
||||
state: parseStorageItem(items.state)
|
||||
};
|
||||
|
||||
return done(parsed);
|
||||
});
|
||||
}
|
||||
|
|
@ -1,431 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* container width: 345px */
|
||||
|
||||
/* global */
|
||||
html {
|
||||
font-size: 62.5%;
|
||||
/* 1.0 rem = 10px */
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Lato', sans-serif;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
body.pending {
|
||||
height: 257px;
|
||||
width: 345px;
|
||||
}
|
||||
main.blur {
|
||||
opacity: 0.15;
|
||||
}
|
||||
.container {
|
||||
width: 345px;
|
||||
position: relative;
|
||||
}
|
||||
.input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: .375rem .75rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
color: #495057;
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: .25rem;
|
||||
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
||||
box-sizing: border-box;
|
||||
font-size: 16px;
|
||||
}
|
||||
.login-input {
|
||||
margin-top: 4px;
|
||||
}
|
||||
.message {
|
||||
color: #2cae2c;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.alert {
|
||||
padding: 10px 9px;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
.alert-info {
|
||||
color: #0c5460;
|
||||
background-color: #bee5eb;
|
||||
border-color: #f5c6cb;
|
||||
}
|
||||
.alert-error {
|
||||
color: #721c24;
|
||||
background-color: #f8d7da;
|
||||
border-color: #f5c6cb;
|
||||
}
|
||||
kbd {
|
||||
display: inline-block;
|
||||
padding: 3px 5px;
|
||||
font: 11px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
line-height: 10px;
|
||||
color: #444d56;
|
||||
vertical-align: middle;
|
||||
background-color: #fafbfc;
|
||||
border: solid 1px #d1d5da;
|
||||
border-bottom-color: #c6cbd1;
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 -1px 0 #c6cbd1;
|
||||
}
|
||||
.menu {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: white;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
z-index: 1;
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
box-shadow: 0px 3px 2px 0px #cacaca;
|
||||
}
|
||||
.menu .logout-button {
|
||||
width: 100%;
|
||||
border: none;
|
||||
background: none;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
.menu-link {
|
||||
display: block;
|
||||
padding: 10px 13px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
font-size: 16px;
|
||||
|
||||
/* override chrome default */
|
||||
font: inherit !important;
|
||||
}
|
||||
.menu-link:hover {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
.menu-link:not(:hover) {
|
||||
background: inherit;
|
||||
}
|
||||
.menu-overlay {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
top: 44px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
opacity: 0.8;
|
||||
background: #f7f7f7;
|
||||
}
|
||||
.header {
|
||||
background-color: #272a35;
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 8px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.header .logo {
|
||||
width: 32px;
|
||||
}
|
||||
.header .logo-link {
|
||||
height: 32px;
|
||||
}
|
||||
.header .menu-toggle {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.heading {
|
||||
font-size: 2.2rem;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
text-align: center;
|
||||
}
|
||||
.lead {
|
||||
color: #575757;
|
||||
text-align: center;
|
||||
}
|
||||
.page {
|
||||
padding: 16px 28px;
|
||||
}
|
||||
|
||||
/* home */
|
||||
.home {
|
||||
text-align: center;
|
||||
}
|
||||
.home #login-form {
|
||||
text-align: left;
|
||||
}
|
||||
.home #login-form label {
|
||||
display: inline-block;
|
||||
font-weight: 600;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.home .login-btn {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.home .actions {
|
||||
margin-top: 18px;
|
||||
font-size: 1.3rem;
|
||||
color: gray;
|
||||
}
|
||||
.home .actions a {
|
||||
color: gray;
|
||||
font-weight: 600;
|
||||
}
|
||||
.home .actions .signup:visited {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* settings */
|
||||
.settings #settings-form {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.settings .input-row ~ .input-row {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.settings .label {
|
||||
display: inline-block;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.settings .actions {
|
||||
margin-top: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
.settings .restore {
|
||||
margin-top: 8px;
|
||||
display: inline-block;
|
||||
color: gray;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
/* composer */
|
||||
.composer .form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.composer .content-container {
|
||||
position: relative;
|
||||
height: 148px;
|
||||
}
|
||||
.composer .content {
|
||||
border: none;
|
||||
resize: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 11px 11px 18px 11px;
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #e2e2e2;
|
||||
border-top: 0;
|
||||
}
|
||||
.composer .content::placeholder {
|
||||
color: #aaa;
|
||||
}
|
||||
.composer .content:focus {
|
||||
box-shadow: inset 0px 0px 3px #c0c0c0;
|
||||
outline: none;
|
||||
}
|
||||
.composer .shortcut-hint {
|
||||
position: absolute;
|
||||
bottom: 3px;
|
||||
right: 7px;
|
||||
color: gray;
|
||||
font-size: 1.2rem;
|
||||
font-style: italic;
|
||||
}
|
||||
.composer .shortcut-hint:not(.shown) {
|
||||
visibility: hidden;
|
||||
}
|
||||
.composer .submit-button {
|
||||
color: #fff;
|
||||
background-color: #272a35;
|
||||
border-radius: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 400;
|
||||
line-height: 1.25;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
user-select: none;
|
||||
transition: all 0.2s ease-in-out;
|
||||
font-size: 1.4rem;
|
||||
text-decoration: none;
|
||||
padding: 11px 18px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
padding: 6px 0;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
}
|
||||
.composer .submit-button:hover {
|
||||
color: #fff;
|
||||
background-color: #323642;
|
||||
box-shadow: 0px 0px 4px 2px #cacaca;
|
||||
}
|
||||
.composer .book-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.composer .book-value .book-icon {
|
||||
margin-right: 9px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
.composer .book-option {
|
||||
font-size: 1.4rem;
|
||||
padding: 5px 8px;
|
||||
}
|
||||
|
||||
/* success */
|
||||
.success-page {
|
||||
text-align: center;
|
||||
padding: 21px 0;
|
||||
}
|
||||
.success-page .key-list {
|
||||
display: inline-block;
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.success-page .key-item {
|
||||
display: flex;
|
||||
}
|
||||
.success-page .key-item:not(:first-child) {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.success-page .key-desc {
|
||||
font-size: 1.4rem;
|
||||
margin-left: 9px;
|
||||
}
|
||||
.success-page .book-icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.success-page .heading {
|
||||
display: inline-block;
|
||||
font-size: 2.2rem;
|
||||
margin-left: 13px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* buttons */
|
||||
.button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-weight: 400;
|
||||
line-height: 1.25;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
user-select: none;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
border-image: initial;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.25rem;
|
||||
transition: all 0.2s ease-in-out;
|
||||
font-size: 1.4rem;
|
||||
text-decoration: none;
|
||||
padding: 11px 18px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.button:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.button:icon {
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.button-first {
|
||||
color: #ffffff;
|
||||
background-color: #333745;
|
||||
}
|
||||
.button-first:hover {
|
||||
color: #ffffff;
|
||||
background-color: #252833;
|
||||
box-shadow: 0px 0px 4px 2px #cacaca;
|
||||
}
|
||||
|
||||
.button-first-outline {
|
||||
background: transparent;
|
||||
border-color: #333745;
|
||||
color: #333744;
|
||||
}
|
||||
.button-first-outline:hover {
|
||||
color: #333744;
|
||||
}
|
||||
|
||||
.button-second {
|
||||
background: white;
|
||||
color: #343a40;
|
||||
}
|
||||
.button-second:hover {
|
||||
color: #343a40;
|
||||
}
|
||||
|
||||
.button-third {
|
||||
color: #ffffff;
|
||||
background-color: #4577cc;
|
||||
}
|
||||
.button-third:hover {
|
||||
color: #ffffff;
|
||||
background-color: #245fc5;
|
||||
box-shadow: 0px 0px 4px 2px #cacaca;
|
||||
}
|
||||
|
||||
.button-outline {
|
||||
color: #0366d6;
|
||||
background-color: #fff;
|
||||
border: 2px solid #0366d6;
|
||||
}
|
||||
|
||||
.button ~ .button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.button-small {
|
||||
padding: 5px 14px;
|
||||
}
|
||||
.button-stretch {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
.button-no-ui {
|
||||
border: none;
|
||||
background: none;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
@ -1,453 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* React Select
|
||||
* ============
|
||||
* Created by Jed Watson and Joss Mackison for KeystoneJS, http://www.keystonejs.com/
|
||||
* https://twitter.com/jedwatson https://twitter.com/jossmackison https://twitter.com/keystonejs
|
||||
* MIT License: https://github.com/JedWatson/react-select
|
||||
*/
|
||||
.Select {
|
||||
position: relative;
|
||||
}
|
||||
.Select input::-webkit-contacts-auto-fill-button,
|
||||
.Select input::-webkit-credentials-auto-fill-button {
|
||||
display: none !important;
|
||||
}
|
||||
.Select input::-ms-clear {
|
||||
display: none !important;
|
||||
}
|
||||
.Select input::-ms-reveal {
|
||||
display: none !important;
|
||||
}
|
||||
.Select,
|
||||
.Select div,
|
||||
.Select input,
|
||||
.Select span {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.Select.is-disabled .Select-arrow-zone {
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
opacity: 0.35;
|
||||
}
|
||||
.Select.is-disabled > .Select-control {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.Select.is-disabled > .Select-control:hover {
|
||||
box-shadow: none;
|
||||
}
|
||||
.Select.is-open > .Select-control {
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
background: #fff;
|
||||
border-color: #b3b3b3 #ccc #d9d9d9;
|
||||
}
|
||||
.Select.is-open > .Select-control .Select-arrow {
|
||||
top: -2px;
|
||||
border-color: transparent transparent #999;
|
||||
border-width: 0 5px 5px;
|
||||
}
|
||||
.Select.is-searchable.is-open > .Select-control {
|
||||
cursor: text;
|
||||
}
|
||||
.Select.is-searchable.is-focused:not(.is-open) > .Select-control {
|
||||
cursor: text;
|
||||
}
|
||||
.Select.is-focused > .Select-control {
|
||||
background: #fff;
|
||||
}
|
||||
.Select.is-focused:not(.is-open) > .Select-control {
|
||||
border-color: #007eff;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075),
|
||||
0 0 0 3px rgba(0, 126, 255, 0.1);
|
||||
background: #fff;
|
||||
}
|
||||
.Select.has-value.is-clearable.Select--single > .Select-control .Select-value {
|
||||
padding-right: 42px;
|
||||
}
|
||||
.Select.has-value.Select--single
|
||||
> .Select-control .Select-value .Select-value-label,
|
||||
.Select.has-value.is-pseudo-focused.Select--single
|
||||
> .Select-control .Select-value .Select-value-label {
|
||||
color: #333;
|
||||
}
|
||||
.Select.has-value.Select--single
|
||||
> .Select-control .Select-value a.Select-value-label,
|
||||
.Select.has-value.is-pseudo-focused.Select--single
|
||||
> .Select-control .Select-value a.Select-value-label {
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
.Select.has-value.Select--single
|
||||
> .Select-control .Select-value a.Select-value-label:hover,
|
||||
.Select.has-value.is-pseudo-focused.Select--single
|
||||
> .Select-control .Select-value a.Select-value-label:hover,
|
||||
.Select.has-value.Select--single
|
||||
> .Select-control .Select-value a.Select-value-label:focus,
|
||||
.Select.has-value.is-pseudo-focused.Select--single
|
||||
> .Select-control .Select-value a.Select-value-label:focus {
|
||||
color: #007eff;
|
||||
outline: none;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.Select.has-value.Select--single
|
||||
> .Select-control .Select-value a.Select-value-label:focus,
|
||||
.Select.has-value.is-pseudo-focused.Select--single
|
||||
> .Select-control .Select-value a.Select-value-label:focus {
|
||||
background: #fff;
|
||||
}
|
||||
.Select.has-value.is-pseudo-focused .Select-input {
|
||||
opacity: 0;
|
||||
}
|
||||
.Select.is-open .Select-arrow,
|
||||
.Select .Select-arrow-zone:hover > .Select-arrow {
|
||||
border-top-color: #666;
|
||||
}
|
||||
.Select.Select--rtl {
|
||||
direction: rtl;
|
||||
text-align: right;
|
||||
}
|
||||
.Select-control {
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
cursor: default;
|
||||
display: table;
|
||||
border: 1px solid #e2e2e2;
|
||||
border-top: 0;
|
||||
height: 36px;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.Select-control:hover {
|
||||
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
.Select-control .Select-input:focus {
|
||||
outline: none;
|
||||
background: #fff;
|
||||
}
|
||||
.Select-placeholder,
|
||||
.Select--single > .Select-control .Select-value {
|
||||
bottom: 0;
|
||||
color: #aaa;
|
||||
left: 0;
|
||||
line-height: 34px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.Select-input {
|
||||
height: 34px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.Select-input > input {
|
||||
width: 100%;
|
||||
background: none transparent;
|
||||
border: 0 none;
|
||||
box-shadow: none;
|
||||
cursor: default;
|
||||
display: inline-block;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
margin: 0;
|
||||
outline: none;
|
||||
line-height: 17px;
|
||||
/* For IE 8 compatibility */
|
||||
padding: 8px 0 12px;
|
||||
/* For IE 8 compatibility */
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
.is-focused .Select-input > input {
|
||||
cursor: text;
|
||||
}
|
||||
.has-value.is-pseudo-focused .Select-input {
|
||||
opacity: 0;
|
||||
}
|
||||
.Select-control:not(.is-searchable) > .Select-input {
|
||||
outline: none;
|
||||
}
|
||||
.Select-loading-zone {
|
||||
cursor: pointer;
|
||||
display: table-cell;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
width: 16px;
|
||||
}
|
||||
.Select-loading {
|
||||
-webkit-animation: Select-animation-spin 400ms infinite linear;
|
||||
-o-animation: Select-animation-spin 400ms infinite linear;
|
||||
animation: Select-animation-spin 400ms infinite linear;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #ccc;
|
||||
border-right-color: #333;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.Select-clear-zone {
|
||||
-webkit-animation: Select-animation-fadeIn 200ms;
|
||||
-o-animation: Select-animation-fadeIn 200ms;
|
||||
animation: Select-animation-fadeIn 200ms;
|
||||
color: #999;
|
||||
cursor: pointer;
|
||||
display: table-cell;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
width: 17px;
|
||||
}
|
||||
.Select-clear-zone:hover {
|
||||
color: #d0021b;
|
||||
}
|
||||
.Select-clear {
|
||||
display: inline-block;
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
}
|
||||
.Select--multi .Select-clear-zone {
|
||||
width: 17px;
|
||||
}
|
||||
.Select-arrow-zone {
|
||||
cursor: pointer;
|
||||
display: table-cell;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
width: 25px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.Select--rtl .Select-arrow-zone {
|
||||
padding-right: 0;
|
||||
padding-left: 5px;
|
||||
}
|
||||
.Select-arrow {
|
||||
border-color: #999 transparent transparent;
|
||||
border-style: solid;
|
||||
border-width: 5px 5px 2.5px;
|
||||
display: inline-block;
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: relative;
|
||||
}
|
||||
.Select-control > *:last-child {
|
||||
padding-right: 5px;
|
||||
}
|
||||
.Select--multi .Select-multi-value-wrapper {
|
||||
display: inline-block;
|
||||
}
|
||||
.Select .Select-aria-only {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
margin: -1px;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
overflow: hidden;
|
||||
float: left;
|
||||
}
|
||||
@-webkit-keyframes Select-animation-fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes Select-animation-fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.Select-menu-outer {
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-top-color: #e6e6e6;
|
||||
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.06);
|
||||
box-sizing: border-box;
|
||||
margin-top: -1px;
|
||||
max-height: 200px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 100%;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
.Select-menu {
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.Select-option {
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
color: #666666;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
padding: 8px 10px;
|
||||
}
|
||||
.Select-option:last-child {
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
.Select-option.is-selected {
|
||||
background-color: #f5faff;
|
||||
/* Fallback color for IE 8 */
|
||||
background-color: rgba(0, 126, 255, 0.04);
|
||||
color: #333;
|
||||
}
|
||||
.Select-option.is-focused {
|
||||
background-color: #ebf5ff;
|
||||
/* Fallback color for IE 8 */
|
||||
background-color: rgba(0, 126, 255, 0.08);
|
||||
color: #333;
|
||||
}
|
||||
.Select-option.is-disabled {
|
||||
color: #cccccc;
|
||||
cursor: default;
|
||||
}
|
||||
.Select-noresults {
|
||||
box-sizing: border-box;
|
||||
color: #999999;
|
||||
cursor: default;
|
||||
display: block;
|
||||
padding: 8px 10px;
|
||||
}
|
||||
.Select--multi .Select-input {
|
||||
vertical-align: middle;
|
||||
margin-left: 10px;
|
||||
padding: 0;
|
||||
}
|
||||
.Select--multi.Select--rtl .Select-input {
|
||||
margin-left: 0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.Select--multi.has-value .Select-input {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.Select--multi .Select-value {
|
||||
background-color: #ebf5ff;
|
||||
/* Fallback color for IE 8 */
|
||||
background-color: rgba(0, 126, 255, 0.08);
|
||||
border-radius: 2px;
|
||||
border: 1px solid #c2e0ff;
|
||||
/* Fallback color for IE 8 */
|
||||
border: 1px solid rgba(0, 126, 255, 0.24);
|
||||
color: #007eff;
|
||||
display: inline-block;
|
||||
font-size: 0.9em;
|
||||
line-height: 1.4;
|
||||
margin-left: 5px;
|
||||
margin-top: 5px;
|
||||
vertical-align: top;
|
||||
}
|
||||
.Select--multi .Select-value-icon,
|
||||
.Select--multi .Select-value-label {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.Select--multi .Select-value-label {
|
||||
border-bottom-right-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
cursor: default;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
.Select--multi a.Select-value-label {
|
||||
color: #007eff;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
.Select--multi a.Select-value-label:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.Select--multi .Select-value-icon {
|
||||
cursor: pointer;
|
||||
border-bottom-left-radius: 2px;
|
||||
border-top-left-radius: 2px;
|
||||
border-right: 1px solid #c2e0ff;
|
||||
/* Fallback color for IE 8 */
|
||||
border-right: 1px solid rgba(0, 126, 255, 0.24);
|
||||
padding: 1px 5px 3px;
|
||||
}
|
||||
.Select--multi .Select-value-icon:hover,
|
||||
.Select--multi .Select-value-icon:focus {
|
||||
background-color: #d8eafd;
|
||||
/* Fallback color for IE 8 */
|
||||
background-color: rgba(0, 113, 230, 0.08);
|
||||
color: #0071e6;
|
||||
}
|
||||
.Select--multi .Select-value-icon:active {
|
||||
background-color: #c2e0ff;
|
||||
/* Fallback color for IE 8 */
|
||||
background-color: rgba(0, 126, 255, 0.24);
|
||||
}
|
||||
.Select--multi.Select--rtl .Select-value {
|
||||
margin-left: 0;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.Select--multi.Select--rtl .Select-value-icon {
|
||||
border-right: none;
|
||||
border-left: 1px solid #c2e0ff;
|
||||
/* Fallback color for IE 8 */
|
||||
border-left: 1px solid rgba(0, 126, 255, 0.24);
|
||||
}
|
||||
.Select--multi.is-disabled .Select-value {
|
||||
background-color: #fcfcfc;
|
||||
border: 1px solid #e3e3e3;
|
||||
color: #333;
|
||||
}
|
||||
.Select--multi.is-disabled .Select-value-icon {
|
||||
cursor: not-allowed;
|
||||
border-right: 1px solid #e3e3e3;
|
||||
}
|
||||
.Select--multi.is-disabled .Select-value-icon:hover,
|
||||
.Select--multi.is-disabled .Select-value-icon:focus,
|
||||
.Select--multi.is-disabled .Select-value-icon:active {
|
||||
background-color: #fcfcfc;
|
||||
}
|
||||
@keyframes Select-animation-spin {
|
||||
to {
|
||||
transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes Select-animation-spin {
|
||||
to {
|
||||
-webkit-transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"sourceMap": true,
|
||||
"noImplicitAny": false,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"target": "es6",
|
||||
"jsx": "react",
|
||||
"esModuleInterop": true,
|
||||
"allowJs": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"jslib/*": [
|
||||
"../jslib/src/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
/* Copyright (C) 2019, 2020 Monomax Software Pty Ltd
|
||||
*
|
||||
* This file is part of Dnote.
|
||||
*
|
||||
* Dnote is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Dnote is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const packageJson = require('./package.json');
|
||||
|
||||
const ENV = process.env.NODE_ENV;
|
||||
const TARGET = process.env.TARGET;
|
||||
const isProduction = ENV === 'production';
|
||||
|
||||
console.log(`Running webpack in ${ENV} mode`);
|
||||
|
||||
const webUrl = isProduction
|
||||
? 'https://app.getdnote.com'
|
||||
: 'http://127.0.0.1:3000';
|
||||
const apiUrl = isProduction
|
||||
? 'https://api.getdnote.com'
|
||||
: 'http://127.0.0.1:5000';
|
||||
|
||||
const plugins = [
|
||||
new webpack.DefinePlugin({
|
||||
__API_ENDPOINT__: JSON.stringify(apiUrl),
|
||||
__WEB_URL__: JSON.stringify(webUrl),
|
||||
__VERSION__: JSON.stringify(packageJson.version)
|
||||
})
|
||||
];
|
||||
|
||||
const moduleRules = [
|
||||
{
|
||||
test: /\.ts(x?)$/,
|
||||
exclude: /node_modules|_test\.ts(x)$/,
|
||||
loaders: ['ts-loader'],
|
||||
exclude: path.resolve(__dirname, 'node_modules')
|
||||
}
|
||||
];
|
||||
|
||||
module.exports = env => {
|
||||
return {
|
||||
// run in production mode because of Content Security Policy error encountered
|
||||
// when running a JavaScript bundle produced in a development mode
|
||||
mode: 'production',
|
||||
entry: { popup: ['./src/scripts/popup.tsx'] },
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
path: path.resolve(__dirname, 'dist', TARGET, 'scripts')
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js'],
|
||||
alias: {
|
||||
jslib: path.join(__dirname, '../jslib/src')
|
||||
},
|
||||
modules: [path.resolve('node_modules')]
|
||||
},
|
||||
module: { rules: moduleRules },
|
||||
plugins: plugins,
|
||||
optimization: {
|
||||
minimize: isProduction
|
||||
}
|
||||
};
|
||||
};
|
||||
69
go.mod
|
|
@ -1,42 +1,43 @@
|
|||
module github.com/dnote/dnote
|
||||
|
||||
go 1.13
|
||||
go 1.25
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/goquery v1.5.0 // indirect
|
||||
github.com/aymerick/douceur v0.2.0
|
||||
github.com/dnote/actions v0.2.0
|
||||
github.com/dnote/color v1.7.0
|
||||
github.com/dnote/xgo v0.0.0-20200205013105-40be7d6d43ff // indirect
|
||||
github.com/gobuffalo/packr v1.30.1 // indirect
|
||||
github.com/gobuffalo/packr/v2 v2.7.1
|
||||
github.com/google/go-cmp v0.3.1
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/go-github v17.0.0+incompatible
|
||||
github.com/google/go-querystring v1.0.0 // indirect
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/gorilla/css v1.0.0 // indirect
|
||||
github.com/gorilla/mux v1.7.2
|
||||
github.com/jinzhu/gorm v1.9.9
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/justincampbell/bigduration v0.0.0-20160531141349-e45bf03c0666 // indirect
|
||||
github.com/justincampbell/timeago v0.0.0-20160528003754-027f40306f1d
|
||||
github.com/lib/pq v1.1.1
|
||||
github.com/mattn/go-colorable v0.0.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.8 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.10.0
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/csrf v1.7.3
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/gorilla/schema v1.4.1
|
||||
github.com/mattn/go-sqlite3 v1.14.32
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/radovskyb/watcher v1.0.7
|
||||
github.com/robfig/cron v1.2.0
|
||||
github.com/rubenv/sql-migrate v0.0.0-20190618074426-f4d34eae5a5c
|
||||
github.com/sergi/go-diff v1.0.0
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/stripe/stripe-go v61.7.1+incompatible
|
||||
github.com/ziutek/mymysql v1.5.4 // indirect
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 // indirect
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/gomail.v2 v2.0.0-20150902115704-41f357289737
|
||||
gopkg.in/gorp.v1 v1.7.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
github.com/sergi/go-diff v1.3.1
|
||||
github.com/spf13/cobra v1.10.1
|
||||
golang.org/x/crypto v0.45.0
|
||||
golang.org/x/time v0.13.0
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gorm.io/driver/sqlite v1.6.0
|
||||
gorm.io/gorm v1.30.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/stretchr/testify v1.8.1 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/term v0.37.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
)
|
||||
|
|
|
|||
328
go.sum
|
|
@ -1,269 +1,103 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
|
||||
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
|
||||
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
|
||||
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
||||
github.com/dnote/actions v0.2.0 h1:P1ut2/QRKwfAzIIB374vN9A4IanU94C/payEocvngYo=
|
||||
github.com/dnote/actions v0.2.0/go.mod h1:bBIassLhppVQdbC3iaE92SHBpM1HOVe+xZoAlj9ROxw=
|
||||
github.com/dnote/color v1.7.0 h1:8/QGLQKSU8/zcWQaHbMyC1hJRkKO/Uu9M89sH76ecHE=
|
||||
github.com/dnote/color v1.7.0/go.mod h1:75UcP/TH7CNvjQ5pwDumkUS3vkPdGggy7/3fT8MlxHM=
|
||||
github.com/dnote/xgo v0.0.0-20200205013105-40be7d6d43ff h1:DJKdzouhr6u1NzuLbmSWeei9BagH3Nm4mSOzP0RMdc0=
|
||||
github.com/dnote/xgo v0.0.0-20200205013105-40be7d6d43ff/go.mod h1:ruGZjl8WThApI7BAIKV2Q/PnJoudvd6Epjc3z79jWVg=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||
github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8=
|
||||
github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
|
||||
github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
||||
github.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg=
|
||||
github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
||||
github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
|
||||
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
|
||||
github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
|
||||
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
|
||||
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
|
||||
github.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYim4o=
|
||||
github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I=
|
||||
github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jinzhu/gorm v1.9.9 h1:Gc8bP20O+vroFUzZEXA1r7vNGQZGQ+RKgOnriuNF3ds=
|
||||
github.com/jinzhu/gorm v1.9.9/go.mod h1:Kh6hTsSGffh4ui079FHrR5Gg+5D0hgihqDcsDN2BBJY=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/csrf v1.7.3 h1:BHWt6FTLZAb2HtWT5KDBf6qgpZzvtbp9QWDRKZMXJC0=
|
||||
github.com/gorilla/csrf v1.7.3/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E=
|
||||
github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM=
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
|
||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/justincampbell/bigduration v0.0.0-20160531141349-e45bf03c0666 h1:abLciEiilfMf19Q1TFWDrp9j5z5one60dnnpvc6eabg=
|
||||
github.com/justincampbell/bigduration v0.0.0-20160531141349-e45bf03c0666/go.mod h1:xqGOmDZzLOG7+q/CgsbXv10g4tgPsbjhmAxyaTJMvis=
|
||||
github.com/justincampbell/timeago v0.0.0-20160528003754-027f40306f1d h1:qtCcYJK2bebPXEC8Wy+enYxQqmWnT6jlVTHnDGpwvkc=
|
||||
github.com/justincampbell/timeago v0.0.0-20160528003754-027f40306f1d/go.mod h1:U7FWcK1jzZJnYuSnxP6efX3ZoHbK1CEpD0ThYyGNPNI=
|
||||
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE=
|
||||
github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
|
||||
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
|
||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.4.0 h1:LUa41nrWTQNGhzdsZ5lTnkwbNjj6rXTdazA1cSdjkOY=
|
||||
github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rubenv/sql-migrate v0.0.0-20190618074426-f4d34eae5a5c h1:LCELEbde3/GT141OpHRs+jJZrI1tI3ayVd4VqW7Ui2U=
|
||||
github.com/rubenv/sql-migrate v0.0.0-20190618074426-f4d34eae5a5c/go.mod h1:WS0rl9eEliYI8DPnr3TOwz4439pay+qNgzJoVya/DmY=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stripe/stripe-go v61.7.1+incompatible h1:sflLf/SPZxu81RtdypT48tjw6/NYQX55JCSuEm0rkWs=
|
||||
github.com/stripe/stripe-go v61.7.1+incompatible/go.mod h1:A1dQZmO/QypXmsL0T8axYZkSN/uA/T/A64pfKdBAMiY=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A=
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-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-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
|
||||
golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gomail.v2 v2.0.0-20150902115704-41f357289737 h1:NvePS/smRcFQ4bMtTddFtknbGCtoBkJxGmpSpVRafCc=
|
||||
gopkg.in/gomail.v2 v2.0.0-20150902115704-41f357289737/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
||||
gopkg.in/gorp.v1 v1.7.2 h1:j3DWlAyGVv8whO7AcIWznQ2Yj7yJkn34B8s63GViAAw=
|
||||
gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
||||
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
||||
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
|
||||
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
|
||||
|
|
|
|||
|
|
@ -1,20 +1,40 @@
|
|||
FROM alpine:latest
|
||||
FROM busybox:glibc
|
||||
|
||||
ARG tarballName
|
||||
RUN test -n "$tarballName"
|
||||
ARG TARGETPLATFORM
|
||||
ARG version
|
||||
|
||||
# add dependency to execute a golang binary with dynamical linking.
|
||||
RUN apk add --no-cache \
|
||||
libc6-compat
|
||||
RUN test -n "$TARGETPLATFORM" || (echo "TARGETPLATFORM is required" && exit 1)
|
||||
RUN test -n "$version" || (echo "version is required" && exit 1)
|
||||
|
||||
WORKDIR dnote
|
||||
WORKDIR /tmp/tarballs
|
||||
|
||||
COPY "$tarballName" .
|
||||
RUN tar -xvzf "$tarballName"
|
||||
# Copy all architecture tarballs
|
||||
COPY dnote_server_*.tar.gz ./
|
||||
|
||||
# Select and extract the correct tarball based on target platform
|
||||
RUN case "$TARGETPLATFORM" in \
|
||||
"linux/amd64") ARCH="amd64" ;; \
|
||||
"linux/arm64") ARCH="arm64" ;; \
|
||||
"linux/arm/v7") ARCH="arm" ;; \
|
||||
"linux/386") ARCH="386" ;; \
|
||||
*) echo "Unsupported platform: $TARGETPLATFORM" && exit 1 ;; \
|
||||
esac && \
|
||||
TARBALL="dnote_server_${version}_linux_${ARCH}.tar.gz" && \
|
||||
echo "Extracting $TARBALL for $TARGETPLATFORM" && \
|
||||
mkdir -p /dnote && \
|
||||
tar -xvzf "$TARBALL" -C /dnote
|
||||
|
||||
WORKDIR /dnote
|
||||
|
||||
# Set default database path for all processes (main server, docker exec, shells)
|
||||
ENV DBPath=/data/dnote.db
|
||||
|
||||
COPY entrypoint.sh .
|
||||
ENTRYPOINT ["./entrypoint.sh"]
|
||||
|
||||
CMD ./dnote-server start
|
||||
|
||||
EXPOSE 3000
|
||||
EXPOSE 3001
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \
|
||||
CMD wget --no-verbose --tries=1 -O /dev/null http://localhost:3001/health || exit 1
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
# Dnote Docker Image
|
||||
|
||||
The official Dnote docker image.
|
||||
|
||||
## Installing Dnote Server Using Docker
|
||||
|
||||
1. Install [Docker](https://docs.docker.com/install/).
|
||||
2. Install Docker [Compose](https://docs.docker.com/compose/install/).
|
||||
3. Download the [docker-compose.yml](https://raw.githubusercontent.com/dnote/dnote/master/host/docker/docker-compose.yml) file by running:
|
||||
|
||||
```
|
||||
curl https://raw.githubusercontent.com/dnote/dnote/master/host/docker/docker-compose.yml > docker-compose.yml
|
||||
```
|
||||
|
||||
4. Run the following to download the images and run the containers
|
||||
|
||||
```
|
||||
docker-compose pull
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
Visit http://localhost:3000 in your browser to see Dnote running.
|
||||
|
||||
Please see [the installation guide](https://github.com/dnote/dnote/blob/master/SELF_HOSTING.md) for further configuration.
|
||||
|
|
@ -1,13 +1,85 @@
|
|||
#!/usr/bin/env bash
|
||||
# Build Docker image for local testing
|
||||
#
|
||||
# Usage:
|
||||
# # builds for host platform (auto-detected)
|
||||
# ./build.sh 1.0.0
|
||||
#
|
||||
# # builds arm64
|
||||
# ./build.sh 1.0.0 linux/arm64
|
||||
#
|
||||
# # builds multiple platforms
|
||||
# ./build.sh 1.0.0 "linux/amd64,linux/arm64,linux/arm/v7,linux/386"
|
||||
set -eux
|
||||
|
||||
version=$1
|
||||
|
||||
# Detect host platform if not specified
|
||||
if [ -z "${2:-}" ]; then
|
||||
HOST_ARCH=$(uname -m)
|
||||
case "$HOST_ARCH" in
|
||||
x86_64) platform="linux/amd64" ;;
|
||||
aarch64|arm64) platform="linux/arm64" ;;
|
||||
armv7l) platform="linux/arm/v7" ;;
|
||||
i386|i686) platform="linux/386" ;;
|
||||
*)
|
||||
echo "Warning: Unsupported architecture: $HOST_ARCH, defaulting to linux/amd64"
|
||||
platform="linux/amd64"
|
||||
;;
|
||||
esac
|
||||
echo "Auto-detected platform: $platform"
|
||||
else
|
||||
platform=$2
|
||||
fi
|
||||
|
||||
dir=$(dirname "${BASH_SOURCE[0]}")
|
||||
projectDir="$dir/../.."
|
||||
tarballName="dnote_server_${version}_linux_amd64.tar.gz"
|
||||
|
||||
# copy over the build artifact to the Docker build context
|
||||
cp "$projectDir/build/server/$tarballName" "$dir"
|
||||
# Copy all Linux tarballs to Docker build context
|
||||
cp "$projectDir/build/server/dnote_server_${version}_linux_amd64.tar.gz" "$dir/"
|
||||
cp "$projectDir/build/server/dnote_server_${version}_linux_arm64.tar.gz" "$dir/"
|
||||
cp "$projectDir/build/server/dnote_server_${version}_linux_arm.tar.gz" "$dir/"
|
||||
cp "$projectDir/build/server/dnote_server_${version}_linux_386.tar.gz" "$dir/"
|
||||
|
||||
docker build -t dnote/dnote:"$version" --build-arg tarballName="$tarballName" .
|
||||
# Count platforms (check for comma)
|
||||
if [[ "$platform" == *","* ]]; then
|
||||
echo "Building for multiple platforms: $platform"
|
||||
|
||||
# Check if multiarch builder exists, create if not
|
||||
if ! docker buildx ls | grep -q "multiarch"; then
|
||||
echo "Creating multiarch builder for multi-platform builds..."
|
||||
docker buildx create --name multiarch --use
|
||||
docker buildx inspect --bootstrap
|
||||
else
|
||||
echo "Using existing multiarch builder"
|
||||
docker buildx use multiarch
|
||||
fi
|
||||
echo ""
|
||||
|
||||
docker buildx build \
|
||||
--platform "$platform" \
|
||||
-t dnote/dnote:"$version" \
|
||||
-t dnote/dnote:latest \
|
||||
--build-arg version="$version" \
|
||||
"$dir"
|
||||
|
||||
# Switch back to default builder
|
||||
docker buildx use default
|
||||
else
|
||||
echo "Building for single platform: $platform"
|
||||
echo "Image will be loaded to local Docker daemon"
|
||||
|
||||
docker buildx build \
|
||||
--platform "$platform" \
|
||||
-t dnote/dnote:"$version" \
|
||||
-t dnote/dnote:latest \
|
||||
--build-arg version="$version" \
|
||||
--load \
|
||||
"$dir"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Build complete!"
|
||||
if [[ "$platform" != *","* ]]; then
|
||||
echo "Test with: docker run --rm dnote/dnote:$version ./dnote-server version"
|
||||
fi
|
||||
|
|
|
|||
9
host/docker/compose.yml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
services:
|
||||
dnote:
|
||||
image: dnote/dnote:latest
|
||||
container_name: dnote
|
||||
ports:
|
||||
- 3001:3001
|
||||
volumes:
|
||||
- ./dnote_data:/data
|
||||
restart: unless-stopped
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
version: "3"
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:11-alpine
|
||||
environment:
|
||||
POSTGRES_USER: dnote
|
||||
POSTGRES_PASSWORD: dnote
|
||||
POSTGRES_DB: dnote
|
||||
volumes:
|
||||
- ./dnote_data:/var/lib/postgresql/data
|
||||
restart: always
|
||||
|
||||
dnote:
|
||||
image: dnote/dnote
|
||||
environment:
|
||||
GO_ENV: PRODUCTION
|
||||
DBSkipSSL: "true"
|
||||
DBHost: postgres
|
||||
DBPort: 5432
|
||||
DBName: dnote
|
||||
DBUser: dnote
|
||||
DBPassword: dnote
|
||||
WebURL: localhost:3000
|
||||
SmtpHost:
|
||||
SmtpPort:
|
||||
SmtpUsername:
|
||||
SmtpPassword:
|
||||
DisableRegistration: "false"
|
||||
ports:
|
||||
- 3000:3000
|
||||
depends_on:
|
||||
- postgres
|
||||
restart: always
|
||||
|
|
@ -1,25 +1,6 @@
|
|||
#!/bin/sh
|
||||
|
||||
wait_for_db() {
|
||||
HOST=${DBHost:-postgres}
|
||||
PORT=${DBPort:-5432}
|
||||
echo "Waiting for the database connection..."
|
||||
|
||||
attempts=0
|
||||
max_attempts=10
|
||||
while [ $attempts -lt $max_attempts ]; do
|
||||
nc -z "${HOST}" "${PORT}" 2>/dev/null && break
|
||||
echo "Waiting for db at ${HOST}:${PORT}..."
|
||||
sleep 5
|
||||
attempts=$((attempts+1))
|
||||
done
|
||||
|
||||
if [ $attempts -eq $max_attempts ]; then
|
||||
echo "Timed out while waiting for db at ${HOST}:${PORT}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for_db
|
||||
# Set default DBPath to /data if not specified
|
||||
export DBPath=${DBPath:-/data/dnote.db}
|
||||
|
||||
exec "$@"
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -eux
|
||||
|
||||
version=$1
|
||||
|
||||
docker login
|
||||
|
||||
# tag the release
|
||||
docker tag dnote/dnote:"$version" dnote/dnote:latest
|
||||
|
||||
# publish
|
||||
docker push dnote/dnote:"$version"
|
||||
docker push dnote/dnote:latest
|
||||
1
host/smoketest/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
/volume
|
||||
|
|
@ -1 +0,0 @@
|
|||
This directory contains a smoke test for running a self-hosted instance using a virtual machine.
|
||||
9
host/smoketest/Vagrantfile
vendored
|
|
@ -1,9 +0,0 @@
|
|||
# -*- mode: ruby -*-
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "ubuntu/bionic64"
|
||||
config.vm.synced_folder './volume', '/vagrant'
|
||||
config.vm.network "forwarded_port", guest: 2300, host: 2300
|
||||
|
||||
config.vm.provision 'shell', path: './setup.sh', privileged: false
|
||||
end
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# run_test.sh builds a fresh server image, and mounts it on a fresh
|
||||
# virtual machine and runs a smoke test. If a tarball path is not provided,
|
||||
# this script builds a new version and uses it.
|
||||
set -ex
|
||||
|
||||
# tarballPath is an absolute path to a release tarball containing the dnote server.
|
||||
tarballPath=$1
|
||||
|
||||
dir=$(dirname "${BASH_SOURCE[0]}")
|
||||
projectDir="$dir/../.."
|
||||
|
||||
# build
|
||||
if [ -z "$tarballPath" ]; then
|
||||
pushd "$projectDir"
|
||||
make version=integration_test build-server
|
||||
popd
|
||||
tarballPath="$projectDir/build/server/dnote_server_integration_test_linux_amd64.tar.gz"
|
||||
fi
|
||||
|
||||
pushd "$dir"
|
||||
|
||||
# start a virtual machine
|
||||
volume="$dir/volume"
|
||||
rm -rf "$volume"
|
||||
mkdir -p "$volume"
|
||||
cp "$tarballPath" "$volume"
|
||||
cp "$dir/testsuite.sh" "$volume"
|
||||
|
||||
vagrant up
|
||||
|
||||
# run tests
|
||||
set +e
|
||||
if ! vagrant ssh -c "/vagrant/testsuite.sh"; then
|
||||
echo "Test failed. Please see the output."
|
||||
vagrant halt
|
||||
exit 1
|
||||
fi
|
||||
set -e
|
||||
|
||||
vagrant halt
|
||||
popd
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -ex
|
||||
|
||||
sudo apt-get install wget ca-certificates
|
||||
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
||||
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ `lsb_release -cs`-pgdg main" >> /etc/apt/sources.list.d/pgdg.list'
|
||||
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y postgresql-11
|
||||
|
||||
# set up database
|
||||
sudo -u postgres createdb dnote
|
||||
# allow connection from host and allow to connect without password
|
||||
sudo sed -i "/port*/a listen_addresses = '*'" /etc/postgresql/11/main/postgresql.conf
|
||||
sudo sed -i 's/host.*all.*.all.*md5/# &/' /etc/postgresql/11/main/pg_hba.conf
|
||||
sudo sed -i "$ a host all all all trust" /etc/postgresql/11/main/pg_hba.conf
|
||||
sudo service postgresql restart
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# testsuite.sh runs the smoke tests for a self-hosted instance.
|
||||
# It is meant to be run inside a virtual machine which has been
|
||||
# set up by an entry script.
|
||||
set -eu
|
||||
|
||||
echo 'Running a smoke test'
|
||||
|
||||
sudo -u postgres dropdb dnote
|
||||
sudo -u postgres createdb dnote
|
||||
|
||||
cd /vagrant
|
||||
|
||||
tar -xvf dnote_server_integration_test_linux_amd64.tar.gz
|
||||
|
||||
GO_ENV=PRODUCTION \
|
||||
DBHost=localhost \
|
||||
DBPort=5432 \
|
||||
DBName=dnote \
|
||||
DBUser=postgres \
|
||||
DBPassword="" \
|
||||
WebURL=localhost:3000 \
|
||||
./dnote-server -port 2300 start & sleep 3
|
||||
|
||||
assert_http_status() {
|
||||
url=$1
|
||||
expected=$2
|
||||
|
||||
echo "======== [TEST CASE] asserting response status code for $url ========"
|
||||
|
||||
got=$(curl --write-out %"{http_code}" --silent --output /dev/null "$url")
|
||||
|
||||
if [ "$got" != "$expected" ]; then
|
||||
echo "======== ASSERTION FAILED ========"
|
||||
echo "status code for $url: expected: $expected got: $got"
|
||||
echo "=================================="
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_http_status http://localhost:2300 "200"
|
||||
assert_http_status http://localhost:2300/api/health "200"
|
||||
|
||||
echo "======== [SUCCESS] TEST PASSED! ========"
|
||||
19
install.sh
|
|
@ -68,8 +68,14 @@ uname_os() {
|
|||
|
||||
uname_arch() {
|
||||
arch=$(uname -m)
|
||||
case $arch in
|
||||
case $arch in
|
||||
x86_64) arch="amd64" ;;
|
||||
aarch64) arch="arm64" ;;
|
||||
arm64) arch="arm64" ;;
|
||||
armv7l) arch="arm" ;;
|
||||
armv6l) arch="arm" ;;
|
||||
armv5l) arch="arm" ;;
|
||||
arm) arch="arm" ;;
|
||||
x86) arch="386" ;;
|
||||
i686) arch="386" ;;
|
||||
i386) arch="386" ;;
|
||||
|
|
@ -85,8 +91,17 @@ check_platform() {
|
|||
|
||||
found=1
|
||||
case "$platform" in
|
||||
darwin/amd64) found=0;;
|
||||
# Linux
|
||||
linux/amd64) found=0 ;;
|
||||
linux/arm64) found=0 ;;
|
||||
linux/arm) found=0 ;;
|
||||
# macOS
|
||||
darwin/amd64) found=0 ;;
|
||||
darwin/arm64) found=0 ;;
|
||||
# Windows
|
||||
windows/amd64) found=0 ;;
|
||||
# FreeBSD
|
||||
freebsd/amd64) found=0 ;;
|
||||
esac
|
||||
|
||||
return $found
|
||||
|
|
|
|||
3
jslib/.gitignore
vendored
|
|
@ -1,3 +0,0 @@
|
|||
/dist
|
||||
/node_modules
|
||||
/coverage
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
# jslib
|
||||
|
||||
Code shared between Dnote JavaScript projects.
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
collectCoverageFrom: ['./src/**/*.ts']
|
||||
};
|
||||