Show actual information on oauth integration setting page
This commit is contained in:
parent
e475e43fef
commit
bdd862e649
14 changed files with 156 additions and 21 deletions
15
arcgis/oauth.go
Normal file
15
arcgis/oauth.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package arcgis
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/Gleipnir-Technology/bob/dialect/psql/sm"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||
)
|
||||
|
||||
func GetOAuthForUser(ctx context.Context, user *models.User) (*models.OauthToken, error) {
|
||||
return user.UserOauthTokens(
|
||||
sm.OrderBy("created").Desc(),
|
||||
).One(ctx, db.PGInstance.BobDB)
|
||||
}
|
||||
|
|
@ -110,6 +110,7 @@ func HandleOauthAccessCode(ctx context.Context, user *models.User, code string)
|
|||
AccessTokenExpires: omit.From(accessExpires),
|
||||
ArcgisID: omitnull.FromPtr[string](nil),
|
||||
ArcgisLicenseTypeID: omitnull.FromPtr[string](nil),
|
||||
Created: omit.From(time.Now()),
|
||||
InvalidatedAt: omitnull.FromPtr[time.Time](nil),
|
||||
RefreshToken: omit.From(token.RefreshToken),
|
||||
RefreshTokenExpires: omit.From(refreshExpires),
|
||||
|
|
|
|||
|
|
@ -105,6 +105,15 @@ var OauthTokens = Table[
|
|||
Generated: false,
|
||||
AutoIncr: false,
|
||||
},
|
||||
Created: column{
|
||||
Name: "created",
|
||||
DBType: "timestamp without time zone",
|
||||
Default: "",
|
||||
Comment: "",
|
||||
Nullable: false,
|
||||
Generated: false,
|
||||
AutoIncr: false,
|
||||
},
|
||||
},
|
||||
Indexes: oauthTokenIndexes{
|
||||
OauthTokenPkey: index{
|
||||
|
|
@ -156,11 +165,12 @@ type oauthTokenColumns struct {
|
|||
ArcgisLicenseTypeID column
|
||||
RefreshTokenExpires column
|
||||
InvalidatedAt column
|
||||
Created column
|
||||
}
|
||||
|
||||
func (c oauthTokenColumns) AsSlice() []column {
|
||||
return []column{
|
||||
c.ID, c.AccessToken, c.AccessTokenExpires, c.RefreshToken, c.Username, c.UserID, c.ArcgisID, c.ArcgisLicenseTypeID, c.RefreshTokenExpires, c.InvalidatedAt,
|
||||
c.ID, c.AccessToken, c.AccessTokenExpires, c.RefreshToken, c.Username, c.UserID, c.ArcgisID, c.ArcgisLicenseTypeID, c.RefreshTokenExpires, c.InvalidatedAt, c.Created,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2912,6 +2912,7 @@ func (f *Factory) FromExistingOauthToken(m *models.OauthToken) *OauthTokenTempla
|
|||
o.ArcgisLicenseTypeID = func() null.Val[string] { return m.ArcgisLicenseTypeID }
|
||||
o.RefreshTokenExpires = func() time.Time { return m.RefreshTokenExpires }
|
||||
o.InvalidatedAt = func() null.Val[time.Time] { return m.InvalidatedAt }
|
||||
o.Created = func() time.Time { return m.Created }
|
||||
|
||||
ctx := context.Background()
|
||||
if m.R.UserUser != nil {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ type OauthTokenTemplate struct {
|
|||
ArcgisLicenseTypeID func() null.Val[string]
|
||||
RefreshTokenExpires func() time.Time
|
||||
InvalidatedAt func() null.Val[time.Time]
|
||||
Created func() time.Time
|
||||
|
||||
r oauthTokenR
|
||||
f *Factory
|
||||
|
|
@ -125,6 +126,10 @@ func (o OauthTokenTemplate) BuildSetter() *models.OauthTokenSetter {
|
|||
val := o.InvalidatedAt()
|
||||
m.InvalidatedAt = omitnull.FromNull(val)
|
||||
}
|
||||
if o.Created != nil {
|
||||
val := o.Created()
|
||||
m.Created = omit.From(val)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
|
@ -177,6 +182,9 @@ func (o OauthTokenTemplate) Build() *models.OauthToken {
|
|||
if o.InvalidatedAt != nil {
|
||||
m.InvalidatedAt = o.InvalidatedAt()
|
||||
}
|
||||
if o.Created != nil {
|
||||
m.Created = o.Created()
|
||||
}
|
||||
|
||||
o.setModelRels(m)
|
||||
|
||||
|
|
@ -217,6 +225,10 @@ func ensureCreatableOauthToken(m *models.OauthTokenSetter) {
|
|||
val := random_int32(nil)
|
||||
m.UserID = omit.From(val)
|
||||
}
|
||||
if !(m.Created.IsValue()) {
|
||||
val := random_time_Time(nil)
|
||||
m.Created = omit.From(val)
|
||||
}
|
||||
}
|
||||
|
||||
// insertOptRels creates and inserts any optional the relationships on *models.OauthToken
|
||||
|
|
@ -346,6 +358,7 @@ func (m oauthTokenMods) RandomizeAllColumns(f *faker.Faker) OauthTokenMod {
|
|||
OauthTokenMods.RandomArcgisLicenseTypeID(f),
|
||||
OauthTokenMods.RandomRefreshTokenExpires(f),
|
||||
OauthTokenMods.RandomInvalidatedAt(f),
|
||||
OauthTokenMods.RandomCreated(f),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -725,6 +738,37 @@ func (m oauthTokenMods) RandomInvalidatedAtNotNull(f *faker.Faker) OauthTokenMod
|
|||
})
|
||||
}
|
||||
|
||||
// Set the model columns to this value
|
||||
func (m oauthTokenMods) Created(val time.Time) OauthTokenMod {
|
||||
return OauthTokenModFunc(func(_ context.Context, o *OauthTokenTemplate) {
|
||||
o.Created = func() time.Time { return val }
|
||||
})
|
||||
}
|
||||
|
||||
// Set the Column from the function
|
||||
func (m oauthTokenMods) CreatedFunc(f func() time.Time) OauthTokenMod {
|
||||
return OauthTokenModFunc(func(_ context.Context, o *OauthTokenTemplate) {
|
||||
o.Created = f
|
||||
})
|
||||
}
|
||||
|
||||
// Clear any values for the column
|
||||
func (m oauthTokenMods) UnsetCreated() OauthTokenMod {
|
||||
return OauthTokenModFunc(func(_ context.Context, o *OauthTokenTemplate) {
|
||||
o.Created = nil
|
||||
})
|
||||
}
|
||||
|
||||
// Generates a random value for the column using the given faker
|
||||
// if faker is nil, a default faker is used
|
||||
func (m oauthTokenMods) RandomCreated(f *faker.Faker) OauthTokenMod {
|
||||
return OauthTokenModFunc(func(_ context.Context, o *OauthTokenTemplate) {
|
||||
o.Created = func() time.Time {
|
||||
return random_time_Time(f)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (m oauthTokenMods) WithParentsCascading() OauthTokenMod {
|
||||
return OauthTokenModFunc(func(ctx context.Context, o *OauthTokenTemplate) {
|
||||
if isDone, _ := oauthTokenWithParentsCascadingCtx.Value(ctx); isDone {
|
||||
|
|
|
|||
6
db/migrations/00061_oauth_token_created.sql
Normal file
6
db/migrations/00061_oauth_token_created.sql
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
-- +goose Up
|
||||
ALTER TABLE oauth_token ADD COLUMN created TIMESTAMP WITHOUT TIME ZONE;
|
||||
UPDATE oauth_token SET created = now();
|
||||
ALTER TABLE oauth_token ALTER COLUMN created SET NOT NULL;
|
||||
-- +goose Down
|
||||
ALTER TABLE oauth_token DROP COLUMN created;
|
||||
|
|
@ -36,6 +36,7 @@ type OauthToken struct {
|
|||
ArcgisLicenseTypeID null.Val[string] `db:"arcgis_license_type_id" `
|
||||
RefreshTokenExpires time.Time `db:"refresh_token_expires" `
|
||||
InvalidatedAt null.Val[time.Time] `db:"invalidated_at" `
|
||||
Created time.Time `db:"created" `
|
||||
|
||||
R oauthTokenR `db:"-" `
|
||||
}
|
||||
|
|
@ -58,7 +59,7 @@ type oauthTokenR struct {
|
|||
func buildOauthTokenColumns(alias string) oauthTokenColumns {
|
||||
return oauthTokenColumns{
|
||||
ColumnsExpr: expr.NewColumnsExpr(
|
||||
"id", "access_token", "access_token_expires", "refresh_token", "username", "user_id", "arcgis_id", "arcgis_license_type_id", "refresh_token_expires", "invalidated_at",
|
||||
"id", "access_token", "access_token_expires", "refresh_token", "username", "user_id", "arcgis_id", "arcgis_license_type_id", "refresh_token_expires", "invalidated_at", "created",
|
||||
).WithParent("oauth_token"),
|
||||
tableAlias: alias,
|
||||
ID: psql.Quote(alias, "id"),
|
||||
|
|
@ -71,6 +72,7 @@ func buildOauthTokenColumns(alias string) oauthTokenColumns {
|
|||
ArcgisLicenseTypeID: psql.Quote(alias, "arcgis_license_type_id"),
|
||||
RefreshTokenExpires: psql.Quote(alias, "refresh_token_expires"),
|
||||
InvalidatedAt: psql.Quote(alias, "invalidated_at"),
|
||||
Created: psql.Quote(alias, "created"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -87,6 +89,7 @@ type oauthTokenColumns struct {
|
|||
ArcgisLicenseTypeID psql.Expression
|
||||
RefreshTokenExpires psql.Expression
|
||||
InvalidatedAt psql.Expression
|
||||
Created psql.Expression
|
||||
}
|
||||
|
||||
func (c oauthTokenColumns) Alias() string {
|
||||
|
|
@ -111,10 +114,11 @@ type OauthTokenSetter struct {
|
|||
ArcgisLicenseTypeID omitnull.Val[string] `db:"arcgis_license_type_id" `
|
||||
RefreshTokenExpires omit.Val[time.Time] `db:"refresh_token_expires" `
|
||||
InvalidatedAt omitnull.Val[time.Time] `db:"invalidated_at" `
|
||||
Created omit.Val[time.Time] `db:"created" `
|
||||
}
|
||||
|
||||
func (s OauthTokenSetter) SetColumns() []string {
|
||||
vals := make([]string, 0, 10)
|
||||
vals := make([]string, 0, 11)
|
||||
if s.ID.IsValue() {
|
||||
vals = append(vals, "id")
|
||||
}
|
||||
|
|
@ -145,6 +149,9 @@ func (s OauthTokenSetter) SetColumns() []string {
|
|||
if !s.InvalidatedAt.IsUnset() {
|
||||
vals = append(vals, "invalidated_at")
|
||||
}
|
||||
if s.Created.IsValue() {
|
||||
vals = append(vals, "created")
|
||||
}
|
||||
return vals
|
||||
}
|
||||
|
||||
|
|
@ -179,6 +186,9 @@ func (s OauthTokenSetter) Overwrite(t *OauthToken) {
|
|||
if !s.InvalidatedAt.IsUnset() {
|
||||
t.InvalidatedAt = s.InvalidatedAt.MustGetNull()
|
||||
}
|
||||
if s.Created.IsValue() {
|
||||
t.Created = s.Created.MustGet()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *OauthTokenSetter) Apply(q *dialect.InsertQuery) {
|
||||
|
|
@ -187,7 +197,7 @@ func (s *OauthTokenSetter) Apply(q *dialect.InsertQuery) {
|
|||
})
|
||||
|
||||
q.AppendValues(bob.ExpressionFunc(func(ctx context.Context, w io.StringWriter, d bob.Dialect, start int) ([]any, error) {
|
||||
vals := make([]bob.Expression, 10)
|
||||
vals := make([]bob.Expression, 11)
|
||||
if s.ID.IsValue() {
|
||||
vals[0] = psql.Arg(s.ID.MustGet())
|
||||
} else {
|
||||
|
|
@ -248,6 +258,12 @@ func (s *OauthTokenSetter) Apply(q *dialect.InsertQuery) {
|
|||
vals[9] = psql.Raw("DEFAULT")
|
||||
}
|
||||
|
||||
if s.Created.IsValue() {
|
||||
vals[10] = psql.Arg(s.Created.MustGet())
|
||||
} else {
|
||||
vals[10] = psql.Raw("DEFAULT")
|
||||
}
|
||||
|
||||
return bob.ExpressSlice(ctx, w, d, start, vals, "", ", ", "")
|
||||
}))
|
||||
}
|
||||
|
|
@ -257,7 +273,7 @@ func (s OauthTokenSetter) UpdateMod() bob.Mod[*dialect.UpdateQuery] {
|
|||
}
|
||||
|
||||
func (s OauthTokenSetter) Expressions(prefix ...string) []bob.Expression {
|
||||
exprs := make([]bob.Expression, 0, 10)
|
||||
exprs := make([]bob.Expression, 0, 11)
|
||||
|
||||
if s.ID.IsValue() {
|
||||
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
|
||||
|
|
@ -329,6 +345,13 @@ func (s OauthTokenSetter) Expressions(prefix ...string) []bob.Expression {
|
|||
}})
|
||||
}
|
||||
|
||||
if s.Created.IsValue() {
|
||||
exprs = append(exprs, expr.Join{Sep: " = ", Exprs: []bob.Expression{
|
||||
psql.Quote(append(prefix, "created")...),
|
||||
psql.Arg(s.Created),
|
||||
}})
|
||||
}
|
||||
|
||||
return exprs
|
||||
}
|
||||
|
||||
|
|
@ -638,6 +661,7 @@ type oauthTokenWhere[Q psql.Filterable] struct {
|
|||
ArcgisLicenseTypeID psql.WhereNullMod[Q, string]
|
||||
RefreshTokenExpires psql.WhereMod[Q, time.Time]
|
||||
InvalidatedAt psql.WhereNullMod[Q, time.Time]
|
||||
Created psql.WhereMod[Q, time.Time]
|
||||
}
|
||||
|
||||
func (oauthTokenWhere[Q]) AliasedAs(alias string) oauthTokenWhere[Q] {
|
||||
|
|
@ -656,6 +680,7 @@ func buildOauthTokenWhere[Q psql.Filterable](cols oauthTokenColumns) oauthTokenW
|
|||
ArcgisLicenseTypeID: psql.WhereNull[Q, string](cols.ArcgisLicenseTypeID),
|
||||
RefreshTokenExpires: psql.Where[Q, time.Time](cols.RefreshTokenExpires),
|
||||
InvalidatedAt: psql.WhereNull[Q, time.Time](cols.InvalidatedAt),
|
||||
Created: psql.Where[Q, time.Time](cols.Created),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{{ template "sync/layout/base.html" . }}
|
||||
{{ template "sync/layout/authenticated.html" . }}
|
||||
|
||||
{{ define "title" }}Dash{{ end }}
|
||||
{{ define "title" }}Settings - Integrations{{ end }}
|
||||
{{ define "extraheader" }}
|
||||
<style>
|
||||
.integration-card {
|
||||
|
|
@ -69,30 +69,36 @@
|
|||
<tr>
|
||||
<td width="30%"><strong>OAuth Token Status</strong></td>
|
||||
<td>
|
||||
<span class="status-active">
|
||||
<i class="bi bi-check-circle-fill me-1"></i> Active
|
||||
</span>
|
||||
{{ if .ArcGISOAuth.InvalidatedAt.IsNull }}
|
||||
<span class="status-active">
|
||||
<i class="bi bi-check-circle-fill me-1"></i> Active
|
||||
</span>
|
||||
{{ else }}
|
||||
<span class="status-inactive">
|
||||
<i class="bi bi-x-circle-fill me-1"></i> Active
|
||||
</span>
|
||||
{{ end }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Token Expiration</strong></td>
|
||||
<td>26 days remaining (Expires on Dec 31, 2025)</td>
|
||||
<td>{{ .ArcGISOAuth.AccessTokenExpires|timeSince }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Integration Method</strong></td>
|
||||
<td>Web Hooks</td>
|
||||
<td>Polling</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Permission Level</strong></td>
|
||||
<td>Read & Write</td>
|
||||
<td>Read</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button class="btn btn-primary">
|
||||
<a class="btn btn-primary" href="{{ .URL.OAuthRefreshArcGIS }}">
|
||||
<i class="bi bi-arrow-repeat me-2"></i>Refresh OAuth Token
|
||||
</button>
|
||||
</a>
|
||||
<button class="btn btn-outline-danger">
|
||||
<i class="bi bi-trash me-2"></i>Delete Token
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{{ template "sync/layout/authenticated.html" . }}
|
||||
|
||||
{{ define "title" }}Dash{{ end }}
|
||||
{{ define "title" }}Settings{{ end }}
|
||||
{{ define "extraheader" }}
|
||||
<style>
|
||||
.settings-card {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ import (
|
|||
serviceRequestQuickConfirmation = buildTemplate("service-request-quick-confirmation", "base")
|
||||
serviceRequestUpdates = buildTemplate("service-request-updates", "base")
|
||||
settingRoot = buildTemplate("setting-mock", "base")
|
||||
settingIntegration = buildTemplate("setting-integration", "base")
|
||||
settingPesticide = buildTemplate("setting-pesticide", "base")
|
||||
settingPesticideAdd = buildTemplate("setting-pesticide-add", "base")
|
||||
settingUsers = buildTemplate("setting-user", "base")
|
||||
|
|
|
|||
|
|
@ -66,7 +66,8 @@ func Router() chi.Router {
|
|||
r.Method("GET", "/pool/upload", auth.NewEnsureAuth(getPoolUpload))
|
||||
r.Method("GET", "/pool/upload/{id}", auth.NewEnsureAuth(getPoolUploadByID))
|
||||
r.Method("POST", "/pool/upload", auth.NewEnsureAuth(postPoolUpload))
|
||||
r.Method("GET", "/setting", auth.NewEnsureAuth(getSettings))
|
||||
r.Method("GET", "/setting", auth.NewEnsureAuth(getSetting))
|
||||
r.Method("GET", "/setting/integration", auth.NewEnsureAuth(getSettingIntegration))
|
||||
r.Method("GET", "/signout", auth.NewEnsureAuth(getSignout))
|
||||
r.Method("GET", "/source/{globalid}", auth.NewEnsureAuth(getSource))
|
||||
r.Method("GET", "/stadia", auth.NewEnsureAuth(getStadia))
|
||||
|
|
|
|||
|
|
@ -3,11 +3,19 @@ package sync
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/Gleipnir-Technology/nidus-sync/arcgis"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/db/models"
|
||||
"github.com/Gleipnir-Technology/nidus-sync/html"
|
||||
)
|
||||
|
||||
func getSettings(w http.ResponseWriter, r *http.Request, u *models.User) {
|
||||
type ContentSettingIntegration struct {
|
||||
ArcGISOAuth *models.OauthToken
|
||||
URL ContentURL
|
||||
User User
|
||||
}
|
||||
|
||||
func getSetting(w http.ResponseWriter, r *http.Request, u *models.User) {
|
||||
userContent, err := contentForUser(r.Context(), u)
|
||||
if err != nil {
|
||||
respondError(w, "Failed to get user content", err, http.StatusInternalServerError)
|
||||
|
|
@ -19,3 +27,22 @@ func getSettings(w http.ResponseWriter, r *http.Request, u *models.User) {
|
|||
}
|
||||
html.RenderOrError(w, "sync/settings.html", data)
|
||||
}
|
||||
func getSettingIntegration(w http.ResponseWriter, r *http.Request, u *models.User) {
|
||||
ctx := r.Context()
|
||||
userContent, err := contentForUser(ctx, u)
|
||||
if err != nil {
|
||||
respondError(w, "Failed to get user content", err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
oauth, err := arcgis.GetOAuthForUser(ctx, u)
|
||||
if err != nil {
|
||||
respondError(w, "Failed to get oauth", err, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
data := ContentSettingIntegration{
|
||||
ArcGISOAuth: oauth,
|
||||
URL: newContentURL(),
|
||||
User: userContent,
|
||||
}
|
||||
html.RenderOrError(w, "sync/setting-integration.html", data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,8 +60,6 @@ type ContentDashboardLoading struct {
|
|||
User User
|
||||
}
|
||||
|
||||
type ContentPlaceholder struct {
|
||||
}
|
||||
type ContentSignin struct {
|
||||
InvalidCredentials bool
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
)
|
||||
|
||||
type ContentURL struct {
|
||||
OAuthRefreshArcGIS string
|
||||
PoolCSVUpload string
|
||||
SamplePoolCSV string
|
||||
Setting string
|
||||
|
|
@ -18,6 +19,7 @@ type ContentURL struct {
|
|||
|
||||
func newContentURL() ContentURL {
|
||||
return ContentURL{
|
||||
OAuthRefreshArcGIS: config.MakeURLNidus("/arcgis/oauth/begin"),
|
||||
PoolCSVUpload: config.MakeURLNidus("/pool/upload"),
|
||||
SamplePoolCSV: config.MakeURLNidus("/static/file/sample-pool.csv"),
|
||||
Setting: config.MakeURLNidus("/setting"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue