put the ad (quote) banners in - last major feature in MISSINGFUNCS

This commit is contained in:
2026-03-02 22:52:45 -07:00
parent c26e7340c7
commit 5fa05bb086
33 changed files with 125 additions and 2 deletions
+2
View File
@@ -110,6 +110,7 @@ type AmConfig struct {
WorkerTasks int `yaml:"workerTasks"` WorkerTasks int `yaml:"workerTasks"`
} `yaml:"queues"` } `yaml:"queues"`
Caches struct { Caches struct {
Ads int `yaml:"ads"`
Communities int `yaml:"communities"` Communities int `yaml:"communities"`
CommunityProps int `yaml:"communityProps"` CommunityProps int `yaml:"communityProps"`
Conferences int `yaml:"conferences"` Conferences int `yaml:"conferences"`
@@ -254,6 +255,7 @@ func overlayConfig(dest *AmConfig, loaded *AmConfig, defaults *AmConfig) {
dest.Tuning.Queues.EmailSend = overlayInt(loaded.Tuning.Queues.EmailSend, defaults.Tuning.Queues.EmailSend) dest.Tuning.Queues.EmailSend = overlayInt(loaded.Tuning.Queues.EmailSend, defaults.Tuning.Queues.EmailSend)
dest.Tuning.Queues.IPBans = overlayInt(loaded.Tuning.Queues.IPBans, defaults.Tuning.Queues.IPBans) dest.Tuning.Queues.IPBans = overlayInt(loaded.Tuning.Queues.IPBans, defaults.Tuning.Queues.IPBans)
dest.Tuning.Queues.WorkerTasks = overlayInt(loaded.Tuning.Queues.WorkerTasks, defaults.Tuning.Queues.WorkerTasks) dest.Tuning.Queues.WorkerTasks = overlayInt(loaded.Tuning.Queues.WorkerTasks, defaults.Tuning.Queues.WorkerTasks)
dest.Tuning.Caches.Ads = overlayInt(loaded.Tuning.Caches.Ads, defaults.Tuning.Caches.Ads)
dest.Tuning.Caches.Communities = overlayInt(loaded.Tuning.Caches.Communities, defaults.Tuning.Caches.Communities) dest.Tuning.Caches.Communities = overlayInt(loaded.Tuning.Caches.Communities, defaults.Tuning.Caches.Communities)
dest.Tuning.Caches.CommunityProps = overlayInt(loaded.Tuning.Caches.CommunityProps, defaults.Tuning.Caches.CommunityProps) dest.Tuning.Caches.CommunityProps = overlayInt(loaded.Tuning.Caches.CommunityProps, defaults.Tuning.Caches.CommunityProps)
dest.Tuning.Caches.Conferences = overlayInt(loaded.Tuning.Caches.Conferences, defaults.Tuning.Caches.Conferences) dest.Tuning.Caches.Conferences = overlayInt(loaded.Tuning.Caches.Conferences, defaults.Tuning.Caches.Conferences)
+1
View File
@@ -64,6 +64,7 @@ tuning:
ipBans: 32 ipBans: 32
workerTasks: 128 workerTasks: 128
caches: caches:
ads: 30
communities: 50 communities: 50
communityProps: 100 communityProps: 100
conferences: 100 conferences: 100
+88
View File
@@ -0,0 +1,88 @@
/*
* Amsterdam Web Communities System
* Copyright (c) 2025-2026 Erbosoft Metaverse Design Solutions, All Rights Reserved
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
// The database package contains database management and storage logic.
package database
import (
"context"
crand "crypto/rand"
"math/big"
"sync"
"git.erbosoft.com/amy/amsterdam/config"
lru "github.com/hashicorp/golang-lru"
)
// Advert represents an advertising banner.
type Advert struct {
AdId int32 `db:"adid"` // ID of the ad
ImagePath string `db:"imagepath"` // path to the ad image
PathStyle int16 `db:"pathstyle"` // path style
Caption *string `db:"caption"` // caption
LinkURL *string `db:"linkurl"` // link URL
}
// Values for PathStyle.
const (
AdPathStyleContextRelative int16 = 0 // indicates context-relative image path
)
// adCache is the cache for advertisements.
var adCache *lru.Cache = nil
// adCacheMutex synchronizes access to adCache.
var adCacheMutex sync.Mutex
// setupAdCache sets up the ad cache.
func setupAdCache() {
var err error
adCache, err = lru.New(config.GlobalConfig.Tuning.Caches.Ads)
if err != nil {
panic(err)
}
}
// ResolvePath creates an absolute image path based on the stored path data.
func (ad *Advert) ResolvePath() string {
if ad.PathStyle == AdPathStyleContextRelative {
return "/" + ad.ImagePath
}
return ""
}
// AmGetAd gets an ad by ID.
func AmGetAd(ctx context.Context, adid int32) (*Advert, error) {
adCacheMutex.Lock()
defer adCacheMutex.Unlock()
rc, ok := adCache.Get(adid)
if ok {
return rc.(*Advert), nil
}
var theAd Advert
err := amdb.GetContext(ctx, &theAd, "SELECT * FROM adverts WHERE adid = ?", adid)
if err != nil {
return nil, err
}
adCache.Add(adid, &theAd)
return &theAd, nil
}
// AmGetRandomAd gets a random ad from the
func AmGetRandomAd(ctx context.Context) (*Advert, error) {
var num int
err := amdb.GetContext(ctx, &num, "SELECT COUNT(*) FROM adverts")
if err != nil {
return nil, err
}
v1, err := crand.Int(crand.Reader, big.NewInt(int64(num)))
if err != nil {
return nil, err
}
return AmGetAd(ctx, int32(v1.Int64())+1)
}
+1
View File
@@ -28,6 +28,7 @@ func SetupDb() (func(), error) {
db, err := sqlx.Open(config.GlobalConfig.Database.Driver, config.GlobalConfig.Database.Dsn) db, err := sqlx.Open(config.GlobalConfig.Database.Driver, config.GlobalConfig.Database.Dsn)
if err == nil { if err == nil {
amdb = db amdb = db
setupAdCache()
setupUserCache() setupUserCache()
setupContactsCache() setupContactsCache()
setupCommunityCache() setupCommunityCache()
+1 -1
View File
@@ -11,7 +11,7 @@ _(italicized items can be deferred)_
- _Calendar (top menu link)_ - _Calendar (top menu link)_
- _Chat (top menu link)_ - _Chat (top menu link)_
- _Documentation (top menu link)_ - _Documentation (top menu link)_
- Quote banner rotation - ~~Quote banner rotation~~
- ~~Sysadmin Menu:~~ - ~~Sysadmin Menu:~~
- ~~Edit Global Properties~~ - ~~Edit Global Properties~~
- ~~View/Edit IP Address Bans~~ - ~~View/Edit IP Address Bans~~
+1
View File
@@ -64,6 +64,7 @@ func setupEcho() *echo.Echo {
} }
e.Match(GetAndPost, "/TODO/*", ui.AmWrap(NotImplPage), uiset...) e.Match(GetAndPost, "/TODO/*", ui.AmWrap(NotImplPage), uiset...)
e.GET("/img/*", ui.AmServeImage) e.GET("/img/*", ui.AmServeImage)
e.GET("/images/*", ui.AmServeImage)
if config.GlobalConfig.Rendering.VeniceCompatibleImageURLs { if config.GlobalConfig.Rendering.VeniceCompatibleImageURLs {
e.GET("/venice/imagedata/:id", ui.AmServeVeniceCompatibleImage) e.GET("/venice/imagedata/:id", ui.AmServeVeniceCompatibleImage)
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

+9
View File
@@ -34,6 +34,9 @@ import (
//go:embed static_images/* //go:embed static_images/*
var static_images embed.FS var static_images embed.FS
//go:embed adbanners/*
var ad_banners embed.FS
// Constants for default photo sizes. // Constants for default photo sizes.
const ( const (
UserPhotoWidth = 100 UserPhotoWidth = 100
@@ -71,6 +74,12 @@ func AmServeImage(c echo.Context) error {
if err == nil { if err == nil {
return c.Blob(http.StatusOK, mimeTypeFromFilename(components[3]), b) return c.Blob(http.StatusOK, mimeTypeFromFilename(components[3]), b)
} }
case "ads/":
var b []byte
b, err = ad_banners.ReadFile(filepath.Join("adbanners", components[3]))
if err == nil {
return c.Blob(http.StatusOK, mimeTypeFromFilename(components[3]), b)
}
case "store/": case "store/":
var id int var id int
id, err = strconv.Atoi(components[3]) id, err = strconv.Atoi(components[3])
+12
View File
@@ -16,6 +16,7 @@ import (
"net/http" "net/http"
"time" "time"
"git.erbosoft.com/amy/amsterdam/database"
"github.com/klauspost/lctime" "github.com/klauspost/lctime"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@@ -129,6 +130,17 @@ func AmSendPageData(ctxt echo.Context, amctxt AmContext, command string, data an
} }
menus[1] = AmMenu("fixed") menus[1] = AmMenu("fixed")
amctxt.VarMap().Set("__leftMenus", menus) amctxt.VarMap().Set("__leftMenus", menus)
ad, err := database.AmGetRandomAd(ctxt.Request().Context())
if err != nil {
ad = &database.Advert{
AdId: -1,
ImagePath: "",
PathStyle: -1,
Caption: nil,
LinkURL: nil,
}
}
amctxt.VarMap().Set("__bannerad", ad)
if tmp := amctxt.GetScratch("frame_suppressLogin"); tmp != nil { if tmp := amctxt.GetScratch("frame_suppressLogin"); tmp != nil {
amctxt.VarMap().Set("__suppressLogin", true) amctxt.VarMap().Set("__suppressLogin", true)
} }
+10 -1
View File
@@ -49,7 +49,16 @@
<!-- Banner Ad --> <!-- Banner Ad -->
<div class="w-2/5 flex justify-end"> <div class="w-2/5 flex justify-end">
<img src="/img/builtin/placeholder_ad.gif" alt="" class="w-96 h-15 mx-2 my-2"> {{ p := __bannerad.ResolvePath() }}
{{ if p != "" }}
{{ if isset(__bannerad.LinkURL) }}
<a href="{{ __bannerad.LinkURL }}"><img src="{{ p }}" {{ if isset(__bannerad.Caption) }}alt="{{ __bannerad.Caption }}"{{ end }} class="w-96 h-15 mx-2 my-2"></a>
{{ else }}
<img src="{{ p }}" {{ if isset(__bannerad.Caption) }}alt="{{ __bannerad.Caption }}"{{ end }} class="w-96 h-15 mx-2 my-2">
{{ end }}
{{ else }}
&nbsp;
{{ end }}
</div> </div>
</div> </div>