Cleanup (#102)
* cleanup * cleanup go mod * Remove doc generation * add no integration test * fix broken test * rename workflow
103
.github/workflows/docs.yml
vendored
@@ -1,103 +0,0 @@
|
||||
name: Generate docs
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
name: Generate docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go 1.13
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.13
|
||||
id: go
|
||||
|
||||
- name: Install Protoc
|
||||
uses: arduino/setup-protoc@master
|
||||
|
||||
- name: Check out this code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: services
|
||||
|
||||
- name: Check out micro code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'micro/micro'
|
||||
path: 'micro'
|
||||
ref: 'master'
|
||||
|
||||
- name: Enable caching
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Install protoc gen micro plugin
|
||||
working-directory: micro/cmd/protoc-gen-micro
|
||||
run: |
|
||||
go get -u github.com/golang/protobuf/protoc-gen-go
|
||||
go install
|
||||
|
||||
- name: Install redoc cli
|
||||
run: |
|
||||
# https://github.com/actions/virtual-environments/issues/599
|
||||
sudo npm install -g redoc-cli
|
||||
|
||||
- name: Install openapi plugin
|
||||
working-directory: micro/cmd/protoc-gen-openapi
|
||||
run: |
|
||||
go install
|
||||
|
||||
- name: Install hugo
|
||||
run: sudo snap install hugo --channel=extended
|
||||
|
||||
- name: Generate openapi spec and html
|
||||
working-directory: services
|
||||
run: |
|
||||
go run cmd/docgen/main.go .
|
||||
env:
|
||||
MICRO_ADMIN_TOKEN: ${{ secrets.MICRO_ADMIN_TOKEN }}
|
||||
|
||||
- name: Deploy
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: s0/git-publish-subdir-action@develop
|
||||
env:
|
||||
REPO: self
|
||||
BRANCH: gh-pages
|
||||
FOLDER: services/docs
|
||||
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
||||
|
||||
- name: Generate package
|
||||
working-directory: services
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: |
|
||||
go run cmd/tsgen/main.go .
|
||||
|
||||
# publish to github first under micro/services
|
||||
# .npmrc has settings for it
|
||||
- uses: JS-DevTools/npm-publish@v1
|
||||
#if: github.ref == 'refs/heads/master'
|
||||
with:
|
||||
access: public
|
||||
package: services/clients/ts/package.json
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
# publish to npm m3o/services
|
||||
- name: Change npm settings
|
||||
working-directory: services
|
||||
run: |
|
||||
rm clients/ts/.npmrc
|
||||
sed -i 's/micro/m3o/g' clients/ts/package.json
|
||||
|
||||
- uses: JS-DevTools/npm-publish@v1
|
||||
#if: github.ref == 'refs/heads/master'
|
||||
with:
|
||||
access: public
|
||||
package: services/clients/ts/package.json
|
||||
token: ${{ secrets.NPM_SITE_TOKEN }}
|
||||
2
.github/workflows/integration.yml
vendored
@@ -49,7 +49,7 @@ jobs:
|
||||
run: |
|
||||
bash services/test/image/test-docker.sh
|
||||
|
||||
- name: Test Blog services
|
||||
- name: Test Services
|
||||
working-directory: services/test/integration
|
||||
run: |
|
||||
go clean -testcache && GOMAXPROCS=4 go test -timeout 15m --tags=integration -v ./...
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
FROM alpine
|
||||
ADD blog /blog
|
||||
ENTRYPOINT [ "/blog" ]
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
GOPATH:=$(shell go env GOPATH)
|
||||
.PHONY: init
|
||||
init:
|
||||
go get -u github.com/golang/protobuf/proto
|
||||
go get -u github.com/golang/protobuf/protoc-gen-go
|
||||
go get github.com/micro/micro/v3/cmd/protoc-gen-micro
|
||||
.PHONY: proto
|
||||
proto:
|
||||
protoc --openapi_out=. --proto_path=$$GOPATH/src:. --micro_out=. --go_out=:. proto/blog.proto
|
||||
|
||||
.PHONY: docs
|
||||
docs:
|
||||
protoc --openapi_out=. --proto_path=$$GOPATH/src:. --micro_out=. --go_out=:. proto/blog.proto
|
||||
@redoc-cli bundle api-blog.json
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go build -o blog *.go
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -v ./... -cover
|
||||
|
||||
.PHONY: docker
|
||||
docker:
|
||||
docker build . -t blog:latest
|
||||
@@ -1,51 +0,0 @@
|
||||
# Blog
|
||||
|
||||
This is a full end to end example of writing a multi-service blog application
|
||||
|
||||
## Usage
|
||||
|
||||
Check out the [blog tutorial](https://m3o.dev/tutorials/building-a-blog) on the developer docs.
|
||||
|
||||
## How it works
|
||||
|
||||
### Present
|
||||
|
||||
The blog services are designed so a user can deploy them to their own micro namespace, write content with their Micro account with commands like
|
||||
|
||||
```sh
|
||||
micro posts save --id=7 --tags=News,Finance --title="Breaking News" --content="The stock market has just crashed"
|
||||
```
|
||||
|
||||
and display content on their frontend by consuming the API:
|
||||
|
||||
```sh
|
||||
curl -H "Authorization: Bearer $MICRO_API_TOKEN" "Micro-Namespace: $NAMESPACE" https://api.m3o.com/tags/list
|
||||
|
||||
|
||||
{
|
||||
"tags": [
|
||||
{
|
||||
"type": "post-tag",
|
||||
"slug": "news",
|
||||
"title": "News",
|
||||
"count": "3"
|
||||
}
|
||||
]
|
||||
]
|
||||
```
|
||||
|
||||
There are no comments provided yet, just posts and tags.
|
||||
Access is governed by auth rules, ie. Posts List, Tags List is open, Posts Save requires a Micro login.
|
||||
|
||||
### Future possibilities
|
||||
|
||||
#### Enable non Micro users to write posts, comments
|
||||
|
||||
If we provide a user/login service (markedly different from auth, it can be a simple session based auth) to enable non Micro users to register, the following can be done:
|
||||
|
||||
- A user (let's call the user Alice from this point) launches posts, tags, login service in their namespace.
|
||||
- Alice opens up said endpoints
|
||||
- People (let's call them Yoga Pants Co and Drone Inc) hosting JS and HTML on Netlify or Github Pages could create accounts in the services hosted by Alice. In this way, Alice, by having a Micro account becomes a headless CMS provider. Multiple blogs can be created on top of Alice's service instances.
|
||||
|
||||
Questions:
|
||||
- How will Yoga Pants Co or Drone Inc pay Alice or M3O for the costs of their backend hosting?
|
||||
@@ -1,3 +0,0 @@
|
||||
package main
|
||||
|
||||
//go:generate make proto
|
||||
@@ -1,55 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
proto "github.com/micro/services/blog/proto"
|
||||
comments "github.com/micro/services/comments/proto"
|
||||
posts "github.com/micro/services/posts/proto"
|
||||
tags "github.com/micro/services/tags/proto"
|
||||
)
|
||||
|
||||
type Blog struct {
|
||||
ps posts.PostsService
|
||||
cs comments.CommentsService
|
||||
ts tags.TagsService
|
||||
}
|
||||
|
||||
func NewBlog(ps posts.PostsService,
|
||||
cs comments.CommentsService,
|
||||
ts tags.TagsService) *Blog {
|
||||
return &Blog{
|
||||
ps: ps,
|
||||
cs: cs,
|
||||
ts: ts,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Blog) Latest(ctx context.Context, req *proto.LatestRequest, rsp *proto.LatestResponse) error {
|
||||
resp, err := e.ps.Query(ctx, &posts.QueryRequest{Limit: 1})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(resp.Posts) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
rsp.Latest = resp.Posts[0]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Blog) Posts(ctx context.Context, req *proto.PostsRequest, rsp *proto.PostsResponse) error {
|
||||
resp, err := e.ps.Query(ctx, &posts.QueryRequest{
|
||||
Limit: req.Limit,
|
||||
Offset: req.Offset,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rsp.Posts = resp.Posts
|
||||
return nil
|
||||
}
|
||||
31
blog/main.go
@@ -1,31 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/micro/services/blog/handler"
|
||||
proto "github.com/micro/services/blog/proto"
|
||||
comments "github.com/micro/services/comments/proto"
|
||||
posts "github.com/micro/services/posts/proto"
|
||||
tags "github.com/micro/services/tags/proto"
|
||||
|
||||
"github.com/micro/micro/v3/service"
|
||||
"github.com/micro/micro/v3/service/logger"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create service
|
||||
srv := service.New(
|
||||
service.Name("blog"),
|
||||
)
|
||||
|
||||
// Register handler
|
||||
proto.RegisterBlogHandler(srv.Server(), handler.NewBlog(
|
||||
posts.NewPostsService("posts", srv.Client()),
|
||||
comments.NewCommentsService("comments", srv.Client()),
|
||||
tags.NewTagsService("tags", srv.Client()),
|
||||
))
|
||||
|
||||
// Run service
|
||||
if err := srv.Run(); err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
service blog
|
||||
@@ -1,206 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: proto/blog.proto
|
||||
|
||||
package blog
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
proto1 "github.com/micro/services/posts/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type LatestRequest struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *LatestRequest) Reset() { *m = LatestRequest{} }
|
||||
func (m *LatestRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*LatestRequest) ProtoMessage() {}
|
||||
func (*LatestRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_fc5203cdc85000bc, []int{0}
|
||||
}
|
||||
|
||||
func (m *LatestRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_LatestRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *LatestRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_LatestRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *LatestRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_LatestRequest.Merge(m, src)
|
||||
}
|
||||
func (m *LatestRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_LatestRequest.Size(m)
|
||||
}
|
||||
func (m *LatestRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_LatestRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_LatestRequest proto.InternalMessageInfo
|
||||
|
||||
type LatestResponse struct {
|
||||
Latest *proto1.Post `protobuf:"bytes,1,opt,name=latest,proto3" json:"latest,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *LatestResponse) Reset() { *m = LatestResponse{} }
|
||||
func (m *LatestResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*LatestResponse) ProtoMessage() {}
|
||||
func (*LatestResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_fc5203cdc85000bc, []int{1}
|
||||
}
|
||||
|
||||
func (m *LatestResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_LatestResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *LatestResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_LatestResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *LatestResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_LatestResponse.Merge(m, src)
|
||||
}
|
||||
func (m *LatestResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_LatestResponse.Size(m)
|
||||
}
|
||||
func (m *LatestResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_LatestResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_LatestResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *LatestResponse) GetLatest() *proto1.Post {
|
||||
if m != nil {
|
||||
return m.Latest
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PostsRequest struct {
|
||||
Limit int64 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"`
|
||||
Offset int64 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PostsRequest) Reset() { *m = PostsRequest{} }
|
||||
func (m *PostsRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*PostsRequest) ProtoMessage() {}
|
||||
func (*PostsRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_fc5203cdc85000bc, []int{2}
|
||||
}
|
||||
|
||||
func (m *PostsRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_PostsRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *PostsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_PostsRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *PostsRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_PostsRequest.Merge(m, src)
|
||||
}
|
||||
func (m *PostsRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_PostsRequest.Size(m)
|
||||
}
|
||||
func (m *PostsRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_PostsRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_PostsRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *PostsRequest) GetLimit() int64 {
|
||||
if m != nil {
|
||||
return m.Limit
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *PostsRequest) GetOffset() int64 {
|
||||
if m != nil {
|
||||
return m.Offset
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type PostsResponse struct {
|
||||
Posts []*proto1.Post `protobuf:"bytes,1,rep,name=posts,proto3" json:"posts,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PostsResponse) Reset() { *m = PostsResponse{} }
|
||||
func (m *PostsResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*PostsResponse) ProtoMessage() {}
|
||||
func (*PostsResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_fc5203cdc85000bc, []int{3}
|
||||
}
|
||||
|
||||
func (m *PostsResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_PostsResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *PostsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_PostsResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *PostsResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_PostsResponse.Merge(m, src)
|
||||
}
|
||||
func (m *PostsResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_PostsResponse.Size(m)
|
||||
}
|
||||
func (m *PostsResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_PostsResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_PostsResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *PostsResponse) GetPosts() []*proto1.Post {
|
||||
if m != nil {
|
||||
return m.Posts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*LatestRequest)(nil), "blog.LatestRequest")
|
||||
proto.RegisterType((*LatestResponse)(nil), "blog.LatestResponse")
|
||||
proto.RegisterType((*PostsRequest)(nil), "blog.PostsRequest")
|
||||
proto.RegisterType((*PostsResponse)(nil), "blog.PostsResponse")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("proto/blog.proto", fileDescriptor_fc5203cdc85000bc) }
|
||||
|
||||
var fileDescriptor_fc5203cdc85000bc = []byte{
|
||||
// 238 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x50, 0xb1, 0x4e, 0xc3, 0x30,
|
||||
0x10, 0x25, 0xb4, 0xc9, 0x70, 0xa5, 0x80, 0xae, 0x15, 0x8a, 0x32, 0x15, 0xb3, 0x30, 0xc5, 0x22,
|
||||
0xa8, 0x1b, 0x13, 0x33, 0x03, 0xca, 0x1f, 0x90, 0xc8, 0x0d, 0x96, 0x1c, 0x2e, 0xcd, 0xb9, 0x7c,
|
||||
0x3f, 0xaa, 0xcf, 0x91, 0x48, 0xb7, 0xf7, 0xde, 0xf9, 0xbd, 0x77, 0x3e, 0xb8, 0x1f, 0x46, 0xf2,
|
||||
0xa4, 0x1b, 0x47, 0x5d, 0x19, 0x20, 0x2e, 0xcf, 0xb8, 0x78, 0xe9, 0xac, 0xff, 0x3e, 0x35, 0x65,
|
||||
0x4b, 0xbd, 0xee, 0x6d, 0x3b, 0x92, 0x66, 0x33, 0xfe, 0xda, 0xd6, 0xb0, 0x1e, 0x88, 0x3d, 0x6b,
|
||||
0xf1, 0x05, 0x2c, 0x46, 0x75, 0x07, 0xeb, 0x8f, 0x2f, 0x6f, 0xd8, 0xd7, 0xe6, 0x78, 0x32, 0xec,
|
||||
0xd5, 0x1e, 0x6e, 0x27, 0x81, 0x07, 0xfa, 0x61, 0x83, 0x4f, 0x90, 0xb9, 0xa0, 0xe4, 0xc9, 0x2e,
|
||||
0x79, 0x5e, 0x55, 0xab, 0x52, 0x02, 0x3e, 0x89, 0x7d, 0x1d, 0x47, 0xea, 0x0d, 0x6e, 0xce, 0x9c,
|
||||
0x63, 0x0c, 0x6e, 0x21, 0x75, 0xb6, 0xb7, 0xe2, 0x59, 0xd4, 0x42, 0xf0, 0x01, 0x32, 0x3a, 0x1c,
|
||||
0xd8, 0xf8, 0xfc, 0x3a, 0xc8, 0x91, 0xa9, 0x0a, 0xd6, 0xd1, 0x1d, 0x3b, 0x1f, 0x21, 0x0d, 0x25,
|
||||
0x79, 0xb2, 0x5b, 0x5c, 0x56, 0xca, 0xa4, 0x3a, 0xc2, 0xf2, 0xdd, 0x51, 0x87, 0x7b, 0xc8, 0x64,
|
||||
0x61, 0xdc, 0x94, 0xe1, 0x22, 0xb3, 0xff, 0x14, 0xdb, 0xb9, 0x28, 0xf9, 0xea, 0x0a, 0x2b, 0x48,
|
||||
0x43, 0x25, 0xa2, 0x3c, 0xf8, 0xbf, 0x7d, 0xb1, 0x99, 0x69, 0x93, 0xa7, 0xc9, 0xc2, 0xcd, 0x5e,
|
||||
0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x04, 0x74, 0x9b, 0x75, 0x80, 0x01, 0x00, 0x00,
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: proto/blog.proto
|
||||
|
||||
package blog
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
_ "github.com/micro/services/posts/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
import (
|
||||
context "context"
|
||||
api "github.com/micro/micro/v3/service/api"
|
||||
client "github.com/micro/micro/v3/service/client"
|
||||
server "github.com/micro/micro/v3/service/server"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ api.Endpoint
|
||||
var _ context.Context
|
||||
var _ client.Option
|
||||
var _ server.Option
|
||||
|
||||
// Api Endpoints for Blog service
|
||||
|
||||
func NewBlogEndpoints() []*api.Endpoint {
|
||||
return []*api.Endpoint{}
|
||||
}
|
||||
|
||||
// Client API for Blog service
|
||||
|
||||
type BlogService interface {
|
||||
// Latest returns the latest blog post
|
||||
Latest(ctx context.Context, in *LatestRequest, opts ...client.CallOption) (*LatestResponse, error)
|
||||
// Posts returns all the posts
|
||||
Posts(ctx context.Context, in *PostsRequest, opts ...client.CallOption) (*PostsResponse, error)
|
||||
}
|
||||
|
||||
type blogService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewBlogService(name string, c client.Client) BlogService {
|
||||
return &blogService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *blogService) Latest(ctx context.Context, in *LatestRequest, opts ...client.CallOption) (*LatestResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Blog.Latest", in)
|
||||
out := new(LatestResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *blogService) Posts(ctx context.Context, in *PostsRequest, opts ...client.CallOption) (*PostsResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Blog.Posts", in)
|
||||
out := new(PostsResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Blog service
|
||||
|
||||
type BlogHandler interface {
|
||||
// Latest returns the latest blog post
|
||||
Latest(context.Context, *LatestRequest, *LatestResponse) error
|
||||
// Posts returns all the posts
|
||||
Posts(context.Context, *PostsRequest, *PostsResponse) error
|
||||
}
|
||||
|
||||
func RegisterBlogHandler(s server.Server, hdlr BlogHandler, opts ...server.HandlerOption) error {
|
||||
type blog interface {
|
||||
Latest(ctx context.Context, in *LatestRequest, out *LatestResponse) error
|
||||
Posts(ctx context.Context, in *PostsRequest, out *PostsResponse) error
|
||||
}
|
||||
type Blog struct {
|
||||
blog
|
||||
}
|
||||
h := &blogHandler{hdlr}
|
||||
return s.Handle(s.NewHandler(&Blog{h}, opts...))
|
||||
}
|
||||
|
||||
type blogHandler struct {
|
||||
BlogHandler
|
||||
}
|
||||
|
||||
func (h *blogHandler) Latest(ctx context.Context, in *LatestRequest, out *LatestResponse) error {
|
||||
return h.BlogHandler.Latest(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *blogHandler) Posts(ctx context.Context, in *PostsRequest, out *PostsResponse) error {
|
||||
return h.BlogHandler.Posts(ctx, in, out)
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package blog;
|
||||
|
||||
import "github.com/micro/services/posts/proto/posts.proto";
|
||||
|
||||
service Blog {
|
||||
// Latest returns the latest blog post
|
||||
rpc Latest(LatestRequest) returns (LatestResponse) {}
|
||||
// Posts returns all the posts
|
||||
rpc Posts(PostsRequest) returns (PostsResponse) {};
|
||||
}
|
||||
|
||||
message LatestRequest {}
|
||||
|
||||
message LatestResponse{
|
||||
posts.Post latest = 1;
|
||||
}
|
||||
|
||||
message PostsRequest {
|
||||
int64 limit = 1;
|
||||
int64 offset = 2;
|
||||
}
|
||||
|
||||
message PostsResponse {
|
||||
repeated posts.Post posts = 1;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
# Docgen
|
||||
|
||||
This scripts takes open api specs that are generated in each folder by `make proto` (see `api-users.json` and similar in each folder), and existing `README.md` and generates docs that appear on services.m3o.com.
|
||||
|
||||
The readmes are taken verbatim and autogenerated client call examples are appended to them to produce an output readme, so there is no need to write curl or micro cli or any other examples. Focus on the describing the service in the readmes.
|
||||
|
||||
Some rules on how to write protos so they nicely appear in the output of this script:
|
||||
|
||||
- The request types (eg. `LoginRequest`) comments will be taken and used as a description for the endpoint (eg. `Login`) itself. This might change.
|
||||
- The proto message field comments will be taken and displayed to craft them with care
|
||||
|
||||
To provide example values use the following format:
|
||||
|
||||
```shell
|
||||
// rss feed name
|
||||
// eg. a16z
|
||||
string name = 1;
|
||||
```
|
||||
|
||||
The part after the `eg. ` until the newline will be used as example value.
|
||||
@@ -1,462 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/stoewer/go-strcase"
|
||||
)
|
||||
|
||||
func saveMeta(service, readme, openapiJSON, examplesJSON string, pricing map[string]int64) error {
|
||||
client := &http.Client{}
|
||||
|
||||
apiSpec := map[string]interface{}{
|
||||
"name": service,
|
||||
"description": readme,
|
||||
"open_api_json": openapiJSON,
|
||||
"pricing": pricing,
|
||||
"examples_json": examplesJSON,
|
||||
}
|
||||
|
||||
//Encode the data
|
||||
postBody, _ := json.Marshal(map[string]interface{}{
|
||||
"api": apiSpec,
|
||||
})
|
||||
rbody := bytes.NewBuffer(postBody)
|
||||
|
||||
//Leverage Go's HTTP Post function to make request
|
||||
req, err := http.NewRequest("POST", "https://api.m3o.com/publicapi/Publish", rbody)
|
||||
|
||||
// Add auth headers here if needed
|
||||
req.Header.Add("Authorization", `Bearer `+os.Getenv("MICRO_ADMIN_TOKEN"))
|
||||
resp, err := client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
io.Copy(ioutil.Discard, resp.Body)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
postContentPath = "docs/hugo-tania/site/content/post"
|
||||
docsURL = "services.m3o.com"
|
||||
)
|
||||
|
||||
func main() {
|
||||
files, err := ioutil.ReadDir(os.Args[1])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
workDir, _ := os.Getwd()
|
||||
|
||||
docPath := filepath.Join(workDir, "docs")
|
||||
err = ioutil.WriteFile(filepath.Join(docPath, "CNAME"), []byte(docsURL), 0777)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to CNAME")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
if f.IsDir() && !strings.HasPrefix(f.Name(), ".") {
|
||||
serviceDir := filepath.Join(workDir, f.Name())
|
||||
serviceFiles, err := ioutil.ReadDir(serviceDir)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to read service dir", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
skip := false
|
||||
for _, serviceFile := range serviceFiles {
|
||||
if serviceFile.Name() == "skip" {
|
||||
skip = true
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Println("Processing folder", serviceDir)
|
||||
makeProto := exec.Command("make", "docs")
|
||||
makeProto.Dir = serviceDir
|
||||
fmt.Println(serviceDir)
|
||||
outp, err := makeProto.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Println("Failed to make docs", string(outp))
|
||||
os.Exit(1)
|
||||
}
|
||||
serviceName := f.Name()
|
||||
dat, err := ioutil.ReadFile(filepath.Join(serviceDir, "README.md"))
|
||||
if err != nil {
|
||||
fmt.Println("Failed to read readme", string(outp))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
contentDir := filepath.Join(workDir, postContentPath)
|
||||
err = os.MkdirAll(contentDir, 0777)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to create content dir", string(outp))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
apiJSON := filepath.Join(serviceDir, "api-"+serviceName+".json")
|
||||
js, err := ioutil.ReadFile(apiJSON)
|
||||
if err != nil {
|
||||
apiJSON := filepath.Join(serviceDir, "api-protobuf.json")
|
||||
js, err = ioutil.ReadFile(apiJSON)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to read json spec", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
spec := &openapi3.Swagger{}
|
||||
err = json.Unmarshal(js, &spec)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to unmarshal", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// not every service has examples
|
||||
examples, _ := ioutil.ReadFile(filepath.Join(serviceDir, "examples.json"))
|
||||
|
||||
pricingRaw, _ := ioutil.ReadFile(filepath.Join(serviceDir, "pricing.json"))
|
||||
pricing := map[string]int64{}
|
||||
if len(pricingRaw) > 0 {
|
||||
json.Unmarshal(pricingRaw, &pricing)
|
||||
}
|
||||
|
||||
err = saveMeta(serviceName, string(dat), string(js), string(examples), pricing)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to save data to publicapi service", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = saveSpec(dat, contentDir, serviceName, spec)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to save to spec file", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
openAPIDir := filepath.Join(docPath, serviceName, "api")
|
||||
err = os.MkdirAll(openAPIDir, 0777)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to create api folder", string(outp))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = CopyFile(filepath.Join(serviceDir, "redoc-static.html"), filepath.Join(docPath, serviceName, "api", "index.html"))
|
||||
if err != nil {
|
||||
fmt.Println("Failed to copy redoc", string(outp))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cmd := exec.Command("hugo", "-D", "-d", "../../")
|
||||
cmd.Dir = filepath.Join(docPath, "hugo-tania", "site")
|
||||
outp, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Println("Build hugo site", string(outp))
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type specType struct {
|
||||
name string
|
||||
tag string
|
||||
includeReadme bool
|
||||
filePostFix string
|
||||
titlePostFix string
|
||||
template string
|
||||
}
|
||||
|
||||
var specTypes = []specType{
|
||||
{
|
||||
name: "default markdown",
|
||||
tag: "Readme",
|
||||
filePostFix: ".md",
|
||||
template: defTempl,
|
||||
includeReadme: true,
|
||||
},
|
||||
{
|
||||
name: "microjs markdown",
|
||||
tag: "Micro.js",
|
||||
filePostFix: "-microjs.md",
|
||||
titlePostFix: " Micro.js",
|
||||
template: microJSTempl,
|
||||
includeReadme: false,
|
||||
},
|
||||
}
|
||||
|
||||
var servicesToTags = map[string][]string{
|
||||
"users": []string{"Backend"},
|
||||
"helloworld": []string{"Backend"},
|
||||
"emails": []string{"Communications"},
|
||||
"sms": []string{"Communications"},
|
||||
"posts": []string{"Headless CMS"},
|
||||
"tags": []string{"Headless CMS"},
|
||||
"feeds": []string{"Headless CMS"},
|
||||
"datastore": []string{"Backend"},
|
||||
"geocoding": []string{"Logistics"},
|
||||
"places": []string{"Logistics"},
|
||||
"routing": []string{"Logistics"},
|
||||
"etas": []string{"Logistics"},
|
||||
"notes": []string{"Misc"},
|
||||
"messages": []string{"Misc"},
|
||||
}
|
||||
|
||||
func saveSpec(originalMarkDown []byte, contentDir, serviceName string, spec *openapi3.Swagger) error {
|
||||
for _, v := range specTypes {
|
||||
fmt.Println("Processing ", v.name)
|
||||
contentFile := filepath.Join(contentDir, serviceName+v.filePostFix)
|
||||
var app []byte
|
||||
if v.includeReadme {
|
||||
app = originalMarkDown
|
||||
}
|
||||
tags := []string{v.tag}
|
||||
serviceTags, ok := servicesToTags[serviceName]
|
||||
if ok {
|
||||
tags = append(tags, serviceTags...)
|
||||
}
|
||||
tagsString := "\n- " + strings.Join(tags, "\n- ")
|
||||
|
||||
err := ioutil.WriteFile(contentFile, append([]byte("---\ntitle: "+serviceName+v.titlePostFix+"\nservicename: "+serviceName+"\nlabels: "+tagsString+"\n---\n"), app...), 0777)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to write post content to %v:\n%v\n", contentFile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fi, err := os.OpenFile(contentFile, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpl, err := template.New("test").Funcs(template.FuncMap{
|
||||
"params": func(p openapi3.Parameters) string {
|
||||
ls := ""
|
||||
for _, v := range p {
|
||||
//if v.Value.In == "body" {
|
||||
bs, _ := v.MarshalJSON()
|
||||
ls += string(bs) + ", "
|
||||
//}
|
||||
}
|
||||
return ls
|
||||
},
|
||||
// @todo should take SpecRef here not RequestBodyRef
|
||||
"schemaJSON": func(prepend int, ref string) string {
|
||||
for k, v := range spec.Components.Schemas {
|
||||
// ie. #/components/requestBodies/PostsSaveRequest contains
|
||||
// SaveRequest, can't see any other way to correlate
|
||||
if strings.HasSuffix(ref, k) {
|
||||
bs, _ := json.MarshalIndent(schemaToMap(v, spec.Components.Schemas), "", strings.Repeat(" ", prepend)+" ")
|
||||
// last line wont get prepended so we fix that here
|
||||
parts := strings.Split(string(bs), "\n")
|
||||
// skip if it's only 1 line, ie it's '{}'
|
||||
if len(parts) <= 1 {
|
||||
return string(bs)
|
||||
}
|
||||
parts[len(parts)-1] = parts[len(parts)-1]
|
||||
return strings.Join(parts, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
return "Schema related to " + ref + " not found"
|
||||
|
||||
},
|
||||
"schemaDescription": func(ref string) string {
|
||||
for k, v := range spec.Components.Schemas {
|
||||
// ie. #/components/requestBodies/PostsSaveRequest contains
|
||||
// SaveRequest, can't see any other way to correlate
|
||||
if strings.HasSuffix(ref, k) {
|
||||
return v.Value.Description
|
||||
}
|
||||
}
|
||||
|
||||
return "Schema related to " + ref + " not found"
|
||||
},
|
||||
// turn chat/Chat/History
|
||||
// to Chat History
|
||||
"titleize": func(s string) string {
|
||||
parts := strings.Split(s, "/")
|
||||
if len(parts) > 2 {
|
||||
return strings.Join(parts[2:], " ")
|
||||
}
|
||||
return strings.Join(parts, " ")
|
||||
},
|
||||
"firstResponseRef": func(rs openapi3.Responses) string {
|
||||
return rs.Get(200).Ref
|
||||
},
|
||||
}).Parse(v.template)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = tmpl.Execute(fi, spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func schemaToMap(spec *openapi3.SchemaRef, schemas map[string]*openapi3.SchemaRef) map[string]interface{} {
|
||||
var recurse func(props map[string]*openapi3.SchemaRef) map[string]interface{}
|
||||
getAtomic := func(v *openapi3.SchemaRef) interface{} {
|
||||
switch v.Value.Type {
|
||||
case "string":
|
||||
if len(v.Value.Description) > 0 {
|
||||
return strings.Replace(v.Value.Description, "\n", ".", -1)
|
||||
} else {
|
||||
return v.Value.Type
|
||||
}
|
||||
case "number":
|
||||
return 1
|
||||
case "boolean":
|
||||
return true
|
||||
}
|
||||
return "UNKOWN TYPE " + v.Value.Type
|
||||
}
|
||||
recurse = func(props map[string]*openapi3.SchemaRef) map[string]interface{} {
|
||||
ret := map[string]interface{}{}
|
||||
for k, v := range props {
|
||||
k = strcase.SnakeCase(k)
|
||||
|
||||
if v.Value.Type == "object" {
|
||||
ret[k] = recurse(v.Value.Properties)
|
||||
continue
|
||||
}
|
||||
if v.Value.Type == "array" {
|
||||
if v.Value.Items.Value.Type != "object" {
|
||||
ret[k] = []interface{}{getAtomic(v.Value.Items)}
|
||||
} else {
|
||||
ret[k] = []interface{}{recurse(v.Value.Items.Value.Properties)}
|
||||
}
|
||||
continue
|
||||
}
|
||||
ret[k] = getAtomic(v)
|
||||
|
||||
}
|
||||
return ret
|
||||
}
|
||||
return recurse(spec.Value.Properties)
|
||||
}
|
||||
|
||||
const defTempl = `
|
||||
## cURL
|
||||
|
||||
{{ range $key, $value := .Paths }}
|
||||
### {{ $key | titleize }}
|
||||
<!-- We use the request body description here as endpoint descriptions are not
|
||||
being lifted correctly from the proto by the openapi spec generator -->
|
||||
{{ $value.Post.RequestBody.Ref | schemaDescription }}
|
||||
` + "```" + `shell
|
||||
> curl 'https://api.m3o.com{{ $key }}' \
|
||||
-H 'micro-namespace: $yourNamespace' \
|
||||
-H 'authorization: Bearer $yourToken' \
|
||||
-d {{ $value.Post.RequestBody.Ref | schemaJSON 0 }};
|
||||
# Response
|
||||
{{ $value.Post.Responses | firstResponseRef | schemaJSON 0 }}
|
||||
` + "```" + `
|
||||
|
||||
{{ end }}
|
||||
`
|
||||
|
||||
const microJSTempl = `
|
||||
## Micro.js
|
||||
|
||||
{{ range $key, $value := .Paths }}
|
||||
### {{ $key | titleize }}
|
||||
<!-- We use the request body description here as endpoint descriptions are not
|
||||
being lifted correctly from the proto by the openapi spec generator -->
|
||||
{{ $value.Post.RequestBody.Ref | schemaDescription }}
|
||||
` + "```" + `html
|
||||
<script src="https://web.m3o.com/assets/micro.js"></script>
|
||||
<script type="text/javascript">
|
||||
document.addEventListener("DOMContentLoaded", function (event) {
|
||||
// Login is only required for endpoints doing authorization
|
||||
Micro.requireLogin(function () {
|
||||
Micro.post(
|
||||
"{{ $key }}",
|
||||
"micro",
|
||||
{{ $value.Post.RequestBody.Ref | schemaJSON 8 }},
|
||||
function (data) {
|
||||
console.log("Success.");
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
` + "```" + `
|
||||
|
||||
{{ end }}
|
||||
`
|
||||
|
||||
// CopyFile copies a file from src to dst. If src and dst files exist, and are
|
||||
// the same, then return success. Otherise, attempt to create a hard link
|
||||
// between the two files. If that fail, copy the file contents from src to dst.
|
||||
// from https://stackoverflow.com/questions/21060945/simple-way-to-copy-a-file-in-golang
|
||||
func CopyFile(src, dst string) (err error) {
|
||||
sfi, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !sfi.Mode().IsRegular() {
|
||||
// cannot copy non-regular files (e.g., directories,
|
||||
// symlinks, devices, etc.)
|
||||
return fmt.Errorf("CopyFile: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String())
|
||||
}
|
||||
dfi, err := os.Stat(dst)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if !(dfi.Mode().IsRegular()) {
|
||||
return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String())
|
||||
}
|
||||
if os.SameFile(sfi, dfi) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = os.Link(src, dst); err == nil {
|
||||
return
|
||||
}
|
||||
err = copyFileContents(src, dst)
|
||||
return
|
||||
}
|
||||
|
||||
// copyFileContents copies the contents of the file named src to the file named
|
||||
// by dst. The file will be created if it does not already exist. If the
|
||||
// destination file exists, all it's contents will be replaced by the contents
|
||||
// of the source file.
|
||||
func copyFileContents(src, dst string) (err error) {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer in.Close()
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
cerr := out.Close()
|
||||
if err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
if _, err = io.Copy(out, in); err != nil {
|
||||
return
|
||||
}
|
||||
err = out.Sync()
|
||||
return
|
||||
}
|
||||
2
comments/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
|
||||
comment-service
|
||||
@@ -1,3 +0,0 @@
|
||||
FROM alpine
|
||||
ADD comments-service /comments-service
|
||||
ENTRYPOINT [ "/comments-service" ]
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
GOPATH:=$(shell go env GOPATH)
|
||||
MODIFY=Mgithub.com/micro/micro/proto/api/api.proto=github.com/micro/micro/v3/proto/api
|
||||
|
||||
.PHONY: proto
|
||||
proto:
|
||||
|
||||
protoc --openapi_out=. --proto_path=. --micro_out=${MODIFY}:. --go_out=${MODIFY}:. proto/comments.proto
|
||||
|
||||
.PHONY: docs
|
||||
docs:
|
||||
|
||||
protoc --openapi_out=. --proto_path=. --micro_out=${MODIFY}:. --go_out=${MODIFY}:. proto/comments.proto
|
||||
@redoc-cli bundle api-comments.json
|
||||
|
||||
.PHONY: build
|
||||
build: proto
|
||||
|
||||
go build -o comments-service *.go
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -v ./... -cover
|
||||
|
||||
.PHONY: docker
|
||||
docker:
|
||||
docker build . -t comments-service:latest
|
||||
@@ -1,3 +0,0 @@
|
||||
# Comments Service
|
||||
|
||||
Still yet to implement
|
||||
@@ -1,3 +0,0 @@
|
||||
package main
|
||||
|
||||
//go:generate make proto
|
||||
@@ -1,46 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/micro/dev/model"
|
||||
"github.com/micro/micro/v3/service/store"
|
||||
pb "github.com/micro/services/comments/proto"
|
||||
)
|
||||
|
||||
type Comments struct {
|
||||
comments model.Model
|
||||
idIndex model.Index
|
||||
postIndex model.Index
|
||||
}
|
||||
|
||||
func NewComments() *Comments {
|
||||
postIndex := model.ByEquality("post")
|
||||
postIndex.Order.Type = model.OrderTypeDesc
|
||||
postIndex.Order.FieldName = "created"
|
||||
|
||||
idIndex := model.ByEquality("id")
|
||||
idIndex.Order.Type = model.OrderTypeUnordered
|
||||
|
||||
return &Comments{
|
||||
comments: model.New(store.DefaultStore, "users", model.Indexes(postIndex), nil),
|
||||
postIndex: postIndex,
|
||||
idIndex: idIndex,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Comments) New(ctx context.Context, req *pb.NewRequest, rsp *pb.NewResponse) error {
|
||||
return c.comments.Save(pb.Comment{
|
||||
Id: uuid.New().String(),
|
||||
Post: req.Post,
|
||||
Author: req.Author,
|
||||
Message: req.Message,
|
||||
Created: time.Now().Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Comments) List(ctx context.Context, req *pb.ListRequest, rsp *pb.ListResponse) error {
|
||||
return c.comments.List(c.postIndex.ToQuery(req.Post), &rsp.Comments)
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/micro/micro/v3/service"
|
||||
"github.com/micro/micro/v3/service/logger"
|
||||
"github.com/micro/services/comments/handler"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create the service
|
||||
srv := service.New(
|
||||
service.Name("comments"),
|
||||
)
|
||||
|
||||
// Register Handler
|
||||
srv.Handle(handler.NewComments())
|
||||
|
||||
// Run service
|
||||
if err := srv.Run(); err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
service comments
|
||||
@@ -1,459 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.15.5
|
||||
// source: proto/comments.proto
|
||||
|
||||
package comments
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
type Comment struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Post string `protobuf:"bytes,2,opt,name=post,proto3" json:"post,omitempty"`
|
||||
Author string `protobuf:"bytes,3,opt,name=author,proto3" json:"author,omitempty"`
|
||||
Message string `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"`
|
||||
Created int64 `protobuf:"varint,5,opt,name=created,proto3" json:"created,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Comment) Reset() {
|
||||
*x = Comment{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_comments_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Comment) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Comment) ProtoMessage() {}
|
||||
|
||||
func (x *Comment) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_comments_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Comment.ProtoReflect.Descriptor instead.
|
||||
func (*Comment) Descriptor() ([]byte, []int) {
|
||||
return file_proto_comments_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Comment) GetId() string {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Comment) GetPost() string {
|
||||
if x != nil {
|
||||
return x.Post
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Comment) GetAuthor() string {
|
||||
if x != nil {
|
||||
return x.Author
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Comment) GetMessage() string {
|
||||
if x != nil {
|
||||
return x.Message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Comment) GetCreated() int64 {
|
||||
if x != nil {
|
||||
return x.Created
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type NewRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// post id
|
||||
Post string `protobuf:"bytes,1,opt,name=post,proto3" json:"post,omitempty"`
|
||||
// message to leave
|
||||
Author string `protobuf:"bytes,2,opt,name=author,proto3" json:"author,omitempty"`
|
||||
Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"`
|
||||
}
|
||||
|
||||
func (x *NewRequest) Reset() {
|
||||
*x = NewRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_comments_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *NewRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*NewRequest) ProtoMessage() {}
|
||||
|
||||
func (x *NewRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_comments_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use NewRequest.ProtoReflect.Descriptor instead.
|
||||
func (*NewRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_comments_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *NewRequest) GetPost() string {
|
||||
if x != nil {
|
||||
return x.Post
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *NewRequest) GetAuthor() string {
|
||||
if x != nil {
|
||||
return x.Author
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *NewRequest) GetMessage() string {
|
||||
if x != nil {
|
||||
return x.Message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type NewResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *NewResponse) Reset() {
|
||||
*x = NewResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_comments_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *NewResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*NewResponse) ProtoMessage() {}
|
||||
|
||||
func (x *NewResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_comments_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use NewResponse.ProtoReflect.Descriptor instead.
|
||||
func (*NewResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_comments_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
type ListRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Post string `protobuf:"bytes,1,opt,name=post,proto3" json:"post,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ListRequest) Reset() {
|
||||
*x = ListRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_comments_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ListRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ListRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_comments_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ListRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_comments_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *ListRequest) GetPost() string {
|
||||
if x != nil {
|
||||
return x.Post
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ListResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Comments []*Comment `protobuf:"bytes,1,rep,name=comments,proto3" json:"comments,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ListResponse) Reset() {
|
||||
*x = ListResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_comments_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ListResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ListResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_comments_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ListResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_comments_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *ListResponse) GetComments() []*Comment {
|
||||
if x != nil {
|
||||
return x.Comments
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_proto_comments_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_proto_comments_proto_rawDesc = []byte{
|
||||
0x0a, 0x14, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73,
|
||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73,
|
||||
0x22, 0x79, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69,
|
||||
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70,
|
||||
0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x12,
|
||||
0x16, 0x0a, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,
|
||||
0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
|
||||
0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01,
|
||||
0x28, 0x03, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x22, 0x52, 0x0a, 0x0a, 0x4e,
|
||||
0x65, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x73,
|
||||
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x73, 0x74, 0x12, 0x16, 0x0a,
|
||||
0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61,
|
||||
0x75, 0x74, 0x68, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22,
|
||||
0x0d, 0x0a, 0x0b, 0x4e, 0x65, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21,
|
||||
0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x70, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6f, 0x73,
|
||||
0x74, 0x22, 0x3d, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x2d, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20,
|
||||
0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x43,
|
||||
0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73,
|
||||
0x32, 0x79, 0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x34, 0x0a, 0x03,
|
||||
0x4e, 0x65, 0x77, 0x12, 0x14, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4e,
|
||||
0x65, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
|
||||
0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4e, 0x65, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x00, 0x12, 0x37, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x15, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x16, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x4c, 0x69, 0x73,
|
||||
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x10, 0x5a, 0x0e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x62, 0x06, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_proto_comments_proto_rawDescOnce sync.Once
|
||||
file_proto_comments_proto_rawDescData = file_proto_comments_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_proto_comments_proto_rawDescGZIP() []byte {
|
||||
file_proto_comments_proto_rawDescOnce.Do(func() {
|
||||
file_proto_comments_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_comments_proto_rawDescData)
|
||||
})
|
||||
return file_proto_comments_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_proto_comments_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
|
||||
var file_proto_comments_proto_goTypes = []interface{}{
|
||||
(*Comment)(nil), // 0: comments.Comment
|
||||
(*NewRequest)(nil), // 1: comments.NewRequest
|
||||
(*NewResponse)(nil), // 2: comments.NewResponse
|
||||
(*ListRequest)(nil), // 3: comments.ListRequest
|
||||
(*ListResponse)(nil), // 4: comments.ListResponse
|
||||
}
|
||||
var file_proto_comments_proto_depIdxs = []int32{
|
||||
0, // 0: comments.ListResponse.comments:type_name -> comments.Comment
|
||||
1, // 1: comments.Comments.New:input_type -> comments.NewRequest
|
||||
3, // 2: comments.Comments.List:input_type -> comments.ListRequest
|
||||
2, // 3: comments.Comments.New:output_type -> comments.NewResponse
|
||||
4, // 4: comments.Comments.List:output_type -> comments.ListResponse
|
||||
3, // [3:5] is the sub-list for method output_type
|
||||
1, // [1:3] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_proto_comments_proto_init() }
|
||||
func file_proto_comments_proto_init() {
|
||||
if File_proto_comments_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_proto_comments_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Comment); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_comments_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*NewRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_comments_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*NewResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_comments_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ListRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_comments_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ListResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_proto_comments_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 5,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_proto_comments_proto_goTypes,
|
||||
DependencyIndexes: file_proto_comments_proto_depIdxs,
|
||||
MessageInfos: file_proto_comments_proto_msgTypes,
|
||||
}.Build()
|
||||
File_proto_comments_proto = out.File
|
||||
file_proto_comments_proto_rawDesc = nil
|
||||
file_proto_comments_proto_goTypes = nil
|
||||
file_proto_comments_proto_depIdxs = nil
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
// Code generated by protoc-gen-micro. DO NOT EDIT.
|
||||
// source: proto/comments.proto
|
||||
|
||||
package comments
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
import (
|
||||
context "context"
|
||||
api "github.com/micro/micro/v3/service/api"
|
||||
client "github.com/micro/micro/v3/service/client"
|
||||
server "github.com/micro/micro/v3/service/server"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ api.Endpoint
|
||||
var _ context.Context
|
||||
var _ client.Option
|
||||
var _ server.Option
|
||||
|
||||
// Api Endpoints for Comments service
|
||||
|
||||
func NewCommentsEndpoints() []*api.Endpoint {
|
||||
return []*api.Endpoint{}
|
||||
}
|
||||
|
||||
// Client API for Comments service
|
||||
|
||||
type CommentsService interface {
|
||||
New(ctx context.Context, in *NewRequest, opts ...client.CallOption) (*NewResponse, error)
|
||||
List(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error)
|
||||
}
|
||||
|
||||
type commentsService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewCommentsService(name string, c client.Client) CommentsService {
|
||||
return &commentsService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *commentsService) New(ctx context.Context, in *NewRequest, opts ...client.CallOption) (*NewResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Comments.New", in)
|
||||
out := new(NewResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *commentsService) List(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Comments.List", in)
|
||||
out := new(ListResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Comments service
|
||||
|
||||
type CommentsHandler interface {
|
||||
New(context.Context, *NewRequest, *NewResponse) error
|
||||
List(context.Context, *ListRequest, *ListResponse) error
|
||||
}
|
||||
|
||||
func RegisterCommentsHandler(s server.Server, hdlr CommentsHandler, opts ...server.HandlerOption) error {
|
||||
type comments interface {
|
||||
New(ctx context.Context, in *NewRequest, out *NewResponse) error
|
||||
List(ctx context.Context, in *ListRequest, out *ListResponse) error
|
||||
}
|
||||
type Comments struct {
|
||||
comments
|
||||
}
|
||||
h := &commentsHandler{hdlr}
|
||||
return s.Handle(s.NewHandler(&Comments{h}, opts...))
|
||||
}
|
||||
|
||||
type commentsHandler struct {
|
||||
CommentsHandler
|
||||
}
|
||||
|
||||
func (h *commentsHandler) New(ctx context.Context, in *NewRequest, out *NewResponse) error {
|
||||
return h.CommentsHandler.New(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *commentsHandler) List(ctx context.Context, in *ListRequest, out *ListResponse) error {
|
||||
return h.CommentsHandler.List(ctx, in, out)
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package comments;
|
||||
option go_package = "./proto;comments";
|
||||
|
||||
service Comments {
|
||||
rpc New(NewRequest) returns (NewResponse) {}
|
||||
rpc List(ListRequest) returns (ListResponse) {}
|
||||
}
|
||||
|
||||
message Comment {
|
||||
string id = 1;
|
||||
string post = 2;
|
||||
string author = 3;
|
||||
string message = 4;
|
||||
int64 created = 5;
|
||||
}
|
||||
|
||||
message NewRequest {
|
||||
// post id
|
||||
string post = 1;
|
||||
// message to leave
|
||||
string author = 2;
|
||||
string message = 3;
|
||||
}
|
||||
|
||||
message NewResponse {}
|
||||
|
||||
message ListRequest{
|
||||
string post = 1;
|
||||
}
|
||||
|
||||
message ListResponse{
|
||||
repeated Comment comments = 1;
|
||||
}
|
||||
1
docs/hugo-tania/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
exampleSite/resources
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 WingLim
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,84 +0,0 @@
|
||||
# Hugo Theme Tania
|
||||
|
||||
A simple theme for bloggers.
|
||||
|
||||
## Demo
|
||||
|
||||
[Example Site](https://hugo-tania.netlify.app/)
|
||||
|
||||
[](https://app.netlify.com/sites/hugo-tania/deploys)
|
||||
|
||||
## Introduction
|
||||
Most of the styles for this theme come from [taniarascia.com](https://github.com/taniarascia/taniarascia.com)
|
||||
|
||||
I like it's style, so I transplant it to Hugo.
|
||||
|
||||
And is that why this theme called Tania.
|
||||
|
||||
Thank Tania Rascia again.
|
||||
|
||||
Here is some features:
|
||||
|
||||
- Dark mode(It can switch automatically or manually)
|
||||
- Footnotes(Float on the right side)
|
||||
|
||||
## Usage
|
||||
|
||||
### Installation
|
||||
|
||||
In your site's root dir
|
||||
|
||||
```bash
|
||||
git submodule add https://github.com/WingLim/hugo-tania themes/hugo-tania
|
||||
```
|
||||
|
||||
Edit your site config following `site/config.yaml`.
|
||||
|
||||
### Params
|
||||
|
||||
`titleEmoji` will show before the blog title on site navbar.
|
||||
|
||||
```yaml
|
||||
titleEmoji: '😎'
|
||||
```
|
||||
|
||||
`socialOptions` will show on index bio with `_index.md` content.
|
||||
Account with icon can set as below:
|
||||
```yaml
|
||||
socialOptions:
|
||||
dev-to:
|
||||
facebook:
|
||||
github:
|
||||
instagram:
|
||||
linkedin:
|
||||
medium:
|
||||
stack-overflow:
|
||||
telegram:
|
||||
twitter:
|
||||
twitch:
|
||||
whatsapp:
|
||||
```
|
||||
|
||||
### Layout
|
||||
|
||||
`articles` layout is for showing all articles you write.
|
||||
|
||||
Add `articles.md` to site `content` dir, and write as below:
|
||||
|
||||
```markdown
|
||||
---
|
||||
title: Articles
|
||||
subtitle: Posts, tutorials, snippets, musings, and everything else.
|
||||
date: 2020-11-26
|
||||
type: section
|
||||
layout: "archives"
|
||||
---
|
||||
```
|
||||
|
||||
## Thanks to
|
||||
- [你好黑暗,我的老朋友 —— 为网站添加用户友好的深色模式支持](https://blog.skk.moe/post/hello-darkmode-my-old-friend/)
|
||||
- [Footnotes, citations, and sidenotes](https://prose.yihui.org/about/#footnotes-citations-and-sidenotes)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/WingLim/hugo-tania/blob/main/LICENSE)
|
||||
@@ -1,171 +0,0 @@
|
||||
renderFootnotes = function () {
|
||||
const removeEl = (el) => {
|
||||
if (!el) return;
|
||||
el.remove ? el.remove() : el.parentNode.removeChild(el);
|
||||
};
|
||||
|
||||
const insertAfter = (target, sib) => {
|
||||
target.after ? target.after(sib) : (
|
||||
target.parentNode.insertBefore(sib, target.nextSibling)
|
||||
);
|
||||
};
|
||||
|
||||
const insideOut = (el) => {
|
||||
var p = el.parentNode, x = el.innerHTML,
|
||||
c = document.createElement('div'); // a tmp container
|
||||
insertAfter(p, c);
|
||||
c.appendChild(el);
|
||||
el.innerHTML = '';
|
||||
el.appendChild(p);
|
||||
p.innerHTML = x; // let the original parent have the content of its child
|
||||
insertAfter(c, c.firstElementChild);
|
||||
removeEl(c);
|
||||
};
|
||||
|
||||
document.querySelectorAll('.footnotes > ol > li[id^="fn"], #refs > div[id^="ref-"]').forEach(function (fn) {
|
||||
a = document.querySelectorAll('a[href="#' + fn.id + '"]');
|
||||
if (a.length === 0) return;
|
||||
a.forEach(function (el) { el.removeAttribute('href') });
|
||||
a = a[0];
|
||||
side = document.createElement('div');
|
||||
side.className = 'side side-right';
|
||||
if (/^fn/.test(fn.id)) {
|
||||
side.innerHTML = fn.innerHTML;
|
||||
var number = a.innerText; // footnote number
|
||||
side.firstElementChild.innerHTML = '<span class="bg-number">' + number +
|
||||
'</span> ' + side.firstElementChild.innerHTML;
|
||||
removeEl(side.querySelector('a[href^="#fnref"]')); // remove backreference
|
||||
a.parentNode.tagName === 'SUP' && insideOut(a);
|
||||
} else {
|
||||
side.innerHTML = fn.outerHTML;
|
||||
a = a.parentNode;
|
||||
}
|
||||
insertAfter(a, side);
|
||||
a.classList.add('note-ref');
|
||||
removeEl(fn);
|
||||
})
|
||||
document.querySelectorAll('.footnotes, #refs').forEach(function (fn) {
|
||||
var items = fn.children;
|
||||
if (fn.id === 'refs') return items.length === 0 && removeEl(fn);
|
||||
// there must be a <hr> and an <ol> left
|
||||
if (items.length !== 2 || items[0].tagName !== 'HR' || items[1].tagName !== 'OL') return;
|
||||
items[1].childElementCount === 0 && removeEl(fn);
|
||||
});
|
||||
}();
|
||||
|
||||
renderAnchor = function () {
|
||||
for (let num = 1; num <= 6; num++) {
|
||||
// search h1-h6
|
||||
const headers = document.querySelectorAll('.article-post>h' + num);
|
||||
for (let i = 0; i < headers.length; i++) {
|
||||
const header = headers[i];
|
||||
// add anchor before h1-h6
|
||||
header.innerHTML = `<a href="#${header.id}" class="anchor"><svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>${header.innerHTML}`;
|
||||
}
|
||||
}
|
||||
}();
|
||||
|
||||
switchDarkMode = function () {
|
||||
const rootElement = document.documentElement; // <html>
|
||||
const darkModeStorageKey = 'user-color-scheme'; // use as localStorage's key
|
||||
const rootElementDarkModeAttributeName = 'data-user-color-scheme';
|
||||
const darkModeTogglebuttonElement = document.getElementById('dark-mode-button');
|
||||
|
||||
const setLS = (k, v) => {
|
||||
try {
|
||||
localStorage.setItem(k, v);
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
const removeLS = (k) => {
|
||||
try {
|
||||
localStorage.removeItem(k);
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
const getLS = (k) => {
|
||||
try {
|
||||
return localStorage.getItem(k);
|
||||
} catch (e) {
|
||||
return null // the same as localStorage.getItem() get nothing
|
||||
}
|
||||
}
|
||||
|
||||
const getModeFromCSSMediaQuery = () => {
|
||||
// use matchMedia API
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||
}
|
||||
|
||||
const resetRootDarkModeAttributeAndLS = () => {
|
||||
rootElement.removeAttribute(rootElementDarkModeAttributeName);
|
||||
removeLS(darkModeStorageKey);
|
||||
}
|
||||
|
||||
const validColorModeKeys = {
|
||||
'dark': true,
|
||||
'light': true
|
||||
}
|
||||
|
||||
const modeIcons = {
|
||||
'dark': '☀️',
|
||||
'light': '🌙'
|
||||
}
|
||||
|
||||
const setModeButtonIcon = (mode) => {
|
||||
darkModeTogglebuttonElement.innerHTML = modeIcons[mode]
|
||||
}
|
||||
|
||||
const applyCustomDarkModeSettings = (mode) => {
|
||||
// receive user's operation or get previous mode from localStorage
|
||||
const currentSetting = mode || getLS(darkModeStorageKey);
|
||||
|
||||
if (currentSetting === getModeFromCSSMediaQuery()) {
|
||||
// When the user selected mode equal prefers-color-scheme
|
||||
// reset and restored to automatic mode
|
||||
nowMode = getModeFromCSSMediaQuery()
|
||||
resetRootDarkModeAttributeAndLS();
|
||||
} else if (validColorModeKeys[currentSetting]) {
|
||||
nowMode = currentSetting
|
||||
rootElement.setAttribute(rootElementDarkModeAttributeName, currentSetting);
|
||||
} else {
|
||||
// 首次访问或从未使用过开关、localStorage 中没有存储的值,currentSetting 是 null
|
||||
// 或者 localStorage 被篡改,currentSetting 不是合法值
|
||||
nowMode = getModeFromCSSMediaQuery()
|
||||
resetRootDarkModeAttributeAndLS();
|
||||
}
|
||||
setModeButtonIcon(nowMode)
|
||||
}
|
||||
|
||||
const invertDarkModeObj = {
|
||||
'dark': 'light',
|
||||
'light': 'dark'
|
||||
}
|
||||
|
||||
const toggleCustomDarkMode = () => {
|
||||
let currentSetting = getLS(darkModeStorageKey);
|
||||
|
||||
if (validColorModeKeys[currentSetting]) {
|
||||
// get mode from localStorage and set the opposite
|
||||
currentSetting = invertDarkModeObj[currentSetting];
|
||||
} else if (currentSetting === null) {
|
||||
// if get null from localStorage
|
||||
// get mode from prefers-color-scheme and set the opposite
|
||||
currentSetting = invertDarkModeObj[getModeFromCSSMediaQuery()];
|
||||
} else {
|
||||
// get anything error, return
|
||||
return;
|
||||
}
|
||||
// set opposite mode into localStorage
|
||||
setLS(darkModeStorageKey, currentSetting);
|
||||
|
||||
return currentSetting;
|
||||
}
|
||||
|
||||
// when page loaded set page mode
|
||||
applyCustomDarkModeSettings();
|
||||
|
||||
darkModeTogglebuttonElement.addEventListener('click', () => {
|
||||
// handle user click switch dark mode button
|
||||
applyCustomDarkModeSettings(toggleCustomDarkMode());
|
||||
})
|
||||
}();
|
||||
@@ -1,51 +0,0 @@
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-mode: dark;
|
||||
}
|
||||
|
||||
:root:not([data-user-color-scheme]) {
|
||||
--h1-color: white;
|
||||
--font-color: #b3b9c5;
|
||||
--heading-color: #ffd479;
|
||||
--dark-font-color: #ced4da;
|
||||
--background: #1f2022;
|
||||
--medium-font-color: #dee2e6;
|
||||
--light-font-color: #868e96;
|
||||
--light-background: #2D2D31;
|
||||
--light-background-hover: #3b3b3e;
|
||||
--code-background-color: #2e2e30;
|
||||
--border: #404040;
|
||||
--link-color: #6ab0f3;
|
||||
--link-color-darker: #4a72a5;
|
||||
--link-hover-color: #e1a6f2;
|
||||
--navbar-color: #1d1d1d;
|
||||
--blockquote: #2b2b2b;
|
||||
--blockquote-left: #191919;
|
||||
--transparent-text: rgba(255, 255, 255, 0.7);
|
||||
--transparent-bg: rgba(0, 0, 0, 0.2);
|
||||
--light-transparent-bg: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
}
|
||||
|
||||
[data-user-color-scheme='dark'] {
|
||||
--h1-color: white;
|
||||
--font-color: #b3b9c5;
|
||||
--heading-color: #ffd479;
|
||||
--dark-font-color: #ced4da;
|
||||
--background: #1f2022;
|
||||
--medium-font-color: #dee2e6;
|
||||
--light-font-color: #868e96;
|
||||
--light-background: #2D2D31;
|
||||
--light-background-hover: #3b3b3e;
|
||||
--code-background-color: #2e2e30;
|
||||
--border: #404040;
|
||||
--link-color: #6ab0f3;
|
||||
--link-color-darker: #4a72a5;
|
||||
--link-hover-color: #e1a6f2;
|
||||
--navbar-color: #1d1d1d;
|
||||
--blockquote: #2b2b2b;
|
||||
--blockquote-left: #191919;
|
||||
--transparent-text: rgba(255, 255, 255, 0.7);
|
||||
--transparent-bg: rgba(0, 0, 0, 0.2);
|
||||
--light-transparent-bg: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/* Grid and flex */
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
.flex-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.flex-two-thirds {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/* Headings */
|
||||
|
||||
h1 {
|
||||
color: var(--h1-color);
|
||||
}
|
||||
|
||||
h3,
|
||||
h4 {
|
||||
color: var(--medium-font-color);
|
||||
}
|
||||
|
||||
h2,
|
||||
h5 {
|
||||
color: var(--heading-color);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5 {
|
||||
margin: 0 0 1.5rem 0;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
h1:not(:first-child),
|
||||
h2:not(:first-child),
|
||||
h3:not(:first-child),
|
||||
h4:not(:first-child) {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.5rem;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.75rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 4px solid var(--light-background);
|
||||
|
||||
code {
|
||||
font-size: 1.75rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.5rem;
|
||||
color: var(--font-color);
|
||||
font-weight: 600;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
code {
|
||||
font-size: 1.4rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.3rem;
|
||||
color: var(--font-color);
|
||||
font-weight: 500;
|
||||
margin-bottom: 1rem;
|
||||
border-bottom: 2px solid var(--light-background);
|
||||
padding-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.9rem;
|
||||
|
||||
code {
|
||||
font-size: 1.9rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.7rem;
|
||||
color: var(--font-color);
|
||||
font-weight: 600;
|
||||
|
||||
code {
|
||||
font-size: 1.6rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-weight: 400;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
&.link {
|
||||
display: block;
|
||||
padding: 0.25rem 0;
|
||||
margin: 0.25rem 0;
|
||||
border-radius: 0.35rem;
|
||||
font-weight: 600;
|
||||
color: var(--dark-font-color);
|
||||
|
||||
&:hover {
|
||||
color: var(--link-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.button {
|
||||
display: inline-block;
|
||||
border: 2px solid var(--link-color);
|
||||
padding: 0.3rem 0.6rem;
|
||||
margin-right: 0.75rem;
|
||||
font-weight: 500;
|
||||
background: var(--link-color);
|
||||
color: white;
|
||||
border-radius: 0.35rem;
|
||||
font-size: 0.9rem;
|
||||
|
||||
&.large {
|
||||
padding: 0.8rem 1rem;
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
border: 2px solid #edf2ff;
|
||||
background: #edf2ff;
|
||||
color: #3b5bdb;
|
||||
}
|
||||
|
||||
&:hover, &.secondary:hover {
|
||||
border: 2px solid var(--link-color-darker);
|
||||
background: var(--link-color-darker);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
a.button {
|
||||
font-size: 0.9rem;
|
||||
|
||||
&.large {
|
||||
padding: 1rem 1.5rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p.subtitle {
|
||||
color: var(--medium-font-color);
|
||||
font-size: 1.3rem;
|
||||
font-weight: 300;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
p.subtitle {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/* Links */
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
|
||||
&.image-link {
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
code[class*='language-'] {
|
||||
color: var(--link-color) !important;
|
||||
|
||||
&:hover {
|
||||
background: var(--link-color) !important;
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,379 +0,0 @@
|
||||
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the line height in all browsers.
|
||||
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.15;
|
||||
/* 1 */
|
||||
-webkit-text-size-adjust: 100%;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the `main` element consistently in IE.
|
||||
*/
|
||||
|
||||
main {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
/* 1 */
|
||||
height: 0;
|
||||
/* 1 */
|
||||
overflow: visible;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace;
|
||||
/* 1 */
|
||||
font-size: 1em;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the gray background on active links in IE 10.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Chrome 57-
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none;
|
||||
/* 1 */
|
||||
text-decoration: underline;
|
||||
/* 2 */
|
||||
text-decoration: underline dotted;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace;
|
||||
/* 1 */
|
||||
font-size: 1em;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers.
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit;
|
||||
/* 1 */
|
||||
font-size: 100%;
|
||||
/* 1 */
|
||||
line-height: 1.15;
|
||||
/* 1 */
|
||||
margin: 0;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input {
|
||||
/* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select {
|
||||
/* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the padding in Firefox.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box;
|
||||
/* 1 */
|
||||
color: inherit;
|
||||
/* 2 */
|
||||
display: table;
|
||||
/* 1 */
|
||||
max-width: 100%;
|
||||
/* 1 */
|
||||
padding: 0;
|
||||
/* 3 */
|
||||
white-space: normal;
|
||||
/* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE 10+.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10.
|
||||
* 2. Remove the padding in IE 10.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box;
|
||||
/* 1 */
|
||||
padding: 0;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield;
|
||||
/* 1 */
|
||||
outline-offset: -2px;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button;
|
||||
/* 1 */
|
||||
font: inherit;
|
||||
/* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||
*/
|
||||
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Misc
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10+.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
/* Scaffolding */
|
||||
|
||||
html {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Roboto',
|
||||
Roboto, Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI',
|
||||
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
color: var(--font-color);
|
||||
font-weight: 400;
|
||||
font-size: 1rem;
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
section {
|
||||
margin: 2rem 0;
|
||||
|
||||
&:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
>h2 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 1.6rem;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
section>h2 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.index h2,
|
||||
section>h2 {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
|
||||
.index h2,
|
||||
section>h2 {
|
||||
border-bottom-width: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
a.section-button {
|
||||
font-weight: 500;
|
||||
background: var(--light-background);
|
||||
color: var(--dark-font-color);
|
||||
padding: 0.4rem 0.6rem;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 0.3rem;
|
||||
border-width: 0 !important;
|
||||
white-space: nowrap;
|
||||
line-height: 1;
|
||||
margin-left: 2rem;
|
||||
margin-top: 0.5rem;
|
||||
|
||||
&:hover {
|
||||
background: var(--light-background-hover);
|
||||
color: var(--heading-color);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
section {
|
||||
margin: 3.5rem 0;
|
||||
|
||||
>h2 {
|
||||
font-size: 2rem;
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1025px;
|
||||
padding: 0 1.5rem;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
&.page p {
|
||||
max-width: 600px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
.container {
|
||||
padding: 0 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
p,
|
||||
ol,
|
||||
ul,
|
||||
dl,
|
||||
table,
|
||||
blockquote {
|
||||
font-size: 1.05rem;
|
||||
margin: 0 0 1.5rem 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
|
||||
p,
|
||||
ol,
|
||||
ul,
|
||||
dl,
|
||||
table,
|
||||
blockquote {
|
||||
font-size: 1.125rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
ul li p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul li ul {
|
||||
padding-left: 1rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul li ul li {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ol li ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.task-list-item [type='checkbox'] {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 2rem 0;
|
||||
padding: 1rem;
|
||||
background: var(--blockquote);
|
||||
font-weight: 400;
|
||||
border-left: 5px solid var(--blockquote-left);
|
||||
}
|
||||
|
||||
blockquote :not(pre)>code[class*='language-'] {
|
||||
background: rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
blockquote {
|
||||
padding: 2rem 0 2rem 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: #3b5bdb;
|
||||
color: white;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
:root {
|
||||
--color-mode: light;
|
||||
--h1-color: #343a40;
|
||||
--font-color: #495057;
|
||||
--heading-color: #343a40;
|
||||
--background: white;
|
||||
--dark-font-color: #1b1d25;
|
||||
--medium-font-color: #60656c;
|
||||
--light-font-color: #858b93;
|
||||
--light-background: #f1f4f8;
|
||||
--light-background-hover: #e1e6ed;
|
||||
--border: #d6d9de;
|
||||
--link-color: #5183f5;
|
||||
--link-color-darker: #364fc7;
|
||||
--navbar-color: #1b1d25;
|
||||
--blockquote: #f9f9f9;
|
||||
--blockquote-left: #e3e6eb;
|
||||
--transparent-text: rgba(0, 0, 0, 0.7);
|
||||
--transparent-bg: rgba(0, 0, 0, 0.05);
|
||||
--light-transparent-bg: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
.my {
|
||||
padding-top: 2rem;
|
||||
a {
|
||||
border: none;
|
||||
margin-right: 1rem;
|
||||
|
||||
&:hover {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/* Footer */
|
||||
|
||||
.footer {
|
||||
padding: 2rem 0;
|
||||
|
||||
>.flex {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
a {
|
||||
border-radius: 0.35rem;
|
||||
margin: 0 0.05rem;
|
||||
font-weight: 400;
|
||||
font-size: 1rem;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.5rem;
|
||||
margin: 0 0.5rem;
|
||||
color: var(--light-font-color);
|
||||
|
||||
&:hover {
|
||||
color: var(--heading-color);
|
||||
background: var(--light-background);
|
||||
}
|
||||
|
||||
&.img {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
margin: 0 0.75rem;
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flex nav {
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
.footer {
|
||||
>.flex {
|
||||
align-items: flex-start;
|
||||
margin-left: -1.5rem;
|
||||
margin-right: -1.5rem;
|
||||
}
|
||||
|
||||
a {
|
||||
padding: 0.75rem;
|
||||
|
||||
&.img {
|
||||
padding: 0 0.5rem;
|
||||
margin: 0 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media print {
|
||||
.footer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/* Helpers */
|
||||
|
||||
.small {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
time,
|
||||
.meta {
|
||||
color: var(--light-font-color);
|
||||
font-size: 0.85rem;
|
||||
white-space: nowrap;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.meta {
|
||||
color: var(--medium-font-color);
|
||||
font-size: 1rem;
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
$code-font-size: 0.9rem !default;
|
||||
$code-color: #f8f8f2 !default;
|
||||
$code-background: #373b41 !default;
|
||||
$gray: #cacaca !default;
|
||||
$code-font-family: Consolas, Monaco, Menlo, "DejaVu Sans Mono",
|
||||
"Bitstream Vera Sans Mono", "Courier New", monospace !default;
|
||||
|
||||
code, pre {
|
||||
font-size: $code-font-size;
|
||||
font-family: $code-font-family;
|
||||
background: $code-background;
|
||||
}
|
||||
|
||||
:not(pre) > code {
|
||||
color: var(--dark-font-color);
|
||||
background: var(--light-background);
|
||||
}
|
||||
|
||||
code {
|
||||
padding: 3px 5px;
|
||||
border-radius: 4px;
|
||||
color: $code-color;
|
||||
}
|
||||
|
||||
pre > code {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.highlight > .chroma {
|
||||
margin: 0;
|
||||
|
||||
border-radius: 5px;
|
||||
overflow-x: auto;
|
||||
box-shadow: 1px 1px 2px rgba(0,0,0,0.125);
|
||||
position: relative;
|
||||
background: $code-background;
|
||||
|
||||
code {
|
||||
padding: 30px 10px 10px;
|
||||
}
|
||||
|
||||
code[data-lang]::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
padding: 2px 10px;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
font-size: $code-font-size;
|
||||
line-height: 1.9;
|
||||
font-weight: bold;
|
||||
color: #b1b1b1;
|
||||
background: darken($code-background, 3%);
|
||||
content: attr(data-lang);
|
||||
}
|
||||
|
||||
table {
|
||||
position: relative;
|
||||
border: none;
|
||||
|
||||
code {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.lntd {
|
||||
&:first-child {
|
||||
width: 10px;
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
padding: 30px 7px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
vertical-align: top;
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
padding: 30px 10px 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table, tr, td {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* LineHighlight */ .hl { display: block; width: 100%;background-color: black }
|
||||
/* LineNumbersTable */ .lnt { color: #7f7f7f }
|
||||
/* LineNumbers */ .ln { padding: 0 0.4em 0 0.4em;color: #7f7f7f }
|
||||
|
||||
.err { color: #960050 } /* Error */
|
||||
.c { color: #999999 } /* Comment */
|
||||
.err { color: #f2777a } /* Error */
|
||||
.k { color: #cc99cc } /* Keyword */
|
||||
.l { color: #f99157 } /* Literal */
|
||||
.n { color: #cccccc } /* Name */
|
||||
.o { color: #66cccc } /* Operator */
|
||||
.p { color: #cccccc } /* Punctuation */
|
||||
.cm { color: #999999 } /* Comment.Multiline */
|
||||
.cp { color: #999999 } /* Comment.Preproc */
|
||||
.c1 { color: #999999 } /* Comment.Single */
|
||||
.cs { color: #999999 } /* Comment.Special */
|
||||
.gd { color: #f2777a } /* Generic.Deleted */
|
||||
.ge { font-style: italic } /* Generic.Emph */
|
||||
.gh { color: #cccccc; font-weight: bold } /* Generic.Heading */
|
||||
.gi { color: #99cc99 } /* Generic.Inserted */
|
||||
.gp { color: #999999; font-weight: bold } /* Generic.Prompt */
|
||||
.gs { font-weight: bold } /* Generic.Strong */
|
||||
.gu { color: #66cccc; font-weight: bold } /* Generic.Subheading */
|
||||
.kc { color: #cc99cc } /* Keyword.Constant */
|
||||
.kd { color: #cc99cc } /* Keyword.Declaration */
|
||||
.kn { color: #66cccc } /* Keyword.Namespace */
|
||||
.kp { color: #cc99cc } /* Keyword.Pseudo */
|
||||
.kr { color: #cc99cc } /* Keyword.Reserved */
|
||||
.kt { color: #ffcc66 } /* Keyword.Type */
|
||||
.ld { color: #99cc99 } /* Literal.Date */
|
||||
.m { color: #f99157 } /* Literal.Number */
|
||||
.s { color: #99cc99 } /* Literal.String */
|
||||
.na { color: #6699cc } /* Name.Attribute */
|
||||
.nb { color: #cccccc } /* Name.Builtin */
|
||||
.nc { color: #ffcc66 } /* Name.Class */
|
||||
.no { color: #f2777a } /* Name.Constant */
|
||||
.nd { color: #66cccc } /* Name.Decorator */
|
||||
.ni { color: #cccccc } /* Name.Entity */
|
||||
.ne { color: #f2777a } /* Name.Exception */
|
||||
.nf { color: #6699cc } /* Name.Function */
|
||||
.nl { color: #cccccc } /* Name.Label */
|
||||
.nn { color: #ffcc66 } /* Name.Namespace */
|
||||
.nx { color: #6699cc } /* Name.Other */
|
||||
.py { color: #cccccc } /* Name.Property */
|
||||
.nt { color: #66cccc } /* Name.Tag */
|
||||
.nv { color: #f2777a } /* Name.Variable */
|
||||
.ow { color: #66cccc } /* Operator.Word */
|
||||
.w { color: #cccccc } /* Text.Whitespace */
|
||||
.mf { color: #f99157 } /* Literal.Number.Float */
|
||||
.mh { color: #f99157 } /* Literal.Number.Hex */
|
||||
.mi { color: #f99157 } /* Literal.Number.Integer */
|
||||
.mo { color: #f99157 } /* Literal.Number.Oct */
|
||||
.sb { color: #99cc99 } /* Literal.String.Backtick */
|
||||
.sc { color: #cccccc } /* Literal.String.Char */
|
||||
.sd { color: #999999 } /* Literal.String.Doc */
|
||||
.s2 { color: #99cc99 } /* Literal.String.Double */
|
||||
.se { color: #f99157 } /* Literal.String.Escape */
|
||||
.sh { color: #99cc99 } /* Literal.String.Heredoc */
|
||||
.si { color: #f99157 } /* Literal.String.Interpol */
|
||||
.sx { color: #99cc99 } /* Literal.String.Other */
|
||||
.sr { color: #99cc99 } /* Literal.String.Regex */
|
||||
.s1 { color: #99cc99 } /* Literal.String.Single */
|
||||
.ss { color: #99cc99 } /* Literal.String.Symbol */
|
||||
.bp { color: #cccccc } /* Name.Builtin.Pseudo */
|
||||
.vc { color: #f2777a } /* Name.Variable.Class */
|
||||
.vg { color: #f2777a } /* Name.Variable.Global */
|
||||
.vi { color: #f2777a } /* Name.Variable.Instance */
|
||||
.il { color: #f99157 } /* Literal.Number.Integer.Long */
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
/* Navbar */
|
||||
|
||||
main {
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
main {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji {
|
||||
margin: 0 0.4rem 0 0.1rem;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background: var(--navbar-color);
|
||||
box-shadow: 0 3px 13px rgba(100, 110, 140, 0.1),
|
||||
0 2px 4px rgba(100, 110, 140, 0.15);
|
||||
z-index: 2;
|
||||
padding: 0.5rem;
|
||||
|
||||
.flex {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
a {
|
||||
border-radius: 0.35rem;
|
||||
margin: 0 0.05rem;
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
font-weight: 400;
|
||||
font-size: 0.85rem;
|
||||
border: 1px solid transparent;
|
||||
padding: 0 0.3rem;
|
||||
background: transparent;
|
||||
|
||||
&:first-of-type {
|
||||
margin-left: -1rem;
|
||||
}
|
||||
|
||||
.emoji {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.brand {
|
||||
font-weight: 500;
|
||||
color: white;
|
||||
white-space: nowrap;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 1;
|
||||
background: transparent;
|
||||
|
||||
.emoji {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 22px;
|
||||
width: 22px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: 1rem;
|
||||
margin-right: -1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 500px) {
|
||||
.navbar a {
|
||||
font-size: 0.95rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
.emoji {
|
||||
margin: 0 0.5rem 0 0.1rem;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
position: static;
|
||||
padding: 1.5rem 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
|
||||
.flex {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
a {
|
||||
padding: 0.75rem 1.25rem;
|
||||
margin: 0 0.25rem;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 400;
|
||||
color: var(--font-color);
|
||||
|
||||
&:first-of-type {
|
||||
margin-left: -1.5rem;
|
||||
}
|
||||
|
||||
&:hover, &[aria-current='page'] {
|
||||
background: var(--light-background);
|
||||
color: var(--dark-font-color);
|
||||
}
|
||||
|
||||
&.brand {
|
||||
font-size: 1.3rem;
|
||||
margin-right: 3rem;
|
||||
border: none;
|
||||
background: transparent !important;
|
||||
color: var(--dark-font-color);
|
||||
|
||||
&:hover {
|
||||
background: var(--light-background) !important;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#dark-mode-button:hover {
|
||||
background: var(--light-background);
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
.navbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#dark-mode-button {
|
||||
display: flex;
|
||||
border: none;
|
||||
padding: 0.2rem 0.7rem 0.2rem 0.5rem;
|
||||
border-radius: 0.35rem;
|
||||
box-sizing: content-box;
|
||||
cursor: pointer;
|
||||
font-size: 1.1rem;
|
||||
background: transparent;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
#dark-mode-button {
|
||||
padding: 0.95rem 0.75rem;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
/* Post */
|
||||
|
||||
header {
|
||||
padding: 1.5rem 0;
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
display: inline-block;
|
||||
font-weight: 600;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
u {
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
padding: 0.4rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.article-header {
|
||||
padding-top: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
.container {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.thumb {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: 700;
|
||||
font-size: 1.8rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 1.2rem;
|
||||
color: var(--light-font-color);
|
||||
font-weight: 300;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.post-meta {
|
||||
margin-top: 1rem;
|
||||
padding: 0;
|
||||
color: var(--light-font-color);
|
||||
font-size: 0.9rem;
|
||||
|
||||
a {
|
||||
color: var(--font-color);
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
time {
|
||||
margin-top: 0.2rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.tags {
|
||||
margin-top: 0.5rem;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.article-post {
|
||||
margin-bottom: 2rem;
|
||||
|
||||
a {
|
||||
box-shadow: 0px -2px 0px rgba(189, 195, 199, 0.5) inset;
|
||||
transition: all .3s ease;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0px -10px 0px rgba(189, 195, 199, 0.7) inset;
|
||||
}
|
||||
}
|
||||
|
||||
@for $i from 1 through 6 {
|
||||
h#{$i} {
|
||||
.anchor {
|
||||
stroke: var(--link-color);
|
||||
stroke-width: 1px;
|
||||
fill: var(--link-color);
|
||||
font-weight: 700;
|
||||
left: -0.25rem;
|
||||
border-width: 0;
|
||||
float: left;
|
||||
line-height: 1;
|
||||
margin-left: -20px;
|
||||
padding-right: 4px;
|
||||
box-shadow: none;
|
||||
transition: none;
|
||||
|
||||
.icon {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.anchor {
|
||||
box-shadow: none;
|
||||
.icon {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
.article-post {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.article-post h1,
|
||||
.article-post h2,
|
||||
.article-post h3,
|
||||
.article-post h4,
|
||||
.article-post h5 {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.post-meta {
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 3rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
header u {
|
||||
background: linear-gradient(transparent 85%, #bac8ff 0);
|
||||
}
|
||||
|
||||
.article-header {
|
||||
padding-top: 3rem;
|
||||
|
||||
h1 {
|
||||
padding-top: 0;
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.thumb {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 1.8rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1100px) {
|
||||
.article-header h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.post-thumbnail {
|
||||
display: block !important;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
.new-post,
|
||||
.popular-post {
|
||||
display: inline-block;
|
||||
color: #111;
|
||||
padding: 0.3rem 0.4rem;
|
||||
border-radius: 0.3rem;
|
||||
font-size: 0.85rem;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.new-post {
|
||||
background: #d3f9d8;
|
||||
}
|
||||
|
||||
.popular-post {
|
||||
background: #dce6fd;
|
||||
color: var(--heading-color);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
|
||||
.new-post,
|
||||
.popular-post {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/* Posts */
|
||||
|
||||
.post-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
time {
|
||||
display: block;
|
||||
flex: 0 0 65px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.posts .post:last-of-type a {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
.post {
|
||||
a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: stretch;
|
||||
padding: 1rem 0;
|
||||
border-bottom: 2px solid var(--light-background);
|
||||
|
||||
&:hover {
|
||||
border-radius: 0.3rem;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin-top: 0.1rem;
|
||||
margin-bottom: 0;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
.posts {
|
||||
margin-left: -1rem;
|
||||
margin-right: -1rem;
|
||||
}
|
||||
|
||||
.post {
|
||||
a {
|
||||
padding: 0.75rem 1rem;
|
||||
border-bottom-color: transparent;
|
||||
|
||||
&:hover {
|
||||
background: var(--light-background);
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.post-row time {
|
||||
flex: 0 0 80px;
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/* Projects */
|
||||
|
||||
.projects .project:last-of-type {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
.project {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.75rem 0;
|
||||
border-bottom: 2px solid var(--light-background);
|
||||
|
||||
.icon {
|
||||
font-size: 1.5rem;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 0.25rem;
|
||||
border-width: 0;
|
||||
|
||||
&:hover h3 {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&.button {
|
||||
margin-left: 1.5rem;
|
||||
border-width: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 1.1rem;
|
||||
color: var(--light-font-color);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
.project {
|
||||
border-bottom-width: 0;
|
||||
|
||||
h3 {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
.side {
|
||||
width: 200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.side-right {
|
||||
float: right;
|
||||
clear: right;
|
||||
margin-right: calc(-200px - 2em);
|
||||
|
||||
p {
|
||||
font-size: .9rem;
|
||||
}
|
||||
}
|
||||
|
||||
.note-ref {
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.bg-number {
|
||||
background: var(--light-background);
|
||||
font-size: .9rem;
|
||||
color: var(--font-color);
|
||||
text-decoration: none;
|
||||
padding: 1px 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
.side {
|
||||
width: 100%;
|
||||
padding: 0 2em;
|
||||
}
|
||||
|
||||
.side-right {
|
||||
float: none;
|
||||
clear: both;
|
||||
margin: 1em auto;
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
|
||||
.note-ref:hover ~ .side {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
margin-left: 1rem;
|
||||
padding: .5rem;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/* Suggested */
|
||||
|
||||
.suggested {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
margin-left: -1rem;
|
||||
margin-right: -1rem;
|
||||
padding: 0;
|
||||
|
||||
span {
|
||||
font-weight: 400;
|
||||
display: block;
|
||||
font-size: 0.9rem;
|
||||
color: var(--transparent-text);
|
||||
}
|
||||
|
||||
a {
|
||||
background: none;
|
||||
margin: 0.5rem 1rem;
|
||||
border-bottom: none;
|
||||
transition: all 0.2s ease;
|
||||
padding: 1.5rem;
|
||||
border-radius: 0.35rem;
|
||||
color: var(--heading-color);
|
||||
border: 1px solid #eeeeee;
|
||||
|
||||
&:hover {
|
||||
transform: translate3D(0, -1px, 0);
|
||||
background: var(--light-background-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
.suggested {
|
||||
flex-direction: row;
|
||||
|
||||
a {
|
||||
flex: 0 0 calc(50% - 2rem);
|
||||
|
||||
&:first-of-type {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/* Tables */
|
||||
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
thead,
|
||||
tbody {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
th {
|
||||
border-bottom: 2px solid var(--border);
|
||||
}
|
||||
|
||||
tfoot th {
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
td {
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
text-align: left;
|
||||
padding: 0.75rem;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
tbody tr:nth-child(even) {
|
||||
background-color: var(--light-background);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 800px) {
|
||||
table {
|
||||
display: table;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
thead,
|
||||
tbody {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/* Tags */
|
||||
|
||||
.count {
|
||||
font-weight: 700;
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex !important;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin-left: -0.5rem;
|
||||
margin-right: -0.5rem;
|
||||
|
||||
>a {
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
background: var(--light-background);
|
||||
color: var(--font-color);
|
||||
margin: 0.2rem;
|
||||
padding: 0.5rem 0.6rem;
|
||||
font-size: 0.8rem;
|
||||
border-radius: 0.3rem;
|
||||
border-bottom: 0;
|
||||
white-space: nowrap;
|
||||
line-height: 1;
|
||||
|
||||
&:hover {
|
||||
background: var(--light-background-hover);
|
||||
color: var(--heading-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mark {
|
||||
background: #ffec99;
|
||||
}
|
||||
|
||||
.utterances {
|
||||
margin-left: -4px;
|
||||
margin-right: -4px;
|
||||
}
|
||||
|
||||
kbd {
|
||||
background-color: #f7f7f7;
|
||||
border: 2px solid rgba(0, 0, 0, 0.3);
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), inset 0 0 0 2px #fff;
|
||||
color: #333;
|
||||
display: inline-block;
|
||||
font-family: Helvetica Neue, Inter, -apple-system, BlinkMacSystemFont,
|
||||
Helvetica, Arial, sans-serif;
|
||||
line-height: 1.4;
|
||||
margin: 0 0.1em;
|
||||
padding: 0.1em 0.6em;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#comments {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
#comments h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@media print {
|
||||
|
||||
.comments,
|
||||
#comments {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
.terms {
|
||||
a {
|
||||
padding: .75rem 1.25rem;
|
||||
margin: 0 .25rem;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 400;
|
||||
color: var(--font-color);
|
||||
border-radius: .35rem;
|
||||
|
||||
&:hover {
|
||||
background: var(--light-background);
|
||||
color: var(--dark-font-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
@import "base/normalize";
|
||||
@import "base/reset";
|
||||
@import "base/variables";
|
||||
@import "base/grid";
|
||||
@import "base/scaffolding";
|
||||
@import "base/headings";
|
||||
@import "base/links";
|
||||
|
||||
|
||||
@import "components/navbar";
|
||||
@import "components/bio";
|
||||
@import "components/posts";
|
||||
@import "components/post_tag";
|
||||
@import "components/footer";
|
||||
@import "components/helpers";
|
||||
@import "components/table";
|
||||
@import "components/tags";
|
||||
|
||||
@import "components/post";
|
||||
@import "components/highlight";
|
||||
@import "components/side";
|
||||
@import "components/suggested";
|
||||
@import "components/projects";
|
||||
@import "components/terms";
|
||||
|
||||
@import "base/dark";
|
||||
|
Before Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 27 KiB |
@@ -1,9 +0,0 @@
|
||||
{{ partial "header.html" . }}
|
||||
|
||||
<div class="container">
|
||||
<main>
|
||||
<h1>404 NOT FOUND</h1>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{{ partial "footer.html" . }}
|
||||
@@ -1,41 +0,0 @@
|
||||
{{ define "main" }}
|
||||
|
||||
<header>
|
||||
<div class="container">
|
||||
<h1>{{ .Title }}</h1>
|
||||
<p class="subtitle">{{ .Params.subtitle }}</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{{ $pages := where .Site.RegularPages "Type" "in" .Site.Params.mainSections }}
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<section>
|
||||
{{ range $pages.GroupByDate "2006" }}
|
||||
<section>
|
||||
<div class="posts">
|
||||
{{ range.Pages }}
|
||||
<div class="post">
|
||||
<a href="{{ .RelPermalink }}">
|
||||
<div class="post-row">
|
||||
<h3>
|
||||
{{ .Title }}
|
||||
</h3>
|
||||
<!-- <time>{{ .Date.Format "Jan 02" }}</time>-->
|
||||
</div>
|
||||
<!--<div class="new-post">New!</div>-->
|
||||
</a>
|
||||
<div style="padding-left: 1em; margin-bottom: 2em;">
|
||||
<span style="color: #444">{{ .Summary }}</span>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{ end }}
|
||||
@@ -1,12 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ .Site.LanguageCode }}">
|
||||
{{ partial "head.html" . }}
|
||||
|
||||
<body class="dark">
|
||||
{{ partial "header.html" . }}
|
||||
<main>
|
||||
{{ block "main" . }}{{ end }}
|
||||
</main>
|
||||
{{ partial "footer.html" . }}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,94 +0,0 @@
|
||||
{{ define "main" }}
|
||||
|
||||
<div class="container">
|
||||
<section class="my">
|
||||
<div class="content">
|
||||
{{ with.Content }}
|
||||
{{ . }}
|
||||
{{ end }}
|
||||
<div class="bio-social">
|
||||
{{ range $name, $path := $.Param "socialOptions" }}
|
||||
{{ if (and $path (ne $name "email")) }}
|
||||
<a
|
||||
href="{{ $path | safeURL }}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
title="{{ $name }}"
|
||||
aria-label="{{ $name }}"
|
||||
>
|
||||
{{ partial (print "svgs/social/" $name ".svg") (dict "width" 25 "height" 25) }}
|
||||
</a>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{{ $pages := where .Site.RegularPages "Type" "in" .Site.Params.mainSections }}
|
||||
{{ $projects := where .Site.RegularPages "Section" "projects" }}
|
||||
|
||||
<div class="container">
|
||||
<section>
|
||||
<div class="posts">
|
||||
{{ $pages = .Site.Taxonomies.labels.readme }}
|
||||
{{ range $i,$e := $pages }}
|
||||
|
||||
{{if modBool $i 2}}
|
||||
<div class="container">
|
||||
<nav class="flex container suggested">
|
||||
<a rel="prev" href="{{ .RelPermalink }}" title="{{ $e.Title }}">
|
||||
<span style="font-size: 1.5em; font-weight: bold;">
|
||||
{{ $e.Title }}</span
|
||||
>
|
||||
<span style="color: #777">{{ $e.Summary }}</span>
|
||||
</a>
|
||||
{{ end }}
|
||||
|
||||
{{if not (modBool $i 2)}}
|
||||
|
||||
<a rel="next" href="{{ .RelPermalink }}" title="{{ $e.Title }}">
|
||||
<span style="font-size: 1.5em; font-weight: bold;">
|
||||
{{ $e.Title }}</span
|
||||
>
|
||||
<span style="color: #777">{{ $e.Summary }}</span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
|
||||
<!-- todo: do not copypaste it all -->
|
||||
</div>
|
||||
</section>
|
||||
{{ if gt (len $projects) 0}}
|
||||
<section>
|
||||
<h2>Projects</h2>
|
||||
<div class="projects">
|
||||
{{ range $projects.ByWeight }}
|
||||
<div class="project">
|
||||
<div>
|
||||
<a href="{{ .Params.link }}" target="_blank" rel="noreferrer">
|
||||
<div class="icon">{{ .Params.icon }}</div>
|
||||
<h3>{{ .Title }}</h3>
|
||||
</a>
|
||||
<div class="description">{{ .Params.description }}</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<a
|
||||
href="{{ .Params.repo }}"
|
||||
class="button"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Source</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</section>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
{{ end }}
|
||||
@@ -1,37 +0,0 @@
|
||||
{{ define "main" }}
|
||||
|
||||
{{ $pages := .Pages }}
|
||||
{{ $pages = (.Paginate $pages).Pages }}
|
||||
|
||||
<header>
|
||||
<div class="container">
|
||||
<h1>{{ .Title }}</h1>
|
||||
<p class="subtitle">
|
||||
<span class="count">{{ len $pages }}</span> posts found.
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<section>
|
||||
<section>
|
||||
<div class="posts">
|
||||
{{ range $pages }}
|
||||
<div class="post">
|
||||
<a href="{{ .RelPermalink }}">
|
||||
<div class="post-row">
|
||||
<time>{{ .Date.Format "Jan 02" }}</time>
|
||||
<h3>{{ .Title }}</h3>
|
||||
</div>
|
||||
<!--<div class="new-post">New!</div>-->
|
||||
</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{ end }}
|
||||
@@ -1,39 +0,0 @@
|
||||
{{ define "main" }}
|
||||
|
||||
<div class="container">
|
||||
<article>
|
||||
<header class="article-header">
|
||||
<div class="thumb">
|
||||
<div>
|
||||
<h1><a href="/{{ .Params.servicename }}">{{ .Params.servicename }}</a></h1>
|
||||
<div>
|
||||
<a href="/{{ .Params.servicename }}"><u>Readme</u></a>
|
||||
|
||||
<a href="/{{ .Params.servicename }}-microjs"><u>Micro.js</u></a>
|
||||
|
||||
<a href="/{{ .Params.servicename }}/api"><u>API Spec</u></a>
|
||||
|
||||
<a href="{{ $.Site.Params.source }}/tree/master/{{ .Params.servicename }}"><u>Source</u></a>
|
||||
</div>
|
||||
<div class="post-meta">
|
||||
<!-- <div>
|
||||
By {{ .Params.author }} on <time>{{ .Date.Format "January 02, 2006" }}</time>
|
||||
</div>
|
||||
-->
|
||||
<div class="tags">
|
||||
{{ range (.GetTerms "labels") }}
|
||||
<a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</article>
|
||||
|
||||
<div class="article-post">
|
||||
{{ .Content }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ end }}
|
||||
@@ -1,22 +0,0 @@
|
||||
{{ define "main" }}
|
||||
|
||||
{{ $pages := .Pages }}
|
||||
{{ $pages = (.Paginate $pages).Pages }}
|
||||
|
||||
<header>
|
||||
<div class="container">
|
||||
<h1>{{ .Title }}</h1>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<div class="container">
|
||||
<div class="terms">
|
||||
{{ range (index .Site.Taxonomies (.Title | lower)) }}
|
||||
<a href="{{ .Page.RelPermalink }}">{{ .Page.Title }} <span class="bg-number">{{ .Count }}</span></a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{ end }}
|
||||
@@ -1,21 +0,0 @@
|
||||
<footer class="footer flex">
|
||||
<section class="container">
|
||||
<nav class="footer-links">
|
||||
{{ range .Site.Menus.footer }}
|
||||
<a href="{{ .URL | relURL }}">{{ .Name }}</a>
|
||||
{{ end }}
|
||||
</nav>
|
||||
</section>
|
||||
{{- if and (or .Params.mathjax (and .Site.Params.mathjax (ne .Params.mathjax false))) (or .IsPage .IsHome) }}
|
||||
<script type="text/javascript">
|
||||
window.MathJax = {
|
||||
tex: {
|
||||
inlineMath: [['$','$'], ['\\(','\\)']],
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<script async src="https://cdn.jsdelivr.net/npm/mathjax@3.0.5/es5/tex-mml-chtml.js" integrity="sha256-HGLuEfFcsUJGhvB8cQ8nr0gai9EucOOaIxFw7qxmd+w=" crossorigin="anonymous"></script>
|
||||
{{- end }}
|
||||
{{ $features := resources.Get "js/features.js" | minify | fingerprint }}
|
||||
<script async src="{{ $features.RelPermalink }}"></script>
|
||||
</footer>
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="theme-color" content="dark">
|
||||
<title>{{ with .Title }} {{ . }} | {{ end }}{{ .Site.Title }}</title>
|
||||
<!-- uncomment this to add favicon -->
|
||||
<!-- <link rel="icon" type="image/png" href="{{ "/icons/favicon.png" | relURL }}" /> -->
|
||||
{{ $style := resources.Get "sass/main.scss" | toCSS | minify | fingerprint }}
|
||||
<link rel="stylesheet" href="{{ $style.RelPermalink }}">
|
||||
</head>
|
||||
@@ -1,22 +0,0 @@
|
||||
<nav class="navbar">
|
||||
<div class="container">
|
||||
<div class="flex">
|
||||
<div>
|
||||
<a class="brand" href="{{ relURL "/" }}">
|
||||
{{ if .Site.Params.titleEmoji }}
|
||||
<span class="emoji">
|
||||
{{ .Site.Params.titleEmoji }}
|
||||
</span>
|
||||
{{ end }}
|
||||
{{ .Site.Title }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex">
|
||||
{{ range .Site.Menus.header }}
|
||||
<a href="{{ .URL | relURL }}"{{ if eq .URL $.RelPermalink }} aria-current="page"{{ end }}>{{ .Pre }}{{ .Name }}{{ .Post }}</a>
|
||||
{{ end }}
|
||||
<!-- <button id="dark-mode-button"></button> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -1 +0,0 @@
|
||||
<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="{{ .width }}" height="{{ .height }}"><path d="M 2 7 L 2 25 L 30 25 L 30 7 L 2 7 z M 4 9 L 28 9 L 28 23 L 4 23 L 4 9 z M 6 11 L 6 21 L 9 21 C 10.654 21 12 19.654 12 18 L 12 14 C 12 12.346 10.654 11 9 11 L 6 11 z M 16 11 C 14.897 11 14 11.897 14 13 L 14 19 C 14 20.103 14.897 21 16 21 L 18 21 L 18 19 L 16 19 L 16 17 L 18 17 L 18 15 L 16 15 L 16 13 L 18 13 L 18 11 L 16 11 z M 19.691406 11 L 21.775391 20.025391 C 21.907391 20.595391 22.415 21 23 21 C 23.585 21 24.092609 20.595391 24.224609 20.025391 L 26.308594 11 L 24.255859 11 L 23 16.439453 L 21.744141 11 L 19.691406 11 z M 8 13 L 9 13 C 9.552 13 10 13.448 10 14 L 10 18 C 10 18.552 9.552 19 9 19 L 8 19 L 8 13 z"/></svg>
|
||||
|
Before Width: | Height: | Size: 763 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="{{ .width }}" height="{{ .height }}" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path fill="currentColor" d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-.4 4.25l-7.07 4.42c-.32.2-.74.2-1.06 0L4.4 8.25c-.25-.16-.4-.43-.4-.72 0-.67.73-1.07 1.3-.72L12 11l6.7-4.19c.57-.35 1.3.05 1.3.72 0 .29-.15.56-.4.72z"/></svg>
|
||||
|
Before Width: | Height: | Size: 415 B |
@@ -1,5 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{{ .width }}" height="{{ .height }}" viewBox="0 0 24 24" version="1.1">
|
||||
<g id="surface2747">
|
||||
<path fill="currentColor" d="M 11.664062 2.003906 C 6.621094 2.171875 2.375 6.25 2.023438 11.289062 C 1.65625 16.617188 5.46875 21.121094 10.507812 21.878906 L 10.507812 14.648438 L 8.890625 14.648438 C 8.164062 14.648438 7.578125 14.0625 7.578125 13.335938 C 7.578125 12.609375 8.164062 12.023438 8.890625 12.023438 L 10.503906 12.023438 L 10.503906 10.273438 C 10.503906 7.378906 11.914062 6.105469 14.324219 6.105469 C 14.679688 6.105469 14.984375 6.113281 15.242188 6.128906 C 15.878906 6.15625 16.371094 6.6875 16.371094 7.324219 C 16.371094 7.988281 15.835938 8.523438 15.171875 8.523438 L 14.730469 8.523438 C 13.710938 8.523438 13.351562 9.492188 13.351562 10.585938 L 13.351562 12.023438 L 15.222656 12.023438 C 15.8125 12.023438 16.265625 12.550781 16.175781 13.132812 L 16.066406 13.835938 C 15.992188 14.304688 15.589844 14.652344 15.113281 14.652344 L 13.351562 14.652344 L 13.351562 21.898438 C 18.234375 21.234375 22 17.0625 22 12 C 22 6.367188 17.339844 1.820312 11.664062 2.003906 Z M 11.664062 2.003906 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,5 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{{ .width }}" height="{{ .height }}" viewBox="0 0 24 24" version="1.1">
|
||||
<g id="surface3680">
|
||||
<path fill="currentColor" d="M 10.898438 2.101562 C 6.300781 2.601562 2.601562 6.300781 2.101562 10.800781 C 1.601562 15.5 4.300781 19.699219 8.398438 21.300781 C 8.699219 21.398438 9 21.199219 9 20.800781 L 9 19.199219 C 9 19.199219 8.601562 19.300781 8.101562 19.300781 C 6.699219 19.300781 6.101562 18.101562 6 17.398438 C 5.898438 17 5.699219 16.699219 5.398438 16.398438 C 5.101562 16.300781 5 16.300781 5 16.199219 C 5 16 5.300781 16 5.398438 16 C 6 16 6.5 16.699219 6.699219 17 C 7.199219 17.800781 7.800781 18 8.101562 18 C 8.5 18 8.800781 17.898438 9 17.800781 C 9.101562 17.101562 9.398438 16.398438 10 16 C 7.699219 15.5 6 14.199219 6 12 C 6 10.898438 6.5 9.800781 7.199219 9 C 7.101562 8.800781 7 8.300781 7 7.601562 C 7 7.199219 7 6.601562 7.300781 6 C 7.300781 6 8.699219 6 10.101562 7.300781 C 10.601562 7.101562 11.300781 7 12 7 C 12.699219 7 13.398438 7.101562 14 7.300781 C 15.300781 6 16.800781 6 16.800781 6 C 17 6.601562 17 7.199219 17 7.601562 C 17 8.398438 16.898438 8.800781 16.800781 9 C 17.5 9.800781 18 10.800781 18 12 C 18 14.199219 16.300781 15.5 14 16 C 14.601562 16.5 15 17.398438 15 18.300781 L 15 20.898438 C 15 21.199219 15.300781 21.5 15.699219 21.398438 C 19.398438 19.898438 22 16.300781 22 12.101562 C 22 6.101562 16.898438 1.398438 10.898438 2.101562 Z M 10.898438 2.101562 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1 +0,0 @@
|
||||
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="{{ .width }}" height="{{ .height }}"><path fill="currentColor" d="M 11.46875 5 C 7.917969 5 5 7.914063 5 11.46875 L 5 20.53125 C 5 24.082031 7.914063 27 11.46875 27 L 20.53125 27 C 24.082031 27 27 24.085938 27 20.53125 L 27 11.46875 C 27 7.917969 24.085938 5 20.53125 5 Z M 11.46875 7 L 20.53125 7 C 23.003906 7 25 8.996094 25 11.46875 L 25 20.53125 C 25 23.003906 23.003906 25 20.53125 25 L 11.46875 25 C 8.996094 25 7 23.003906 7 20.53125 L 7 11.46875 C 7 8.996094 8.996094 7 11.46875 7 Z M 21.90625 9.1875 C 21.402344 9.1875 21 9.589844 21 10.09375 C 21 10.597656 21.402344 11 21.90625 11 C 22.410156 11 22.8125 10.597656 22.8125 10.09375 C 22.8125 9.589844 22.410156 9.1875 21.90625 9.1875 Z M 16 10 C 12.699219 10 10 12.699219 10 16 C 10 19.300781 12.699219 22 16 22 C 19.300781 22 22 19.300781 22 16 C 22 12.699219 19.300781 10 16 10 Z M 16 12 C 18.222656 12 20 13.777344 20 16 C 20 18.222656 18.222656 20 16 20 C 13.777344 20 12 18.222656 12 16 C 12 13.777344 13.777344 12 16 12 Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="#000000" viewBox="0 0 30 30" width="{{ .width }}" height="{{ .height }}"><path fill="currentColor" d="M24,4H6C4.895,4,4,4.895,4,6v18c0,1.105,0.895,2,2,2h18c1.105,0,2-0.895,2-2V6C26,4.895,25.105,4,24,4z M10.954,22h-2.95 v-9.492h2.95V22z M9.449,11.151c-0.951,0-1.72-0.771-1.72-1.72c0-0.949,0.77-1.719,1.72-1.719c0.948,0,1.719,0.771,1.719,1.719 C11.168,10.38,10.397,11.151,9.449,11.151z M22.004,22h-2.948v-4.616c0-1.101-0.02-2.517-1.533-2.517 c-1.535,0-1.771,1.199-1.771,2.437V22h-2.948v-9.492h2.83v1.297h0.04c0.394-0.746,1.356-1.533,2.791-1.533 c2.987,0,3.539,1.966,3.539,4.522V22z"/></svg>
|
||||
|
Before Width: | Height: | Size: 635 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="#000000" viewBox="0 0 30 30" width="{{ .width }}" height="{{ .height }}"><path fill="currentColor" d="M24,4H6C4.895,4,4,4.895,4,6v18c0,1.105,0.895,2,2,2h18c1.105,0,2-0.895,2-2V6C26,4.895,25.105,4,24,4z M22.542,21h-5.931 v-0.333L18,19.445V12.47L14.589,21h-0.533l-3.806-8.597v6.087l1.74,2.177V21H7.458v-0.333l1.799-2.177v-7.242L7.658,9.249 c0,0,0-0.289,0-0.244h4.376l3.399,7.353l2.932-7.353h4.154v0.244L21,10.526v8.919l1.542,1.222V21z"/></svg>
|
||||
|
Before Width: | Height: | Size: 488 B |
@@ -1 +0,0 @@
|
||||
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="{{ .width }}" height="{{ .height }}"><path fill="currentColor" d="M 9.914063 0.71875 L 9.085938 1.28125 L 11.894531 5.367188 L 12.71875 4.800781 Z M 7.695313 2.453125 L 7.039063 3.207031 L 10.917969 6.582031 L 11.574219 5.828125 Z M 6.226563 4.804688 L 5.773438 5.695313 L 10.210938 7.953125 L 10.664063 7.0625 Z M 5.363281 7.140625 L 5.136719 8.109375 L 9.976563 9.242188 L 10.203125 8.265625 Z M 3 9 L 3 14 L 12 14 L 12 9 L 11 9 L 11 13 L 4 13 L 4 9 Z M 5.03125 9.25 L 4.96875 10.25 L 9.972656 10.566406 L 10.035156 9.570313 Z M 5 11 L 5 12 L 10 12 L 10 11 Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 651 B |
@@ -1 +0,0 @@
|
||||
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="{{ .width }}" height="{{ .height }}"><path fill="currentColor" d="M 25 3 C 13.59 3 4.209375 11.680781 3.109375 22.800781 L 14.300781 28.529297 C 15.430781 27.579297 16.9 27 18.5 27 L 18.550781 27 C 18.940781 26.4 19.389375 25.649141 19.859375 24.869141 C 20.839375 23.259141 21.939531 21.439062 23.019531 20.039062 C 23.259531 15.569063 26.97 12 31.5 12 C 36.19 12 40 15.81 40 20.5 C 40 25.03 36.430937 28.740469 31.960938 28.980469 C 30.560938 30.060469 28.750859 31.160859 27.130859 32.130859 C 26.350859 32.610859 25.6 33.059219 25 33.449219 L 25 33.5 C 25 37.09 22.09 40 18.5 40 C 14.91 40 12 37.09 12 33.5 C 12 33.33 12.009531 33.17 12.019531 33 L 3.2792969 28.519531 C 4.9692969 38.999531 14.05 47 25 47 C 37.15 47 47 37.15 47 25 C 47 12.85 37.15 3 25 3 z M 31.5 14 C 27.92 14 25 16.92 25 20.5 C 25 24.08 27.92 27 31.5 27 C 35.08 27 38 24.08 38 20.5 C 38 16.92 35.08 14 31.5 14 z M 31.5 16 C 33.99 16 36 18.01 36 20.5 C 36 22.99 33.99 25 31.5 25 C 29.01 25 27 22.99 27 20.5 C 27 18.01 29.01 16 31.5 16 z M 18.5 29 C 17.71 29 16.960313 29.200312 16.320312 29.570312 L 19.640625 31.269531 C 20.870625 31.899531 21.350469 33.410625 20.730469 34.640625 C 20.280469 35.500625 19.41 36 18.5 36 C 18.11 36 17.729375 35.910469 17.359375 35.730469 L 14.029297 34.019531 C 14.289297 36.259531 16.19 38 18.5 38 C 20.99 38 23 35.99 23 33.5 C 23 31.01 20.99 29 18.5 29 z"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1 +0,0 @@
|
||||
<svg data-name="telegram" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="{{ .width }}" height="{{ .height }}"><path fill="currentColor" d="M 26.070313 3.996094 C 25.734375 4.011719 25.417969 4.109375 25.136719 4.21875 L 25.132813 4.21875 C 24.847656 4.332031 23.492188 4.902344 21.433594 5.765625 C 19.375 6.632813 16.703125 7.757813 14.050781 8.875 C 8.753906 11.105469 3.546875 13.300781 3.546875 13.300781 L 3.609375 13.277344 C 3.609375 13.277344 3.25 13.394531 2.875 13.652344 C 2.683594 13.777344 2.472656 13.949219 2.289063 14.21875 C 2.105469 14.488281 1.957031 14.902344 2.011719 15.328125 C 2.101563 16.050781 2.570313 16.484375 2.90625 16.722656 C 3.246094 16.964844 3.570313 17.078125 3.570313 17.078125 L 3.578125 17.078125 L 8.460938 18.722656 C 8.679688 19.425781 9.949219 23.597656 10.253906 24.558594 C 10.433594 25.132813 10.609375 25.492188 10.828125 25.765625 C 10.933594 25.90625 11.058594 26.023438 11.207031 26.117188 C 11.265625 26.152344 11.328125 26.179688 11.390625 26.203125 C 11.410156 26.214844 11.429688 26.21875 11.453125 26.222656 L 11.402344 26.210938 C 11.417969 26.214844 11.429688 26.226563 11.441406 26.230469 C 11.480469 26.242188 11.507813 26.246094 11.558594 26.253906 C 12.332031 26.488281 12.953125 26.007813 12.953125 26.007813 L 12.988281 25.980469 L 15.871094 23.355469 L 20.703125 27.0625 L 20.8125 27.109375 C 21.820313 27.550781 22.839844 27.304688 23.378906 26.871094 C 23.921875 26.433594 24.132813 25.875 24.132813 25.875 L 24.167969 25.785156 L 27.902344 6.65625 C 28.007813 6.183594 28.035156 5.742188 27.917969 5.3125 C 27.800781 4.882813 27.5 4.480469 27.136719 4.265625 C 26.769531 4.046875 26.40625 3.980469 26.070313 3.996094 Z M 25.96875 6.046875 C 25.964844 6.109375 25.976563 6.101563 25.949219 6.222656 L 25.949219 6.234375 L 22.25 25.164063 C 22.234375 25.191406 22.207031 25.25 22.132813 25.308594 C 22.054688 25.371094 21.992188 25.410156 21.667969 25.28125 L 15.757813 20.75 L 12.1875 24.003906 L 12.9375 19.214844 C 12.9375 19.214844 22.195313 10.585938 22.59375 10.214844 C 22.992188 9.84375 22.859375 9.765625 22.859375 9.765625 C 22.886719 9.3125 22.257813 9.632813 22.257813 9.632813 L 10.082031 17.175781 L 10.078125 17.15625 L 4.242188 15.191406 L 4.242188 15.1875 C 4.238281 15.1875 4.230469 15.183594 4.226563 15.183594 C 4.230469 15.183594 4.257813 15.171875 4.257813 15.171875 L 4.289063 15.15625 L 4.320313 15.144531 C 4.320313 15.144531 9.53125 12.949219 14.828125 10.71875 C 17.480469 9.601563 20.152344 8.476563 22.207031 7.609375 C 24.261719 6.746094 25.78125 6.113281 25.867188 6.078125 C 25.949219 6.046875 25.910156 6.046875 25.96875 6.046875 Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -1 +0,0 @@
|
||||
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 30" width="{{ .width }}" height="{{ .height }}"><path fill="currentColor" d="M 6 4 L 4 8 L 4 24 L 9 24 L 9 27 L 13 27 L 16 24 L 20 24 L 26 18 L 26 4 L 6 4 z M 8 6 L 24 6 L 24 17 L 21 20 L 15 20 L 12 23 L 12 20 L 8 20 L 8 6 z M 13 9 L 13 16 L 15 16 L 15 9 L 13 9 z M 17 9 L 17 16 L 19 16 L 19 9 L 17 9 z"/></svg>
|
||||
|
Before Width: | Height: | Size: 382 B |
@@ -1,5 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{{ .width }}" height="{{ .height }}" viewBox="0 0 32 32" version="1.1">
|
||||
<g id="surface676">
|
||||
<path fill="currentColor" d="M 28 8.558594 C 27.117188 8.949219 26.167969 9.214844 25.171875 9.332031 C 26.1875 8.722656 26.96875 7.757812 27.335938 6.609375 C 26.386719 7.171875 25.332031 7.582031 24.210938 7.804688 C 23.3125 6.847656 22.03125 6.246094 20.617188 6.246094 C 17.898438 6.246094 15.691406 8.453125 15.691406 11.171875 C 15.691406 11.558594 15.734375 11.933594 15.820312 12.292969 C 11.726562 12.089844 8.097656 10.128906 5.671875 7.148438 C 5.246094 7.875 5.003906 8.722656 5.003906 9.625 C 5.003906 11.332031 5.871094 12.839844 7.195312 13.722656 C 6.386719 13.695312 5.628906 13.476562 4.964844 13.105469 C 4.964844 13.128906 4.964844 13.148438 4.964844 13.167969 C 4.964844 15.554688 6.660156 17.546875 8.914062 17.996094 C 8.5 18.109375 8.066406 18.171875 7.617188 18.171875 C 7.300781 18.171875 6.988281 18.140625 6.691406 18.082031 C 7.316406 20.039062 9.136719 21.460938 11.289062 21.503906 C 9.605469 22.824219 7.480469 23.609375 5.175781 23.609375 C 4.777344 23.609375 4.386719 23.585938 4 23.539062 C 6.179688 24.9375 8.765625 25.753906 11.546875 25.753906 C 20.605469 25.753906 25.558594 18.25 25.558594 11.742188 C 25.558594 11.53125 25.550781 11.316406 25.542969 11.105469 C 26.503906 10.410156 27.339844 9.542969 28 8.558594 Z M 28 8.558594 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1 +0,0 @@
|
||||
<svg data-name="whatsapp" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="{{ .width }}" height="{{ .height }}"> <path d="M 12.011719 2 C 6.5057187 2 2.0234844 6.478375 2.0214844 11.984375 C 2.0204844 13.744375 2.4814687 15.462563 3.3554688 16.976562 L 2 22 L 7.2324219 20.763672 C 8.6914219 21.559672 10.333859 21.977516 12.005859 21.978516 L 12.009766 21.978516 C 17.514766 21.978516 21.995047 17.499141 21.998047 11.994141 C 22.000047 9.3251406 20.962172 6.8157344 19.076172 4.9277344 C 17.190172 3.0407344 14.683719 2.001 12.011719 2 z M 12.009766 4 C 14.145766 4.001 16.153109 4.8337969 17.662109 6.3417969 C 19.171109 7.8517969 20.000047 9.8581875 19.998047 11.992188 C 19.996047 16.396187 16.413812 19.978516 12.007812 19.978516 C 10.674812 19.977516 9.3544062 19.642812 8.1914062 19.007812 L 7.5175781 18.640625 L 6.7734375 18.816406 L 4.8046875 19.28125 L 5.2851562 17.496094 L 5.5019531 16.695312 L 5.0878906 15.976562 C 4.3898906 14.768562 4.0204844 13.387375 4.0214844 11.984375 C 4.0234844 7.582375 7.6067656 4 12.009766 4 z M 8.4765625 7.375 C 8.3095625 7.375 8.0395469 7.4375 7.8105469 7.6875 C 7.5815469 7.9365 6.9355469 8.5395781 6.9355469 9.7675781 C 6.9355469 10.995578 7.8300781 12.182609 7.9550781 12.349609 C 8.0790781 12.515609 9.68175 15.115234 12.21875 16.115234 C 14.32675 16.946234 14.754891 16.782234 15.212891 16.740234 C 15.670891 16.699234 16.690438 16.137687 16.898438 15.554688 C 17.106437 14.971687 17.106922 14.470187 17.044922 14.367188 C 16.982922 14.263188 16.816406 14.201172 16.566406 14.076172 C 16.317406 13.951172 15.090328 13.348625 14.861328 13.265625 C 14.632328 13.182625 14.464828 13.140625 14.298828 13.390625 C 14.132828 13.640625 13.655766 14.201187 13.509766 14.367188 C 13.363766 14.534188 13.21875 14.556641 12.96875 14.431641 C 12.71875 14.305641 11.914938 14.041406 10.960938 13.191406 C 10.218937 12.530406 9.7182656 11.714844 9.5722656 11.464844 C 9.4272656 11.215844 9.5585938 11.079078 9.6835938 10.955078 C 9.7955938 10.843078 9.9316406 10.663578 10.056641 10.517578 C 10.180641 10.371578 10.223641 10.267562 10.306641 10.101562 C 10.389641 9.9355625 10.347156 9.7890625 10.285156 9.6640625 C 10.223156 9.5390625 9.737625 8.3065 9.515625 7.8125 C 9.328625 7.3975 9.131125 7.3878594 8.953125 7.3808594 C 8.808125 7.3748594 8.6425625 7.375 8.4765625 7.375 z"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB |
@@ -1,27 +0,0 @@
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>{{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }}</title>
|
||||
<link>{{ .Permalink }}</link>
|
||||
<description>Recent content {{ if ne .Title .Site.Title }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }}</description>
|
||||
<generator>Hugo -- gohugo.io</generator>{{ with .Site.LanguageCode }}
|
||||
<language>{{.}}</language>{{end}}{{ with .Site.Author.email }}
|
||||
<managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Author.email }}
|
||||
<webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
|
||||
<copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
|
||||
<lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
|
||||
<atom:link href="{{.Permalink}}" rel="self" type="application/rss+xml" />
|
||||
|
||||
{{ range first 30 .Data.Pages }}
|
||||
<item>
|
||||
<title>{{ .Title }}</title>
|
||||
<link>{{ .Permalink }}</link>
|
||||
<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
|
||||
{{ with .Params.author }}<author>{{.}}</author>{{end}}
|
||||
<guid>{{ .Permalink }}</guid>
|
||||
<description>
|
||||
{{ (replace .Content "" "") | html }}
|
||||
</description>
|
||||
</item>
|
||||
{{ end }}
|
||||
</channel>
|
||||
</rss>
|
||||
@@ -1,17 +0,0 @@
|
||||
[build]
|
||||
publish = "site/public"
|
||||
|
||||
[build.environment]
|
||||
HUGO_VERSION = "0.74.3"
|
||||
HUGO_THEME = "repo"
|
||||
|
||||
[context.production]
|
||||
command = "cd site && hugo --gc --themesDir ../.. -b ${URL}"
|
||||
[context.production.environment]
|
||||
HUGO_ENV = "production"
|
||||
|
||||
[context.branch-deploy]
|
||||
command = "cd site && hugo --gc --themesDir ../.. -b ${DEPLOY_PRIME_URL}"
|
||||
|
||||
[context.deploy-preview]
|
||||
command = "cd site && hugo --gc --themesDir ../.. -b ${DEPLOY_PRIME_URL}"
|
||||
@@ -1 +0,0 @@
|
||||
{"Target":"sass/main.min.96090b4177a3194fa2de0860f2c55524d6582b68a41222fe4030905ef033075a.css","MediaType":"text/css","Data":{"Integrity":"sha256-lgkLQXejGU+i3ghg8sVVJNZYK2ikEiL+QDCQXvAzB1o="}}
|
||||
@@ -1,35 +0,0 @@
|
||||
baseurl: 'https://services.m3o.com'
|
||||
languageCode: 'en-us'
|
||||
title: 'Micro Services'
|
||||
themesDir: '../..'
|
||||
theme: 'hugo-tania'
|
||||
summaryLength: 10
|
||||
|
||||
params:
|
||||
source: "https://github.com/micro/services"
|
||||
titleEmoji: ''
|
||||
socialOptions:
|
||||
github:
|
||||
twitter:
|
||||
|
||||
menu:
|
||||
header:
|
||||
- name: Explore
|
||||
url: '/explore/'
|
||||
footer:
|
||||
- name: '© 2020 Micro Services, Inc.'
|
||||
url: 'https://m3o.com'
|
||||
|
||||
markup:
|
||||
highlight:
|
||||
noClasses: false
|
||||
lineNos: true
|
||||
goldmark:
|
||||
renderer:
|
||||
unsafe: true
|
||||
|
||||
permalinks:
|
||||
post: /:filename
|
||||
|
||||
taxonomies:
|
||||
tag: "labels"
|
||||
@@ -1 +0,0 @@
|
||||
Explore, discover and use Micro Services.
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
title: Services
|
||||
subtitle: Explore Micro services API documentation, usage and specs
|
||||
date: 2020-11-26
|
||||
type: section
|
||||
layout: 'archives'
|
||||
---
|
||||
@@ -1,25 +0,0 @@
|
||||
# theme.toml template for a Hugo theme
|
||||
# See https://github.com/gohugoio/hugoThemes#themetoml for an example
|
||||
|
||||
name = "Tania"
|
||||
license = "MIT"
|
||||
licenselink = "https://github.com/WingLim/hugo-tania/blob/master/LICENSE"
|
||||
description = "A simple theme for bloggers"
|
||||
homepage = "https://hugo-tanhia.netlify.app"
|
||||
tags = [
|
||||
"blog",
|
||||
"responsive",
|
||||
"light",
|
||||
"dark"
|
||||
]
|
||||
features = ["darkmode"]
|
||||
min_version = "0.74.0"
|
||||
|
||||
[author]
|
||||
name = "WingLim"
|
||||
homepage = "https://limxw.com"
|
||||
|
||||
[original]
|
||||
author = "Tania Rascia"
|
||||
homepage = "https://www.taniarascia.com/"
|
||||
repo = "https://github.com/taniarascia/taniarascia.com"
|
||||
2
feeds/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
|
||||
feeds
|
||||
@@ -1,3 +0,0 @@
|
||||
FROM alpine
|
||||
ADD feeds /feeds
|
||||
ENTRYPOINT [ "/feeds" ]
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
GOPATH:=$(shell go env GOPATH)
|
||||
.PHONY: init
|
||||
init:
|
||||
go get -u github.com/golang/protobuf/proto
|
||||
go get -u github.com/golang/protobuf/protoc-gen-go
|
||||
go get github.com/micro/micro/v3/cmd/protoc-gen-micro
|
||||
.PHONY: proto
|
||||
proto:
|
||||
protoc --openapi_out=. --proto_path=. --micro_out=. --go_out=:. proto/feeds.proto
|
||||
|
||||
.PHONY: docs
|
||||
docs:
|
||||
protoc --openapi_out=. --proto_path=. --micro_out=. --go_out=:. proto/feeds.proto
|
||||
@redoc-cli bundle api-feeds.json
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go build -o feeds *.go
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -v ./... -cover
|
||||
|
||||
.PHONY: docker
|
||||
docker:
|
||||
docker build . -t feeds:latest
|
||||
@@ -1,6 +0,0 @@
|
||||
A single uniform API for crawling and indexing RSS feeds
|
||||
|
||||
# Feeds Service
|
||||
|
||||
Designed to populate the posts service with RSS feeds from other blogs. Useful for migration or just to get outside content into the posts service.
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
package main
|
||||
|
||||
//go:generate make proto
|
||||
@@ -1,133 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/SlyMarbo/rss"
|
||||
log "github.com/micro/micro/v3/service/logger"
|
||||
"github.com/micro/services/feeds/parser"
|
||||
feeds "github.com/micro/services/feeds/proto"
|
||||
posts "github.com/micro/services/posts/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
rssSync sync.RWMutex
|
||||
rssFeeds = map[string]*rss.Feed{}
|
||||
)
|
||||
|
||||
func (e *Feeds) fetchAll() {
|
||||
fs := []*feeds.Feed{}
|
||||
err := e.feeds.Read(e.feedsNameIndex.ToQuery(nil), &fs)
|
||||
if err != nil {
|
||||
log.Errorf("Error listing feeds: %v", err)
|
||||
return
|
||||
}
|
||||
if len(fs) == 0 {
|
||||
log.Infof("No feeds to fetch")
|
||||
return
|
||||
}
|
||||
for _, feed := range fs {
|
||||
err = e.fetch(feed)
|
||||
if err != nil {
|
||||
log.Errorf("Error saving post: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Feeds) fetch(f *feeds.Feed) error {
|
||||
url := f.Url
|
||||
log.Infof("Fetching address %v", url)
|
||||
|
||||
// see if there's an existing rss feed
|
||||
rssSync.RLock()
|
||||
fd, ok := rssFeeds[f.Name]
|
||||
rssSync.RUnlock()
|
||||
|
||||
if !ok {
|
||||
// create a new one if it doesn't exist
|
||||
var err error
|
||||
fd, err = rss.Fetch(f.Url)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error fetching address %v: %v", url, err)
|
||||
}
|
||||
// save the feed
|
||||
rssSync.Lock()
|
||||
rssFeeds[f.Name] = fd
|
||||
rssSync.Unlock()
|
||||
} else {
|
||||
// otherwise update the existing feed
|
||||
fd.Items = []*rss.Item{}
|
||||
fd.Unread = 0
|
||||
if err := fd.Update(); err != nil {
|
||||
return fmt.Errorf("Error updating address %v: %v", url, err)
|
||||
}
|
||||
}
|
||||
|
||||
// set the refresh time
|
||||
fd.Refresh = time.Now()
|
||||
domain := getDomain(url)
|
||||
|
||||
// range over the feed and save the items
|
||||
for _, item := range fd.Items {
|
||||
id := fmt.Sprintf("%x", md5.Sum([]byte(item.ID)))
|
||||
|
||||
err := e.entries.Create(feeds.Entry{
|
||||
Id: id,
|
||||
Url: item.Link,
|
||||
Title: item.Title,
|
||||
Domain: domain,
|
||||
Content: item.Summary,
|
||||
Date: item.Date.Unix(),
|
||||
Category: f.Category,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error saving item: %v", err)
|
||||
}
|
||||
|
||||
var tags []string
|
||||
if len(f.Category) > 0 {
|
||||
tags = append(tags, f.Category)
|
||||
}
|
||||
|
||||
// check if content exists
|
||||
content := item.Content
|
||||
if len(content) == 0 && len(item.Summary) > 0 {
|
||||
content = item.Summary
|
||||
}
|
||||
|
||||
// if we have a parser which returns content use it
|
||||
// e.g cnbc
|
||||
c, err := parser.Parse(item.Link)
|
||||
if err == nil && len(c) > 0 {
|
||||
content = c
|
||||
}
|
||||
|
||||
// @todo make this optional
|
||||
_, err = e.postsService.Save(context.TODO(), &posts.SaveRequest{
|
||||
Id: id,
|
||||
Title: item.Title,
|
||||
Content: content,
|
||||
Timestamp: item.Date.Unix(),
|
||||
Metadata: map[string]string{
|
||||
"domain": domain,
|
||||
"link": item.Link,
|
||||
},
|
||||
Tags: tags,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getDomain(address string) string {
|
||||
uri, _ := url.Parse(address)
|
||||
return uri.Host
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/micro/micro/v3/service/errors"
|
||||
log "github.com/micro/micro/v3/service/logger"
|
||||
"github.com/micro/micro/v3/service/model"
|
||||
|
||||
feeds "github.com/micro/services/feeds/proto"
|
||||
posts "github.com/micro/services/posts/proto"
|
||||
)
|
||||
|
||||
type Feeds struct {
|
||||
feeds model.Model
|
||||
entries model.Model
|
||||
postsService posts.PostsService
|
||||
feedsIdIndex model.Index
|
||||
feedsNameIndex model.Index
|
||||
entriesDateIndex model.Index
|
||||
entriesURLIndex model.Index
|
||||
}
|
||||
|
||||
func NewFeeds(postsService posts.PostsService) *Feeds {
|
||||
idIndex := model.ByEquality("url")
|
||||
idIndex.Order.Type = model.OrderTypeUnordered
|
||||
|
||||
nameIndex := model.ByEquality("name")
|
||||
nameIndex.Unique = true
|
||||
nameIndex.Order.Type = model.OrderTypeUnordered
|
||||
|
||||
dateIndex := model.ByEquality("date")
|
||||
dateIndex.Order.Type = model.OrderTypeDesc
|
||||
|
||||
entriesURLIndex := model.ByEquality("url")
|
||||
entriesURLIndex.Order.Type = model.OrderTypeDesc
|
||||
entriesURLIndex.Order.FieldName = "date"
|
||||
|
||||
f := &Feeds{
|
||||
feeds: model.NewModel(
|
||||
model.WithKey("Name"),
|
||||
model.WithNamespace("feeds"),
|
||||
model.WithIndexes(nameIndex),
|
||||
),
|
||||
entries: model.NewModel(
|
||||
model.WithNamespace("entries"),
|
||||
model.WithIndexes(dateIndex, entriesURLIndex),
|
||||
),
|
||||
postsService: postsService,
|
||||
feedsIdIndex: idIndex,
|
||||
feedsNameIndex: nameIndex,
|
||||
entriesDateIndex: dateIndex,
|
||||
entriesURLIndex: entriesURLIndex,
|
||||
}
|
||||
|
||||
// register model instances
|
||||
f.feeds.Register(new(feeds.Feed))
|
||||
f.entries.Register(new(feeds.Entry))
|
||||
|
||||
go f.crawl()
|
||||
return f
|
||||
}
|
||||
|
||||
func (e *Feeds) crawl() {
|
||||
e.fetchAll()
|
||||
tick := time.NewTicker(1 * time.Minute)
|
||||
for _ = range tick.C {
|
||||
e.fetchAll()
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Feeds) Add(ctx context.Context, req *feeds.AddRequest, rsp *feeds.AddResponse) error {
|
||||
log.Info("Received Feeds.Add request")
|
||||
|
||||
if len(req.Name) == 0 {
|
||||
return errors.BadRequest("feeds.add", "require name")
|
||||
}
|
||||
|
||||
rssSync.RLock()
|
||||
defer rssSync.RUnlock()
|
||||
|
||||
// check if the feed already exists
|
||||
if _, ok := rssFeeds[req.Name]; ok {
|
||||
return errors.BadRequest("feeds.add", "%s already exists", req.Name)
|
||||
}
|
||||
|
||||
f := feeds.Feed{
|
||||
Name: req.Name,
|
||||
Url: req.Url,
|
||||
Category: req.Category,
|
||||
}
|
||||
|
||||
// create the feed
|
||||
e.feeds.Create(f)
|
||||
|
||||
// schedule immediate fetch
|
||||
go e.fetch(&f)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Feeds) Entries(ctx context.Context, req *feeds.EntriesRequest, rsp *feeds.EntriesResponse) error {
|
||||
log.Info("Received Feeds.Entries request")
|
||||
return e.entries.Read(e.entriesURLIndex.ToQuery(req.Url), &rsp.Entries)
|
||||
}
|
||||
|
||||
func (e *Feeds) List(ctx context.Context, req *feeds.ListRequest, rsp *feeds.ListResponse) error {
|
||||
var feeds []*feeds.Feed
|
||||
q := model.QueryAll()
|
||||
q.Index.FieldName = "Name"
|
||||
err := e.feeds.Read(q, &feeds)
|
||||
if err != nil {
|
||||
return errors.InternalServerError("feeds.list", "failed to read list of feeds: %v", err)
|
||||
}
|
||||
|
||||
rsp.Feeds = feeds
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Feeds) Remove(ctx context.Context, req *feeds.RemoveRequest, rsp *feeds.RemoveResponse) error {
|
||||
if len(req.Name) == 0 {
|
||||
return errors.BadRequest("feeds.remove", "blank name provided")
|
||||
}
|
||||
|
||||
e.feeds.Delete(model.QueryEquals("name", req.Name))
|
||||
return nil
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
pb "github.com/micro/services/feeds/proto"
|
||||
posts "github.com/micro/services/posts/proto"
|
||||
|
||||
"github.com/micro/services/feeds/handler"
|
||||
|
||||
"github.com/micro/micro/v3/service"
|
||||
"github.com/micro/micro/v3/service/logger"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create service
|
||||
srv := service.New(
|
||||
service.Name("feeds"),
|
||||
service.Version("latest"),
|
||||
)
|
||||
|
||||
// Register handler
|
||||
pb.RegisterFeedsHandler(srv.Server(), handler.NewFeeds(posts.NewPostsService("posts", srv.Client())))
|
||||
|
||||
// Run service
|
||||
if err := srv.Run(); err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
service feeds
|
||||
@@ -1,63 +0,0 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
var (
|
||||
parsers = map[string]Parser{
|
||||
"a16z.com": a16zParser,
|
||||
"cnbc.com": cnbcParser,
|
||||
"www.cnbc.com": cnbcParser,
|
||||
}
|
||||
)
|
||||
|
||||
type Parser func(string) (string, error)
|
||||
|
||||
func Parse(uri string) (string, error) {
|
||||
u, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if v, ok := parsers[u.Host]; ok {
|
||||
return v(uri)
|
||||
}
|
||||
return "", errors.New("no parser for url")
|
||||
}
|
||||
|
||||
func classParser(class string) Parser {
|
||||
return func(url string) (string, error) {
|
||||
// Request the HTML page.
|
||||
res, err := http.Get(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
return "", errors.New("bad status code")
|
||||
}
|
||||
|
||||
// Load the HTML document
|
||||
doc, err := goquery.NewDocumentFromReader(res.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return doc.Find(class).Html()
|
||||
}
|
||||
}
|
||||
|
||||
func a16zParser(url string) (string, error) {
|
||||
return classParser(".blog-content")(url)
|
||||
}
|
||||
|
||||
func cnbcParser(url string) (string, error) {
|
||||
return classParser(".PageBuilder-col-9")(url)
|
||||
}
|
||||
@@ -1,810 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.25.0
|
||||
// protoc v3.15.5
|
||||
// source: proto/feeds.proto
|
||||
|
||||
package feeds
|
||||
|
||||
import (
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
type Feed struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// rss feed name
|
||||
// eg. a16z
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
// rss feed url
|
||||
// eg. http://a16z.com/feed/
|
||||
Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"`
|
||||
// category of the feed
|
||||
Category string `protobuf:"bytes,3,opt,name=category,proto3" json:"category,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Feed) Reset() {
|
||||
*x = Feed{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_feeds_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Feed) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Feed) ProtoMessage() {}
|
||||
|
||||
func (x *Feed) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_feeds_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Feed.ProtoReflect.Descriptor instead.
|
||||
func (*Feed) Descriptor() ([]byte, []int) {
|
||||
return file_proto_feeds_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Feed) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Feed) GetUrl() string {
|
||||
if x != nil {
|
||||
return x.Url
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Feed) GetCategory() string {
|
||||
if x != nil {
|
||||
return x.Category
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
|
||||
Domain string `protobuf:"bytes,2,opt,name=domain,proto3" json:"domain,omitempty"`
|
||||
Url string `protobuf:"bytes,3,opt,name=url,proto3" json:"url,omitempty"`
|
||||
Title string `protobuf:"bytes,4,opt,name=title,proto3" json:"title,omitempty"`
|
||||
Content string `protobuf:"bytes,5,opt,name=content,proto3" json:"content,omitempty"`
|
||||
Date int64 `protobuf:"varint,6,opt,name=date,proto3" json:"date,omitempty"`
|
||||
Category string `protobuf:"bytes,7,opt,name=category,proto3" json:"category,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Entry) Reset() {
|
||||
*x = Entry{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_feeds_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Entry) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Entry) ProtoMessage() {}
|
||||
|
||||
func (x *Entry) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_feeds_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Entry.ProtoReflect.Descriptor instead.
|
||||
func (*Entry) Descriptor() ([]byte, []int) {
|
||||
return file_proto_feeds_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Entry) GetId() string {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Entry) GetDomain() string {
|
||||
if x != nil {
|
||||
return x.Domain
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Entry) GetUrl() string {
|
||||
if x != nil {
|
||||
return x.Url
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Entry) GetTitle() string {
|
||||
if x != nil {
|
||||
return x.Title
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Entry) GetContent() string {
|
||||
if x != nil {
|
||||
return x.Content
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Entry) GetDate() int64 {
|
||||
if x != nil {
|
||||
return x.Date
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Entry) GetCategory() string {
|
||||
if x != nil {
|
||||
return x.Category
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type AddRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// rss feed name
|
||||
// eg. a16z
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
// rss feed url
|
||||
// eg. http://a16z.com/feed/
|
||||
Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"`
|
||||
// category to add
|
||||
Category string `protobuf:"bytes,3,opt,name=category,proto3" json:"category,omitempty"`
|
||||
}
|
||||
|
||||
func (x *AddRequest) Reset() {
|
||||
*x = AddRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_feeds_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *AddRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AddRequest) ProtoMessage() {}
|
||||
|
||||
func (x *AddRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_feeds_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AddRequest.ProtoReflect.Descriptor instead.
|
||||
func (*AddRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_feeds_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *AddRequest) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AddRequest) GetUrl() string {
|
||||
if x != nil {
|
||||
return x.Url
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AddRequest) GetCategory() string {
|
||||
if x != nil {
|
||||
return x.Category
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type AddResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *AddResponse) Reset() {
|
||||
*x = AddResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_feeds_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *AddResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AddResponse) ProtoMessage() {}
|
||||
|
||||
func (x *AddResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_feeds_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AddResponse.ProtoReflect.Descriptor instead.
|
||||
func (*AddResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_feeds_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
type EntriesRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// rss feed url
|
||||
// eg. http://a16z.com/feed/
|
||||
Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
|
||||
}
|
||||
|
||||
func (x *EntriesRequest) Reset() {
|
||||
*x = EntriesRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_feeds_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *EntriesRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*EntriesRequest) ProtoMessage() {}
|
||||
|
||||
func (x *EntriesRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_feeds_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use EntriesRequest.ProtoReflect.Descriptor instead.
|
||||
func (*EntriesRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_feeds_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *EntriesRequest) GetUrl() string {
|
||||
if x != nil {
|
||||
return x.Url
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type EntriesResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Entries []*Entry `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"`
|
||||
}
|
||||
|
||||
func (x *EntriesResponse) Reset() {
|
||||
*x = EntriesResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_feeds_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *EntriesResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*EntriesResponse) ProtoMessage() {}
|
||||
|
||||
func (x *EntriesResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_feeds_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use EntriesResponse.ProtoReflect.Descriptor instead.
|
||||
func (*EntriesResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_feeds_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *EntriesResponse) GetEntries() []*Entry {
|
||||
if x != nil {
|
||||
return x.Entries
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *ListRequest) Reset() {
|
||||
*x = ListRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_feeds_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ListRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ListRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_feeds_proto_msgTypes[6]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ListRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_feeds_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
type ListResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Feeds []*Feed `protobuf:"bytes,1,rep,name=feeds,proto3" json:"feeds,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ListResponse) Reset() {
|
||||
*x = ListResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_feeds_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ListResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ListResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_feeds_proto_msgTypes[7]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ListResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_feeds_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *ListResponse) GetFeeds() []*Feed {
|
||||
if x != nil {
|
||||
return x.Feeds
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RemoveRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// rss feed name
|
||||
// eg. a16z
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
}
|
||||
|
||||
func (x *RemoveRequest) Reset() {
|
||||
*x = RemoveRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_feeds_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *RemoveRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RemoveRequest) ProtoMessage() {}
|
||||
|
||||
func (x *RemoveRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_feeds_proto_msgTypes[8]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RemoveRequest.ProtoReflect.Descriptor instead.
|
||||
func (*RemoveRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_feeds_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *RemoveRequest) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type RemoveResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *RemoveResponse) Reset() {
|
||||
*x = RemoveResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_feeds_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *RemoveResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RemoveResponse) ProtoMessage() {}
|
||||
|
||||
func (x *RemoveResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_feeds_proto_msgTypes[9]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RemoveResponse.ProtoReflect.Descriptor instead.
|
||||
func (*RemoveResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_feeds_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
var File_proto_feeds_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_proto_feeds_proto_rawDesc = []byte{
|
||||
0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x66, 0x65, 0x65, 0x64, 0x73, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x12, 0x05, 0x66, 0x65, 0x65, 0x64, 0x73, 0x22, 0x48, 0x0a, 0x04, 0x46, 0x65,
|
||||
0x65, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x65,
|
||||
0x67, 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x74, 0x65,
|
||||
0x67, 0x6f, 0x72, 0x79, 0x22, 0xa1, 0x01, 0x0a, 0x05, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x0e,
|
||||
0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16,
|
||||
0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
|
||||
0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c,
|
||||
0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x18,
|
||||
0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x65,
|
||||
0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x64, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x0a, 0x08,
|
||||
0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
||||
0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x22, 0x4e, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72,
|
||||
0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08,
|
||||
0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
||||
0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x22, 0x0d, 0x0a, 0x0b, 0x41, 0x64, 0x64, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x0a, 0x0e, 0x45, 0x6e, 0x74, 0x72, 0x69,
|
||||
0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x39, 0x0a, 0x0f, 0x45,
|
||||
0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26,
|
||||
0x0a, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||
0x0c, 0x2e, 0x66, 0x65, 0x65, 0x64, 0x73, 0x2e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65,
|
||||
0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0x0d, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x31, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x05, 0x66, 0x65, 0x65, 0x64, 0x73, 0x18, 0x01,
|
||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x66, 0x65, 0x65, 0x64, 0x73, 0x2e, 0x46, 0x65, 0x65,
|
||||
0x64, 0x52, 0x05, 0x66, 0x65, 0x65, 0x64, 0x73, 0x22, 0x23, 0x0a, 0x0d, 0x52, 0x65, 0x6d, 0x6f,
|
||||
0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
|
||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x10, 0x0a,
|
||||
0x0e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32,
|
||||
0xdf, 0x01, 0x0a, 0x05, 0x46, 0x65, 0x65, 0x64, 0x73, 0x12, 0x2e, 0x0a, 0x03, 0x41, 0x64, 0x64,
|
||||
0x12, 0x11, 0x2e, 0x66, 0x65, 0x65, 0x64, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x66, 0x65, 0x65, 0x64, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x06, 0x52, 0x65, 0x6d,
|
||||
0x6f, 0x76, 0x65, 0x12, 0x14, 0x2e, 0x66, 0x65, 0x65, 0x64, 0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f,
|
||||
0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x66, 0x65, 0x65, 0x64,
|
||||
0x73, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x22, 0x00, 0x12, 0x3a, 0x0a, 0x07, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x15, 0x2e,
|
||||
0x66, 0x65, 0x65, 0x64, 0x73, 0x2e, 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x66, 0x65, 0x65, 0x64, 0x73, 0x2e, 0x45, 0x6e, 0x74,
|
||||
0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x31,
|
||||
0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x12, 0x2e, 0x66, 0x65, 0x65, 0x64, 0x73, 0x2e, 0x4c,
|
||||
0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x66, 0x65, 0x65,
|
||||
0x64, 0x73, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||
0x00, 0x42, 0x0d, 0x5a, 0x0b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x66, 0x65, 0x65, 0x64, 0x73,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_proto_feeds_proto_rawDescOnce sync.Once
|
||||
file_proto_feeds_proto_rawDescData = file_proto_feeds_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_proto_feeds_proto_rawDescGZIP() []byte {
|
||||
file_proto_feeds_proto_rawDescOnce.Do(func() {
|
||||
file_proto_feeds_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_feeds_proto_rawDescData)
|
||||
})
|
||||
return file_proto_feeds_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_proto_feeds_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
|
||||
var file_proto_feeds_proto_goTypes = []interface{}{
|
||||
(*Feed)(nil), // 0: feeds.Feed
|
||||
(*Entry)(nil), // 1: feeds.Entry
|
||||
(*AddRequest)(nil), // 2: feeds.AddRequest
|
||||
(*AddResponse)(nil), // 3: feeds.AddResponse
|
||||
(*EntriesRequest)(nil), // 4: feeds.EntriesRequest
|
||||
(*EntriesResponse)(nil), // 5: feeds.EntriesResponse
|
||||
(*ListRequest)(nil), // 6: feeds.ListRequest
|
||||
(*ListResponse)(nil), // 7: feeds.ListResponse
|
||||
(*RemoveRequest)(nil), // 8: feeds.RemoveRequest
|
||||
(*RemoveResponse)(nil), // 9: feeds.RemoveResponse
|
||||
}
|
||||
var file_proto_feeds_proto_depIdxs = []int32{
|
||||
1, // 0: feeds.EntriesResponse.entries:type_name -> feeds.Entry
|
||||
0, // 1: feeds.ListResponse.feeds:type_name -> feeds.Feed
|
||||
2, // 2: feeds.Feeds.Add:input_type -> feeds.AddRequest
|
||||
8, // 3: feeds.Feeds.Remove:input_type -> feeds.RemoveRequest
|
||||
4, // 4: feeds.Feeds.Entries:input_type -> feeds.EntriesRequest
|
||||
6, // 5: feeds.Feeds.List:input_type -> feeds.ListRequest
|
||||
3, // 6: feeds.Feeds.Add:output_type -> feeds.AddResponse
|
||||
9, // 7: feeds.Feeds.Remove:output_type -> feeds.RemoveResponse
|
||||
5, // 8: feeds.Feeds.Entries:output_type -> feeds.EntriesResponse
|
||||
7, // 9: feeds.Feeds.List:output_type -> feeds.ListResponse
|
||||
6, // [6:10] is the sub-list for method output_type
|
||||
2, // [2:6] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_proto_feeds_proto_init() }
|
||||
func file_proto_feeds_proto_init() {
|
||||
if File_proto_feeds_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_proto_feeds_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Feed); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_feeds_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Entry); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_feeds_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*AddRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_feeds_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*AddResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_feeds_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*EntriesRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_feeds_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*EntriesResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_feeds_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ListRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_feeds_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ListResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_feeds_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*RemoveRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_feeds_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*RemoveResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_proto_feeds_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 10,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_proto_feeds_proto_goTypes,
|
||||
DependencyIndexes: file_proto_feeds_proto_depIdxs,
|
||||
MessageInfos: file_proto_feeds_proto_msgTypes,
|
||||
}.Build()
|
||||
File_proto_feeds_proto = out.File
|
||||
file_proto_feeds_proto_rawDesc = nil
|
||||
file_proto_feeds_proto_goTypes = nil
|
||||
file_proto_feeds_proto_depIdxs = nil
|
||||
}
|
||||