Compare commits

...

4 Commits

Author SHA1 Message Date
15e694cae8
September Update 2023.
Some checks are pending
ci/woodpecker/push/build Pipeline is pending
2023-09-19 23:02:16 +01:00
39b37b5f56
Update video placeholder image processing in js. 2023-09-19 23:01:46 +01:00
c12fc92f71
Add video link and custom video thumbnail support.
Some checks are pending
ci/woodpecker/push/build Pipeline is pending
2023-09-16 16:16:29 +01:00
4da16c32ae
Upgrade internals for page versitility.
Some checks are pending
ci/woodpecker/push/build Pipeline is pending
Fix a bug in goinfo
2023-09-16 13:50:06 +01:00
12 changed files with 212 additions and 39 deletions

19
conf/page.go Normal file
View File

@ -0,0 +1,19 @@
package conf
import "strings"
type PageYaml struct {
PageName string `yaml:"pageName"`
PagePath string `yaml:"pagePath"`
}
func (py PageYaml) GetPagePath() string {
toReturn := py.PagePath
if !strings.HasSuffix(toReturn, ".go") {
toReturn += ".go"
}
if !strings.HasPrefix(toReturn, "/") {
toReturn = "/" + toReturn
}
return toReturn
}

View File

@ -9,10 +9,13 @@ import (
type ServeYaml struct {
DataStorage string `yaml:"dataStorage"`
TemplateStorage string `yaml:"templateStorage"`
Domains []string `yaml:"domains"`
RangeSupported bool `yaml:"rangeSupported"`
EnableGoInfoPage bool `yaml:"enableGoInfoPage"`
CacheSettings CacheSettingsYaml `yaml:"cacheSettings"`
PageSettings []PageYaml `yaml:"pageSettings"`
YmlDataFallback bool `yaml:"ymlDataFallback"`
}
func (sy ServeYaml) GetDomainString() string {
@ -39,3 +42,20 @@ func (sy ServeYaml) GetDataStoragePath() string {
return sy.DataStorage
}
}
func (sy ServeYaml) GetTemplateStoragePath() string {
if sy.TemplateStorage == "" || !filepath.IsAbs(sy.TemplateStorage) {
wd, err := os.Getwd()
if err != nil {
return ""
} else {
if sy.TemplateStorage == "" {
return wd
} else {
return path.Join(wd, sy.TemplateStorage)
}
}
} else {
return sy.TemplateStorage
}
}

View File

@ -4,9 +4,13 @@ listen:
webMethod: "http"
identify: true
serve:
dataStorage: ""
domains: []
rangeSupported: true
enableGoInfoPage: true
cacheSettings:
enableTemplateCaching: false
enableTemplateCachePurge: false
enableContentsCaching: true
enableContentsCachePurge: true
maxAge: 3600

View File

@ -150,6 +150,10 @@
<th>Memory Page Size</th>
<td>{{ .PageSize }}</td>
</tr>
<tr>
<th>System Time</th>
<td>{{ .CurrentTime }}</td>
</tr>
</table>
</p>
<p>

View File

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="description" content="Captain ALM's City University Portfolio">
<meta name="keywords" content="CaptainALM Captain_ALM Captain ALM portfolio Alfred Manville projects programming hacking cracking">
<meta name="keywords" content="CaptainALM Captain_ALM Captain ALM portfolio Alfred Manville projects programming hacking cracking city uni cityuni cuol City University of London mycityuni">
<title>City University Portfolio</title>
<link rel="stylesheet" href="{{ .Data.CSSBaseURL }}"/>
{{ if .Light }}
@ -19,7 +19,6 @@
var CssDarkURL = "{{ .Data.CSSDarkURL }}"
var SunImageURL = "{{ .Data.SunImageLocation }}"
var MoonImageURL = "{{ .Data.MoonImageLocation }}"
var PlayImageURL = "{{ .Data.PlayVideoImageLocation }}"
</script>
<script type="application/javascript" src="{{ .Data.JScriptURL }}"></script>
</head>
@ -155,10 +154,15 @@
<div class="item-table-360">
<div id="video-{{ $c }}">
{{ if eq .VideoLocation "" }}
<img src="{{ $.Data.NoVideoImageLocation }}" alt="No Video" width="360px">
<img src="{{ .GetVideoThumbnail $.Data.NoVideoImageLocation }}" alt="No Video" width="360px">
{{ else }}
{{ if .IsVideoLink }}
<a href="{{ .VideoLocation }}">
<img src="{{ .GetVideoThumbnail $.Data.PlayVideoImageLocation }}" alt="Play Video" title="Play" width="360px">
</a>
{{ else }}
<script type="application/javascript">
CreateVideoPlaceholder({{ $c }})
CreateVideoPlaceholder({{ $c }}, "{{ .GetVideoThumbnail $.Data.PlayVideoImageLocation }}")
</script>
<noscript>
<video controls width="360px">
@ -167,6 +171,7 @@
</video>
</noscript>
{{ end }}
{{ end }}
</div>
</div>
</div>

View File

@ -18,7 +18,7 @@ about:
title: "Alfred Manville (Captain ALM)"
content: >
<p>
Hello, I'm Alfred Manville (#age# Years Old).
Hello, I'm Alfred Manville (#age# Years Old) and a third year student at City, University of London.
I'm a free and open-source developer who enjoys networking my laptops together,
writes network software to communicate between them and then tries to break said software.
I also have a <a href="https://youtube.com/c/CaptainALM">Youtube Channel</a> which is in the process of being resumed from a hiatus.
@ -56,7 +56,7 @@ about:
Here is my <a href="https://cdn.captainalm.com/download/keys/alfred@captainalm.com.asc">GPG Key</a> for my email address.
</p>
<p>
My CV is available in the following formats: ( <a href="https://cdn.captainalm.com/download/cvs/AlfredManvilleCV-2022.docx">DOCX</a> | <a href="https://cdn.captainalm.com/download/cvs/AlfredManvilleCV-2022.pdf">PDF</a> )
My CV is available in the following formats: ( <a href="https://cdn.captainalm.com/download/cvs/AlfredManvilleCV-2023.docx">DOCX</a> | <a href="https://cdn.captainalm.com/download/cvs/AlfredManvilleCV-2023.pdf">PDF</a> )
</p>
thumbnailLocation: "resources/assets/imageofyou_t.jpg"
imageLocation: "resources/assets/imageofyou.jpg"
@ -89,6 +89,7 @@ entries:
endDate: "31/10/2021"
videoLocation: "resources/stream/vid-bootcamp.mp4"
videoContentType: "video/mp4"
videoThumbnailLocation: "resources/assets/bootcamp-vid.png"
thumbnailLocations:
- "resources/assets/pic1_t.jpg"
- "resources/assets/pic2_t.jpg"
@ -148,6 +149,7 @@ entries:
endDate: "08/05/2022"
videoLocation: "resources/stream/vid-ninjaformer-2022.mp4"
videoContentType: "video/mp4"
videoThumbnailLocation: "resources/assets/ninjaformer-vid.png"
thumbnailLocations:
- "resources/assets/pic4_t.jpg"
- "resources/assets/pic5_t.jpg"
@ -188,6 +190,7 @@ entries:
endDate: "30/01/2022"
videoLocation: "resources/stream/vid-shadowwork.mp4"
videoContentType: "video/mp4"
videoThumbnailLocation: "resources/assets/shadowwork-vid.png"
thumbnailLocations:
- "resources/assets/shadowwork-2_t.jpg"
- "resources/assets/shadowwork-3_t.jpg"
@ -242,6 +245,7 @@ entries:
endDate: "10/12/2022"
videoLocation: "resources/stream/vid-pycom.mp4"
videoContentType: "video/mp4"
videoThumbnailLocation: "resources/assets/pycom-vid.png"
thumbnailLocations:
- "resources/assets/pycom-1_t.jpg"
- "resources/assets/pycom-2_t.jpg"
@ -257,3 +261,81 @@ entries:
- "File Messaging."
- "Exploit Testing."
- "Failed Exploit Testing."
- name: "Group Project - AirVia ATS (AirTicket Sales)"
content: >
<p>
This group project was creating a Ticket Sales system for the fictional company AirVia LTD, for this the group had to both design the implementation and then write the code for it.
Unfortunately, the project was not finished to a state where all the required features were added in and while all of the backend functionality except for the reports existed,
the GUIs to view and control those backends was not available.
Examples of this include: Sales, Transactions, Discounts and Reports where no GUIs were created or finished for any of these components.
The design of the program was followed with a lot of adaptations (Rather than not being followed at all) and it architecturally made sense with the use of facade implementations;
The use of facade allowed for other people to code against an interface while waiting for a controller to be finished which extended the interface.
</p>
<p>
The database system was handled via an abstraction layer I designed and wrote myself which supports 'locking' a record for keeping consistency when multiple instances of the program are running.
This system uses an auxiliary table that has only the primary key column, a record is locked if it does not exist in the auxiliary table (Cannot delete) and is not locked if it does (Cannot insert);
This allows for atomic locking and unlocking of the record.
The implementation requires the record locked for safe access (Loading, Storing).
The abstraction layer makes use of two base classes, one for a single record and one for a table; with tha table one allowing the creation and deletion of the extending table via a schema and name being provided (As seen in the source code).
I also developed a backup system for the database that supports any table with the specifically supported data types used in the tables defined in the schema - removing the need to use third-party programs like SQLDump.
</p>
<p>
In the end, the following features were implemented: Login, Help / Error / Status Bar, Account System + GUI, Blank Types + GUI, Blanks + GUI, Customers + GUI, Discounts, Flexible Discounts, Sales, Transactions, Refunds, Dashboard + Notifications, Database Interfacing + Backup and Rates + GUI.
</p>
<p>
Find the source code here: <a href="https://github.com/karansambee/IN2018-Team-Project/tree/master">https://github.com/karansambee/IN2018-Team-Project/tree/master</a>
</p>
startDate: "01/02/2023"
endDate: "30/04/2023"
videoLocation: "resources/stream/vid-groupproject-2023.mp4"
videoContentType: "video/mp4"
videoThumbnailLocation: "resources/assets/groupproject-vid.png"
thumbnailLocations:
- "resources/assets/groupproject-1_t.jpg"
- "resources/assets/groupproject-2_t.jpg"
- "resources/assets/groupproject-3_t.jpg"
- "resources/assets/groupproject-4_t.jpg"
- "resources/assets/groupproject-5_t.jpg"
- "resources/assets/groupproject-6_t.jpg"
- "resources/assets/groupproject-7_t.jpg"
- "resources/assets/groupproject-8_t.jpg"
- "resources/assets/groupproject-9_t.jpg"
- "resources/assets/groupproject-10_t.jpg"
imageLocations:
- "resources/assets/groupproject-1.png"
- "resources/assets/groupproject-2.png"
- "resources/assets/groupproject-3.png"
- "resources/assets/groupproject-4.png"
- "resources/assets/groupproject-5.png"
- "resources/assets/groupproject-6.png"
- "resources/assets/groupproject-7.png"
- "resources/assets/groupproject-8.png"
- "resources/assets/groupproject-9.png"
- "resources/assets/groupproject-10.png"
imageAltTexts:
- "Logon Interface."
- "Administrator Dashboard Interface."
- "Blank Modifier."
- "Customer Creator."
- "Disabling an Account."
- "Rate Creator."
- "Blank Type Editor."
- "Help on Force Unlocking (Database Manager Interface)."
- "Account Editor on a Manager within The Dashboard Interface."
- "Part of the Main.java source code Screenshot."
- name: "City-University Promotional Video"
content: >
<p>
Here, I star in an interview for City, University of London's Promotional Marketing Campaign. Join <a href="https://www.city.ac.uk/">City</a>!
</p>
<p>
Find the video here: <a href="https://www.youtube.com/watch?v=tOccImgskec">https://www.youtube.com/watch?v=tOccImgskec</a>
</p>
<p>
Find the general School of Science and Technology video here: <a href="https://www.youtube.com/watch?v=pkTCf4CWFSY">https://www.youtube.com/watch?v=pkTCf4CWFSY</a>
</p>
startDate: "05/06/2023"
endDate: "05/06/2023"
videoLocation: "https://www.youtube.com/watch?v=tOccImgskec"
videoContentType: "text/uri-list"
videoThumbnailLocation: "resources/assets/citypromo-vid.png"

View File

@ -26,9 +26,9 @@ function CreateEntry(id, name, videourl, videotype, start, end, duration) {
duration : parseInt(duration, 10)
};
}
function CreateVideoPlaceholder(id) {
function CreateVideoPlaceholder(id,phImageURL) {
var imgPH = document.createElement("img")
imgPH.src = PlayImageURL
imgPH.src = phImageURL
imgPH.id = "play-"+id
imgPH.alt = "Play Video"
imgPH.title = "Play"

View File

@ -45,6 +45,7 @@ func (gipg *goInfoPage) GetCacheIDExtension(urlParameters url.Values) string {
type goInfoTemplateMarshal struct {
FullOutput bool
CurrentTime time.Time
RegisteredPages []string
CachedPages []string
ProcessID int
@ -98,6 +99,7 @@ func (gipg *goInfoPage) GetContents(urlParameters url.Values) (contentType strin
theBuffer := &io.BufferedWriter{}
err = theTemplate.ExecuteTemplate(theBuffer, templateName, &goInfoTemplateMarshal{
FullOutput: urlParameters.Has("full"),
CurrentTime: time.Now(),
RegisteredPages: regPages,
CachedPages: cacPages,
ProcessID: os.Getpid(),

View File

@ -43,9 +43,9 @@ func NewPageHandler(config conf.ServeYaml) *PageHandler {
CacheSettings: config.CacheSettings,
}
if config.EnableGoInfoPage {
toReturn.PageProviders = GetProviders(config.CacheSettings.EnableTemplateCaching, config.DataStorage, toReturn)
toReturn.PageProviders = GetProviders(config.CacheSettings.EnableTemplateCaching, config.GetDataStoragePath(), toReturn, config.GetTemplateStoragePath(), config.PageSettings, config.YmlDataFallback)
} else {
toReturn.PageProviders = GetProviders(config.CacheSettings.EnableTemplateCaching, config.DataStorage, nil)
toReturn.PageProviders = GetProviders(config.CacheSettings.EnableTemplateCaching, config.GetDataStoragePath(), nil, config.GetTemplateStoragePath(), config.PageSettings, config.YmlDataFallback)
}
return toReturn
}
@ -286,6 +286,9 @@ func (ph *PageHandler) GetRegisteredPages() []string {
}
func (ph *PageHandler) GetCachedPages() []string {
if ph.pageContentsCacheRWMutex == nil {
return make([]string, 0)
}
ph.pageContentsCacheRWMutex.RLock()
defer ph.pageContentsCacheRWMutex.RUnlock()
pages := make([]string, len(ph.PageContentsCache))
@ -298,6 +301,9 @@ func (ph *PageHandler) GetCachedPages() []string {
}
func (ph *PageHandler) GetNumberOfCachedPages() int {
if ph.pageContentsCacheRWMutex == nil {
return 0
}
ph.pageContentsCacheRWMutex.RLock()
defer ph.pageContentsCacheRWMutex.RUnlock()
return len(ph.PageContentsCache)

View File

@ -1,18 +1,26 @@
package pageHandler
import "golang.captainalm.com/cityuni-webserver/pageHandler/pages/index"
import (
"golang.captainalm.com/cityuni-webserver/conf"
"golang.captainalm.com/cityuni-webserver/pageHandler/pages/index"
"strings"
)
var providers map[string]PageProvider
func GetProviders(cacheTemplates bool, dataStorage string, pageHandler *PageHandler) map[string]PageProvider {
func GetProviders(cacheTemplates bool, dataStorage string, pageHandler *PageHandler, templateStorage string, pageSettings []conf.PageYaml, ymlDataFallback bool) map[string]PageProvider {
if providers == nil {
providers = make(map[string]PageProvider)
if pageHandler != nil {
infoPage := newGoInfoPage(pageHandler, dataStorage, cacheTemplates)
providers[infoPage.GetPath()] = infoPage //Go Information Page
}
indexPage := index.NewPage(dataStorage, cacheTemplates)
for _, cpg := range pageSettings { //Register pages
if strings.EqualFold(cpg.PageName, index.PageName) {
indexPage := index.NewPage(dataStorage, cacheTemplates, templateStorage, cpg.GetPagePath(), ymlDataFallback)
providers[indexPage.GetPath()] = indexPage
}
}
}
return providers
}

View File

@ -5,6 +5,7 @@ import (
"html/template"
"math"
"net/http"
"strings"
"time"
)
@ -20,6 +21,7 @@ type EntryYaml struct {
ThumbnailLocations []template.URL `yaml:"thumbnailLocations"`
ImageLocations []template.URL `yaml:"imageLocations"`
ImageAltTexts []string `yaml:"imageAltTexts"`
VideoThumbnailLocation template.URL `yaml:"videoThumbnailLocation"`
}
type ImageReference struct {
@ -28,6 +30,18 @@ type ImageReference struct {
ImageAltText string
}
func (ey EntryYaml) GetVideoThumbnail(usual template.URL) template.URL {
if ey.VideoThumbnailLocation == "" {
return usual
} else {
return ey.VideoThumbnailLocation
}
}
func (ey EntryYaml) IsVideoLink() bool {
return strings.EqualFold(ey.VideoContentType, "text/uri-list")
}
func (ey EntryYaml) GetStartDate() string {
return ey.StartDate.Format(dateFormat)
}

View File

@ -1,6 +1,7 @@
package index
import (
"errors"
"golang.captainalm.com/cityuni-webserver/utils/io"
"gopkg.in/yaml.v3"
"html/template"
@ -12,10 +13,10 @@ import (
"time"
)
const PageName = "index"
const templateName = "index.go.html"
const yamlName = "index.go.yml"
func NewPage(dataStore string, cacheTemplates bool) *Page {
func NewPage(dataStore string, cacheTemplates bool, templateStore string, pagePath string, ymlDataFallback bool) *Page {
var ptm *sync.Mutex
var sdm *sync.Mutex
if cacheTemplates {
@ -23,7 +24,10 @@ func NewPage(dataStore string, cacheTemplates bool) *Page {
sdm = &sync.Mutex{}
}
pageToReturn := &Page{
DataStore: dataStore,
YMLDataFallback: ymlDataFallback,
PagePath: pagePath,
DataPath: path.Join(dataStore, pagePath),
TemplatePath: path.Join(templateStore, templateName),
StoredDataMutex: sdm,
PageTemplateMutex: ptm,
}
@ -31,7 +35,10 @@ func NewPage(dataStore string, cacheTemplates bool) *Page {
}
type Page struct {
DataStore string
YMLDataFallback bool
PagePath string
DataPath string
TemplatePath string
StoredDataMutex *sync.Mutex
StoredData *DataYaml
LastModifiedData time.Time
@ -41,7 +48,7 @@ type Page struct {
}
func (p *Page) GetPath() string {
return "/index.go"
return p.PagePath
}
func (p *Page) GetLastModified() time.Time {
@ -129,16 +136,12 @@ func (p *Page) getPageTemplate() (*template.Template, error) {
defer p.PageTemplateMutex.Unlock()
}
if p.PageTemplate == nil {
thePath := templateName
if p.DataStore != "" {
thePath = path.Join(p.DataStore, thePath)
}
stat, err := os.Stat(thePath)
stat, err := os.Stat(p.TemplatePath)
if err != nil {
return nil, err
}
p.LastModifiedTemplate = stat.ModTime()
loadedData, err := os.ReadFile(thePath)
loadedData, err := os.ReadFile(p.TemplatePath)
if err != nil {
return nil, err
}
@ -161,14 +164,19 @@ func (p *Page) getPageData() (*DataYaml, error) {
defer p.StoredDataMutex.Unlock()
}
if p.StoredData == nil {
thePath := yamlName
if p.DataStore != "" {
thePath = path.Join(p.DataStore, thePath)
}
thePath := p.DataPath
stat, err := os.Stat(thePath)
if err != nil {
if p.YMLDataFallback && errors.Is(err, os.ErrNotExist) {
thePath += ".yml"
stat, err = os.Stat(thePath)
if err != nil {
return nil, err
}
} else {
return nil, err
}
}
p.LastModifiedData = stat.ModTime()
fileHandle, err := os.Open(thePath)
if err != nil {
@ -189,6 +197,7 @@ func (p *Page) getPageData() (*DataYaml, error) {
p.StoredData = dataYaml
}
return dataYaml, nil
} else {
return p.StoredData, nil
}