- graph, dotenv read
- graph, cors and secure header - web, urql client basic setup
This commit is contained in:
parent
46c46a7e71
commit
45c318b897
@ -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
3
.gitignore
vendored
@ -1,5 +1,8 @@
|
||||
node_modules
|
||||
|
||||
# Jetbrains IDE
|
||||
.idea
|
||||
|
||||
# Output
|
||||
.output
|
||||
.vercel
|
||||
|
13
README.md
13
README.md
@ -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)
|
||||
|
@ -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
78
config/config.go
Normal 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
282
config/dotenv/parser.go
Normal 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
69
config/dotenv/read.go
Normal 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
62
config/dotenv/write.go
Normal 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
17
go.mod
@ -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
49
go.sum
@ -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=
|
||||
|
@ -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)
|
||||
|
||||
}
|
||||
|
@ -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()
|
||||
})
|
||||
|
@ -1,2 +1,2 @@
|
||||
schema: 'graph/*.graphql' # or 'http://localhost:9876/graphql'
|
||||
documents: 'graph/**/*'
|
||||
schema: 'graph/*.graphql'
|
||||
documents: 'web/lib/gql/*'
|
||||
|
@ -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...)
|
||||
|
@ -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')
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -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']
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { gql } from '@urql/svelte';
|
||||
export const QryMe = gql`
|
||||
export const Me = gql`
|
||||
query Me {
|
||||
me {
|
||||
id
|
@ -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]
|
||||
});
|
||||
}
|
0
graph/d.ts → web/lib/gql/graph.d.ts
vendored
0
graph/d.ts → web/lib/gql/graph.d.ts
vendored
@ -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()}
|
||||
|
Loading…
Reference in New Issue
Block a user