Skip to content

Djancyp/luna

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

56 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lunalogo Luna

Luna is a Golang-based framework that leverages the Echo web framework for server-side rendering (SSR) of React applications. It provides a seamless, full-stack development experience for building dynamic and performant web applications, making it ideal for full-stack developers looking to integrate the power of Golang on the backend with the flexibility of React on the frontend.

Warning

This framework is beta quality. Expect breaking changes and bugs

DEMO

Check this link for an example production: Demo

Table of Contents

Features

  • Full backend api support with Echo
  • SSR with React
  • Hot reloading development
  • Route based caching
  • Custom Middleware support

Getting Started

Installation

Ensure you have Go and Yarn or npm installed on your system.

go get github.com/Djancyp/luna@v1.0.0

usage

func main() {
  	app, err := luna.New(luna.Config{
		ENV:                 ENV,
		RootPath:            "frontend/",
		PublicPath:          "frontend/public",
		AssetsPath:          "frontend/src/assets",
		ServerEntryPoint:    "frontend/src/entry-server.tsx",
		ClientEntryPoint:    "frontend/src/entry-client.tsx",
		TailwindCSS:         true,
		FaviconPath:         "favicon.svg",
		HotReloadServerPort: 3000,
		Store:               store.ReturnStore,
		Head: pkg.MainHead{
			Attributes: []string{
				`<link rel="manifest" href="/manifest.json" />`,
			},
		},
		Routes: []pkg.ReactRoute{
			{
				Path: "/",
				Head: pkg.Head{
					Title:       "mi-Deck",
					Description: "Home page",
					CssLinks:    googleFont,
				},
				Middleware: []echo.MiddlewareFunc{
					middlewares.UserLoggedin,
				},
				CacheExpiry: time.Now().Add(365 * 24 * time.Hour).Unix(),
			},
			{
				Path: "/dash",
				Head: pkg.Head{
					Title:       "mi-deck - Dash",
					Description: "Dashboard page",
					CssLinks:    googleFont,
				},
				Props: props.ReturnDashProps,
				Middleware: []echo.MiddlewareFunc{
					middlewares.RequireLogin,
				},
			},
			{
				Path: "/decks",
				Head: pkg.Head{
					Title:       "mi-deck - Decks",
					Description: "Decks page",
					CssLinks:    googleFont,
				},
				Props: props.ReturnDeckProps,
				Middleware: []echo.MiddlewareFunc{
					middlewares.RequireLogin,
				},
			},
			{
				Path: "/decks/new",
				Head: pkg.Head{
					Title:       "mi-deck - New Deck",
					Description: "New deck page",
					CssLinks:    googleFont,
				},
				Middleware: []echo.MiddlewareFunc{
					middlewares.RequireLogin,
				},
				CacheExpiry: time.Now().Add(365 * 24 * time.Hour).Unix(),
			},
			{
				Path: "/decks/edit/:id",
				Head: pkg.Head{
					Title:       "mi-deck - Edit Deck",
					Description: "Edit deck page",
					CssLinks:    googleFont,
				},
				Props: props.ReturnEditDeckProps,
				Middleware: []echo.MiddlewareFunc{
					middlewares.RequireLogin,
				},
			},
			{
				Path: "/decks/:id/play",
				Head: pkg.Head{
					Title:       "mi-deck - Edit Deck",
					Description: "Edit deck page",
					CssLinks:    googleFont,
				},
				Props: props.ReturnPlayDeckProps,
				Middleware: []echo.MiddlewareFunc{
					middlewares.RequireLogin,
				},
			},
		},
	})
  // make sure you handle the error
  
  app.Start(":8080")
}

Global store

Every page has access to this on the frontend. You can use create a provider for this or direct access on your components by

const { store } = store;
func ReturnStore(c echo.Context) map[string]interface{} {
	return map[string]interface{}{
		"User": returnUser(c),
	}
}

func returnUser(c echo.Context) map[string]interface{} {
	session, err := gothic.Store.Get(c.Request(), "userSession")
	if err != nil {
		return map[string]interface{}{
			"loggedIn": false,
		}
	}
	if _, ok := session.Values["id"]; !ok {
		return map[string]interface{}{
			"loggedIn": false,
		}
	}
	return map[string]interface{}{
		"loggedIn": true,
		"id":       session.Values["id"],
		"email":    session.Values["email"],
		"name":     session.Values["name"],
    "avatar":   session.Values["picture"],
	}
}

Page props

Props are page level data that can be passed. You can Access the data on the frontend by using the store object

const { lates, totals, analytics } = props;
func ReturnDashProps(c echo.Context, params map[string]string) map[string]interface{} {
  // u can use params to get url params
  // paramId := params["id"]


	// get session
	props := map[string]interface{}{}
	session, err := gothic.Store.Get(c.Request(), "userSession")
	if err != nil {
		return props
	}
	// Check if the user ID is set in the session
	if _, ok := session.Values["id"]; !ok {
		return props
	}
	id := session.Values["id"].(string)
	lates := getLatest(id)
	totals := getTotals(id)
	analytics := getAnalitics(id)
	return map[string]interface{}{
		"lates":     lates,
		"totals":    totals,
		"analytics": analytics,
	}
}

func getLatest(id string) []database.GetDecksByUserIDRow {
	data := []database.GetDecksByUserIDRow{}
	ctx := context.Background()
	db := database.New(database.DB)

	body := database.GetDecksByUserIDParams{
		UserID: id,
		Limit:  2,
		Offset: 0,
	}

	decks, err := db.GetDecksByUserID(ctx, body)
	if err != nil && sql.ErrNoRows != err {
		return data
	}
	if sql.ErrNoRows == err {
		return []database.GetDecksByUserIDRow{}
	}
	if decks == nil {
		return []database.GetDecksByUserIDRow{}
	}

	return decks
}

type Totals struct {
	Decks int64
	Cards int
}

func getTotals(id string) Totals {
	data := Totals{
		Decks: 0,
		Cards: 0,
	}

	if id == "" {
		return data
	}

	ctx := context.Background()
	db := database.New(database.DB)

	deckTotal, err := db.CountDecksByUserID(ctx, id)
	if err != nil && sql.ErrNoRows != err {
		return data
	}

	cardTotal, err := db.CountCardsByUserID(ctx, id)
	if err != nil && sql.ErrNoRows != err {
		return data
	}
	data.Cards = int(cardTotal)

	data.Decks = deckTotal

	return data
}

func getAnalitics(id string) []database.MonthlyUserProgress {
	data := []database.MonthlyUserProgress{}
	if id == "" {
		return data
	}
	ctx := context.Background()
	db := database.New(database.DB)

	analitics, err := db.GetMonthlyUserProgress(ctx, id)
	if err != nil && sql.ErrNoRows != err {
		return data
	}
	if sql.ErrNoRows == err {
		return []database.MonthlyUserProgress{}
	}
	if analitics == nil {
		return []database.MonthlyUserProgress{}
	}
	return analitics
}

Check this link for an example project: Example

Contributing

Thank you for considering contributing to this project! Contributions are what make the open-source community such a valuable place to learn, inspire, and create. Any contributions you make are greatly appreciated.

To contribute:

  1. Fork the repository.
  2. Create a branch for your feature (git checkout -b feature/NewFeature).
  3. Commit your changes (git commit -m 'Add some NewFeature').
  4. Push to the branch (git push origin feature/NewFeature).
  5. Open a pull request.

Please make sure to update tests as appropriate and adhere to the code of conduct in all interactions.

Special Thanks

License

This project is licensed under the MIT License. See the LICENSE file for more information.

About

go react ssr

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages