Fix auth and start configuring monaco
Signed-off-by: jolheiser <john.olheiser@gmail.com>main
parent
9065e9684b
commit
99cd67d90b
2
main.go
2
main.go
|
@ -43,7 +43,7 @@ func main() {
|
||||||
log.Fatal().Err(err).Msg("")
|
log.Fatal().Err(err).Msg("")
|
||||||
}
|
}
|
||||||
|
|
||||||
r := router.New(router.Config{
|
r := router.New(router.App{
|
||||||
Domain: *domainFlag,
|
Domain: *domainFlag,
|
||||||
SessionSecret: *sessionSecretFlag,
|
SessionSecret: *sessionSecretFlag,
|
||||||
GiteaURL: *giteaURLFlag,
|
GiteaURL: *giteaURLFlag,
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"go.jolheiser.com/gistea/router/session"
|
||||||
|
"go.jolheiser.com/gistea/static"
|
||||||
|
)
|
||||||
|
|
||||||
|
type App struct {
|
||||||
|
Domain string
|
||||||
|
SessionSecret string
|
||||||
|
|
||||||
|
GiteaURL string
|
||||||
|
GiteaClientKey string
|
||||||
|
GiteaClientSecret string
|
||||||
|
|
||||||
|
store *session.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) Tmpl(w io.Writer, r *http.Request, page static.Page, data map[string]any) error {
|
||||||
|
info, _ := a.store.Info(r)
|
||||||
|
ctx := static.Context{
|
||||||
|
"Domain": a.Domain,
|
||||||
|
"GiteaURL": a.GiteaURL,
|
||||||
|
"Page": page,
|
||||||
|
"Session": info,
|
||||||
|
"Data": data,
|
||||||
|
}
|
||||||
|
return static.Tmpl(w, static.Base, ctx)
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"go.jolheiser.com/gistea/static"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *App) newGist(w http.ResponseWriter, r *http.Request) {
|
||||||
|
page := static.New
|
||||||
|
|
||||||
|
repos, err := a.store.Repos(r)
|
||||||
|
if err != nil {
|
||||||
|
page = static.Landing
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.Tmpl(w, r, page, map[string]any{
|
||||||
|
"Repos": repos,
|
||||||
|
}); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/markbates/goth/gothic"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *App) login(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user, err := gothic.CompleteUserAuth(w, r)
|
||||||
|
if err != nil {
|
||||||
|
gothic.BeginAuthHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.store.CompleteAuth(w, r, user.AccessToken); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, a.Domain, http.StatusFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) loginCallback(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user, err := gothic.CompleteUserAuth(w, r)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := a.store.CompleteAuth(w, r, user.AccessToken); err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, a.Domain, http.StatusFound)
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"go.jolheiser.com/gistea/router/session"
|
||||||
"go.jolheiser.com/gistea/static"
|
"go.jolheiser.com/gistea/static"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
|
@ -16,35 +17,27 @@ import (
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
func New(app App) *chi.Mux {
|
||||||
Domain string
|
app.GiteaURL = strings.TrimRight(app.GiteaURL, "/")
|
||||||
SessionSecret string
|
app.Domain = strings.TrimRight(app.Domain, "/")
|
||||||
|
callbackURL := fmt.Sprintf("%s/_/auth/callback", app.Domain)
|
||||||
GiteaURL string
|
|
||||||
GiteaClientKey string
|
|
||||||
GiteaClientSecret string
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(cfg Config) *chi.Mux {
|
|
||||||
cfg.GiteaURL = strings.TrimRight(cfg.GiteaURL, "/")
|
|
||||||
cfg.Domain = strings.TrimRight(cfg.Domain, "/")
|
|
||||||
callbackURL := fmt.Sprintf("%s/auth/callback", cfg.Domain)
|
|
||||||
|
|
||||||
goth.UseProviders(
|
goth.UseProviders(
|
||||||
gitea.NewCustomisedURL(cfg.GiteaClientKey, cfg.GiteaClientSecret, callbackURL,
|
gitea.NewCustomisedURL(app.GiteaClientKey, app.GiteaClientSecret, callbackURL,
|
||||||
fmt.Sprintf("%s/login/oauth/authorize", cfg.GiteaURL),
|
fmt.Sprintf("%s/login/oauth/authorize", app.GiteaURL),
|
||||||
fmt.Sprintf("%s/login/oauth/access_token", cfg.GiteaURL),
|
fmt.Sprintf("%s/login/oauth/access_token", app.GiteaURL),
|
||||||
fmt.Sprintf("%s/api/v1/user", cfg.GiteaURL),
|
fmt.Sprintf("%s/api/v1/user", app.GiteaURL),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
gothStore := sessions.NewCookieStore([]byte(cfg.GiteaClientSecret))
|
gothStore := sessions.NewCookieStore([]byte(app.GiteaClientSecret))
|
||||||
gothStore.Options.HttpOnly = true
|
gothStore.Options.HttpOnly = true
|
||||||
gothic.Store = gothStore
|
gothic.Store = gothStore
|
||||||
gothic.GetProviderName = func(_ *http.Request) (string, error) {
|
gothic.GetProviderName = func(_ *http.Request) (string, error) {
|
||||||
return "gitea", nil
|
return "gitea", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
store := NewSessionStore(cfg.SessionSecret, cfg.GiteaURL)
|
store := session.NewStore(app.SessionSecret, app.GiteaURL)
|
||||||
|
app.store = store
|
||||||
|
|
||||||
r := chi.NewMux()
|
r := chi.NewMux()
|
||||||
r.Use(middleware.Logger)
|
r.Use(middleware.Logger)
|
||||||
|
@ -55,50 +48,18 @@ func New(cfg Config) *chi.Mux {
|
||||||
NoColor: true,
|
NoColor: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Route("/", func(r chi.Router) {
|
r.Route("/_", func(r chi.Router) {
|
||||||
r.Use(store.Middleware)
|
r.Mount("/css", static.CSS)
|
||||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
r.Mount("/js", static.JS)
|
||||||
repos, err := store.Repos(r)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := static.Tmpl(w, static.Base, static.Context{
|
|
||||||
"repos": repos,
|
|
||||||
"page": static.New,
|
|
||||||
}); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
r.Route("/auth", func(r chi.Router) {
|
r.Route("/auth", func(r chi.Router) {
|
||||||
r.Get("/login", func(w http.ResponseWriter, r *http.Request) {
|
r.Get("/login", app.login)
|
||||||
user, err := gothic.CompleteUserAuth(w, r)
|
r.Get("/callback", app.loginCallback)
|
||||||
if err != nil {
|
})
|
||||||
gothic.BeginAuthHandler(w, r)
|
})
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := store.Auth(w, r, user.AccessToken); err != nil {
|
r.Route("/", func(r chi.Router) {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
r.Get("/", app.newGist)
|
||||||
return
|
|
||||||
}
|
|
||||||
http.Redirect(w, r, cfg.Domain, http.StatusFound)
|
|
||||||
})
|
|
||||||
r.Get("/callback", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
user, err := gothic.CompleteUserAuth(w, r)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := store.Auth(w, r, user.AccessToken); err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
http.Redirect(w, r, cfg.Domain, http.StatusFound)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package router
|
package session
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/gob"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
@ -9,14 +11,26 @@ import (
|
||||||
"github.com/markbates/goth/gothic"
|
"github.com/markbates/goth/gothic"
|
||||||
)
|
)
|
||||||
|
|
||||||
const sessionCookie = "_gistea_session"
|
const (
|
||||||
|
sessionCookie = "_gistea_session"
|
||||||
|
infoKey = "_gistea_info"
|
||||||
|
)
|
||||||
|
|
||||||
type SessionStore struct {
|
type Store struct {
|
||||||
Store sessions.Store
|
Store sessions.Store
|
||||||
GiteaURL string
|
GiteaURL string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SessionStore) Middleware(next http.Handler) http.Handler {
|
type Info struct {
|
||||||
|
Org string
|
||||||
|
Token string
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
gob.Register(&Info{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) RequireAuth(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
sess, err := s.Store.Get(r, sessionCookie)
|
sess, err := s.Store.Get(r, sessionCookie)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -24,7 +38,7 @@ func (s *SessionStore) Middleware(next http.Handler) http.Handler {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := sess.Values["authenticated"]; !ok {
|
if _, ok := sess.Values[infoKey]; !ok {
|
||||||
gothic.BeginAuthHandler(w, r)
|
gothic.BeginAuthHandler(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -33,7 +47,25 @@ func (s *SessionStore) Middleware(next http.Handler) http.Handler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SessionStore) Auth(w http.ResponseWriter, r *http.Request, token string) error {
|
func (s *Store) Info(r *http.Request) (*Info, error) {
|
||||||
|
sess, err := s.Store.Get(r, sessionCookie)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if infoAny, ok := sess.Values[infoKey]; ok {
|
||||||
|
if info, ok := infoAny.(*Info); ok {
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errors.New("could not get session info")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) HasAuth(r *http.Request) bool {
|
||||||
|
i, _ := s.Info(r)
|
||||||
|
return i != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) CompleteAuth(w http.ResponseWriter, r *http.Request, token string) error {
|
||||||
client, err := gitea.NewClient(s.GiteaURL, gitea.SetToken(token))
|
client, err := gitea.NewClient(s.GiteaURL, gitea.SetToken(token))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -63,39 +95,31 @@ func (s *SessionStore) Auth(w http.ResponseWriter, r *http.Request, token string
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sess.Values["authenticated"] = true
|
sess.Values[infoKey] = &Info{
|
||||||
sess.Values["org"] = org
|
Org: org,
|
||||||
sess.Values["token"] = token
|
Token: token,
|
||||||
|
}
|
||||||
return s.Store.Save(r, w, sess)
|
return s.Store.Save(r, w, sess)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SessionStore) client(r *http.Request) (*gitea.Client, error) {
|
func (s *Store) Repos(r *http.Request) ([]*gitea.Repository, error) {
|
||||||
sess, err := s.Store.Get(r, sessionCookie)
|
info, err := s.Info(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return gitea.NewClient(s.GiteaURL, gitea.SetToken(sess.Values["token"].(string)))
|
client, err := gitea.NewClient(s.GiteaURL, gitea.SetToken(info.Token))
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SessionStore) Repos(r *http.Request) ([]*gitea.Repository, error) {
|
|
||||||
sess, err := s.Store.Get(r, sessionCookie)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := s.client(r)
|
repos, _, err := client.ListOrgRepos(info.Org, gitea.ListOrgReposOptions{})
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
repos, _, err := client.ListOrgRepos(sess.Values["org"].(string), gitea.ListOrgReposOptions{})
|
|
||||||
return repos, err
|
return repos, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSessionStore(sessionSecret, giteURL string) *SessionStore {
|
func NewStore(sessionSecret, giteURL string) *Store {
|
||||||
store := sessions.NewCookieStore([]byte(sessionSecret))
|
store := sessions.NewCookieStore([]byte(sessionSecret))
|
||||||
return &SessionStore{
|
return &Store{
|
||||||
Store: store,
|
Store: store,
|
||||||
GiteaURL: giteURL,
|
GiteaURL: giteURL,
|
||||||
}
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
.form-input {
|
||||||
|
border-radius: 10px;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-head {
|
||||||
|
border: black solid thin;
|
||||||
|
border-top-left-radius: 10px;
|
||||||
|
border-top-right-radius: 10px;
|
||||||
|
padding: 0 1em;
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
require.config({paths: {'vs': 'https://unpkg.com/monaco-editor@latest/min/vs'}});
|
||||||
|
|
||||||
|
// Before loading vs/editor/editor.main, define a global MonacoEnvironment that overwrites
|
||||||
|
// the default worker url location (used when creating WebWorkers). The problem here is that
|
||||||
|
// HTML5 does not allow cross-domain web workers, so we need to proxy the instantiation of
|
||||||
|
// a web worker through a same-domain script
|
||||||
|
window.MonacoEnvironment = {
|
||||||
|
getWorkerUrl: function (workerId, label) {
|
||||||
|
return `data:text/javascript;charset=utf-8,${encodeURIComponent(`
|
||||||
|
self.MonacoEnvironment = {
|
||||||
|
baseUrl: 'https://unpkg.com/monaco-editor@latest/min/'
|
||||||
|
};
|
||||||
|
importScripts('https://unpkg.com/monaco-editor@latest/min/vs/base/worker/workerMain.js');`
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let editors = [];
|
||||||
|
|
||||||
|
require(["vs/editor/editor.main"], function () {
|
||||||
|
let $filenames = document.querySelectorAll(".monaco-filename");
|
||||||
|
document.querySelectorAll('.monaco-editor').forEach((elem, idx) => {
|
||||||
|
let $editor = monaco.editor.create(elem, {
|
||||||
|
language: 'text',
|
||||||
|
theme: 'vs-dark',
|
||||||
|
});
|
||||||
|
elem.classList.remove("loading", "loading-lg");
|
||||||
|
let $filename = $filenames[idx];
|
||||||
|
editors.push({
|
||||||
|
"filename": $filename,
|
||||||
|
"editor": $editor
|
||||||
|
});
|
||||||
|
$filename.addEventListener('change', () => {
|
||||||
|
monaco.editor.setModelLanguage($editor.getModel(), $filename.value.split(".").pop());
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
//monaco.editor.setModelLanguage($editor.getModel(), "go");
|
||||||
|
});
|
|
@ -1,49 +1,16 @@
|
||||||
package static
|
package static
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"embed"
|
||||||
_ "embed"
|
"net/http"
|
||||||
"fmt"
|
|
||||||
"html/template"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Context = map[string]any
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
templates = make(map[string]*template.Template)
|
//go:embed css
|
||||||
|
css embed.FS
|
||||||
|
CSS = http.StripPrefix("/_", http.FileServer(http.FS(css)))
|
||||||
|
|
||||||
//go:embed templates/base.tmpl
|
//go:embed js
|
||||||
baseTmpl string
|
js embed.FS
|
||||||
Base = "base"
|
JS = http.StripPrefix("/_", http.FileServer(http.FS(js)))
|
||||||
|
|
||||||
//go:embed templates/new.tmpl
|
|
||||||
newTmpl string
|
|
||||||
New = "new"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
templates[Base] = template.Must(template.New("").Funcs(funcMap).Parse(baseTmpl))
|
|
||||||
templates[New] = template.Must(template.New("").Funcs(funcMap).Parse(newTmpl))
|
|
||||||
}
|
|
||||||
|
|
||||||
func Tmpl(w io.Writer, name string, ctx any) error {
|
|
||||||
if tmpl, ok := templates[name]; ok {
|
|
||||||
return tmpl.Execute(w, ctx)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("unknown template %q", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
var funcMap = template.FuncMap{
|
|
||||||
"tmpl": func(name string, ctx any) template.HTML {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err := Tmpl(&buf, name, ctx)
|
|
||||||
if err != nil {
|
|
||||||
log.Err(err).Msg("")
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return template.HTML(buf.String())
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package static
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Context map[string]any
|
||||||
|
|
||||||
|
type Page string
|
||||||
|
|
||||||
|
var (
|
||||||
|
templates = make(map[Page]*template.Template)
|
||||||
|
|
||||||
|
//go:embed templates/base.tmpl
|
||||||
|
baseTmpl string
|
||||||
|
Base Page = "base"
|
||||||
|
|
||||||
|
//go:embed templates/new.tmpl
|
||||||
|
newTmpl string
|
||||||
|
New Page = "new"
|
||||||
|
|
||||||
|
//go:embed templates/landing.tmpl
|
||||||
|
landingTmpl string
|
||||||
|
Landing Page = "landing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
templates[Base] = template.Must(template.New("").Funcs(funcMap).Parse(baseTmpl))
|
||||||
|
templates[New] = template.Must(template.New("").Funcs(funcMap).Parse(newTmpl))
|
||||||
|
templates[Landing] = template.Must(template.New("").Funcs(funcMap).Parse(landingTmpl))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Tmpl(w io.Writer, name Page, ctx Context) error {
|
||||||
|
if tmpl, ok := templates[name]; ok {
|
||||||
|
return tmpl.Execute(w, ctx)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unknown template %q", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
var funcMap = template.FuncMap{
|
||||||
|
"tmpl": func(name Page, ctx Context) template.HTML {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
err := Tmpl(&buf, name, ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Err(err).Msg("")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return template.HTML(buf.String())
|
||||||
|
},
|
||||||
|
}
|
|
@ -6,19 +6,25 @@
|
||||||
<link rel="stylesheet" href="https://unpkg.com/spectre.css/dist/spectre.min.css">
|
<link rel="stylesheet" href="https://unpkg.com/spectre.css/dist/spectre.min.css">
|
||||||
<link rel="stylesheet" href="https://unpkg.com/spectre.css/dist/spectre-exp.min.css">
|
<link rel="stylesheet" href="https://unpkg.com/spectre.css/dist/spectre-exp.min.css">
|
||||||
<link rel="stylesheet" href="https://unpkg.com/spectre.css/dist/spectre-icons.min.css">
|
<link rel="stylesheet" href="https://unpkg.com/spectre.css/dist/spectre-icons.min.css">
|
||||||
|
<link rel="stylesheet" href="/_/css/gistea.css">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="bg-dark">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<header class="navbar">
|
<header class="navbar">
|
||||||
<section class="navbar-section">
|
<section class="navbar-section">
|
||||||
<a href="/" class="navbar-brand mr-2">Gistea</a>
|
<a href="{{.Domain}}" class="navbar-brand mr-2">Gistea</a>
|
||||||
<a href="/" class="btn btn-link">Back to Gitea</a>
|
<a href="{{.GiteaURL}}" class="btn btn-link">Back to Gitea</a>
|
||||||
<a href="/" class="btn btn-link">Your gists</a>
|
{{if .Session}}<a href="/" class="btn btn-link">Your gists</a>{{end}}
|
||||||
</section>
|
</section>
|
||||||
|
{{if not .Session}}
|
||||||
|
<section class="navbar-section">
|
||||||
|
<a href="{{.Domain}}/_/auth/login" class="navbar-brand mr-2">Login</a>
|
||||||
|
</section>
|
||||||
|
{{end}}
|
||||||
</header>
|
</header>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
{{if .page}}{{tmpl .page .}}{{end}}
|
{{tmpl .Page .}}
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<div class="container">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column col-6 col-mx-auto">
|
||||||
|
Please login
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,43 +1,19 @@
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column col-6 col-mx-auto">
|
<div class="column col-6 col-mx-auto">
|
||||||
<input class="form-input" type="text" id="description" placeholder="Gist description">
|
<input class="form-input bg-dark" type="text" id="description" placeholder="Gist description">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column col-6 col-mx-auto">
|
<div class="column col-6 col-mx-auto">
|
||||||
<div id="monaco-editor" style="height: 20em"></div>
|
<div class="form-head">
|
||||||
|
<input class="monaco-filename form-input bg-dark col-6" type="text" placeholder="Filename (with extension)">
|
||||||
|
</div>
|
||||||
|
<div class="monaco-editor loading loading-lg" style="height: 20em"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="text/javascript" src="https://unpkg.com/monaco-editor@latest/min/vs/loader.js"></script>
|
<script type="text/javascript" src="https://unpkg.com/monaco-editor@latest/min/vs/loader.js"></script>
|
||||||
<script>
|
<script src="{{.Domain}}/_/js/monaco.js"></script>
|
||||||
require.config({ paths: { 'vs': 'https://unpkg.com/monaco-editor@latest/min/vs' }});
|
|
||||||
|
|
||||||
// Before loading vs/editor/editor.main, define a global MonacoEnvironment that overwrites
|
|
||||||
// the default worker url location (used when creating WebWorkers). The problem here is that
|
|
||||||
// HTML5 does not allow cross-domain web workers, so we need to proxy the instantiation of
|
|
||||||
// a web worker through a same-domain script
|
|
||||||
window.MonacoEnvironment = {
|
|
||||||
getWorkerUrl: function(workerId, label) {
|
|
||||||
return `data:text/javascript;charset=utf-8,${encodeURIComponent(`
|
|
||||||
self.MonacoEnvironment = {
|
|
||||||
baseUrl: 'https://unpkg.com/monaco-editor@latest/min/'
|
|
||||||
};
|
|
||||||
importScripts('https://unpkg.com/monaco-editor@latest/min/vs/base/worker/workerMain.js');`
|
|
||||||
)}`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
require(["vs/editor/editor.main"], function () {
|
|
||||||
monaco.editor.create(document.querySelector('#monaco-editor'), {
|
|
||||||
value: `function x() {
|
|
||||||
console.log("Hello world!");
|
|
||||||
}`,
|
|
||||||
language: 'html',
|
|
||||||
theme: 'vs-dark',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
Loading…
Reference in New Issue