- graph, dotenv read
- graph, cors and secure header - web, urql client basic setup
This commit is contained in:
		| @@ -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()} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user