- graph, dotenv read

- graph, cors and secure header
- web, urql client basic setup
This commit is contained in:
Ankit Patial 2024-11-04 11:35:43 +05:30
parent 46c46a7e71
commit 45c318b897
22 changed files with 590 additions and 64 deletions

View File

@ -1,4 +1,5 @@
WEB_PORT = 3006
WEB_URL = http://localhost:3006
GRAPH_PORT = 3009
GRAPH_URL = http://localhost:3009/query
VITE_GRAPH_URL = http://localhost:3009/query
GRAPH_URL = http://localhost:3009
VITE_GRAPH_URL = http://localhost:3009

3
.gitignore vendored
View File

@ -1,5 +1,8 @@
node_modules
# Jetbrains IDE
.idea
# Output
.output
.vercel

View File

@ -9,3 +9,16 @@
```bash
go install github.com/go-task/task/v3/cmd/task@latest
```
## Go packages
- [gqlgen](https://gqlgen.com/)
- [mux](https://gitserver.in/patialtech/mux)
## Web modules
- [SvelteKit](https://svelte.dev/docs/kit/introduction)
- [Tailwind](https://tailwindcss.com/docs/installation)
- [daisyui](https://daisyui.com/docs/install/)
- [urql](https://commerce.nearform.com/open-source/urql/docs/basics/svelte/)
- [GraphQL Codegen](https://the-guild.dev/graphql/codegen)

View File

@ -1,10 +1,10 @@
import { CodegenConfig } from '@graphql-codegen/cli';
const config: CodegenConfig = {
schema: process.env.GRAPH_URL,
documents: ['./graph/*.ts'],
schema: `${process.env.GRAPH_URL}/query`,
documents: ['./web/lib/gql/*.ts'],
generates: {
'./graph/d.ts': {
'./web/lib/gql/graph.d.ts': {
plugins: ['typescript', 'typescript-operations']
}
}

78
config/config.go Normal file
View File

@ -0,0 +1,78 @@
package config
import (
"fmt"
"reflect"
"strconv"
"gitserver.in/patialtech/rano/config/dotenv"
"gitserver.in/patialtech/rano/pkg/logger"
)
const Env = "development"
var conf *Config
func init() {
envVar, err := dotenv.Read(fmt.Sprintf(".env.%s", Env))
if err != nil {
panic(err)
}
conf = &Config{}
conf.loadEnv(envVar)
}
// Read config for Env
func Read() *Config {
if conf == nil {
panic("config not initialized")
}
return conf
}
type Config struct {
WebPort int `env:"WEB_PORT"`
WebURL string `env:"WEB_URL"`
GraphPort int `env:"GRAPH_PORT"`
GrapURL string `env:"GRAPH_URL"`
}
func (c *Config) loadEnv(vars map[string]string) {
if c == nil {
return
}
val := reflect.Indirect(reflect.ValueOf(c))
for i := 0; i < val.NumField(); i++ {
f := val.Type().Field(i)
tag := f.Tag.Get("env")
if tag == "" {
continue
}
v, found := vars[tag]
if !found {
logger.Warn("var %q not found in file .env.%s", tag, Env)
continue
}
field := val.FieldByName(f.Name)
if !field.IsValid() {
continue
}
switch f.Type.Kind() {
case reflect.Int:
if intV, err := strconv.ParseInt(v, 10, 64); err != nil {
panic(err)
} else {
field.SetInt(intV)
}
default:
field.SetString(v)
}
}
}

282
config/dotenv/parser.go Normal file
View File

@ -0,0 +1,282 @@
package dotenv
import (
"bytes"
"errors"
"fmt"
"os"
"regexp"
"strings"
"unicode"
)
//
// copied from https://github.com/joho/godotenv/blob/main/parser.go
//
const (
charComment = '#'
prefixSingleQuote = '\''
prefixDoubleQuote = '"'
exportPrefix = "export"
)
func parseBytes(src []byte, out map[string]string) error {
src = bytes.Replace(src, []byte("\r\n"), []byte("\n"), -1)
cutset := src
for {
cutset = getStatementStart(cutset)
if cutset == nil {
// reached end of file
break
}
key, left, err := locateKeyName(cutset)
if err != nil {
return err
}
value, left, err := extractVarValue(left, out)
if err != nil {
return err
}
out[key] = value
cutset = left
}
return nil
}
// getStatementPosition returns position of statement begin.
//
// It skips any comment line or non-whitespace character.
func getStatementStart(src []byte) []byte {
pos := indexOfNonSpaceChar(src)
if pos == -1 {
return nil
}
src = src[pos:]
if src[0] != charComment {
return src
}
// skip comment section
pos = bytes.IndexFunc(src, isCharFunc('\n'))
if pos == -1 {
return nil
}
return getStatementStart(src[pos:])
}
// locateKeyName locates and parses key name and returns rest of slice
func locateKeyName(src []byte) (key string, cutset []byte, err error) {
// trim "export" and space at beginning
src = bytes.TrimLeftFunc(src, isSpace)
if bytes.HasPrefix(src, []byte(exportPrefix)) {
trimmed := bytes.TrimPrefix(src, []byte(exportPrefix))
if bytes.IndexFunc(trimmed, isSpace) == 0 {
src = bytes.TrimLeftFunc(trimmed, isSpace)
}
}
// locate key name end and validate it in single loop
offset := 0
loop:
for i, char := range src {
rchar := rune(char)
if isSpace(rchar) {
continue
}
switch char {
case '=', ':':
// library also supports yaml-style value declaration
key = string(src[0:i])
offset = i + 1
break loop
case '_':
default:
// variable name should match [A-Za-z0-9_.]
if unicode.IsLetter(rchar) || unicode.IsNumber(rchar) || rchar == '.' {
continue
}
return "", nil, fmt.Errorf(
`unexpected character %q in variable name near %q`,
string(char), string(src))
}
}
if len(src) == 0 {
return "", nil, errors.New("zero length string")
}
// trim whitespace
key = strings.TrimRightFunc(key, unicode.IsSpace)
cutset = bytes.TrimLeftFunc(src[offset:], isSpace)
return key, cutset, nil
}
// extractVarValue extracts variable value and returns rest of slice
func extractVarValue(src []byte, vars map[string]string) (value string, rest []byte, err error) {
quote, hasPrefix := hasQuotePrefix(src)
if !hasPrefix {
// unquoted value - read until end of line
endOfLine := bytes.IndexFunc(src, isLineEnd)
// Hit EOF without a trailing newline
if endOfLine == -1 {
endOfLine = len(src)
if endOfLine == 0 {
return "", nil, nil
}
}
// Convert line to rune away to do accurate countback of runes
line := []rune(string(src[0:endOfLine]))
// Assume end of line is end of var
endOfVar := len(line)
if endOfVar == 0 {
return "", src[endOfLine:], nil
}
// Work backwards to check if the line ends in whitespace then
// a comment (ie asdasd # some comment)
for i := endOfVar - 1; i >= 0; i-- {
if line[i] == charComment && i > 0 {
if isSpace(line[i-1]) {
endOfVar = i
break
}
}
}
trimmed := strings.TrimFunc(string(line[0:endOfVar]), isSpace)
return expandVariables(trimmed, vars), src[endOfLine:], nil
}
// lookup quoted string terminator
for i := 1; i < len(src); i++ {
if char := src[i]; char != quote {
continue
}
// skip escaped quote symbol (\" or \', depends on quote)
if prevChar := src[i-1]; prevChar == '\\' {
continue
}
// trim quotes
trimFunc := isCharFunc(rune(quote))
value = string(bytes.TrimLeftFunc(bytes.TrimRightFunc(src[0:i], trimFunc), trimFunc))
if quote == prefixDoubleQuote {
// unescape newlines for double quote (this is compat feature)
// and expand environment variables
value = expandVariables(expandEscapes(value), vars)
}
return value, src[i+1:], nil
}
// return formatted error if quoted string is not terminated
valEndIndex := bytes.IndexFunc(src, isCharFunc('\n'))
if valEndIndex == -1 {
valEndIndex = len(src)
}
return "", nil, fmt.Errorf("unterminated quoted value %s", src[:valEndIndex])
}
func expandEscapes(str string) string {
out := escapeRegex.ReplaceAllStringFunc(str, func(match string) string {
c := strings.TrimPrefix(match, `\`)
switch c {
case "n":
return "\n"
case "r":
return "\r"
default:
return match
}
})
return unescapeCharsRegex.ReplaceAllString(out, "$1")
}
func indexOfNonSpaceChar(src []byte) int {
return bytes.IndexFunc(src, func(r rune) bool {
return !unicode.IsSpace(r)
})
}
// hasQuotePrefix reports whether charset starts with single or double quote and returns quote character
func hasQuotePrefix(src []byte) (prefix byte, isQuored bool) {
if len(src) == 0 {
return 0, false
}
switch prefix := src[0]; prefix {
case prefixDoubleQuote, prefixSingleQuote:
return prefix, true
default:
return 0, false
}
}
func isCharFunc(char rune) func(rune) bool {
return func(v rune) bool {
return v == char
}
}
// isSpace reports whether the rune is a space character but not line break character
//
// this differs from unicode.IsSpace, which also applies line break as space
func isSpace(r rune) bool {
switch r {
case '\t', '\v', '\f', '\r', ' ', 0x85, 0xA0:
return true
}
return false
}
func isLineEnd(r rune) bool {
if r == '\n' || r == '\r' {
return true
}
return false
}
var (
escapeRegex = regexp.MustCompile(`\\.`)
expandVarRegex = regexp.MustCompile(`(\\)?(\$)(\()?\{?([A-Z0-9_]+)?\}?`)
unescapeCharsRegex = regexp.MustCompile(`\\([^$])`)
)
func expandVariables(v string, m map[string]string) string {
return expandVarRegex.ReplaceAllStringFunc(v, func(s string) string {
submatch := expandVarRegex.FindStringSubmatch(s)
if submatch == nil {
return s
}
if submatch[1] == "\\" || submatch[2] == "(" {
return submatch[0][1:]
} else if submatch[4] != "" {
if val, ok := m[submatch[4]]; ok {
return val
}
if val, ok := os.LookupEnv(submatch[4]); ok {
return val
}
return m[submatch[4]]
}
return s
})
}

69
config/dotenv/read.go Normal file
View File

@ -0,0 +1,69 @@
package dotenv
import (
"bytes"
"io"
"os"
)
//
// copied from https://github.com/joho/godotenv/blob/main/godotenv.go
//
// Read all env (with same file loading semantics as Load) but return values as
// a map rather than automatically writing values into env
func Read(filenames ...string) (envMap map[string]string, err error) {
filenames = filenamesOrDefault(filenames)
envMap = make(map[string]string)
for _, filename := range filenames {
individualEnvMap, individualErr := readFile(filename)
if individualErr != nil {
err = individualErr
return // return early on a spazout
}
for key, value := range individualEnvMap {
envMap[key] = value
}
}
return
}
func filenamesOrDefault(filenames []string) []string {
if len(filenames) == 0 {
return []string{".env"}
}
return filenames
}
func readFile(filename string) (envMap map[string]string, err error) {
file, err := os.Open(filename)
if err != nil {
return
}
defer file.Close()
return parse(file)
}
// parse reads an env file from io.Reader, returning a map of keys and values.
func parse(r io.Reader) (map[string]string, error) {
var buf bytes.Buffer
_, err := io.Copy(&buf, r)
if err != nil {
return nil, err
}
return unmarshal(buf.Bytes())
}
// unmarshal parses env file from byte slice of chars, returning a map of keys and values.
func unmarshal(src []byte) (map[string]string, error) {
out := make(map[string]string)
err := parseBytes(src, out)
return out, err
}

62
config/dotenv/write.go Normal file
View File

@ -0,0 +1,62 @@
package dotenv
import (
"fmt"
"os"
"sort"
"strconv"
"strings"
)
//
// copied from https://github.com/joho/godotenv/blob/main/godotenv.go
//
const doubleQuoteSpecialChars = "\\\n\r\"!$`"
// Write serializes the given environment and writes it to a file.
func Write(envMap map[string]string, filename string) error {
content, err := marshal(envMap)
if err != nil {
return err
}
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
_, err = file.WriteString(content + "\n")
if err != nil {
return err
}
return file.Sync()
}
// marshal outputs the given environment as a dotenv-formatted environment file.
// Each line is in the format: KEY="VALUE" where VALUE is backslash-escaped.
func marshal(envMap map[string]string) (string, error) {
lines := make([]string, 0, len(envMap))
for k, v := range envMap {
if d, err := strconv.Atoi(v); err == nil {
lines = append(lines, fmt.Sprintf(`%s=%d`, k, d))
} else {
lines = append(lines, fmt.Sprintf(`%s="%s"`, k, doubleQuoteEscape(v)))
}
}
sort.Strings(lines)
return strings.Join(lines, "\n"), nil
}
func doubleQuoteEscape(line string) string {
for _, c := range doubleQuoteSpecialChars {
toReplace := "\\" + string(c)
if c == '\n' {
toReplace = `\n`
}
if c == '\r' {
toReplace = `\r`
}
line = strings.Replace(line, string(c), toReplace, -1)
}
return line
}

17
go.mod
View File

@ -4,25 +4,24 @@ go 1.23.2
require (
github.com/99designs/gqlgen v0.17.55
github.com/vektah/gqlparser v1.3.1
github.com/vektah/gqlparser/v2 v2.5.18
gitserver.in/patialtech/mux v0.0.3
gitserver.in/patialtech/mux v0.3.1
)
require (
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/agnivade/levenshtein v1.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sosodev/duration v1.3.1 // indirect
github.com/urfave/cli/v2 v2.27.4 // indirect
github.com/urfave/cli/v2 v2.27.5 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/tools v0.24.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/tools v0.26.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

49
go.sum
View File

@ -2,26 +2,24 @@ github.com/99designs/gqlgen v0.17.55 h1:3vzrNWYyzSZjGDFo68e5j9sSauLxfKvLp+6ioRok
github.com/99designs/gqlgen v0.17.55/go.mod h1:3Bq768f8hgVPGZxL8aY9MaYmbxa6llPM/qu1IGH1EJo=
github.com/PuerkitoBio/goquery v1.9.3 h1:mpJr/ikUA9/GNJB/DBZcGeFDXUtosHRyRrwh7KGdTG0=
github.com/PuerkitoBio/goquery v1.9.3/go.mod h1:1ndLHPdTz+DyQPICCWYlYQMPl0oXZj0G6D4LCYA6u4U=
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY=
github.com/agnivade/levenshtein v1.2.0/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
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/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo=
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@ -30,39 +28,32 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4=
github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8=
github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ=
github.com/vektah/gqlparser v1.3.1 h1:8b0IcD3qZKWJQHSzynbDlrtP3IxVydZ2DZepCGofqfU=
github.com/vektah/gqlparser v1.3.1/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/vektah/gqlparser/v2 v2.5.18 h1:zSND3GtutylAQ1JpWnTHcqtaRZjl+y3NROeW8vuNo6Y=
github.com/vektah/gqlparser/v2 v2.5.18/go.mod h1:6HLzf7JKv9Fi3APymudztFQNmLXR5qJeEo6BOFcXVfc=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
gitserver.in/patialtech/mux v0.0.3 h1:Td0nAqArLGjB2pAVatEX2/pKaNqHvj4jqLD+PJHobxc=
gitserver.in/patialtech/mux v0.0.3/go.mod h1:/pYaLBNkRiMuxMKn9e2X0BIWt1bvHM19yQE/cJsm0q0=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
gitserver.in/patialtech/mux v0.3.1 h1:lbhQVr2vBvTcUp64Qjd2+4/s2lQXiDtsl8c+PpZvnDE=
gitserver.in/patialtech/mux v0.3.1/go.mod h1:/pYaLBNkRiMuxMKn9e2X0BIWt1bvHM19yQE/cJsm0q0=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@ -10,7 +10,7 @@ import (
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/handler/extension"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/vektah/gqlparser/gqlerror"
"github.com/vektah/gqlparser/v2/gqlerror"
"gitserver.in/patialtech/rano/graph/generated"
"gitserver.in/patialtech/rano/pkg/logger"
)
@ -149,4 +149,5 @@ func GraphiQL(queryEndpoint string) func(w http.ResponseWriter, r *http.Request)
func Query(w http.ResponseWriter, r *http.Request) {
// srv.ServeHTTP(w, r.WithContext(userCtx(r)))
srv.ServeHTTP(w, r)
}

View File

@ -1,33 +1,42 @@
package main
import (
"fmt"
"net/http"
"os"
"gitserver.in/patialtech/mux"
"gitserver.in/patialtech/mux/middleware"
"gitserver.in/patialtech/rano/config"
"gitserver.in/patialtech/rano/graph"
"gitserver.in/patialtech/rano/pkg/logger"
)
func main() {
r := mux.NewRouter()
// CORS
r.Use(middleware.CORS(middleware.CORSOption{
AllowedHeaders: []string{"Content-Type"},
MaxAge: 60,
}))
// Secure Headers
r.Use(middleware.Helmet(middleware.HelmetOption{
ContentSecurityPolicy: middleware.CSP{
ScriptSrc: []string{"self", "https://cdn.jsdelivr.net", "unsafe-inline"},
},
}))
// graphiql
r.Get("/graphiql", graph.GraphiQL("/query"))
r.GET("/graphiql", graph.GraphiQL("/query"))
// graph query
r.Post("/query", graph.Query)
r.POST("/query", graph.Query)
// catch all
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
r.GET("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello there"))
})
port := os.Getenv("GRAPH_PORT")
if port == "" {
port = "4001"
}
r.Serve(func(srv *http.Server) error {
srv.Addr = ":" + port
srv.Addr = fmt.Sprintf(":%d", config.Read().GraphPort)
logger.Info("graph server listening on %s", srv.Addr)
return srv.ListenAndServe()
})

View File

@ -1,2 +1,2 @@
schema: 'graph/*.graphql' # or 'http://localhost:9876/graphql'
documents: 'graph/**/*'
schema: 'graph/*.graphql'
documents: 'web/lib/gql/*'

View File

@ -10,6 +10,11 @@ func Info(msg string, args ...any) {
slog.Info(fmt.Sprintf(msg, a...), b...)
}
func Warn(msg string, args ...any) {
a, b := getArgs(args)
slog.Warn(fmt.Sprintf(msg, a...), b...)
}
func Error(err error, args ...any) {
a, b := getArgs(args)
slog.Error(fmt.Sprintf(err.Error(), a...), b...)

View File

@ -17,7 +17,6 @@ const config = {
assets: path.resolve(webDir, 'public')
},
alias: {
$graph: path.resolve(webDir, 'graph'),
$image: path.resolve(webDir, 'assets', 'image'),
$svg: path.resolve(webDir, 'assets', 'svg')
},

View File

@ -3,8 +3,6 @@ version: '3'
env:
ENV: development
dotenv: ['.env.{{.ENV}}']
tasks:
gen:
desc: use go generate, for graph files
@ -30,10 +28,12 @@ tasks:
codegen:
desc: generate graph types
dotenv: ['.env.{{.ENV}}']
cmds:
- cmd: deno task codegen
web:
desc: run web in dev mode
dotenv: ['.env.{{.ENV}}']
cmds:
- cmd: deno task dev

View File

@ -6,7 +6,7 @@ export default defineConfig({
server: {
port: parseInt(process.env.WEB_PORT ?? '3001'),
fs: {
allow: ['./web/app.css', './web/assets']
allow: ['./web/assets', './web/app.css', './graph']
}
}
});

View File

View File

@ -1,5 +1,5 @@
import { gql } from '@urql/svelte';
export const QryMe = gql`
export const Me = gql`
query Me {
me {
id

View File

@ -3,6 +3,6 @@ import { Client, cacheExchange, fetchExchange } from '@urql/svelte';
export function newClient(url: string) {
return new Client({
url,
exchanges: [cacheExchange, fetchExchange]
exchanges: [ fetchExchange]
});
}

View File

@ -1,10 +1,24 @@
<script lang="ts">
import { beforeNavigate } from '$app/navigation';
import { updated } from '$app/stores';
import { setContextClient } from '@urql/svelte';
import { newClient } from '$lib/client';
import { newClient } from '$lib/gql/client';
import '../app.css';
import { Me } from '$lib/gql/auth.gql';
const { children } = $props();
setContextClient(newClient(import.meta.env.VITE_GRAPH_URL ?? ''));
const host = import.meta.env.VITE_GRAPH_URL ?? ''
const client = newClient(`${host}/query`);
setContextClient(client);
beforeNavigate(({ willUnload, to }) => {
if ($updated && !willUnload && to?.url) {
location.href = to.url.href;
}
});
const p = client.query(Me, {}).toPromise();
</script>
{@render children()}