Add admin utilities for adding projects
This commit is contained in:
parent
7bf07c488e
commit
0184cd1625
|
@ -0,0 +1,231 @@
|
||||||
|
package admintools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.handmade.network/hmn/hmn/src/db"
|
||||||
|
"git.handmade.network/hmn/hmn/src/models"
|
||||||
|
"git.handmade.network/hmn/hmn/src/parsing"
|
||||||
|
"git.handmade.network/hmn/hmn/src/website"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func addProjectCommands(adminCommand *cobra.Command) {
|
||||||
|
projectCommand := &cobra.Command{
|
||||||
|
Use: "project",
|
||||||
|
Short: "Admin commands for managing projects",
|
||||||
|
}
|
||||||
|
adminCommand.AddCommand(projectCommand)
|
||||||
|
|
||||||
|
addCreateProjectCommand(projectCommand)
|
||||||
|
addProjectTagCommand(projectCommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addCreateProjectCommand(projectCommand *cobra.Command) {
|
||||||
|
createProjectCommand := &cobra.Command{
|
||||||
|
Use: "create",
|
||||||
|
Short: "Create a new project",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
name, _ := cmd.Flags().GetString("name")
|
||||||
|
slug, _ := cmd.Flags().GetString("slug")
|
||||||
|
blurb, _ := cmd.Flags().GetString("blurb")
|
||||||
|
description, _ := cmd.Flags().GetString("description")
|
||||||
|
personal, _ := cmd.Flags().GetBool("personal")
|
||||||
|
userIDs, _ := cmd.Flags().GetIntSlice("userids")
|
||||||
|
|
||||||
|
descParsed := parsing.ParseMarkdown(description, parsing.ForumRealMarkdown)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
conn := db.NewConnPool(1, 1)
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
tx, err := conn.Begin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer tx.Rollback(ctx)
|
||||||
|
|
||||||
|
p, err := website.FetchProject(ctx, tx, nil, models.HMNProjectID, website.ProjectsQuery{
|
||||||
|
IncludeHidden: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
hmn := p.Project
|
||||||
|
|
||||||
|
newProjectID, err := db.QueryInt(ctx, tx,
|
||||||
|
`
|
||||||
|
INSERT INTO handmade_project (
|
||||||
|
slug,
|
||||||
|
name,
|
||||||
|
blurb,
|
||||||
|
description,
|
||||||
|
color_1,
|
||||||
|
color_2,
|
||||||
|
featured,
|
||||||
|
hidden,
|
||||||
|
descparsed,
|
||||||
|
blog_enabled,
|
||||||
|
forum_enabled,
|
||||||
|
all_last_updated,
|
||||||
|
annotation_last_updated,
|
||||||
|
blog_last_updated,
|
||||||
|
forum_last_updated,
|
||||||
|
lifecycle,
|
||||||
|
date_approved,
|
||||||
|
date_created,
|
||||||
|
bg_flags,
|
||||||
|
library_enabled,
|
||||||
|
personal
|
||||||
|
) VALUES (
|
||||||
|
$1, -- slug
|
||||||
|
$2, -- name
|
||||||
|
$3, -- blurb
|
||||||
|
$4, -- description
|
||||||
|
$5, -- color_1
|
||||||
|
$6, -- color_2
|
||||||
|
FALSE, -- featured
|
||||||
|
FALSE, -- hidden
|
||||||
|
$7, -- descparsed
|
||||||
|
FALSE, -- blog_enabled
|
||||||
|
FALSE, -- forum_enabled
|
||||||
|
NOW(), -- all_last_updated
|
||||||
|
'epoch', -- annotation_last_updated
|
||||||
|
'epoch', -- blog_last_updated
|
||||||
|
'epoch', -- forum_last_updated
|
||||||
|
$8, -- lifecycle
|
||||||
|
NOW(), -- date_approved
|
||||||
|
NOW(), -- date_created
|
||||||
|
0, -- bg_flags
|
||||||
|
FALSE, -- library_enabled
|
||||||
|
$9 -- personal
|
||||||
|
)
|
||||||
|
RETURNING id
|
||||||
|
`,
|
||||||
|
slug,
|
||||||
|
name,
|
||||||
|
blurb,
|
||||||
|
description,
|
||||||
|
hmn.Color1,
|
||||||
|
hmn.Color2,
|
||||||
|
descParsed,
|
||||||
|
models.ProjectLifecycleActive,
|
||||||
|
personal,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, userID := range userIDs {
|
||||||
|
_, err := tx.Exec(ctx,
|
||||||
|
`
|
||||||
|
INSERT INTO handmade_user_projects (user_id, project_id)
|
||||||
|
VALUES ($1, $2)
|
||||||
|
`,
|
||||||
|
userID,
|
||||||
|
newProjectID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Created new project with id: %d\n", newProjectID)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
createProjectCommand.Flags().String("name", "", "")
|
||||||
|
createProjectCommand.Flags().String("slug", "", "")
|
||||||
|
createProjectCommand.Flags().String("blurb", "", "")
|
||||||
|
createProjectCommand.Flags().String("description", "", "")
|
||||||
|
createProjectCommand.Flags().Bool("personal", true, "")
|
||||||
|
createProjectCommand.Flags().IntSlice("userids", nil, "")
|
||||||
|
createProjectCommand.MarkFlagRequired("name")
|
||||||
|
createProjectCommand.MarkFlagRequired("userids")
|
||||||
|
projectCommand.AddCommand(createProjectCommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addProjectTagCommand(projectCommand *cobra.Command) {
|
||||||
|
projectTagCommand := &cobra.Command{
|
||||||
|
Use: "tag",
|
||||||
|
Short: "Create or update a project's tag",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
projectID, _ := cmd.Flags().GetInt("projectid")
|
||||||
|
tag, _ := cmd.Flags().GetString("tag")
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
conn := db.NewConnPool(1, 1)
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
tx, err := conn.Begin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer tx.Rollback(ctx)
|
||||||
|
|
||||||
|
p, err := website.FetchProject(ctx, tx, nil, projectID, website.ProjectsQuery{
|
||||||
|
IncludeHidden: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Project.TagID == nil {
|
||||||
|
// Create a tag
|
||||||
|
tagID, err := db.QueryInt(ctx, tx,
|
||||||
|
`
|
||||||
|
INSERT INTO tags (text) VALUES ($1)
|
||||||
|
RETURNING id
|
||||||
|
`,
|
||||||
|
tag,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach it to the project
|
||||||
|
_, err = tx.Exec(ctx,
|
||||||
|
`
|
||||||
|
UPDATE handmade_project
|
||||||
|
SET tag = $1
|
||||||
|
WHERE id = $2
|
||||||
|
`,
|
||||||
|
tagID, projectID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Update the text of an existing one
|
||||||
|
_, err := tx.Exec(ctx,
|
||||||
|
`
|
||||||
|
UPDATE tags
|
||||||
|
SET text = $1
|
||||||
|
WHERE id = (SELECT tag FROM handmade_project WHERE id = $2)
|
||||||
|
`,
|
||||||
|
tag, projectID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit(ctx)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Project now has tag: %s\n", tag)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
projectTagCommand.Flags().Int("projectid", 0, "")
|
||||||
|
projectTagCommand.Flags().String("tag", "", "")
|
||||||
|
projectTagCommand.MarkFlagRequired("projectid")
|
||||||
|
projectTagCommand.MarkFlagRequired("tag")
|
||||||
|
projectCommand.AddCommand(projectTagCommand)
|
||||||
|
}
|
|
@ -297,4 +297,6 @@ func init() {
|
||||||
moveThreadsToSubforumCommand.MarkFlagRequired("project_slug")
|
moveThreadsToSubforumCommand.MarkFlagRequired("project_slug")
|
||||||
moveThreadsToSubforumCommand.MarkFlagRequired("subforum_slug")
|
moveThreadsToSubforumCommand.MarkFlagRequired("subforum_slug")
|
||||||
adminCommand.AddCommand(moveThreadsToSubforumCommand)
|
adminCommand.AddCommand(moveThreadsToSubforumCommand)
|
||||||
|
|
||||||
|
addProjectCommands(adminCommand)
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ func FetchProjects(
|
||||||
qb.Add(`AND project.id = ANY ($?)`, q.ProjectIDs)
|
qb.Add(`AND project.id = ANY ($?)`, q.ProjectIDs)
|
||||||
}
|
}
|
||||||
if len(q.Slugs) > 0 {
|
if len(q.Slugs) > 0 {
|
||||||
qb.Add(`AND project.slug = ANY ($?)`, q.Slugs)
|
qb.Add(`AND (project.slug != '' AND project.slug = ANY ($?))`, q.Slugs)
|
||||||
}
|
}
|
||||||
if len(q.Lifecycles) > 0 {
|
if len(q.Lifecycles) > 0 {
|
||||||
qb.Add(`AND project.lifecycle = ANY($?)`, q.Lifecycles)
|
qb.Add(`AND project.lifecycle = ANY($?)`, q.Lifecycles)
|
||||||
|
|
|
@ -28,7 +28,7 @@ import (
|
||||||
"github.com/teacat/noire"
|
"github.com/teacat/noire"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool, perfCollector *perf.PerfCollector) http.Handler {
|
func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool) http.Handler {
|
||||||
router := &Router{}
|
router := &Router{}
|
||||||
routes := RouteBuilder{
|
routes := RouteBuilder{
|
||||||
Router: router,
|
Router: router,
|
||||||
|
@ -36,7 +36,7 @@ func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool, pe
|
||||||
return func(c *RequestContext) (res ResponseData) {
|
return func(c *RequestContext) (res ResponseData) {
|
||||||
c.Conn = conn
|
c.Conn = conn
|
||||||
|
|
||||||
logPerf := TrackRequestPerf(c, perfCollector)
|
logPerf := TrackRequestPerf(c)
|
||||||
defer logPerf()
|
defer logPerf()
|
||||||
|
|
||||||
defer LogContextErrorsFromResponse(c, &res)
|
defer LogContextErrorsFromResponse(c, &res)
|
||||||
|
@ -52,7 +52,7 @@ func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool, pe
|
||||||
return func(c *RequestContext) (res ResponseData) {
|
return func(c *RequestContext) (res ResponseData) {
|
||||||
c.Conn = conn
|
c.Conn = conn
|
||||||
|
|
||||||
logPerf := TrackRequestPerf(c, perfCollector)
|
logPerf := TrackRequestPerf(c)
|
||||||
defer logPerf()
|
defer logPerf()
|
||||||
|
|
||||||
defer LogContextErrorsFromResponse(c, &res)
|
defer LogContextErrorsFromResponse(c, &res)
|
||||||
|
@ -74,7 +74,7 @@ func NewWebsiteRoutes(longRequestContext context.Context, conn *pgxpool.Pool, pe
|
||||||
return func(c *RequestContext) (res ResponseData) {
|
return func(c *RequestContext) (res ResponseData) {
|
||||||
c.Conn = conn
|
c.Conn = conn
|
||||||
|
|
||||||
logPerf := TrackRequestPerf(c, perfCollector)
|
logPerf := TrackRequestPerf(c)
|
||||||
defer logPerf()
|
defer logPerf()
|
||||||
|
|
||||||
defer LogContextErrorsFromResponse(c, &res)
|
defer LogContextErrorsFromResponse(c, &res)
|
||||||
|
@ -534,7 +534,7 @@ func getCurrentUserAndSession(c *RequestContext, sessionId string) (*models.User
|
||||||
|
|
||||||
const PerfContextKey = "HMNPerf"
|
const PerfContextKey = "HMNPerf"
|
||||||
|
|
||||||
func TrackRequestPerf(c *RequestContext, perfCollector *perf.PerfCollector) (after func()) {
|
func TrackRequestPerf(c *RequestContext) (after func()) {
|
||||||
c.Perf = perf.MakeNewRequestPerf(c.Route, c.Req.Method, c.Req.URL.Path)
|
c.Perf = perf.MakeNewRequestPerf(c.Route, c.Req.Method, c.Req.URL.Path)
|
||||||
c.ctx = context.WithValue(c.Context(), PerfContextKey, c.Perf)
|
c.ctx = context.WithValue(c.Context(), PerfContextKey, c.Perf)
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ var WebsiteCommand = &cobra.Command{
|
||||||
|
|
||||||
server := http.Server{
|
server := http.Server{
|
||||||
Addr: config.Config.Addr,
|
Addr: config.Config.Addr,
|
||||||
Handler: NewWebsiteRoutes(longRequestContext, conn, perfCollector),
|
Handler: NewWebsiteRoutes(longRequestContext, conn),
|
||||||
}
|
}
|
||||||
|
|
||||||
backgroundJobsDone := zipJobs(
|
backgroundJobsDone := zipJobs(
|
||||||
|
|
Loading…
Reference in New Issue