Compare commits

..

151 commits

Author SHA1 Message Date
dependabot[bot]
f34a96abbe
Bump immutable from 5.1.3 to 5.1.5 in /pkg/server/assets (#718)
Bumps [immutable](https://github.com/immutable-js/immutable-js) from 5.1.3 to 5.1.5.
- [Release notes](https://github.com/immutable-js/immutable-js/releases)
- [Changelog](https://github.com/immutable-js/immutable-js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/immutable-js/immutable-js/compare/v5.1.3...v5.1.5)

---
updated-dependencies:
- dependency-name: immutable
  dependency-version: 5.1.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-04 22:11:45 -08:00
dependabot[bot]
9fa312e3fc
Bump golang.org/x/crypto from 0.42.0 to 0.45.0 (#716)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.42.0 to 0.45.0.
- [Commits](https://github.com/golang/crypto/compare/v0.42.0...v0.45.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.45.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-12 21:48:59 -08:00
Sung
8f37d34df6
Remove ls and cat commands (#715) 2025-11-07 23:53:41 -08:00
Sung
5c416e3a32
Add user list command (#714) 2025-11-01 19:59:51 -07:00
Sung
d5e11c23f6
Update self-hosting doc (#713) 2025-11-01 19:20:18 -07:00
Sung
f1d7123596
Fix log level (#712) 2025-11-01 14:06:33 -07:00
Sung
ce5c9b242a
Rename webUrl to baseUrl (#711) 2025-11-01 00:57:37 -07:00
Sung
e72322f847
Simplify email backend and remove --appEnv (#710)
* Improve logging

* Remove AppEnv

* Simplify email backend
2025-11-01 00:54:27 -07:00
Sung
e0c4cb1545
Use Apache 2.0 license (#708) 2025-10-31 23:41:21 -07:00
Sung
6314749263
Improve empty server sync when multiple clients exist (#706)
* Fix dbPath

* Require full sync when after another client uploads to an empty server

* Avoid orphan notes with empty sync
2025-10-26 17:54:24 -07:00
Sung
ae290a226f
Auto vacuum and manage connections (#705)
* Test concurrent sync

* Auto vacuum and manage connection
2025-10-26 16:59:53 -07:00
Sung
a46afb821f
Fix an edge case of repeated syncs due to orphaned note (#704)
* Split sync test

* Reproduce a bug

* Fix a bug

* Fix in a more correct way

* Add debug logs
2025-10-26 11:43:17 -07:00
Sung
f6a4c6344c
Remove public from CLI (#703)
* Remove public from CLI

* Write migration and test

* Use in-memory db for a test server

* Simplify CLI test db teardown

* Restructure packages to reduce duplication
2025-10-25 20:51:57 -07:00
Sung
e3380a4dfa
Remove unused templates (#702) 2025-10-19 21:42:53 -07:00
Sung
0a5728faf3
Merge user and account (#701) 2025-10-19 21:05:47 -07:00
Sung
b03ca999a5
Remove the unused encrypted and public fields (#700)
* Remove encrypted fields from notes and books

* Remove public from notes

* Use consistent flags
2025-10-19 18:32:20 -07:00
Sung
83ac43b737
Specify DBPath for docker (#699) 2025-10-19 15:38:52 -07:00
Sung
505fc67966
Fix server release for freebsd (#698) 2025-10-19 14:57:29 -07:00
Sung
7d44c541a4
Add Docker images for linux arm64, armv7, 386 (#697)
* Add multi-platform Docker support for ARM64, ARMv7, and 386

* Support freebsd amd64 for server

* Build docker images locally
2025-10-19 14:30:55 -07:00
Sung
850f9cc6c9
Manage users with server CLI (#696) 2025-10-19 11:01:48 -07:00
Sung
41f25514f0
Link to the doc (#695) 2025-10-19 01:01:26 -07:00
Sung
03889a3d7e
Converge if using same book names while syncing (#694)
* Add healthcheck for Docker

* Prevent nil pointer if endpoint is wrong

* Converge if using same book names while syncing
2025-10-18 16:03:12 -07:00
Sung
c8238aa327
Handle errors (#693) 2025-10-12 16:17:01 -07:00
Sung
346bd9afb1
Add dbPath flag and update apiEndpoint flag (#692)
* Allow to specify CLI db path as a flag

* Make API endpoint flag per command and change case
2025-10-12 15:08:11 -07:00
Sung
74119b1d0b
Automate release process (#691) 2025-10-12 13:02:59 -07:00
Sung
24491bc68a
Allow to upload all data to an empty server (#690)
* Handle server switch

* Avoid losing data in case of race

* Simplify
2025-10-12 12:03:20 -07:00
Sung
e0f68fc8d8
Rate limit client (#689) 2025-10-11 16:14:20 -07:00
Sung
fd7b2a78b2
Remove email verification (#688)
* Remove email verified flag

* Fix sass deprecation warnings
2025-10-11 13:35:39 -07:00
Sung
ca5af5e34a
Run server on port 3001 (#687)
* Simplify docker compose file

* Run on port 3001
2025-10-11 12:41:51 -07:00
Sung
162ceb4ad1
Simplify installation (#686) 2025-10-10 21:20:33 -07:00
Sung
637d4c6861
Simplify contribution (#685) 2025-10-05 22:26:48 -07:00
Sung
a62c7f9e93
Build cli v0.15.2 (#684) 2025-10-05 21:26:12 -07:00
Sung
5df3e7af70
Document change (#683) 2025-10-05 17:59:19 -07:00
Sung
b141b677bb
Cross-platform build for server (#682) 2025-10-05 17:56:13 -07:00
Sung
61162e2add
Use SQLite on the server (#681)
* Use SQLite on server

* Remove pro

* Simplify

* Use flag

* Automate release
2025-10-05 17:02:30 -07:00
Sung
5928f9619f
Upgrade go and dependencies (#679) 2025-10-04 00:25:12 -07:00
Sung
a26037f83a
Add sync test (#678) 2025-09-28 22:25:34 -07:00
Sung
b37820da1f
Remove Pro (#674) 2025-09-28 21:09:22 -07:00
Sung
8ffd44f362
Add 2025 (#673) 2025-08-08 22:31:20 -07:00
dependabot[bot]
895dcaa59f
Bump braces from 3.0.2 to 3.0.3 in /pkg/server/assets (#670)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-30 17:43:11 +10:00
dependabot[bot]
1ff4c07528
Bump golang.org/x/net from 0.20.0 to 0.23.0 (#669)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.20.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.20.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-11 13:50:36 +10:00
Sung
bdc980e5ce
Release CLI 0.15.1 (#661) 2024-02-03 11:54:46 +11:00
Sung
f1c0d0bc04
Upgrade color library (#660) 2024-02-03 10:29:28 +11:00
Sung
f0ba8a1fa6
Upgrade to Go 1.21 (#658) 2024-01-28 14:44:21 +11:00
Sung
4dded18395
Update the instruction to use Docker compose v2 (#657) 2024-01-28 12:50:26 +11:00
Sung
818ebcce69
Add 2024 (#656) 2024-01-28 11:50:22 +11:00
Sung
94ad949453
Release CLI v0.15.0 (#647) 2023-05-27 18:08:19 +10:00
Sung
5eae1299f7
Allow to disable automatic upgrade check in CLI (#646) 2023-05-27 17:49:34 +10:00
Sung
2de1798fdd
Remove Darwin release (#644) 2023-03-18 16:57:40 +11:00
Sung
558bf8dbe1
Run CI (#643)
This reverts commit f18677e498.
2023-03-18 16:55:16 +11:00
Sung
664520e027
Allow to download darwin/arm64 using the installation script (#642) 2023-03-18 16:52:34 +11:00
Sung
f18677e498
Remove autoclose of inactive issue (#641) 2023-03-12 13:48:14 +11:00
Sung
b47c792d5f
Fix homebrew release (#640) 2023-03-10 21:32:10 +11:00
Sung
489d4f407a
Document CLI 0.14.0 release (#639) 2023-03-10 21:26:23 +11:00
Sung
17b4c7d6cf
Disable completion command (#637) 2023-03-10 21:05:18 +11:00
Sung
fd5f92c247
Close inactive issues (#636) 2023-03-10 20:29:56 +11:00
Sung
0ef9e20e8d
Release server v.2.1.1 (#635) 2023-03-04 13:28:49 +11:00
Sung
c3214f12f0
Fix missing assets in a server release (#634)
* Fix missing assets in a server release

* Fix missing library error
2023-03-04 13:26:36 +11:00
Sung
4258ea1715
Release server-v2.1.0 (#633)
* Release server-v2.1.0

* Fix smoke test
2023-03-04 11:57:40 +11:00
Sung
d52a5bdf48
Use OnPremises (#631) 2023-03-04 10:52:35 +11:00
Sung
dd6f8c7f65
Upgrade dependencies (#630)
* Upgrade dependencies

* Fix broken test due to upstream behavior
2023-03-03 22:10:25 +11:00
Sung
b07858a3df
Increase the upgrade check interval to 8 weeks (#629) 2023-03-03 21:37:12 +11:00
Sung
420f30fb92
Separate homebrew command for testing (#625) 2023-02-11 19:06:03 +11:00
Sung
4978e57fbe
Upgarde to use gh (#624) 2023-02-11 17:35:41 +11:00
Sung
86bfc44f47
Fix version tag (#622) 2023-02-10 23:21:24 +11:00
Sung
577a6e8a57
Release CLI 0.13.0 (#621) 2023-02-10 22:01:46 +11:00
Sung
9a83ac3c51
Distribute CLI for macOS with Apple silicon (#620) 2023-02-10 21:57:53 +11:00
Zach Nickel
ff0ae9ac29
Add note from stdin content (#616)
* add note from stdin content

* add tests and update help text
2023-02-10 21:34:23 +11:00
Sung
4a780574eb
Upgrade dependencies (#619) 2023-02-05 13:10:45 +11:00
Michael Robson
4007647c34
Update dnoterc location in SELF_HOSTING.md (#608)
Change to match move from ~/.dnote/dnoterc to current ~/.config/dnote/dnoterc
2023-02-05 12:44:06 +11:00
Sung
01b70bdcaa
Fix build status in README (#618) 2023-02-05 12:35:51 +11:00
Sung
5e6b96f0d6
Run CI (#617) 2023-02-05 12:30:03 +11:00
Sung
ee45144cd7
Add 2023 (#615) 2023-02-05 11:02:25 +11:00
Sung Won Cho
62fe48e451
Remove unused files (#601) 2022-05-10 20:35:43 +10:00
Sung Won Cho
8e5c051437
Add 2022 (#600) 2022-05-10 20:19:04 +10:00
Sung Won Cho
14284057ea
Release server 2.0.0 (#598) 2022-05-09 21:37:02 +10:00
Sung Won Cho
5bba57fd29
Remove dependency on packr (#597)
* Embed files

* Build CLI

* Remove packr

* Embed view directory

* Embed static files

* Make view engine

* Populate build info
2022-05-09 20:34:23 +10:00
Sung Won Cho
f649b5225b
Remove unused file (#596) 2022-04-25 11:51:00 +10:00
Sung Won Cho
26e8da8f0a
Ignore test files (#595) 2022-04-24 17:28:14 +10:00
Sung Won Cho
f7ac7f5ab6
Remove unused JavaScript projects (#594)
* Update sender

* Remove unused projects

* Fix style
2022-04-24 14:48:13 +10:00
Sung Won Cho
a4b7a07bff
Preserve backwards compatibility of health endpoint (#593) 2022-04-24 11:32:04 +10:00
Sung Won Cho
01a378c5b1
Simplify by removing web interface (#590)
* Implement MVC

* Implement settings

* Improve layout

* Lock sass dependency
2022-04-24 10:54:39 +10:00
Sung Won Cho
172f608b66
Remove Forum (#582) 2022-02-17 20:06:15 +11:00
Sung Won Cho
9a054e9ff6
Fix build annotation (#578) 2022-02-12 09:11:44 +11:00
Sung Won Cho
504ba44852
Add 2021 (#574) 2021-12-08 22:26:15 +11:00
Sung Won Cho
6eb68d1817
Document upgrade guide (#530) 2021-01-03 14:06:38 +11:00
Sung Won Cho
fe32dc4f2d
Release CLI 0.12.0 (#529) 2021-01-03 12:37:10 +11:00
Sung Won Cho
a4640977b5
Allow to skip frontmatter when viewing note (#528) 2021-01-03 12:32:24 +11:00
Sung Won Cho
e9f3b080d5
Use XDG base directory (#527)
* Create platform specific directory definitions

* Fix CLI integration test

* Rename dirs to paths and get config path

* Namespace

* Fix initialization of dirs

* Simplify and change description

* Simplify

* Fix build flag

* Bump sqlite version

* Bump xgo
2021-01-03 12:11:22 +11:00
Sung Won Cho
1b7473149c
Update install_postgres.sh (#525) 2021-01-03 11:37:34 +11:00
Monomax Bot
a9fe1e987e
Upgrade dependencies. (#501) 2020-08-25 18:36:16 +10:00
Sung Won Cho
258fd0995f
Fix lint (#500) 2020-08-23 12:25:24 +10:00
Sung Won Cho
cba1df56cc
Update dep (#481)
* Upgrade dep and fix lint

* Add lint fix

* Bump browser dep
2020-06-11 19:17:35 +10:00
Alessandro Buggin
32c13b3b53
Update docker-compose.yml to pull latest version (#477) 2020-06-03 18:05:20 +10:00
Sung Won Cho
0d44112904
Release server v1.0.4 (#473) 2020-05-23 10:56:00 +10:00
Sung Won Cho
2ca6818b9a
Invalidate existing sessions upon password change (#471) 2020-05-23 10:41:23 +10:00
Sung Won Cho
a060176cbb
Remove obsolete columns (#470) 2020-05-22 17:19:10 +10:00
Sung Won Cho
6acc2936e3
Reduce bundle size (#469)
* Rename handlers to api

* Fix imports

* Fix test

* Abstract

* Fix warning

* wip

* Split session

* Pass db

* Fix test

* Fix test

* Remove payment

* Fix state

* Fix flow

* Check password when changing email

* Add test methods

* Fix timestamp

* Document

* Remove clutter

* Redirect to login

* Fix

* Fix
2020-05-22 16:30:05 +10:00
Sung Won Cho
91d5c710ed
Release server 1.0.3 (#462) 2020-05-03 09:39:04 +10:00
Sung Won Cho
cee2db2510
Fix timeline (#460)
* Fix timeline

* Document change
2020-05-01 11:30:32 +10:00
Monomax Bot
1a0361922b
Upgrade dependencies. (#459) 2020-05-01 08:42:07 +10:00
Sung Won Cho
6515aacf42
Update install.sh (#457) 2020-04-26 11:13:11 +10:00
Sung Won Cho
fa9f43167d
Release CLI 0.11.1 (#456) 2020-04-25 19:13:26 +10:00
Sung Won Cho
5864e34bdd
Display helpful error if accidentally pointing to catchall handler (#455)
* Display helpful error if accidentally pointing to catchall handler

* Document change

* Fix logout

* Fix test
2020-04-25 18:48:44 +10:00
Sung Won Cho
ebe360d7ff
Dynamic hostname (#454)
* Display dynamic hostname for login

* Document change
2020-04-25 16:56:33 +10:00
Sung Won Cho
4b700f5e66
Fix upgrade url (#453) 2020-04-25 16:56:24 +10:00
Sung Won Cho
1c96a2bbaa
Sort notes by last activity (#452)
* Upgrade dep

* Sort by last activity

* Upgrade node

* Document change
2020-04-25 16:08:51 +10:00
Sung Won Cho
ae8531fe11
Speed up ci (#451) 2020-04-25 15:24:33 +10:00
Monomax Bot
96d3c50780
Upgrade dependencies (#450)
* Upgrade dependencies.

* Fix test

* Improve name

* Update doc

* Upgrade deps

* Fix lint

* Fix lint

Co-authored-by: Sung Won Cho <sung@monomax.sh>
2020-04-25 11:38:39 +10:00
Sung Won Cho
cabe02aa71
Arm64 build (#449)
* Build for arm64

* Build CLI for arm64

* Fix test
2020-04-24 18:44:43 +10:00
Craig Kaiser
5f77d92076
Add install instructions for self hosted using Apache2 (#448) 2020-04-24 15:45:11 +10:00
Sung Won Cho
6e690ea4da
Fix rules (#438)
* Fix network

* Fix migrations for table that no longer exist

* Document changed behavior
2020-03-29 09:43:42 +11:00
Sung Won Cho
0d9a1810bb
Add toggle for inactive email preference (#435) 2020-03-22 11:38:24 +11:00
Sung Won Cho
1e3efa9bb2
Remove deprecated classic migrations (#433)
* Remove repetition and digest

* Document change

* Remove classic

* Document change

* Document change

* Upgrade dep

* Fix changelog

* Upgrade dep

* fix

* fix
2020-03-22 11:00:50 +11:00
Sung Won Cho
a9f052b19c
Remove repetition (#432)
* Remove repetition and digest

* Document change

* Fix test

* Fix test
2020-03-22 10:36:13 +11:00
Sung Won Cho
b2da22acae
Add doc (#418) 2020-02-13 10:03:08 +10:00
Sung Won Cho
c9ec331672 Encapsulate config load (#414)
* Use config package

* Add config test

* Fix test
2020-02-10 09:56:47 +10:00
Sung Won Cho
9d79ea1b0e
fix typo (#413) 2020-02-07 13:01:16 +10:00
Sung Won Cho
9c94b273fb
Allow to upgrade (#412) 2020-02-07 12:29:02 +10:00
Sung Won Cho
fb4ccf1ada
Allow to use yearly (#411)
* Toggle

* Display breakdown

* Process yearly plan

* Display summary

* Fix style
2020-02-07 11:49:07 +10:00
Sung Won Cho
f35cb307b3
Add deprecation notice (#410) 2020-02-06 13:08:44 +10:00
Sung Won Cho
22373a78e1
Fix release (#409) 2020-02-06 12:48:28 +10:00
Sung Won Cho
054a3c47ac
Remove deprecated items (#408)
* Update README

* Remove deprecated item

* Remove deprecated item

* Upgrade web dependencies

* Upgrade dev depdenencies

* Document changes

* upgrade
2020-02-06 10:24:30 +10:00
Sung Won Cho
6c8cba57b5
Fix client (#407) 2020-02-05 15:28:45 +10:00
Sung Won Cho
cd80f9b72e
Release CLI 0.11.0 (#404)
* Release CLI 0.11.0

* Update outdated ldflags

* Fix release

* Fix date
2020-02-05 11:58:51 +10:00
Sung Won Cho
3c5819f1d1
Avoid refocus at the end of the textarea input (#405) 2020-02-05 08:12:09 +10:00
Sung Won Cho
58a2b581aa
Allow to specify username and password as flags (#403)
* Allow to specify username and password as flags

* Format
2020-02-04 12:30:26 +10:00
Sung Won Cho
df4763947c
Fix installation script (#398) 2020-02-03 14:59:32 +10:00
Sung Won Cho
b92bff942b
Deprecate spaced repetition (#397)
* Change button style

* Require pro

* Update README

* Change wording

* Require sudo
2020-02-03 14:29:42 +10:00
Sung Won Cho
e6b5aea147
Add client field (#396)
* Record client

* Fix test
2020-01-31 08:49:12 +10:00
Sung Won Cho
3c650187c5
Update README (#390)
* Update README

* Update wording
2020-01-11 17:19:59 +10:00
Sung Won Cho
6e22c918ec
Wrap words in note content (#389)
* Fix word-wrap

* Document change

* Fix date
2020-01-09 08:19:21 +10:00
Sung Won Cho
48d7af483c
Release 0.4.0 (#386)
* Document changes

* Update README

* Make Docker installation official
2020-01-08 15:46:58 +10:00
Sung Won Cho
d6496b27d1
Fix email type (#385) 2020-01-08 09:27:55 +11:00
Sung Won Cho
ea51514c85
Fix digest note review (#383)
* Fix issue where unreviewed note is displayed as reviewed

* Show unreviewed first

* Fix menu alignment on mobile

* Show completion message

* Display help on empty digest list

* Fix email type
2020-01-07 16:03:45 +11:00
Sung Won Cho
952e0e62f3
Update copyright notice for the new year (#382)
* Update script

* Update the year in the license
2020-01-07 11:59:40 +11:00
Sung Won Cho
91414da0ac
Digests on web (#380)
* Implement operations

* Implement digest endpoints

* Implement digests reducer and basic page

* Make note component reusable

* Implement digest page

* Add license

* Fix style and accessbility

* Fix loading

* Fix query

* Test

* Remove debug
2020-01-07 11:42:48 +11:00
Sung Won Cho
1427e910b7
0.3.4 (#378) 2019-12-24 13:16:23 +07:00
Sung Won Cho
3152ca770b
Alert when we stop learning (#375)
* Send password reset alert

* Send inactive reminder

* Send subscription confirmation email
2019-12-24 13:07:27 +07:00
Sung Won Cho
4caf82a998
Add syntax highlighting style (#377) 2019-12-24 12:48:05 +07:00
Sung Won Cho
912af37c9b
Revert "Upgrade dependencies. (#374)" (#376)
This reverts commit beceecb74d.
2019-12-24 10:20:07 +07:00
Monomax Bot
beceecb74d Upgrade dependencies. (#374) 2019-12-24 08:51:40 +07:00
Sung Won Cho
fce862621d
Fix release (#370) 2019-12-17 18:03:14 +07:00
Sung Won Cho
a18d9c817f
Fix release (#369) 2019-12-17 17:46:01 +07:00
Sung Won Cho
422f59ef79
0.3.3 (#368)
* Document release 0.3.3

* Bump
2019-12-17 17:17:09 +07:00
Monomax Bot
cd1950213a Upgrade dependencies (#367)
* Upgrade dependencies.

* Bump major
2019-12-17 17:06:35 +07:00
Sung Won Cho
7f981c9f57
Allow to disable registration (#365)
* Allow to disable registration

* Document changes

* Add env vars to templates

* Fix test

* fix
2019-12-17 15:15:06 +07:00
Sung Won Cho
3e41b29a74
Decouple web from App implementation (#364)
* Decouple app from web

* Simplify

* Fix test

* Encapsulate SSL logic to dbconn

* Fix test

* Fix email type
2019-12-17 12:26:42 +07:00
Sung Won Cho
7856d09a92
Fix OnPremise signup (#361)
* Allow to provide test server url directly

* Set on premise

* Fix release title
2019-12-16 15:27:41 +07:00
Sung Won Cho
891be61031
Improve email and signup for self-hosting users (#355)
* Add app abstraction

* Abstract out email sending

* Get sender

* Test

* Move operations to app

* Test

* Test

* Add license

* Fix lint
2019-12-14 12:10:48 +07:00
Sung Won Cho
295dcefff1
Upgrade (#360) 2019-12-14 11:41:33 +07:00
890 changed files with 25650 additions and 88991 deletions

View file

@ -1,72 +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
},
"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
View 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
View 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
View 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
View file

@ -3,3 +3,7 @@
.vagrant
*.log
node_modules
/test
tmp
*.db
/server

View file

@ -1,4 +0,0 @@
{
"singleQuote": true
}

View file

@ -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

View file

@ -1,133 +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.
### Unreleased
#### Added
- Send welcome email with login instructions upon reigstering
### 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.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.

View file

@ -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
View file

@ -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.

149
Makefile
View file

@ -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,64 +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)" \
"${shasum -a 256 "${cliOutputDir}/dnote_$(version)_darwin_amd64.tar.gz" | cut -d ' ' -f 1}" \
)
.PHONY: release-cli
release-server: clean build-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

View file

@ -1,65 +1,64 @@
![Dnote](assets/logo.png)
=========================
Dnote is a simple personal knowledge base.
![Build Status](https://github.com/dnote/dnote/actions/workflows/ci.yml/badge.svg)
[![Build Status](https://travis-ci.org/dnote/dnote.svg?branch=master)](https://travis-ci.org/dnote/dnote)
Dnote is a simple command line notebook. Single binary, no dependencies. Since 2017.
## What is Dnote?
Dnote is a lightweight personal knowledge base. The main design goal is to **keep you focused** by providing a way of swiftly capturing new information **without having to switch environment**. To that end, you can use Dnote as a command line interface, browser extension, web client, or an IDE plugin.
It also offers a seamless **multi device sync**, and **automated spaced repetition** to retain your memory.
For more details, see the [download page](https://www.getdnote.com/download) and [features](https://www.getdnote.com/pricing).
![A demo of Dnote CLI](assets/cli.gif)
## Quick install
The quickest way to try Dnote is to install the command line interface.
### Install with Homebrew
On macOS, you can install using Homebrew:
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
brew tap dnote/dnote
brew install dnote
# Add a note (or omit -c to launch your editor)
dnote add linux -c "Check disk usage with df -h"
# to upgrade to the latest version
brew upgrade dnote
# View notes in a book
dnote view linux
# Full-text search
dnote find "disk usage"
# Sync notes
dnote sync
```
### Install with script
## Installation
You can use the installation script to install the latest version:
```bash
# Linux, macOS, FreeBSD, Windows
curl -s https://www.getdnote.com/install | sh
curl -s https://raw.githubusercontent.com/dnote/dnote/master/pkg/cli/install.sh | sh
# macOS with Homebrew
brew install dnote
```
In some cases, you might need an elevated permission:
Or [download binary](https://github.com/dnote/dnote/releases).
curl -s https://raw.githubusercontent.com/dnote/dnote/master/pkg/cli/install.sh | sudo sh
## Server (Optional)
### Install with tarball
Server is a binary with SQLite embedded. No database setup is required.
You can download the binary for your platform manually from the [releases page](https://github.com/dnote/dnote/releases).
If using docker, create a compose.yml:
## Personal knowledge base
```yaml
services:
dnote:
image: dnote/dnote:latest
container_name: dnote
ports:
- 3001:3001
volumes:
- ./dnote_data:/data
restart: unless-stopped
```
Dnote is great for building a personal knowledge base because:
Then run:
* It is fully open source.
* Your data is stored locally first and in a SQLite format which is [suitable for continued accessibility](https://www.sqlite.org/locrsf.html).
* It provides a way of instantly capturing new lessons without distracting you.
* It automates spaced repetition to help you retain your memory.
```bash
docker-compose up -d
```
You can read more in the following user stories:
Or see the [guide](https://www.getdnote.com/docs/server/manual) for binary installation.
- [How I Built a Personal Knowledge Base for Myself](https://www.getdnote.com/blog/how-i-built-personal-knowledge-base-for-myself/)
- [I Wrote Down Everything I Learned While Programming for a Month](https://www.getdnote.com/blog/writing-everything-i-learn-coding-for-a-month/)
## Documentation
## See Also
- [Homepage](https://www.getdnote.com)
- [Forum](https://forum.getdnote.com)
See the [Dnote doc](https://www.getdnote.com/docs).

View file

@ -1,165 +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 \
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.
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=WebURL=$WebURL
Environment=DBUser=$DBUser
Environment=DBPassword=$DBPassword
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`
### Enable Pro version
After signing up with an account, enable the pro version to access all features.
Log into the `dnote` Postgres database and execute the following query:
```sql
UPDATE users SET cloud = true FROM accounts WHERE accounts.user_id = users.id AND accounts.email = '$yourEmail';
```
Replace `$yourEmail` with the email you used to create the account.
### 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
View file

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 KiB

5
browser/.gitignore vendored
View file

@ -1,5 +0,0 @@
/dist
/package
/node_modules
.DS_Store
extension.tar.gz

View file

@ -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`

View file

@ -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

View file

@ -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.
![Dnote browser demo](assets/demo.gif)
## 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 519 KiB

View file

@ -1,100 +0,0 @@
/* Copyright (C) 2019 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'));

View file

@ -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"
}
}
}
}

View file

@ -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"
}
}
}
}

10448
browser/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -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.0",
"react": "^16.11.0",
"react-dom": "^16.11.0",
"react-redux": "^7.0.0",
"react-select": "^3.0.8",
"redux": "^4.0.4",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.2.0"
},
"devDependencies": {
"@babel/core": "^7.7.4",
"@babel/preset-env": "^7.7.4",
"@types/react": "^16.9.11",
"@types/react-dom": "^16.9.3",
"concurrently": "^5.0.0",
"del": "^5.0.0",
"gulp": "^4.0.0",
"gulp-if": "^3.0.0",
"gulp-imagemin": "^6.1.1",
"gulp-livereload": "^4.0.2",
"gulp-replace": "^1.0.0",
"gulp-zip": "^5.0.1",
"prettier": "^1.18.2",
"ts-loader": "^6.2.1",
"typescript": "^3.6.4",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.9"
}
}

View file

@ -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

View file

@ -1,3 +0,0 @@
#!/bin/bash
tar --exclude='./node_modules' --exclude='./package' --exclude='./dist' -zcvf extension.tar.gz * .eslintrc

View file

@ -1,21 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,24 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -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

View file

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -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>

View file

@ -1,118 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,46 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,117 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,29 +0,0 @@
/* Copyright (C) 2019 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>
);

View file

@ -1,239 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,37 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,54 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,108 +0,0 @@
/* Copyright (C) 2019 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="&#9679;&#9679;&#9679;&#9679;&#9679;&#9679;&#9679;&#9679;"
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&#39;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;

View file

@ -1,60 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,63 +0,0 @@
/* Copyright (C) 2019 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>
);

View file

@ -1,61 +0,0 @@
/* Copyright (C) 2019 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>
);

View file

@ -1,156 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,105 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,65 +0,0 @@
/* Copyright (C) 2019 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 = '';
}
);
});

View file

@ -1,46 +0,0 @@
/* Copyright (C) 2019 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
};
}

View file

@ -1,45 +0,0 @@
/* Copyright (C) 2019 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;
}
}

View file

@ -1,39 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,78 +0,0 @@
/* Copyright (C) 2019 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));
});
};
}

View file

@ -1,66 +0,0 @@
/* Copyright (C) 2019 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;
}
}

View file

@ -1,53 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,65 +0,0 @@
/* Copyright (C) 2019 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
};
}

View file

@ -1,65 +0,0 @@
/* Copyright (C) 2019 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;
}
}

View file

@ -1,57 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,43 +0,0 @@
/* Copyright (C) 2019 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);
}

View file

@ -1,70 +0,0 @@
/* Copyright (C) 2019 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))
);
}

View file

@ -1,26 +0,0 @@
/* Copyright (C) 2019 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 }
};
}

View file

@ -1,40 +0,0 @@
/* Copyright (C) 2019 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;
}
}

View file

@ -1,34 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,32 +0,0 @@
/* Copyright (C) 2019 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
};
}

View file

@ -1,42 +0,0 @@
/* Copyright (C) 2019 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;
}
}

View file

@ -1,38 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,43 +0,0 @@
/* Copyright (C) 2019 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
>;

View file

@ -1,23 +0,0 @@
/* Copyright (C) 2019 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__
};

View file

@ -1,55 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,66 +0,0 @@
/* Copyright (C) 2019 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
});
}

View file

@ -1,27 +0,0 @@
/* Copyright (C) 2019 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;

View file

@ -1,63 +0,0 @@
/* Copyright (C) 2019 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);
});
}

View file

@ -1,431 +0,0 @@
/* Copyright (C) 2019 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;
}

View file

@ -1,453 +0,0 @@
/* Copyright (C) 2019 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);
}
}

View file

@ -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/*"
]
}
}
}

View file

@ -1,76 +0,0 @@
/* Copyright (C) 2019 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
}
};
};

68
go.mod
View file

@ -1,41 +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/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
)

326
go.sum
View file

@ -1,267 +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/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=

View file

@ -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

View file

@ -1,28 +0,0 @@
# Dnote Docker Image
The official Dnote docker image.
## Installing Dnote Server Using Docker
*Installing Dnote through Docker is currently in beta.* For the an alternative installation guide, please see [the installation guide](https://github.com/dnote/dnote/blob/master/SELF_HOSTING.md).
### Steps
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.
```
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_HOST.md) for further configuration.

View file

@ -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
View file

@ -0,0 +1,9 @@
services:
dnote:
image: dnote/dnote:latest
container_name: dnote
ports:
- 3001:3001
volumes:
- ./dnote_data:/data
restart: unless-stopped

View file

@ -1,33 +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:
ports:
- 3000:3000
depends_on:
- postgres
restart: always

View file

@ -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 "$@"

View file

@ -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

View file

@ -1 +0,0 @@
/volume

View file

@ -1 +0,0 @@
This directory contains a smoke test for running a self-hosted instance using a virtual machine.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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! ========"

View file

@ -2,7 +2,7 @@
#
# This script installs Dnote into your PATH (/usr/bin/local)
# Use it like this:
# $ curl https://raw.githubusercontent.com/dnote/dnote/master/cli/install.sh | sh
# $ curl https://raw.githubusercontent.com/dnote/dnote/master/install.sh | sh
#
set -eu
@ -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
@ -131,6 +146,8 @@ verify_checksum() {
}
install_dnote() {
sudo_cmd=""
os=$(uname_os)
arch=$(uname_arch)
@ -147,6 +164,18 @@ install_dnote() {
tmpdir="$(mktemp -d)"
bindir=${bindir:-/usr/local/bin}
if hash sudo 2>/dev/null; then
sudo_cmd="sudo"
echo "You need a root privilege to install Dnote binary to $bindir"
if ! is_command "$sudo_cmd"; then
print_error "command not found: sudo. You need a root privilege to continue the installation."
exit 1;
fi
fi
# create destination directory if not exists
$sudo_cmd mkdir -p "$bindir"
# get the latest version
resp=$(http_get "https://api.github.com/repos/$owner/$repo/releases")
@ -181,13 +210,12 @@ install_dnote() {
print_step "Inflating the binary."
(cd "${tmpdir}" && tar -xzf "${tarball}")
install -d "${bindir}"
install "${tmpdir}/${binary}" "${bindir}/"
$sudo_cmd install -d "${bindir}"
$sudo_cmd install "${tmpdir}/${binary}" "${bindir}/"
print_success "dnote v${version} was successfully installed in $bindir."
}
exit_error() {
# shellcheck disable=SC2181
if [ "$?" -ne 0 ]; then

3
jslib/.gitignore vendored
View file

@ -1,3 +0,0 @@
/dist
/node_modules
/coverage

View file

@ -1,3 +0,0 @@
# jslib
Code shared between Dnote JavaScript projects.

View file

@ -1,4 +0,0 @@
module.exports = {
preset: 'ts-jest',
collectCoverageFrom: ['./src/**/*.ts']
};

4941
jslib/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,30 +0,0 @@
{
"name": "jslib",
"repository": "https://github.com/dnote/dnote",
"version": "0.1.0",
"description": "An internal JavaScript SDK for Dnote",
"types": "dist/types/index.d.ts",
"scripts": {
"clean": "rm -rf ./dist",
"build": "tsc",
"build:watch": "tsc --watch",
"test": "jest --coverage",
"test:watch": "jest --watch",
"lint": "../node_modules/.bin/eslint ./src --ext .ts,.tsx,.js"
},
"author": "Monomax Software Pty Ltd",
"license": "AGPL-3.0-or-later",
"dependencies": {
"@types/history": "^4.7.3",
"history": "^4.10.1",
"lodash": "^4.17.15",
"qs": "^6.9.1"
},
"devDependencies": {
"@types/jest": "^24.0.23",
"jest": "^24.9.0",
"prettier": "^1.19.1",
"ts-jest": "^24.2.0",
"typescript": "^3.7.3"
}
}

Some files were not shown because too many files have changed in this diff Show more