mirror of
https://github.com/usememos/memos.git
synced 2025-06-05 22:09:59 +02:00
feat: system api with profiles
This commit is contained in:
38
.github/workflows/build-and-push-release-image.yml
vendored
Normal file
38
.github/workflows/build-and-push-release-image.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
name: build-and-push-docker-image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
# Run on pushing branches like `release/1.0.0`
|
||||||
|
- "release/v*.*.*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Extract build args
|
||||||
|
# Extract version from branch name
|
||||||
|
# Example: branch name `release/v1.0.0` sets up env.VERSION=1.0.0
|
||||||
|
run: |
|
||||||
|
echo "VERSION=${GITHUB_REF_NAME#release/v}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_NEOSMEMO_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_NEOSMEMO_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
id: buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
|
||||||
|
- name: Build and Push
|
||||||
|
id: docker_build
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: ./
|
||||||
|
file: ./Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: ${{ secrets.DOCKER_NEOSMEMO_USERNAME }}/memos:${{ env.VERSION }}
|
@ -4,40 +4,28 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"memos/common"
|
||||||
"memos/server"
|
"memos/server"
|
||||||
"memos/store"
|
"memos/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Main struct {
|
type Main struct {
|
||||||
profile *Profile
|
profile *common.Profile
|
||||||
|
|
||||||
server *server.Server
|
server *server.Server
|
||||||
|
|
||||||
db *store.DB
|
db *store.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
func Execute() {
|
|
||||||
m := Main{}
|
|
||||||
profile := GetProfile()
|
|
||||||
m.profile = &profile
|
|
||||||
|
|
||||||
err := m.Run()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%+v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Main) Run() error {
|
func (m *Main) Run() error {
|
||||||
db := store.NewDB(m.profile.dsn, m.profile.mode)
|
db := store.NewDB(m.profile)
|
||||||
|
|
||||||
if err := db.Open(); err != nil {
|
if err := db.Open(); err != nil {
|
||||||
return fmt.Errorf("cannot open db: %w", err)
|
return fmt.Errorf("cannot open db: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
m.db = db
|
m.db = db
|
||||||
|
|
||||||
s := server.NewServer(m.profile.port, m.profile.mode)
|
s := server.NewServer(m.profile)
|
||||||
|
|
||||||
s.ShortcutService = store.NewShortcutService(db)
|
s.ShortcutService = store.NewShortcutService(db)
|
||||||
s.MemoService = store.NewMemoService(db)
|
s.MemoService = store.NewMemoService(db)
|
||||||
@ -53,3 +41,16 @@ func (m *Main) Run() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Execute() {
|
||||||
|
profile := common.GetProfile()
|
||||||
|
m := Main{
|
||||||
|
profile: &profile,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := m.Run()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%+v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package cmd
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -9,12 +9,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Profile struct {
|
type Profile struct {
|
||||||
// mode can be "release" or "dev"
|
// Mode can be "release" or "dev"
|
||||||
mode string
|
Mode string `json:"mode"`
|
||||||
// port is the binding port for server.
|
// Port is the binding port for server.
|
||||||
port int
|
Port int `json:"port"`
|
||||||
// dsn points to where Memos stores its own data
|
// DSN points to where Memos stores its own data
|
||||||
dsn string
|
DSN string `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkDSN(dataDir string) (string, error) {
|
func checkDSN(dataDir string) (string, error) {
|
||||||
@ -61,8 +61,8 @@ func GetProfile() Profile {
|
|||||||
dsn := fmt.Sprintf("file:%s/memos_%s.db", dataDir, mode)
|
dsn := fmt.Sprintf("file:%s/memos_%s.db", dataDir, mode)
|
||||||
|
|
||||||
return Profile{
|
return Profile{
|
||||||
mode: mode,
|
Mode: mode,
|
||||||
port: port,
|
Port: port,
|
||||||
dsn: dsn,
|
DSN: dsn,
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,6 +3,7 @@ package server
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"memos/api"
|
"memos/api"
|
||||||
|
"memos/common"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/securecookie"
|
"github.com/gorilla/securecookie"
|
||||||
@ -15,15 +16,15 @@ import (
|
|||||||
type Server struct {
|
type Server struct {
|
||||||
e *echo.Echo
|
e *echo.Echo
|
||||||
|
|
||||||
|
Profile *common.Profile
|
||||||
|
|
||||||
UserService api.UserService
|
UserService api.UserService
|
||||||
MemoService api.MemoService
|
MemoService api.MemoService
|
||||||
ShortcutService api.ShortcutService
|
ShortcutService api.ShortcutService
|
||||||
ResourceService api.ResourceService
|
ResourceService api.ResourceService
|
||||||
|
|
||||||
port int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(port int, mode string) *Server {
|
func NewServer(profile *common.Profile) *Server {
|
||||||
e := echo.New()
|
e := echo.New()
|
||||||
e.Debug = true
|
e.Debug = true
|
||||||
e.HideBanner = true
|
e.HideBanner = true
|
||||||
@ -46,17 +47,19 @@ func NewServer(port int, mode string) *Server {
|
|||||||
HTML5: true,
|
HTML5: true,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
// In dev mode, set the const secret key to make login session persistence.
|
||||||
secret := []byte("justmemos")
|
secret := []byte("justmemos")
|
||||||
if mode != "dev" {
|
if profile.Mode == "release" {
|
||||||
secret = securecookie.GenerateRandomKey(16)
|
secret = securecookie.GenerateRandomKey(16)
|
||||||
}
|
}
|
||||||
e.Use(session.Middleware(sessions.NewCookieStore(secret)))
|
e.Use(session.Middleware(sessions.NewCookieStore(secret)))
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
e: e,
|
e: e,
|
||||||
port: port,
|
Profile: profile,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Webhooks api skips auth checker.
|
||||||
webhookGroup := e.Group("/h")
|
webhookGroup := e.Group("/h")
|
||||||
s.registerWebhookRoutes(webhookGroup)
|
s.registerWebhookRoutes(webhookGroup)
|
||||||
|
|
||||||
@ -64,6 +67,7 @@ func NewServer(port int, mode string) *Server {
|
|||||||
apiGroup.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
|
apiGroup.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
return BasicAuthMiddleware(s.UserService, next)
|
return BasicAuthMiddleware(s.UserService, next)
|
||||||
})
|
})
|
||||||
|
s.registerSystemRoutes(apiGroup)
|
||||||
s.registerAuthRoutes(apiGroup)
|
s.registerAuthRoutes(apiGroup)
|
||||||
s.registerUserRoutes(apiGroup)
|
s.registerUserRoutes(apiGroup)
|
||||||
s.registerMemoRoutes(apiGroup)
|
s.registerMemoRoutes(apiGroup)
|
||||||
@ -74,5 +78,5 @@ func NewServer(port int, mode string) *Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) Run() error {
|
func (server *Server) Run() error {
|
||||||
return server.e.Start(fmt.Sprintf(":%d", server.port))
|
return server.e.Start(fmt.Sprintf(":%d", server.Profile.Port))
|
||||||
}
|
}
|
||||||
|
19
server/system.go
Normal file
19
server/system.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) registerSystemRoutes(g *echo.Group) {
|
||||||
|
g.GET("/ping", func(c echo.Context) error {
|
||||||
|
c.Response().Header().Set(echo.HeaderContentType, echo.MIMEApplicationJSONCharsetUTF8)
|
||||||
|
if err := json.NewEncoder(c.Response().Writer).Encode(composeResponse(s.Profile)); err != nil {
|
||||||
|
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to compose system profile").SetInternal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
@ -36,7 +36,7 @@ CREATE TABLE memo (
|
|||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
created_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
updated_ts BIGINT NOT NULL DEFAULT (strftime('%s', 'now')),
|
||||||
-- allowed row status are 'NORMAL', 'HIDDEN'.
|
-- allowed row status are 'NORMAL', 'PINNED', 'HIDDEN'.
|
||||||
row_status TEXT NOT NULL DEFAULT 'NORMAL',
|
row_status TEXT NOT NULL DEFAULT 'NORMAL',
|
||||||
content TEXT NOT NULL DEFAULT '',
|
content TEXT NOT NULL DEFAULT '',
|
||||||
creator_id INTEGER NOT NULL,
|
creator_id INTEGER NOT NULL,
|
||||||
@ -69,7 +69,7 @@ CREATE TABLE shortcut (
|
|||||||
title TEXT NOT NULL DEFAULT '',
|
title TEXT NOT NULL DEFAULT '',
|
||||||
payload TEXT NOT NULL DEFAULT '',
|
payload TEXT NOT NULL DEFAULT '',
|
||||||
creator_id INTEGER NOT NULL,
|
creator_id INTEGER NOT NULL,
|
||||||
-- allowed row status are 'NORMAL', 'ARCHIVED'.
|
-- allowed row status are 'NORMAL', 'PINNED'.
|
||||||
row_status TEXT NOT NULL DEFAULT 'NORMAL',
|
row_status TEXT NOT NULL DEFAULT 'NORMAL',
|
||||||
FOREIGN KEY(creator_id) REFERENCES users(id)
|
FOREIGN KEY(creator_id) REFERENCES users(id)
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
INSERT INTO memo
|
INSERT INTO
|
||||||
(`content`, `creator_id`)
|
memo (
|
||||||
|
`content`,
|
||||||
|
`creator_id`
|
||||||
|
)
|
||||||
VALUES
|
VALUES
|
||||||
('👋 Welcome to memos', 101);
|
(
|
||||||
|
'#memos 👋 Welcome to memos',
|
||||||
|
101
|
||||||
|
);
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"memos/common"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
@ -19,18 +20,17 @@ var seedFS embed.FS
|
|||||||
|
|
||||||
type DB struct {
|
type DB struct {
|
||||||
Db *sql.DB
|
Db *sql.DB
|
||||||
|
|
||||||
// datasource name
|
// datasource name
|
||||||
DSN string
|
DSN string
|
||||||
// mode: release or dev
|
// mode should be release or dev
|
||||||
mode string
|
mode string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDB returns a new instance of DB associated with the given datasource name.
|
// NewDB returns a new instance of DB associated with the given datasource name.
|
||||||
func NewDB(dsn string, mode string) *DB {
|
func NewDB(profile *common.Profile) *DB {
|
||||||
db := &DB{
|
db := &DB{
|
||||||
DSN: dsn,
|
DSN: profile.DSN,
|
||||||
mode: mode,
|
mode: profile.Mode,
|
||||||
}
|
}
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user