* cleanup

* cleanup go mod

* Remove doc generation

* add no integration test

* fix broken test

* rename workflow
This commit is contained in:
Asim Aslam
2021-05-17 13:01:13 +01:00
committed by GitHub
parent 0afdc8a369
commit cecabe336f
147 changed files with 8 additions and 10373 deletions

View File

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

View File

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

View File

@@ -1,3 +0,0 @@
FROM alpine
ADD blog /blog
ENTRYPOINT [ "/blog" ]

View File

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

View File

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

View File

@@ -1,3 +0,0 @@
package main
//go:generate make proto

View File

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

View File

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

View File

@@ -1 +0,0 @@
service blog

View File

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

View File

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

View File

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

View File

View File

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

View File

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

@@ -1,2 +0,0 @@
comment-service

View File

@@ -1,3 +0,0 @@
FROM alpine
ADD comments-service /comments-service
ENTRYPOINT [ "/comments-service" ]

View File

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

View File

@@ -1,3 +0,0 @@
# Comments Service
Still yet to implement

View File

@@ -1,3 +0,0 @@
package main
//go:generate make proto

View File

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

View File

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

View File

@@ -1 +0,0 @@
service comments

View File

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

View File

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

View File

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

View File

View File

@@ -1 +0,0 @@
exampleSite/resources

View File

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

View File

@@ -1,84 +0,0 @@
# Hugo Theme Tania
A simple theme for bloggers.
## Demo
[Example Site](https://hugo-tania.netlify.app/)
[![Netlify Status](https://api.netlify.com/api/v1/badges/bae5db51-7cc6-41e2-9615-029ade8aa264/deploy-status)](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)

View File

@@ -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());
})
}();

View File

@@ -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);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +0,0 @@
*,
*::before,
*::after {
box-sizing: border-box;
}

View File

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

View File

@@ -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);
}

View File

@@ -1,11 +0,0 @@
.my {
padding-top: 2rem;
a {
border: none;
margin-right: 1rem;
&:hover {
border: none;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
}
}
}

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -1,9 +0,0 @@
{{ partial "header.html" . }}
<div class="container">
<main>
<h1>404 NOT FOUND</h1>
</main>
</div>
{{ partial "footer.html" . }}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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>
&nbsp;
<a href="/{{ .Params.servicename }}-microjs"><u>Micro.js</u></a>
&nbsp;
<a href="/{{ .Params.servicename }}/api"><u>API Spec</u></a>
&nbsp;
<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 }}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1 +0,0 @@
{"Target":"sass/main.min.96090b4177a3194fa2de0860f2c55524d6582b68a41222fe4030905ef033075a.css","MediaType":"text/css","Data":{"Integrity":"sha256-lgkLQXejGU+i3ghg8sVVJNZYK2ikEiL+QDCQXvAzB1o="}}

View File

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

View File

@@ -1 +0,0 @@
Explore, discover and use Micro Services.

View File

@@ -1,7 +0,0 @@
---
title: Services
subtitle: Explore Micro services API documentation, usage and specs
date: 2020-11-26
type: section
layout: 'archives'
---

View File

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

View File

2
feeds/.gitignore vendored
View File

@@ -1,2 +0,0 @@
feeds

View File

@@ -1,3 +0,0 @@
FROM alpine
ADD feeds /feeds
ENTRYPOINT [ "/feeds" ]

View File

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

View File

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

View File

View File

@@ -1,3 +0,0 @@
package main
//go:generate make proto

View File

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

View File

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

View File

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

View File

@@ -1 +0,0 @@
service feeds

View File

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

View File

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

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