add oauth flow routes
This commit is contained in:
parent
369e8d3f6e
commit
6ff437be99
@ -1,4 +1,14 @@
|
|||||||
port: 10230
|
port: 10230
|
||||||
google:
|
google:
|
||||||
api_key: ''
|
api_key: ''
|
||||||
|
client_id: ''
|
||||||
|
auth_url: ''
|
||||||
|
token_url: ''
|
||||||
|
client_secret: ''
|
||||||
|
redirect_url: ''
|
||||||
|
scopes:
|
||||||
|
- https://www.googleapis.com/auth/calendar.readonly
|
||||||
|
- https://www.googleapis.com/auth/calendar.events.readonly
|
||||||
|
storage:
|
||||||
|
path: '/tmp/cal.json'
|
||||||
|
|
||||||
|
2
go.mod
2
go.mod
@ -6,6 +6,8 @@ require (
|
|||||||
git.trj.tw/golang/gofb v0.0.0-20191210044940-169b79d0cbe2
|
git.trj.tw/golang/gofb v0.0.0-20191210044940-169b79d0cbe2
|
||||||
git.trj.tw/golang/utils v0.0.0-20190225142552-b019626f0349
|
git.trj.tw/golang/utils v0.0.0-20190225142552-b019626f0349
|
||||||
github.com/fogleman/gg v1.3.0
|
github.com/fogleman/gg v1.3.0
|
||||||
|
github.com/gin-contrib/cors v1.3.0
|
||||||
|
github.com/gin-gonic/gin v1.5.0
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||||
github.com/joho/godotenv v1.3.0
|
github.com/joho/godotenv v1.3.0
|
||||||
github.com/otakukaze/envconfig v1.0.0
|
github.com/otakukaze/envconfig v1.0.0
|
||||||
|
52
go.sum
52
go.sum
@ -2,28 +2,80 @@ git.trj.tw/golang/gofb v0.0.0-20191210044940-169b79d0cbe2 h1:P/3jKmWVSaXU9TbvdYM
|
|||||||
git.trj.tw/golang/gofb v0.0.0-20191210044940-169b79d0cbe2/go.mod h1:UohCvL4t/inqH38WSGwHZ1jR4OfUKj0D8MJ42asQZXE=
|
git.trj.tw/golang/gofb v0.0.0-20191210044940-169b79d0cbe2/go.mod h1:UohCvL4t/inqH38WSGwHZ1jR4OfUKj0D8MJ42asQZXE=
|
||||||
git.trj.tw/golang/utils v0.0.0-20190225142552-b019626f0349 h1:V6ifeiJ3ExnjaUylTOz37n6z5uLwm6fjKjnztbTCaQI=
|
git.trj.tw/golang/utils v0.0.0-20190225142552-b019626f0349 h1:V6ifeiJ3ExnjaUylTOz37n6z5uLwm6fjKjnztbTCaQI=
|
||||||
git.trj.tw/golang/utils v0.0.0-20190225142552-b019626f0349/go.mod h1:yE+qbsUsijCTdwsaQRkPT1CXYk7ftMzXsCaaYx/0QI0=
|
git.trj.tw/golang/utils v0.0.0-20190225142552-b019626f0349/go.mod h1:yE+qbsUsijCTdwsaQRkPT1CXYk7ftMzXsCaaYx/0QI0=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
|
github.com/gin-contrib/cors v1.3.0 h1:PolezCc89peu+NgkIWt9OB01Kbzt6IP0J/JvkG6xxlg=
|
||||||
|
github.com/gin-contrib/cors v1.3.0/go.mod h1:artPvLlhkF7oG06nK8v3U8TNz6IeX+w1uzCSEId5/Vc=
|
||||||
|
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||||
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
|
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||||
|
github.com/gin-gonic/gin v1.5.0 h1:fi+bqFAx/oLK54somfCtEZs9HeH1LHVoEPUgARpTqyc=
|
||||||
|
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||||
|
github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc=
|
||||||
|
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
||||||
|
github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM=
|
||||||
|
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
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/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
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.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
|
||||||
|
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||||
|
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||||
|
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
|
||||||
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/otakukaze/envconfig v1.0.0 h1:VGu7NoDFbReFO72m5Z15h5PH5gVk5fVLZ6iHF58ggFk=
|
github.com/otakukaze/envconfig v1.0.0 h1:VGu7NoDFbReFO72m5Z15h5PH5gVk5fVLZ6iHF58ggFk=
|
||||||
github.com/otakukaze/envconfig v1.0.0/go.mod h1:v2dNv5NX1Lakw3FTAkbxYURyaiOy68M8QpMTZz+ogfs=
|
github.com/otakukaze/envconfig v1.0.0/go.mod h1:v2dNv5NX1Lakw3FTAkbxYURyaiOy68M8QpMTZz+ogfs=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
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/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.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/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||||
|
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||||
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
|
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||||
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
github.com/ungerik/go-cairo v0.0.0-20191014050614-4a03f432a432 h1:luu+HbKrZKlIjiaclwrldreEEvVPYX/ujRbTGkKz61c=
|
github.com/ungerik/go-cairo v0.0.0-20191014050614-4a03f432a432 h1:luu+HbKrZKlIjiaclwrldreEEvVPYX/ujRbTGkKz61c=
|
||||||
github.com/ungerik/go-cairo v0.0.0-20191014050614-4a03f432a432/go.mod h1:0ErpLiOxxE1oY+R4stiKut6/DbUJHnOp6U+e4d8zcTs=
|
github.com/ungerik/go-cairo v0.0.0-20191014050614-4a03f432a432/go.mod h1:0ErpLiOxxE1oY+R4stiKut6/DbUJHnOp6U+e4d8zcTs=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/image v0.0.0-20191206065243-da761ea9ff43 h1:gQ6GUSD102fPgli+Yb4cR/cGaHF7tNBt+GYoRCpGC7s=
|
golang.org/x/image v0.0.0-20191206065243-da761ea9ff43 h1:gQ6GUSD102fPgli+Yb4cR/cGaHF7tNBt+GYoRCpGC7s=
|
||||||
golang.org/x/image v0.0.0-20191206065243-da761ea9ff43/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20191206065243-da761ea9ff43/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
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-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||||
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||||
|
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||||
|
gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc=
|
||||||
|
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
16
main.go
16
main.go
@ -1,7 +1,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"go-cal/pkg/config"
|
"go-cal/pkg/config"
|
||||||
|
"go-cal/pkg/storage"
|
||||||
|
"go-cal/route/routes"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
_ "github.com/joho/godotenv/autoload"
|
_ "github.com/joho/godotenv/autoload"
|
||||||
@ -14,6 +17,19 @@ func main() {
|
|||||||
log.Fatalf("Load config file fail, %v\n", err)
|
log.Fatalf("Load config file fail, %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conf := config.Get()
|
||||||
|
s := storage.New()
|
||||||
|
if err = s.Load(conf.Storage.Path, true); err != nil {
|
||||||
|
log.Fatalf("load storage data from file fail : %v", err)
|
||||||
|
}
|
||||||
|
if err = s.Write(); err != nil {
|
||||||
|
log.Fatalf("write storage data to file fail : %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e := routes.NewEngine()
|
||||||
|
routes.SetRoutes(e)
|
||||||
|
|
||||||
|
e.Run(fmt.Sprintf(":%d", conf.Port))
|
||||||
// fb := gofb.NewFramebuffer()
|
// fb := gofb.NewFramebuffer()
|
||||||
// fb.Init()
|
// fb.Init()
|
||||||
// defer fb.Release()
|
// defer fb.Release()
|
||||||
|
@ -1,6 +1,17 @@
|
|||||||
package google
|
package google
|
||||||
|
|
||||||
import "net/url"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go-cal/pkg/config"
|
||||||
|
"go-cal/pkg/types"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
var baseURL = "https://www.googleapis.com/"
|
var baseURL = "https://www.googleapis.com/"
|
||||||
|
|
||||||
@ -22,3 +33,130 @@ func getHeaders() map[string]string {
|
|||||||
|
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OAuthExchangeCodeToToken -
|
||||||
|
func OAuthExchangeCodeToToken(code string) (token types.GoogleAuthToken, err error) {
|
||||||
|
conf := config.Get()
|
||||||
|
if conf == nil {
|
||||||
|
return token, errors.New("config not init")
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(conf.Google.TokenURL)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
qs := url.Values{}
|
||||||
|
qs.Set("code", code)
|
||||||
|
qs.Set("client_id", conf.Google.ClientID)
|
||||||
|
qs.Set("client_secret", conf.Google.ClientSecret)
|
||||||
|
qs.Set("redirect_uri", conf.Google.RedirectURL)
|
||||||
|
qs.Set("grant_type", "authorization_code")
|
||||||
|
|
||||||
|
reader := bytes.NewReader([]byte(qs.Encode()))
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", fmt.Sprintf("%s", u.String()), reader)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(b, &token)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// OAuthRefreshToken -
|
||||||
|
func OAuthRefreshToken(refreshToken string) (token string, expiresIn int64, err error) {
|
||||||
|
if len(refreshToken) == 0 {
|
||||||
|
return "", 0, errors.New("refresh token is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
conf := config.Get()
|
||||||
|
if conf == nil {
|
||||||
|
return "", 0, errors.New("config not init")
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(conf.Google.TokenURL)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
qs := url.Values{}
|
||||||
|
qs.Set("client_id", conf.Google.ClientID)
|
||||||
|
qs.Set("client_secret", conf.Google.ClientSecret)
|
||||||
|
qs.Set("grant_type", "refresh_token")
|
||||||
|
|
||||||
|
reader := bytes.NewReader([]byte(qs.Encode()))
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", u.String(), reader)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var tokenData types.GoogleAuthToken
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&tokenData)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tokenData.ExpiresIn += time.Now().Unix()
|
||||||
|
|
||||||
|
return tokenData.AccessToken, tokenData.ExpiresIn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTokenInfo -
|
||||||
|
func GetTokenInfo(token string) (expire int64, err error) {
|
||||||
|
u, err := url.Parse("https://oauth2.googleapis.com/tokeninfo")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
body := struct {
|
||||||
|
Azp string `json:"azp"`
|
||||||
|
Aud string `json:"aud"`
|
||||||
|
Scope string `json:"scope"`
|
||||||
|
Exp json.Number `json:"exp"`
|
||||||
|
ExpiresIn json.Number `json:"expires_in"`
|
||||||
|
AccessType string `json:"access_type"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
qs := url.Values{}
|
||||||
|
qs.Set("access_token", token)
|
||||||
|
|
||||||
|
resp, err := http.Get(fmt.Sprintf("%s?%s", u.String(), qs.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
err = json.NewDecoder(resp.Body).Decode(&body)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
expTime, err := body.Exp.Int64()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return expTime, nil
|
||||||
|
}
|
||||||
|
@ -14,12 +14,24 @@ import (
|
|||||||
// Google config
|
// Google config
|
||||||
type Google struct {
|
type Google struct {
|
||||||
APIKey string `yaml:"api_key" env:"GOOGLE_API_KEY"`
|
APIKey string `yaml:"api_key" env:"GOOGLE_API_KEY"`
|
||||||
|
ClientID string `yaml:"client_id" env:"GOOGLE_CLIENT_ID"`
|
||||||
|
AuthURL string `yaml:"auth_url" env:"GOOGLE_AUTH_URL"`
|
||||||
|
TokenURL string `yaml:"token_url" env:"GOOGLE_TOKEN_URL"`
|
||||||
|
ClientSecret string `yaml:"client_secret" env:"GOOGLE_CLIENT_SECRET"`
|
||||||
|
RedirectURL string `yaml:"redirect_url" env:"GOOGLE_REDIRECT_URL"`
|
||||||
|
Scopes []string `yaml:"scopes" env:"GOOGLE_SCOPES"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage struct
|
||||||
|
type Storage struct {
|
||||||
|
Path string `yaml:"path" env:"STORAGE_FILE_PATH"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config main struct
|
// Config main struct
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Port int `yaml:"port" env:"PORT"`
|
Port int `yaml:"port" env:"PORT"`
|
||||||
Google Google `yaml:"google"`
|
Google Google `yaml:"google"`
|
||||||
|
Storage Storage `yaml:"storage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var conf *Config
|
var conf *Config
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"git.trj.tw/golang/utils"
|
"git.trj.tw/golang/utils"
|
||||||
)
|
)
|
||||||
@ -12,6 +13,7 @@ import (
|
|||||||
type Storage struct {
|
type Storage struct {
|
||||||
m map[string]interface{}
|
m map[string]interface{}
|
||||||
filePath string
|
filePath string
|
||||||
|
sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
var s *Storage
|
var s *Storage
|
||||||
@ -30,24 +32,31 @@ func New() *Storage {
|
|||||||
func Get() *Storage { return s }
|
func Get() *Storage { return s }
|
||||||
|
|
||||||
// Load storage data from file
|
// Load storage data from file
|
||||||
func (p *Storage) Load(filePath string) (err error) {
|
func (p *Storage) Load(filePath string, ignoreNotExist bool) (err error) {
|
||||||
filePath = utils.ParsePath(filePath)
|
filePath = utils.ParsePath(filePath)
|
||||||
if !utils.CheckExists(filePath, false) {
|
exist := utils.CheckExists(filePath, false)
|
||||||
|
if !exist && !ignoreNotExist {
|
||||||
return errors.New("storage file not found")
|
return errors.New("storage file not found")
|
||||||
}
|
}
|
||||||
p.filePath = filePath
|
p.filePath = filePath
|
||||||
|
|
||||||
|
if exist {
|
||||||
b, err := ioutil.ReadFile(filePath)
|
b, err := ioutil.ReadFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal(b, &p.m)
|
if err := json.Unmarshal(b, &p.m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Storage) Write() (err error) {
|
func (p *Storage) Write() (err error) {
|
||||||
|
p.Lock()
|
||||||
|
defer p.Unlock()
|
||||||
b, err := json.Marshal(p.m)
|
b, err := json.Marshal(p.m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -64,7 +73,13 @@ func (p *Storage) Get(key string) (data interface{}, ok bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set data by key
|
// Set data by key
|
||||||
func (p *Storage) Set(key string, value interface{}) { p.m[key] = value }
|
func (p *Storage) Set(key string, value interface{}) {
|
||||||
|
p.m[key] = value
|
||||||
|
_ = p.Write()
|
||||||
|
}
|
||||||
|
|
||||||
// Delete data by key
|
// Delete data by key
|
||||||
func (p *Storage) Delete(key string) { delete(p.m, key) }
|
func (p *Storage) Delete(key string) {
|
||||||
|
delete(p.m, key)
|
||||||
|
_ = p.Write()
|
||||||
|
}
|
||||||
|
9
pkg/types/google.go
Normal file
9
pkg/types/google.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
// GoogleAuthToken -
|
||||||
|
type GoogleAuthToken struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
RefreshToken string `json:"refresh_token,omitempty"`
|
||||||
|
ExpiresIn int64 `json:"expires_in"`
|
||||||
|
TokenType string `json:"token_type"`
|
||||||
|
}
|
71
route/auth/google.go
Normal file
71
route/auth/google.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go-cal/pkg/apis/google"
|
||||||
|
"go-cal/pkg/config"
|
||||||
|
"go-cal/pkg/storage"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RedirectToAuth url
|
||||||
|
func RedirectToAuth(c *gin.Context) {
|
||||||
|
conf := config.Get()
|
||||||
|
if conf == nil {
|
||||||
|
c.AbortWithStatus(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(conf.Google.AuthURL)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = u
|
||||||
|
|
||||||
|
qs := url.Values{}
|
||||||
|
qs.Set("client_id", conf.Google.ClientID)
|
||||||
|
qs.Set("redirect_uri", conf.Google.RedirectURL)
|
||||||
|
qs.Set("scope", strings.Join(conf.Google.Scopes, " "))
|
||||||
|
qs.Set("access_type", "offline")
|
||||||
|
qs.Set("response_type", "code")
|
||||||
|
|
||||||
|
targetURL := u.String() + "?" + qs.Encode()
|
||||||
|
c.Redirect(http.StatusTemporaryRedirect, targetURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OAuthResponse -
|
||||||
|
func OAuthResponse(c *gin.Context) {
|
||||||
|
if respError := c.Query("error"); len(respError) > 0 {
|
||||||
|
c.AbortWithError(http.StatusOK, fmt.Errorf("Get error : %s", respError))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
code := c.Query("code")
|
||||||
|
if len(code) == 0 {
|
||||||
|
c.AbortWithError(http.StatusOK, fmt.Errorf("Get response code fail"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := google.OAuthExchangeCodeToToken(code)
|
||||||
|
if err != nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t := time.Now().Unix()
|
||||||
|
token.ExpiresIn += t
|
||||||
|
|
||||||
|
st := storage.Get()
|
||||||
|
if st == nil {
|
||||||
|
c.AbortWithError(http.StatusInternalServerError, fmt.Errorf("storage object not init"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
st.Set("google_token", token)
|
||||||
|
|
||||||
|
c.Redirect(http.StatusTemporaryRedirect, "/")
|
||||||
|
}
|
29
route/routes/routes.go
Normal file
29
route/routes/routes.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-cal/route/auth"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewEngine -
|
||||||
|
func NewEngine() *gin.Engine {
|
||||||
|
e := gin.New()
|
||||||
|
|
||||||
|
e.Use(gin.Logger())
|
||||||
|
e.Use(gin.Recovery())
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRoutes -
|
||||||
|
func SetRoutes(e *gin.Engine) {
|
||||||
|
e.GET("/health", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusOK, "ok")
|
||||||
|
})
|
||||||
|
|
||||||
|
e.GET("/auth", auth.RedirectToAuth)
|
||||||
|
|
||||||
|
e.GET("/auth/verify", auth.OAuthResponse)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user