Kind Realm (renderable)
Name user
Namespace akkadia / beta
Exported functions 14
Module gno.land/r/akkadia/beta/user
gno 0.9 render.gno gno
⧉
1 package user 2 3 import ( 4 "net/url" 5 "strconv" 6 "strings" 7 8 AdminCreateUser (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string)
AdminDeleteUser (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string)
AdminUpdateUser (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, propKeys string, propValues string)
CreateUser (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string})
DeleteAll (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string})
DeleteUser (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string})
GetListLimit () int
GetTotalUserSize () int
HasUser (addr string) bool
ListUsers (page int, count int) []string
ListUsersByAddresses (addrs ....uverse.address) []map[string]string
Render (iUrl string) string
SetFrozen (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, frozen bool)
SetListLimit (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, limit int)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.
"gno.land/p/jeronimoalbi/pager/v0"
9 "gno.land/r/akkadia/beta/admin"
10 )
11
12 // Render handles RESTful routing and returns Markdown responses.
13 func Render(iUrl string ) string {
14 assertRenderURLLen(iUrl)
15 u, err := url.Parse(iUrl)
16 if err != nil {
17 return "404\n"
18 }
19
20 query := u.Query()
21
22 switch u.Path {
23 case "" :
24 return renderHome()
25 case "users" :
26 return renderUsersList(iUrl)
27 case "user" :
28 addr := address(query.Get( "address" ))
29 return renderUser(iUrl, addr)
30 default :
31 return renderError( "Not found" )
32 }
33 }
34
35 func renderHome() string {
36 output := `# AKKADIA User
37
38 ## Introduction
39
40 User realm stores address-keyed profiles for AKKADIA.
41
42 - **created_at**: recorded when the user is created
43 - **properties**: admin-managed key/value attributes
44
45 ## Stats
46
47 `
48 output += "* **Total Users**: " + strconv.Itoa(GetTotalUserSize()) + "\n"
49 output += `
50 ## Quick Links
51
52 * [Browse Users](./user:users)
53
54 ## User Lookup
55
56 <gno-form path= "user" >
57 <gno-input name= "address" placeholder= "Enter the address" />
58 </gno-form>
59 `
60 return output
61 }
62
63 func renderUsersList(iUrl string ) string {
64 output := "# Users\n\n"
65 output += "[← Home](./user:)\n\n"
66
67 totalUsers := GetTotalUserSize()
68 if totalUsers == 0 {
69 output += "*No users registered yet.*\n"
70 return output
71 }
72
73 pages, err := pager.New(iUrl, totalUsers, pager.WithPageSize( 20 ))
74 if err != nil {
75 return renderError( "Invalid page" )
76 }
77
78 currentPage := pages.Page()
79 addrStrs := ListUsers(currentPage, pages.PageSize())
80 for _, addrStr := range addrStrs {
81 nickname := ""
82 listed := ListUsersByAddresses(address(addrStr))
83 if len(listed) > 0 {
84 nickname = listed[ 0 ][ "nickname" ]
85 }
86 if nickname != "" {
87 output += "* [" + escapeMarkdown(nickname) + " (" + addrStr + ")](./user:user?address=" + addrStr + "&page=" + strconv.Itoa(currentPage) + ")\n"
88 } else {
89 output += "* [" + addrStr + "](./user:user?address=" + addrStr + "&page=" + strconv.Itoa(currentPage) + ")\n"
90 }
91 }
92
93 if pages.HasPages() {
94 output += "\n" + pager.Picker(pages)
95 }
96
97 return output
98 }
99
100 func renderUser(iUrl string , addr address) string {
101 if !addr.IsValid() {
102 return renderError( "Invalid address" )
103 }
104
105 u, _ := url.Parse(iUrl)
106 page := normalizeRenderPage(u.Query().Get( "page" ))
107
108 addrStr := addr.String()
109 if !HasUser(addr) {
110 return renderError( "User not found" )
111 }
112
113 users := ListUsersByAddresses(addr)
114
115 output := "# User Detail\n\n"
116 if page != "" {
117 output += "[← Back to List](./user:users?page=" + page + ")\n\n"
118 } else {
119 output += "[← Home](./user:)\n\n"
120 }
121 output += "* **Address**: " + addrStr + " [[View on Explorer](" + admin.GetExplorerURL() + "/m/explorer/player?address=" + addrStr + ")]\n\n"
122
123 if len(users) > 0 {
124 output += "## Properties\n\n"
125 for k, v := range users[ 0 ] {
126 if k == "address" {
127 continue
128 }
129 output += "* **" + escapeMarkdown(k) + "**: " + escapeMarkdown(v) + "\n"
130 }
131 }
132
133 return output
134 }
135
136 func normalizeRenderPage(rawPage string ) string {
137 if rawPage == "" {
138 return ""
139 }
140 page, err := strconv.Atoi(rawPage)
141 if err != nil || page < 1 {
142 return ""
143 }
144 return strconv.Itoa(page)
145 }
146
147 func renderError(message string ) string {
148 output := "# Error\n\n"
149 output += "> " + message + "\n\n"
150 output += "[← Back to Home](./user:)\n"
151 return output
152 }
153
154 func escapeMarkdown(s string ) string {
155 s = strings.ReplaceAll(s, "\\" , "\\\\" )
156 s = strings.ReplaceAll(s, "\n" , "\\n" )
157 s = strings.ReplaceAll(s, "`" , "\\`" )
158 s = strings.ReplaceAll(s, "*" , "\\*" )
159 s = strings.ReplaceAll(s, "_" , "\\_" )
160 s = strings.ReplaceAll(s, "{" , "\\{" )
161 s = strings.ReplaceAll(s, "}" , "\\}" )
162 s = strings.ReplaceAll(s, "[" , "\\[" )
163 s = strings.ReplaceAll(s, "]" , "\\]" )
164 s = strings.ReplaceAll(s, "(" , "\\(" )
165 s = strings.ReplaceAll(s, ")" , "\\)" )
166 s = strings.ReplaceAll(s, "#" , "\\#" )
167 s = strings.ReplaceAll(s, "+" , "\\+" )
168 s = strings.ReplaceAll(s, "-" , "\\-" )
169 s = strings.ReplaceAll(s, "." , "\\." )
170 s = strings.ReplaceAll(s, "!" , "\\!" )
171 s = strings.ReplaceAll(s, "|" , "\\|" )
172 s = strings.ReplaceAll(s, ">" , "\\>" )
173 return s
174 }
175