package main import ( "bytes" "context" "encoding/json" "flag" "fmt" "log" "os" "path/filepath" "git.godopu.com/lab/landing_page/backend/internal/config" "git.godopu.com/lab/landing_page/backend/internal/db" "git.godopu.com/lab/landing_page/backend/internal/repository" ) func marshalJSONNoEscapeHTML(data any) ([]byte, error) { var buf bytes.Buffer enc := json.NewEncoder(&buf) enc.SetEscapeHTML(false) enc.SetIndent("", " ") if err := enc.Encode(data); err != nil { return nil, err } return bytes.TrimRight(buf.Bytes(), "\n"), nil } func formatTSFile(importStmt, exportConstName, typeAnnotation string, data any) ([]byte, error) { jsonData, err := marshalJSONNoEscapeHTML(data) if err != nil { return nil, fmt.Errorf("marshal JSON: %w", err) } header := "// AUTO-GENERATED by backend/cmd/export-content — DO NOT EDIT BY HAND\n" content := fmt.Sprintf("%s%s\n\nexport const %s: %s = %s;\n", header, importStmt, exportConstName, typeAnnotation, string(jsonData)) return []byte(content), nil } func writeFileSafe(path string, data []byte) error { return os.WriteFile(path, data, 0644) } func main() { config.LoadEnvFile() defaultDB := "anl.db" if envDB := os.Getenv("DB_PATH"); envDB != "" { defaultDB = envDB } dbPath := flag.String("db", defaultDB, "Path to SQLite database file") targetDir := flag.String("out-dir", "../refer_landing_page/content", "Target content directory") flag.Parse() log.Printf("Exporting content from SQLite (%s) to TypeScript files in %s...", *dbPath, *targetDir) database, err := db.Connect(*dbPath) if err != nil { log.Fatalf("Failed to connect to database: %v", err) } defer database.Close() ctx := context.Background() homeRepo := repository.NewHomeRepository(database) membersRepo := repository.NewMembersRepository(database) pubsRepo := repository.NewPublicationsRepository(database) lecturesRepo := repository.NewLecturesRepository(database) standardsRepo := repository.NewStandardizationRepository(database) // 1. Projects projects, err := homeRepo.GetResearchProjects(ctx) if err != nil { log.Fatalf("Get research projects: %v", err) } type TSProject struct { Slug string `json:"slug"` Title string `json:"title"` Abstract string `json:"abstract"` Keywords []string `json:"keywords"` StandardsTrack *string `json:"standardsTrack,omitempty"` StandardsOrg *string `json:"standardsOrg,omitempty"` Period *string `json:"period,omitempty"` Deliverables []any `json:"deliverables"` Funder *string `json:"funder,omitempty"` } var tsProjects []TSProject for _, p := range projects { tsProjects = append(tsProjects, TSProject{ Slug: p.Slug, Title: p.Title, Abstract: p.Abstract, Keywords: p.Keywords, StandardsTrack: p.Organization, StandardsOrg: p.StandardsOrg, Period: p.Period, Deliverables: []any{}, Funder: p.Funder, }) } pBytes, err := formatTSFile(`import { ResearchProject } from "./types";`, "researchProjects", "ResearchProject[]", tsProjects) if err != nil { log.Fatalf("Format projects.ts failed: %v", err) } if err := writeFileSafe(filepath.Join(*targetDir, "projects.ts"), pBytes); err != nil { log.Fatalf("Write projects.ts failed: %v", err) } log.Println("✓ Exported projects.ts") // 2. Members & Alumni members, err := membersRepo.GetMembers(ctx) if err != nil { log.Fatalf("Get members: %v", err) } type TSMember struct { Ko string `json:"ko"` En string `json:"en"` Degree string `json:"degree"` Affiliation *string `json:"affiliation,omitempty"` } var tsMembers []TSMember for _, m := range members { tsMembers = append(tsMembers, TSMember{ Ko: m.NameKo, En: m.NameEn, Degree: m.Degree, Affiliation: m.Affiliation, }) } alumni, err := membersRepo.GetAlumni(ctx) if err != nil { log.Fatalf("Get alumni: %v", err) } type TSAlumnus struct { Ko string `json:"ko"` En string `json:"en"` Degree string `json:"degree"` GraduatedAt string `json:"graduatedAt"` Major *string `json:"major,omitempty"` CurrentPosition *string `json:"currentPosition,omitempty"` } var tsAlumni []TSAlumnus for _, a := range alumni { tsAlumni = append(tsAlumni, TSAlumnus{ Ko: a.NameKo, En: a.NameEn, Degree: a.Degree, GraduatedAt: a.GraduatedAt, Major: a.Major, CurrentPosition: a.CurrentPosition, }) } mJSON, err := marshalJSONNoEscapeHTML(tsMembers) if err != nil { log.Fatalf("Marshal members failed: %v", err) } aJSON, err := marshalJSONNoEscapeHTML(tsAlumni) if err != nil { log.Fatalf("Marshal alumni failed: %v", err) } membersTS := fmt.Sprintf("// AUTO-GENERATED by backend/cmd/export-content — DO NOT EDIT BY HAND\nimport { Member, Alumnus } from \"./types\";\n\nexport const activeMembers: Member[] = %s;\n\nexport const alumni: Alumnus[] = %s;\n", string(mJSON), string(aJSON)) if err := writeFileSafe(filepath.Join(*targetDir, "members.ts"), []byte(membersTS)); err != nil { log.Fatalf("Write members.ts failed: %v", err) } log.Println("✓ Exported members.ts") // 3. Publications & Patents pubs, err := pubsRepo.GetPublications(ctx, "") if err != nil { log.Fatalf("Get publications: %v", err) } type TSPublication struct { Category string `json:"category"` Title string `json:"title"` Authors *string `json:"authors,omitempty"` Venue string `json:"venue"` Volume *string `json:"volume,omitempty"` PublishedAt string `json:"publishedAt"` KCI bool `json:"kci,omitempty"` DOI *string `json:"doi,omitempty"` IsHighlight bool `json:"isHighlight,omitempty"` } var tsPubs []TSPublication for _, p := range pubs { tsPubs = append(tsPubs, TSPublication{ Category: p.Category, Title: p.Title, Authors: p.Authors, Venue: p.Venue, Volume: p.Volume, PublishedAt: p.PublishedAt, KCI: p.KCI, DOI: p.DOI, IsHighlight: p.IsHighlight, }) } patents, err := pubsRepo.GetPatents(ctx) if err != nil { log.Fatalf("Get patents: %v", err) } type TSPatent struct { Category string `json:"category"` Title string `json:"title"` Inventors string `json:"inventors"` ApplicationNo string `json:"applicationNo"` ApplicationAt string `json:"applicationAt"` RegistrationNo *string `json:"registrationNo,omitempty"` RegistrationAt *string `json:"registrationAt,omitempty"` Country string `json:"country"` PublishedAt string `json:"publishedAt"` } var tsPatents []TSPatent for _, pat := range patents { tsPatents = append(tsPatents, TSPatent{ Category: "patent", Title: pat.Title, Inventors: pat.Inventors, ApplicationNo: pat.ApplicationNo, ApplicationAt: pat.ApplicationAt, RegistrationNo: pat.RegistrationNo, RegistrationAt: pat.RegistrationAt, Country: pat.Country, PublishedAt: pat.PublishedAt, }) } pubsJSON, err := marshalJSONNoEscapeHTML(tsPubs) if err != nil { log.Fatalf("Marshal publications failed: %v", err) } patentsJSON, err := marshalJSONNoEscapeHTML(tsPatents) if err != nil { log.Fatalf("Marshal patents failed: %v", err) } pubsTS := fmt.Sprintf("// AUTO-GENERATED by backend/cmd/export-content — DO NOT EDIT BY HAND\nimport { Publication, Patent } from \"./types\";\n\nexport const publications: Publication[] = %s;\n\nexport const patents: Patent[] = %s;\n", string(pubsJSON), string(patentsJSON)) if err := writeFileSafe(filepath.Join(*targetDir, "publications.ts"), []byte(pubsTS)); err != nil { log.Fatalf("Write publications.ts failed: %v", err) } log.Println("✓ Exported publications.ts (with isHighlight metadata & unescaped HTML entities)") // 4. Lectures semesters, err := lecturesRepo.GetSemestersWithCourses(ctx) if err != nil { log.Fatalf("Get semesters: %v", err) } type TSCourse struct { Code string `json:"code"` En string `json:"en"` Note *string `json:"note,omitempty"` } type TSSemester struct { Term string `json:"term"` Year int `json:"year"` Courses []TSCourse `json:"courses"` } var tsSemesters []TSSemester for _, s := range semesters { var tsCourses []TSCourse for _, c := range s.Courses { tsCourses = append(tsCourses, TSCourse{ Code: c.Code, En: c.NameEn, Note: c.Note, }) } tsSemesters = append(tsSemesters, TSSemester{ Term: s.Term, Year: s.Year, Courses: tsCourses, }) } lecturesBytes, err := formatTSFile(`import { Semester } from "./types";`, "semesters", "Semester[]", tsSemesters) if err != nil { log.Fatalf("Format lectures.ts failed: %v", err) } if err := writeFileSafe(filepath.Join(*targetDir, "lectures.ts"), lecturesBytes); err != nil { log.Fatalf("Write lectures.ts failed: %v", err) } log.Println("✓ Exported lectures.ts") // 5. Standards standards, err := standardsRepo.GetStandardsBodiesWithProjects(ctx) if err != nil { log.Fatalf("Get standards bodies: %v", err) } type TSDoc struct { Title string `json:"title"` Ref string `json:"ref"` Status string `json:"status"` PublishedAt *string `json:"publishedAt,omitempty"` } type TSStandardProject struct { WG string `json:"wg"` Name string `json:"name"` Documents []TSDoc `json:"documents"` } type TSStandardsBody struct { Org string `json:"org"` Full string `json:"full"` Scope string `json:"scope"` Period string `json:"period"` Role *string `json:"role,omitempty"` Projects []TSStandardProject `json:"projects"` } var tsStandards []TSStandardsBody for _, b := range standards { var tsProjs []TSStandardProject for _, p := range b.Projects { var tsDocs []TSDoc for _, d := range p.Documents { tsDocs = append(tsDocs, TSDoc{ Title: d.Title, Ref: d.DocRef, Status: d.Status, PublishedAt: d.PublishedAt, }) } tsProjs = append(tsProjs, TSStandardProject{ WG: p.WG, Name: p.Name, Documents: tsDocs, }) } tsStandards = append(tsStandards, TSStandardsBody{ Org: b.Org, Full: b.FullName, Scope: b.Scope, Period: b.Period, Role: b.Role, Projects: tsProjs, }) } standardsBytes, err := formatTSFile(`import { StandardsBody } from "./types";`, "standardsBodies", "StandardsBody[]", tsStandards) if err != nil { log.Fatalf("Format standards.ts failed: %v", err) } if err := writeFileSafe(filepath.Join(*targetDir, "standards.ts"), standardsBytes); err != nil { log.Fatalf("Write standards.ts failed: %v", err) } log.Println("✓ Exported standards.ts") log.Println("=== All content successfully exported from SQLite! ===") }