Init
This commit is contained in:
commit
491be9c1b1
200
.gitignore
vendored
Normal file
200
.gitignore
vendored
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
### GoLand+all template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# AWS User-specific
|
||||||
|
.idea/**/aws.xml
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# SonarLint plugin
|
||||||
|
.idea/sonarlint/
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
### Go template
|
||||||
|
# If you prefer the allow list template instead of the deny list, see community template:
|
||||||
|
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||||
|
#
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
|
||||||
|
### GoLand+iml template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
|
||||||
|
# AWS User-specific
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
|
||||||
|
# SonarLint plugin
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
|
||||||
|
### GoLand template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
|
||||||
|
# AWS User-specific
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
|
||||||
|
# SonarLint plugin
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
|
35
Makefile
Normal file
35
Makefile
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
PROJECT = menuserver
|
||||||
|
|
||||||
|
VERSION = 3.0
|
||||||
|
|
||||||
|
|
||||||
|
ECR_REPO = git.caliban.io/martin
|
||||||
|
# APP_IMAGE = 482681734622.dkr.ecr.eu-west-1.amazonaws.com/$(PROJECT):$(VERSION)
|
||||||
|
APP_IMAGE = $(ECR_REPO)/$(PROJECT):$(VERSION)
|
||||||
|
# APP_IMAGE = $(PROJECT):$(VERSION)
|
||||||
|
NO_CACHE = true
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build:
|
||||||
|
#CC=/usr/local/musl/bin/musl-gcc go build --ldflags '-linkmode external -extldflags "-static"' server.go
|
||||||
|
GCO_ENABLED=0 GOOS=linux go build -o ${PROJECT} server.go
|
||||||
|
|
||||||
|
# docker build ./docker/. -t $(APP_IMAGE) --build-arg VERSION=$(VERSION) --no-cache=$(NO_CACHE) --compress=true
|
||||||
|
docker build --platform linux/amd64 --no-cache -force-rm --tag ${APP_IMAGE} --file ./docker/Dockerfile .
|
||||||
|
|
||||||
|
|
||||||
|
#push docker image to registry
|
||||||
|
.PHONY: push
|
||||||
|
push: build
|
||||||
|
docker push $(APP_IMAGE)
|
||||||
|
|
||||||
|
|
||||||
|
#push docker image to registry
|
||||||
|
.PHONY: run
|
||||||
|
run: build
|
||||||
|
docker run $(APP_IMAGE)
|
||||||
|
|
||||||
|
ver:
|
||||||
|
@echo '$(VERSION)'
|
||||||
|
#echo $ERSION
|
||||||
|
.PHONY: ver
|
BIN
db/menu.db
Normal file
BIN
db/menu.db
Normal file
Binary file not shown.
30
dbconnection/menu.go
Normal file
30
dbconnection/menu.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package dbconnection
|
||||||
|
|
||||||
|
type Menu struct {
|
||||||
|
_ID int64 `json:"_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
Md string `json:"md"`
|
||||||
|
Short string `json:"short"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
Meat int64 `json:"meat"`
|
||||||
|
Mealtype int64 `json:"mealtype"`
|
||||||
|
Lastused int64 `json:"lastused"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MenuStrings struct {
|
||||||
|
_ID int64 `json:"_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
Md string `json:"md"`
|
||||||
|
Short string `json:"short"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
Meat string `json:"meat"`
|
||||||
|
Mealtype string `json:"mealtype"`
|
||||||
|
Lastused int64 `json:"lastused"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type InsertedType struct {
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
_ID string `json:"_id"`
|
||||||
|
}
|
253
dbconnection/sqlite_repository.go
Normal file
253
dbconnection/sqlite_repository.go
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
package dbconnection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SQLiteRepository struct {
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSQLiteRepository(db *sql.DB) *SQLiteRepository {
|
||||||
|
return &SQLiteRepository{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SQLiteRepository) All() ([]Menu, error) {
|
||||||
|
rows, err := r.db.Query("SELECT _id, short, hash, name, meat, mealtype FROM menu")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var all []Menu
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var menu Menu
|
||||||
|
if err := rows.Scan(&menu._ID, &menu.Short, &menu.Hash, &menu.Name, &menu.Meat, &menu.Mealtype); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
all = append(all, menu)
|
||||||
|
}
|
||||||
|
return all, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SQLiteRepository) GetOneShort(ShortCode string) (Menu, error) {
|
||||||
|
rows, err := r.db.Query("select md from menu where short = ?", ShortCode)
|
||||||
|
|
||||||
|
var empty Menu
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return empty, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var item Menu
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var menu Menu
|
||||||
|
if err := rows.Scan(&menu.Md); err != nil {
|
||||||
|
return empty, err
|
||||||
|
}
|
||||||
|
|
||||||
|
item = menu
|
||||||
|
}
|
||||||
|
|
||||||
|
return item, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SQLiteRepository) GetOneHash(Hash string) (Menu, error) {
|
||||||
|
log.Printf("Get one hash: %s\n", Hash)
|
||||||
|
|
||||||
|
rows, err := r.db.Query("select * from menu where hash = ?", Hash)
|
||||||
|
|
||||||
|
var empty Menu
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println("GetOneHash query error")
|
||||||
|
return empty, err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var item Menu
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var menu Menu
|
||||||
|
if err := rows.Scan(&menu._ID, &menu.Name, &menu.Url, &menu.Md, &menu.Short, &menu.Hash, &menu.Meat, &menu.Mealtype, &menu.Lastused); err != nil {
|
||||||
|
return empty, err
|
||||||
|
}
|
||||||
|
|
||||||
|
item = menu
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("%+v\n", item)
|
||||||
|
return item, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SQLiteRepository) InsertOne(newitem MenuStrings) (InsertedType, error) {
|
||||||
|
log.Printf("Insert One: %s\n", newitem)
|
||||||
|
|
||||||
|
stmt, err := r.db.Prepare("INSERT INTO menu(name, url, md, short, hash, meat, mealtype, lastused) VALUES (?,?,?,?,?,?,?,?)")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
meat, _ := strconv.ParseInt(newitem.Meat, 10, 64)
|
||||||
|
mealtype, _ := strconv.ParseInt(newitem.Meat, 10, 64)
|
||||||
|
lastused, _ := strconv.ParseInt(newitem.Meat, 10, 64)
|
||||||
|
|
||||||
|
b, err := stmt.Exec(newitem.Name, newitem.Url, newitem.Md, newitem.Short, newitem.Hash, meat, mealtype, lastused)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var returnVal InsertedType
|
||||||
|
|
||||||
|
lastID, err := b.LastInsertId()
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
returnVal = InsertedType{
|
||||||
|
Msg: "Row inserted",
|
||||||
|
_ID: strconv.FormatInt(lastID, 10),
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("B:%+v\n", b)
|
||||||
|
|
||||||
|
}
|
||||||
|
return returnVal, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SQLiteRepository) GetRandom(timestamp int64) ([]Menu, error) {
|
||||||
|
|
||||||
|
ts := strconv.FormatInt(timestamp, 10)
|
||||||
|
|
||||||
|
rows, err := r.db.Query("SELECT _id, short, hash, name, meat, mealtype FROM menu where mealtype =1 and lastused<? order by RANDOM()", ts)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var all []Menu
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var menu Menu
|
||||||
|
if err := rows.Scan(&menu._ID, &menu.Short, &menu.Hash, &menu.Name, &menu.Meat, &menu.Mealtype); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
all = append(all, menu)
|
||||||
|
}
|
||||||
|
return all, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SQLiteRepository) GetRandomSoup(timestamp int64) (Menu, error) {
|
||||||
|
var empty Menu
|
||||||
|
|
||||||
|
ts := strconv.FormatInt(timestamp, 10)
|
||||||
|
|
||||||
|
rows, err := r.db.Query("SELECT _id, short, hash, name, meat, mealtype FROM menu where mealtype =2 and lastused<? order by RANDOM() LIMIT 1", ts)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return empty, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var item Menu
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var menu Menu
|
||||||
|
if err := rows.Scan(&menu._ID, &menu.Short, &menu.Hash, &menu.Name, &menu.Meat, &menu.Mealtype); err != nil {
|
||||||
|
return empty, err
|
||||||
|
}
|
||||||
|
|
||||||
|
item = menu
|
||||||
|
}
|
||||||
|
|
||||||
|
return item, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SQLiteRepository) UpdateTimeStamps(updateRecs []Menu, timestamp int64) error {
|
||||||
|
|
||||||
|
log.Printf("Updating timestamps to : %+v", timestamp)
|
||||||
|
|
||||||
|
stmt, err := r.db.Prepare("UPDATE menu SET lastused = ? WHERE _id = ?")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
for _, item := range updateRecs {
|
||||||
|
|
||||||
|
b, err := stmt.Exec(timestamp, item._ID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rowsAffected, err := b.RowsAffected()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
log.Printf("Rows affected:%+v\n", rowsAffected)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *SQLiteRepository) UpdateTimeStampsTransaction(updateRecs []Menu, timestamp int64) error {
|
||||||
|
|
||||||
|
log.Printf("Update timestamps to : %+v", timestamp)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
tx, err := r.db.BeginTx(ctx, nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range updateRecs {
|
||||||
|
|
||||||
|
log.Println(item)
|
||||||
|
|
||||||
|
_, err = tx.ExecContext(ctx, "UPDATE menu SET lastused = ? WHERE _id = ?", item._ID, timestamp)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
// Incase we find any error in the query execution, rollback the transaction
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
} else {
|
||||||
|
log.Println("Records updated")
|
||||||
|
}
|
||||||
|
// UPDATE menu SET lastused = $lastused WHERE _id = $in
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
1
dist/build/bundle.css
vendored
Executable file
1
dist/build/bundle.css
vendored
Executable file
@ -0,0 +1 @@
|
|||||||
|
.filterBar.svelte-17lzm0a{background:var(--medium-color);margin-bottom:1rem;padding:10px 5px}.recipeItem.svelte-qibu9a{display:flex;padding:0.1rem;border-bottom:1px #ccc dotted}.recipeItem.svelte-qibu9a:nth-of-type(odd){background-color:rgba(0, 0, 0, 0.04)}.listItemSix.svelte-qibu9a{flex:6}.listItemThree.svelte-qibu9a{flex:3}.chicken.svelte-qibu9a{background:#8e5241;color:#fff}.beef.svelte-qibu9a{background:#d72414;color:#fff}.pork.svelte-qibu9a{background:#ef96d9;color:#fff}.fish.svelte-qibu9a{background:#005ba0;color:#fff}.egg.svelte-qibu9a{background:#fbc003;color:#000}.vegetable.svelte-qibu9a{background:#00903e;color:#fff}
|
14
dist/build/bundle.css.map
vendored
Normal file
14
dist/build/bundle.css.map
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"file": "bundle.css",
|
||||||
|
"sources": [
|
||||||
|
"../../FilterBar.svelte",
|
||||||
|
"../../RecipeItem.svelte"
|
||||||
|
],
|
||||||
|
"sourcesContent": [
|
||||||
|
"<script>\n import { state } from '../store/store';\n import { onMount } from 'svelte';\n\n let meatVal = '0';\n let mealVal = '0';\n const meals = ['', '1', '2', '128'];\n\n function updateMeat(event) {\n const newVal = event.target.value;\n state.updateMeatFilter(newVal);\n }\n\n function updateMeal(event) {\n const newVal = event.target.value;\n state.updateMealFilter(newVal);\n }\n\n onMount(() => {\n\n setTimeout(() => {\n\n const mtVal = document.getElementById('meatVal').selectedIndex;\n const mlVal = document.getElementById('mealVal').selectedIndex;\n\n if (mtVal !== 0) {\n meatVal = mtVal.toString(10);\n state.updateMeatFilter(meatVal);\n }\n\n if (mlVal !== 0) {\n mealVal = meals[mlVal];\n state.updateMealFilter(mealVal);\n }\n\n }, 25);\n });\n\n</script>\n\n<style>\n .filterBar {\n background: var(--medium-color);\n margin-bottom: 1rem;\n padding: 10px 5px;\n }\n</style>\n\n<div class=\"container\">\n <div class=\"filterBar grid-4\">\n <select id=\"meatVal\" on:change={updateMeat} bind:value={meatVal}>\n <option value=\"0\">All</option>\n <option value=\"1\">Chicken</option>\n <option value=\"2\">Beef</option>\n <option value=\"3\">Pork</option>\n <option value=\"4\">Fish</option>\n <option value=\"5\">Egg</option>\n <option value=\"6\">Vegetable</option>\n </select>\n\n <select id=\"mealVal\" on:change={updateMeal} bind:value={mealVal}>\n <option value=\"0\">All</option>\n <option value=\"1\">Mains</option>\n <option value=\"2\">Soups</option>\n <option value=\"128\">Notes</option>\n </select>\n </div>\n\n</div>\n",
|
||||||
|
"<script>\n import { state } from '../store/store';\n\n export let recipeItem = {};\n\n let meatClass;\n let meatText;\n let url;\n const meats = ['x', 'Chicken', 'Beef', 'Pork', 'Fish', 'Egg', 'Vegetable'];\n\n $:{\n meatText = meats[recipeItem.meat];\n meatClass = (recipeItem.meat === '') ? '' : meats[recipeItem.meat].toLowerCase();\n url = `/view/${recipeItem.short}`;\n }\n\n function editRecipe(hash) {\n state.editRecipe(hash);\n }\n\n</script>\n\n<style>\n .recipeItem {\n display: flex;\n padding: 0.1rem;\n border-bottom: 1px #ccc dotted;\n }\n\n .recipeItem:nth-of-type(odd) {\n background-color: rgba(0, 0, 0, 0.04);\n }\n\n .listItemSix {\n\n flex: 6;\n }\n\n .listItemThree {\n\n flex: 3;\n }\n\n .chicken {\n background: #8e5241;\n color: #fff;\n }\n\n .beef {\n background: #d72414;\n color: #fff;\n }\n\n .pork {\n background: #ef96d9;\n color: #fff;\n }\n\n .fish {\n background: #005ba0;\n color: #fff;\n }\n\n .egg {\n background: #fbc003;\n color: #000;\n }\n\n .vegetable {\n background: #00903e;\n color: #fff;\n }\n</style>\n\n<div class=\"recipeItem\">\n <div class=\"listItemSix\"><a href={url}>{recipeItem.name}</a></div>\n <div class=\"listItemThree\">\n {#if recipeItem.mealtype ===2}\n <span class=\"badge badge-light\">Soup</span>\n {:else if recipeItem.mealtype===128}\n <span class=\"badge badge-dark\">Note</span>\n\n {/if}\n <span class=\"badge {meatClass}\">{meatText}</span>\n </div>\n <div class=\"listItemThree all-center\">\n <button class=\"btn btn-primary btn-sm\" type=\"button\" on:click={editRecipe(recipeItem.hash)}>Edit</button>\n </div>\n</div>\n"
|
||||||
|
],
|
||||||
|
"names": [],
|
||||||
|
"mappings": "AAyCI,UAAU,eAAC,CAAC,AACR,UAAU,CAAE,IAAI,cAAc,CAAC,CAC/B,aAAa,CAAE,IAAI,CACnB,OAAO,CAAE,IAAI,CAAC,GAAG,AACrB,CAAC;ACtBD,WAAW,cAAC,CAAC,AACT,OAAO,CAAE,IAAI,CACb,OAAO,CAAE,MAAM,CACf,aAAa,CAAE,GAAG,CAAC,IAAI,CAAC,MAAM,AAClC,CAAC,AAED,yBAAW,aAAa,GAAG,CAAC,AAAC,CAAC,AAC1B,gBAAgB,CAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,AACzC,CAAC,AAED,YAAY,cAAC,CAAC,AAEV,IAAI,CAAE,CAAC,AACX,CAAC,AAED,cAAc,cAAC,CAAC,AAEZ,IAAI,CAAE,CAAC,AACX,CAAC,AAED,QAAQ,cAAC,CAAC,AACN,UAAU,CAAE,OAAO,CACnB,KAAK,CAAE,IAAI,AACf,CAAC,AAED,KAAK,cAAC,CAAC,AACH,UAAU,CAAE,OAAO,CACnB,KAAK,CAAE,IAAI,AACf,CAAC,AAED,KAAK,cAAC,CAAC,AACH,UAAU,CAAE,OAAO,CACnB,KAAK,CAAE,IAAI,AACf,CAAC,AAED,KAAK,cAAC,CAAC,AACH,UAAU,CAAE,OAAO,CACnB,KAAK,CAAE,IAAI,AACf,CAAC,AAED,IAAI,cAAC,CAAC,AACF,UAAU,CAAE,OAAO,CACnB,KAAK,CAAE,IAAI,AACf,CAAC,AAED,UAAU,cAAC,CAAC,AACR,UAAU,CAAE,OAAO,CACnB,KAAK,CAAE,IAAI,AACf,CAAC"
|
||||||
|
}
|
2
dist/build/bundle.js
vendored
Executable file
2
dist/build/bundle.js
vendored
Executable file
File diff suppressed because one or more lines are too long
1
dist/build/bundle.js.map
vendored
Normal file
1
dist/build/bundle.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
dist/favicon.png
vendored
Executable file
BIN
dist/favicon.png
vendored
Executable file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
503
dist/global.css
vendored
Normal file
503
dist/global.css
vendored
Normal file
@ -0,0 +1,503 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css?family=Roboto');
|
||||||
|
|
||||||
|
/* Global Styles */
|
||||||
|
:root {
|
||||||
|
--primary-color: #64B5F6;
|
||||||
|
--dark-color: #333333;
|
||||||
|
--light-color: #f4f4f4;
|
||||||
|
--danger-color: #dc3545;
|
||||||
|
--success-color: #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataRow {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Utilities */
|
||||||
|
.container {
|
||||||
|
max-width: 1100px;
|
||||||
|
margin: auto;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text Styles*/
|
||||||
|
.x-large {
|
||||||
|
font-size: 4rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.large {
|
||||||
|
font-size: 3rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-primary {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-dark {
|
||||||
|
color: var(--dark-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-success {
|
||||||
|
color: var(--success-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-danger {
|
||||||
|
color: var(--danger-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Center All */
|
||||||
|
.all-center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
margin: auto;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cards */
|
||||||
|
.card {
|
||||||
|
padding: 1rem;
|
||||||
|
border: #ccc 1px dotted;
|
||||||
|
margin: 0.7rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* List */
|
||||||
|
.list {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list li {
|
||||||
|
padding-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Padding */
|
||||||
|
.p {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
.p-1 {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
.p-2 {
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
.p-3 {
|
||||||
|
padding: 3rem;
|
||||||
|
}
|
||||||
|
.py {
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
.py-1 {
|
||||||
|
padding: 1rem 0;
|
||||||
|
}
|
||||||
|
.py-2 {
|
||||||
|
padding: 2rem 0;
|
||||||
|
}
|
||||||
|
.py-3 {
|
||||||
|
padding: 3rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Margin */
|
||||||
|
.m {
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
.m-1 {
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
.m-2 {
|
||||||
|
margin: 2rem;
|
||||||
|
}
|
||||||
|
.m-3 {
|
||||||
|
margin: 3rem;
|
||||||
|
}
|
||||||
|
.my {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
||||||
|
.my-1 {
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
.my-2 {
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
.my-3 {
|
||||||
|
margin: 3rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grid */
|
||||||
|
.grid-2 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
grid-gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-3 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
grid-gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-4 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
grid-gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: inline-block;
|
||||||
|
background: var(--light-color);
|
||||||
|
color: #333;
|
||||||
|
padding: 0.4rem 1.3rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
transition: opacity 0.2s ease-in;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-link {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-block {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sm {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 0.3rem 1rem;
|
||||||
|
margin-right: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 0.6rem;
|
||||||
|
padding: 0.1rem 0.4rem;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0.3rem;
|
||||||
|
background: var(--light-color);
|
||||||
|
color: #333;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
padding: 0.7rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
opacity: 0.9;
|
||||||
|
background: var(--light-color);
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary,
|
||||||
|
.bg-primary,
|
||||||
|
.badge-primary,
|
||||||
|
.alert-primary {
|
||||||
|
background: var(--primary-color);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn-light,
|
||||||
|
.bg-light,
|
||||||
|
.badge-light,
|
||||||
|
.alert-light {
|
||||||
|
background: var(--light-color);
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-dark,
|
||||||
|
.bg-dark,
|
||||||
|
.badge-dark,
|
||||||
|
.alert-dark {
|
||||||
|
background: var(--dark-color);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger,
|
||||||
|
.bg-danger,
|
||||||
|
.badge-danger,
|
||||||
|
.alert-danger {
|
||||||
|
background: var(--danger-color);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success,
|
||||||
|
.bg-success,
|
||||||
|
.badge-success,
|
||||||
|
.alert-success {
|
||||||
|
background: var(--success-color);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-white,
|
||||||
|
.bg-white,
|
||||||
|
.badge-white,
|
||||||
|
.alert-white {
|
||||||
|
background: #fff;
|
||||||
|
color: #333;
|
||||||
|
border: #ccc solid 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.60;
|
||||||
|
-webkit-box-shadow: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.btn:enabled:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-light,
|
||||||
|
.badge-light {
|
||||||
|
border: #ccc solid 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.round-img {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms */
|
||||||
|
input {
|
||||||
|
margin: 1.2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-text {
|
||||||
|
display: block;
|
||||||
|
margin-top: 0.3rem;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='text'],
|
||||||
|
input[type='email'],
|
||||||
|
input[type='password'],
|
||||||
|
input[type='date'],
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.4rem;
|
||||||
|
/*font-size: 1.2rem;*/
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='submit'],
|
||||||
|
button {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th,
|
||||||
|
table td {
|
||||||
|
padding: 1rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
background: var(--light-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navbar */
|
||||||
|
.navbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.7rem 2rem;
|
||||||
|
z-index: 1;
|
||||||
|
width: 100%;
|
||||||
|
opacity: 0.9;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar ul {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar a {
|
||||||
|
color: #fff;
|
||||||
|
padding: 0.45rem;
|
||||||
|
margin: 0 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar a:hover {
|
||||||
|
color: var(--light-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar .welcome span {
|
||||||
|
margin-right: 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile Styles */
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.hide-sm {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-2,
|
||||||
|
.grid-3,
|
||||||
|
.grid-4 {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text Styles */
|
||||||
|
.x-large {
|
||||||
|
font-size: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.large {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navbar */
|
||||||
|
.navbar {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar ul {
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--primary-color: #64B5F6;
|
||||||
|
--dark-color: #333333;
|
||||||
|
--light-color: #f4f4f4;
|
||||||
|
--danger-color: #dc3545;
|
||||||
|
--success-color: #28a745;
|
||||||
|
--medium-color: #999999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-responsive {
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cardV2 {
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 0 4px 0 rgba(0,0,0,.14), 0 3px 4px 0 rgba(0,0,0,.12), 0 1px 5px 0 rgba(0,0,0,.2);
|
||||||
|
/*display: flex;
|
||||||
|
flex-direction: column;*/
|
||||||
|
min-width: 0;
|
||||||
|
/*position: relative;
|
||||||
|
word-wrap: break-word;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
max-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
border: 0;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr:nth-of-type(odd){
|
||||||
|
background-color: rgba(0,0,0,0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td {
|
||||||
|
border-top: 1px solid #e1e1e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.modalWindow {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
background: rgba(0,0,0,0.2);
|
||||||
|
z-index: 99999;
|
||||||
|
opacity:0;
|
||||||
|
pointer-events: none;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
.modalWindow:target {
|
||||||
|
opacity:1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
.modalWindow > div {
|
||||||
|
width: 500px;
|
||||||
|
position: relative;
|
||||||
|
margin: 10% auto;
|
||||||
|
background: #fff;
|
||||||
|
}
|
18
dist/index.html
vendored
Normal file
18
dist/index.html
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset='utf-8'>
|
||||||
|
<meta name='viewport' content='width=device-width,initial-scale=1'>
|
||||||
|
|
||||||
|
<title>Menus</title>
|
||||||
|
|
||||||
|
<link rel='icon' type='image/png' href='/favicon.png'>
|
||||||
|
<link rel='stylesheet' href='/global.css'>
|
||||||
|
<link rel='stylesheet' href='/build/bundle.css'>
|
||||||
|
|
||||||
|
<script defer src='/build/bundle.js'></script>
|
||||||
|
</head>
|
||||||
|
<!-- svelte -->
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
26
docker/Dockerfile
Normal file
26
docker/Dockerfile
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
FROM debian:12-slim
|
||||||
|
# FROM alpine:latest
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN mkdir -p /app/{dist,db}
|
||||||
|
|
||||||
|
COPY ./menuserver /app/
|
||||||
|
|
||||||
|
|
||||||
|
COPY ./dist /app/dist
|
||||||
|
|
||||||
|
# Need the following to get a go app to run inside a docker container
|
||||||
|
# as per: https://www.fairlyusefulcode.co.uk/post/go-alpine-linux/ -- DEAD!!!
|
||||||
|
# RUN apk upgrade musl
|
||||||
|
# RUN apk add gcompat
|
||||||
|
|
||||||
|
# RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
|
||||||
|
# RUN ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
|
||||||
|
|
||||||
|
RUN chmod +x /app/menuserver
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
CMD [ "/app/menuserver"]
|
||||||
|
|
30
go.mod
Normal file
30
go.mod
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
module menuserver
|
||||||
|
|
||||||
|
go 1.22.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/Joker/jade v1.1.3
|
||||||
|
github.com/gofiber/fiber/v2 v2.52.2
|
||||||
|
github.com/gofiber/template/html/v2 v2.1.1
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22
|
||||||
|
github.com/robfig/cron/v3 v3.0.0
|
||||||
|
github.com/stoewer/go-strcase v1.3.0
|
||||||
|
github.com/yuin/goldmark v1.7.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||||
|
github.com/gofiber/template v1.8.3 // indirect
|
||||||
|
github.com/gofiber/utils v1.1.0 // indirect
|
||||||
|
github.com/google/uuid v1.5.0 // indirect
|
||||||
|
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible // indirect
|
||||||
|
github.com/klauspost/compress v1.17.0 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
|
github.com/valyala/fasthttp v1.51.0 // indirect
|
||||||
|
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||||
|
golang.org/x/sys v0.15.0 // indirect
|
||||||
|
)
|
91
go.sum
Normal file
91
go.sum
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc=
|
||||||
|
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
|
||||||
|
github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk=
|
||||||
|
github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM=
|
||||||
|
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||||
|
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/gofiber/fiber/v2 v2.52.2 h1:b0rYH6b06Df+4NyrbdptQL8ifuxw/Tf2DgfkZkDaxEo=
|
||||||
|
github.com/gofiber/fiber/v2 v2.52.2/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
||||||
|
github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc=
|
||||||
|
github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
|
||||||
|
github.com/gofiber/template/html/v2 v2.1.1 h1:QEy3O3EBkvwDthy5bXVGUseOyO6ldJoiDxlF4+MJiV8=
|
||||||
|
github.com/gofiber/template/html/v2 v2.1.1/go.mod h1:2G0GHHOUx70C1LDncoBpe4T6maQbNa4x1CVNFW0wju0=
|
||||||
|
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
|
||||||
|
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
|
||||||
|
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||||
|
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
|
||||||
|
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
|
||||||
|
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
|
||||||
|
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||||
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E=
|
||||||
|
github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
|
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
|
||||||
|
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
|
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
|
||||||
|
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
|
||||||
|
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
||||||
|
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||||
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
|
github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA=
|
||||||
|
github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
|
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||||
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
162
jobhandler/jobhandler.go
Normal file
162
jobhandler/jobhandler.go
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
package jobhandler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/jordan-wright/email"
|
||||||
|
"html/template"
|
||||||
|
"log"
|
||||||
|
"menuserver/dbconnection"
|
||||||
|
"net/smtp"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
from = "Aida <aida@caliban.io>"
|
||||||
|
login = "aida@caliban.io"
|
||||||
|
// recipients = []string{"Martin <martind2000@gmail.com>", "Jessica <ing.arvid@gmail.com>"}
|
||||||
|
recipients = []string{"Martin <martind2000@gmail.com>"}
|
||||||
|
hostname = "mail.caliban.io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DoJob(r *dbconnection.SQLiteRepository) {
|
||||||
|
|
||||||
|
var data PugData
|
||||||
|
|
||||||
|
var store []MenuEntry
|
||||||
|
|
||||||
|
var menuItems []dbconnection.Menu
|
||||||
|
|
||||||
|
srcURL := "http://menu.lan"
|
||||||
|
var types = [7]int{0, 0, 0, 0, 0, 0, 0}
|
||||||
|
limit := 2
|
||||||
|
|
||||||
|
log.Printf("Doing Job....")
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
daystart := (now.Unix() / 86400) * 86400
|
||||||
|
|
||||||
|
data.Ts = now.Format("Monday, January 2, 2006")
|
||||||
|
|
||||||
|
items, err := r.GetRandom(now.Unix())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range items {
|
||||||
|
if types[item.Meat] < limit && len(store) < 5 {
|
||||||
|
entry := MenuEntry{
|
||||||
|
Url: srcURL + "/view/" + item.Short,
|
||||||
|
Meat: item.Meat,
|
||||||
|
Name: item.Name,
|
||||||
|
}
|
||||||
|
store = append(store, entry)
|
||||||
|
types[item.Meat] = types[item.Meat] + 1
|
||||||
|
|
||||||
|
menuItems = append(menuItems, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Menu = store
|
||||||
|
|
||||||
|
soupItem, err := r.GetRandomSoup(now.Unix())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
soupEntry := MenuEntry{
|
||||||
|
Url: srcURL + "/view/" + soupItem.Short,
|
||||||
|
Meat: soupItem.Meat,
|
||||||
|
Name: soupItem.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Soup = soupEntry
|
||||||
|
|
||||||
|
menuItems = append(menuItems, soupItem)
|
||||||
|
|
||||||
|
outputMsg := generateHTML(data)
|
||||||
|
|
||||||
|
subject := "Suggestions 🍽️ - " + data.Ts
|
||||||
|
|
||||||
|
sendSMTP(outputMsg, subject)
|
||||||
|
|
||||||
|
updateTimestamps(r, menuItems, daystart)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateHTML(data PugData) string {
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
t := template.New("mail")
|
||||||
|
|
||||||
|
t, err := t.Parse(`<html lang="en">
|
||||||
|
<head><meta charset="utf-8"/><title>Suggestions</title></head>
|
||||||
|
<body>
|
||||||
|
<h1>Suggestions for {{ .Ts }}</h1>
|
||||||
|
<h2>Mains</h2>
|
||||||
|
<ol>
|
||||||
|
{{ range .Menu }}
|
||||||
|
<li><a href="{{.Url}}">{{ .Name }}</a></li>
|
||||||
|
{{ end }}
|
||||||
|
</ol>
|
||||||
|
<H2>Soup</H2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{{.Soup.Url}}">{{.Soup.Name}}</a></li>
|
||||||
|
</ul>
|
||||||
|
<small>V3.00 -- 😊 -- GO!</small>
|
||||||
|
</body>
|
||||||
|
</html>`)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.Execute(&buf, data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// log.Printf("%+v\n")
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
|
||||||
|
}
|
||||||
|
func getEmojis() []string {
|
||||||
|
return []string{"🍽️", "🍴", "🍳", "👨🏻🍳", "👩🏻🍳", "🥑", "🥕", "🫑", "🍔"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateTimestamps(r *dbconnection.SQLiteRepository, menuItems []dbconnection.Menu, timestamp int64) {
|
||||||
|
|
||||||
|
err := r.UpdateTimeStamps(menuItems, timestamp)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendSMTP(inmsg string, subject string) {
|
||||||
|
|
||||||
|
log.Println("Sending Email")
|
||||||
|
|
||||||
|
e := email.NewEmail()
|
||||||
|
|
||||||
|
auth := smtp.PlainAuth("", login, "Ultra+Topaz+6XQ", hostname)
|
||||||
|
|
||||||
|
e.From = from
|
||||||
|
e.To = []string{from}
|
||||||
|
e.Bcc = recipients
|
||||||
|
e.Subject = subject
|
||||||
|
e.Text = []byte(inmsg)
|
||||||
|
e.HTML = []byte(inmsg)
|
||||||
|
|
||||||
|
err := e.Send(hostname+":587", auth)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
} else {
|
||||||
|
log.Println("Mail sent!")
|
||||||
|
}
|
||||||
|
}
|
13
jobhandler/jobstructs.go
Normal file
13
jobhandler/jobstructs.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package jobhandler
|
||||||
|
|
||||||
|
type PugData struct {
|
||||||
|
Menu []MenuEntry `json:"menu"`
|
||||||
|
Soup MenuEntry `json:"soup"`
|
||||||
|
Ts string `json:"ts"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MenuEntry struct {
|
||||||
|
Url string `json:"url"`
|
||||||
|
Meat int64 `json:"meat"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
9
menuserver.iml
Normal file
9
menuserver.iml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="Go" enabled="true" />
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
17
pug/emailV2.pug
Normal file
17
pug/emailV2.pug
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
html(lang="en")
|
||||||
|
head
|
||||||
|
meta(charset='utf-8')
|
||||||
|
title Suggestions
|
||||||
|
body
|
||||||
|
h1 Suggestions for !{Ts}
|
||||||
|
h2 Mains
|
||||||
|
ol
|
||||||
|
li: a(href="google.com")= line "name"
|
||||||
|
|
||||||
|
H2 Soup
|
||||||
|
ul
|
||||||
|
li: a(href="reddit.com")= "Reddit"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
small V3.00 -- GO!
|
268
server.go
Normal file
268
server.go
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/gofiber/template/html/v2"
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
"github.com/robfig/cron/v3"
|
||||||
|
"github.com/stoewer/go-strcase"
|
||||||
|
"github.com/yuin/goldmark"
|
||||||
|
"log"
|
||||||
|
"menuserver/dbconnection"
|
||||||
|
"menuserver/jobhandler"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const fileName = "./db/menu.db"
|
||||||
|
const htmlFront = "<!DOCTYPE html>\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${data.name}</title>\n <style>\n @import url('https://fonts.googleapis.com/css?family=Roboto');:root {--primary-color: #64B5F6;--dark-color: #333333;--light-color: #f4f4f4;--danger-color: #dc3545;--success-color: #28a745;}* {box-sizing: border-box;margin: 0;padding: 0;}body {font-family: 'Roboto', sans-serif;font-size: 1rem;line-height: 1.6;background-color: #fff;color: #333;}a {color: var(--primary-color);text-decoration: none;}a:hover {color: #666;}ul {list-style: none;}img {width: 100%;}.container {max-width: 1100px;margin: auto;overflow: hidden;padding: 0 2rem;}@media (max-width: 700px) {.hide-sm {display: none;}.grid-2, .grid-3, .grid-4 {grid-template-columns: 1fr;}.x-large {font-size: 3rem;}.large {font-size: 2rem;}.lead {font-size: 1rem;}.navbar {display: block;text-align: center;}.navbar ul {text-align: center;justify-content: center;}}\n </style>\n </head>\n <body>\n <div class=\"container\">"
|
||||||
|
const htmlEnd = "</div>\n \n </body>\n </html>"
|
||||||
|
|
||||||
|
var (
|
||||||
|
version string
|
||||||
|
build string
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
fmt.Printf("Menu server v%+v build %+v\n\n", version, build)
|
||||||
|
|
||||||
|
// setup connections n stuff
|
||||||
|
|
||||||
|
db, err := sql.Open("sqlite3", fileName) // db
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
engine := html.New("./dist", ".html")
|
||||||
|
|
||||||
|
app := fiber.New(fiber.Config{
|
||||||
|
Views: engine,
|
||||||
|
})
|
||||||
|
|
||||||
|
recipes := dbconnection.NewSQLiteRepository(db)
|
||||||
|
|
||||||
|
// Caching..
|
||||||
|
/*app.Use(cache.New(cache.Config{
|
||||||
|
Next: func(c *fiber.Ctx) bool {
|
||||||
|
return c.Query("noCache") == "true"
|
||||||
|
},
|
||||||
|
Expiration: 30 * time.Minute,
|
||||||
|
CacheControl: false,
|
||||||
|
}))*/
|
||||||
|
|
||||||
|
app.Get("/", indexHandler)
|
||||||
|
|
||||||
|
port := os.Getenv("PORT")
|
||||||
|
if port == "" {
|
||||||
|
port = "3000"
|
||||||
|
}
|
||||||
|
|
||||||
|
// routes
|
||||||
|
|
||||||
|
app.Static("/", "./dist")
|
||||||
|
|
||||||
|
app.Get("/view/:recipeId", func(c *fiber.Ctx) error {
|
||||||
|
|
||||||
|
return ViewRecipeHandler(c, recipes)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.Get("/recipes", func(c *fiber.Ctx) error {
|
||||||
|
return RecipeHandler(c, recipes)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.Post("/recipes", func(c *fiber.Ctx) error {
|
||||||
|
return PostRecipeHandler(c, recipes)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.Get("/recipes/:recipeId", func(c *fiber.Ctx) error {
|
||||||
|
return RecipeIdHandler(c, recipes)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.Put("/recipes/:recipeId", func(c *fiber.Ctx) error {
|
||||||
|
return PutRecipeHandler(c, recipes)
|
||||||
|
})
|
||||||
|
|
||||||
|
c := cron.New()
|
||||||
|
var _, cronerr = c.AddFunc("0 8 * * 6", func() { jobhandler.DoJob(recipes) })
|
||||||
|
if cronerr != nil {
|
||||||
|
log.Fatal(cronerr)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
log.Println("Starting cron..")
|
||||||
|
c.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Fatalln(app.Listen(fmt.Sprintf(":%v", port)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func indexHandler(c *fiber.Ctx) error {
|
||||||
|
|
||||||
|
return c.Render("index", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NotImplemented(c *fiber.Ctx, from string) error {
|
||||||
|
|
||||||
|
fmt.Printf("NotImplemented from: %s\n", from)
|
||||||
|
|
||||||
|
return c.SendString("{}")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func RecipeHandler(c *fiber.Ctx, r *dbconnection.SQLiteRepository) error {
|
||||||
|
|
||||||
|
fmt.Println("RecipeHandler")
|
||||||
|
|
||||||
|
items, err := r.All()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// log.Printf("%+v\n", items)
|
||||||
|
|
||||||
|
jsonStr, err := json.Marshal(items)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println("RecipeHandler marshalling")
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
|
||||||
|
return c.SendString(string(jsonStr))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ViewRecipeHandler(c *fiber.Ctx, r *dbconnection.SQLiteRepository) error {
|
||||||
|
|
||||||
|
params := c.AllParams()
|
||||||
|
|
||||||
|
fmt.Printf("ViewRecipeHandler: %s\n", params["recipeId"])
|
||||||
|
|
||||||
|
item, err := r.GetOneShort(params["recipeId"])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := goldmark.Convert([]byte(item.Md), &buf); err != nil {
|
||||||
|
log.Println("ViewRecipeHandler md conversion")
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := []string{htmlFront, buf.String(), htmlEnd}
|
||||||
|
|
||||||
|
output := []byte(strings.Join(s, ""))
|
||||||
|
|
||||||
|
c.Set(fiber.HeaderContentType, fiber.MIMETextHTML)
|
||||||
|
return c.Send(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RecipeIdHandler(c *fiber.Ctx, r *dbconnection.SQLiteRepository) error {
|
||||||
|
|
||||||
|
params := c.AllParams()
|
||||||
|
|
||||||
|
fmt.Printf("RecipeID Handler: %s\n", params["recipeId"])
|
||||||
|
|
||||||
|
item, err := r.GetOneHash(params["recipeId"])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonStr, err := json.Marshal(item)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println("RecipeIDHandler marshalling")
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
|
||||||
|
return c.SendString(string(jsonStr))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func hash(instring string) uint64 {
|
||||||
|
|
||||||
|
var workhash uint64
|
||||||
|
workhash = 5381
|
||||||
|
index := len(instring)
|
||||||
|
|
||||||
|
buf := bytes.NewBufferString(instring)
|
||||||
|
|
||||||
|
for i := 0; i < index; i++ {
|
||||||
|
c, err := buf.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
workhash = (workhash * 33) ^ uint64(c)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return workhash >> 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ShortHash(instring string) string {
|
||||||
|
|
||||||
|
h := hash(instring)
|
||||||
|
|
||||||
|
return strconv.FormatInt(int64(h), 16)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostRecipeHandler(c *fiber.Ctx, r *dbconnection.SQLiteRepository) error {
|
||||||
|
|
||||||
|
fmt.Printf("RecipeID Post Handler: \n")
|
||||||
|
|
||||||
|
payload := dbconnection.MenuStrings{}
|
||||||
|
|
||||||
|
if err := c.BodyParser(&payload); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.Hash = ShortHash(payload.Name)
|
||||||
|
payload.Short = strcase.KebabCase(payload.Name)
|
||||||
|
payload.Lastused = 0
|
||||||
|
|
||||||
|
var returnVal dbconnection.InsertedType
|
||||||
|
returnVal, _ = r.InsertOne(payload)
|
||||||
|
|
||||||
|
jsonStr, err := json.Marshal(returnVal)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println("PostRecipeHandler marshalling")
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
|
||||||
|
return c.SendString(string(jsonStr))
|
||||||
|
}
|
||||||
|
|
||||||
|
func PutRecipeHandler(c *fiber.Ctx, r *dbconnection.SQLiteRepository) error {
|
||||||
|
fmt.Printf("RecipeID Put Handler:\n")
|
||||||
|
|
||||||
|
payload := dbconnection.MenuStrings{}
|
||||||
|
|
||||||
|
if err := c.BodyParser(&payload); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("%+v\n", payload)
|
||||||
|
|
||||||
|
c.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
|
||||||
|
return c.SendString("{}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// string splitter for kebab case
|
||||||
|
// [A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+
|
27
stuff.txt
Normal file
27
stuff.txt
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// web server
|
||||||
|
|
||||||
|
type Foo struct {
|
||||||
|
Number int `json:"number"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
}
|
||||||
|
|
||||||
|
foo_marshalled, err := json.Marshal(Foo{Number: 1, Title: "test"})
|
||||||
|
fmt.Fprint(w, string(foo_marshalled)) // write response to ResponseWriter (w)
|
||||||
|
|
||||||
|
|
||||||
|
first character being uppercase makes it visible to json, then with `json:number` bit tells
|
||||||
|
json it's proper name
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
* attached to a type (*string) indicates a pointer to the type.
|
||||||
|
|
||||||
|
* attached to a variable in an assignment (*v = ...) indicates an indirect assignment.
|
||||||
|
That is, change the value pointed at by the variable.
|
||||||
|
|
||||||
|
* attached to a variable or expression (*v) indicates a pointer dereference.
|
||||||
|
That is, take the value the variable is pointing at.
|
||||||
|
|
||||||
|
& attached to a variable or expression (&v) indicates a reference.
|
||||||
|
That is, create a pointer to the value of the variable or to the field.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user