more changes
This commit is contained in:
parent
e40f82f636
commit
7aa2dee280
17
app.go
17
app.go
@ -98,6 +98,19 @@ func (a *App) startup(ctx context.Context, logger logger.Logger) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Greet returns a greeting for the given name
|
// Greet returns a greeting for the given name
|
||||||
func (a *App) Greet(name string) string {
|
func (a *App) Greet(token string) string {
|
||||||
return fmt.Sprintf("Hello %s, It's show time!", name)
|
// Create fansly API instance
|
||||||
|
fanslyAPI := handlers.NewFanslyAPIController(token, a.Logger)
|
||||||
|
|
||||||
|
// Get the user info
|
||||||
|
account, accountErr := fanslyAPI.GetMe()
|
||||||
|
if accountErr != nil {
|
||||||
|
return "Failed to get account info: " + accountErr.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the response we got
|
||||||
|
a.Logger.Info(fmt.Sprintf("[Greet] Account info: %+v", account))
|
||||||
|
|
||||||
|
// Return the greeting
|
||||||
|
return fmt.Sprintf("Hello %s! You have %d fans and %d posts likes.", account.Username, account.FollowCount, account.PostLikes)
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import {useState} from 'react';
|
import { useState } from 'react';
|
||||||
import logo from './assets/images/logo-universal.png';
|
import logo from './assets/images/logo-universal.png';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import {Greet} from "../wailsjs/go/main/App";
|
import { Greet } from '../wailsjs/go/main/App';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [resultText, setResultText] = useState("Please enter your name below 👇");
|
const [resultText, setResultText] = useState(
|
||||||
|
'Enter your fansly API token, then press Go!',
|
||||||
|
);
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const updateName = (e: any) => setName(e.target.value);
|
const updateName = (e: any) => setName(e.target.value);
|
||||||
const updateResultText = (result: string) => setResultText(result);
|
const updateResultText = (result: string) => setResultText(result);
|
||||||
@ -14,15 +16,26 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div id="App">
|
<div id='App'>
|
||||||
<img src={logo} id="logo" alt="logo"/>
|
<img src={logo} id='logo' alt='logo' />
|
||||||
<div id="result" className="result">{resultText}</div>
|
<div id='result' className='result'>
|
||||||
<div id="input" className="input-box">
|
{resultText}
|
||||||
<input id="name" className="input" onChange={updateName} autoComplete="off" name="input" type="text"/>
|
</div>
|
||||||
<button className="btn" onClick={greet}>Greet</button>
|
<div id='input' className='input-box'>
|
||||||
|
<input
|
||||||
|
id='name'
|
||||||
|
className='input'
|
||||||
|
onChange={updateName}
|
||||||
|
autoComplete='off'
|
||||||
|
name='input'
|
||||||
|
type='text'
|
||||||
|
/>
|
||||||
|
<button className='btn' onClick={greet}>
|
||||||
|
Go!
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App
|
export default App;
|
||||||
|
111
handlers/fansly.go
Normal file
111
handlers/fansly.go
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"FanslySync/structs"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FanslyAPIController struct {
|
||||||
|
client *http.Client
|
||||||
|
token string
|
||||||
|
logger logger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Fansly client with the provided token (optional).
|
||||||
|
func NewFanslyAPIController(token string, log logger.Logger) *FanslyAPIController {
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &FanslyAPIController{
|
||||||
|
client: client,
|
||||||
|
token: token,
|
||||||
|
logger: log,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET issues a GET to /api/v1/{path}, optionally adding the Auth header,
|
||||||
|
// and unmarshals the JSON response into the result parameter.
|
||||||
|
func (f *FanslyAPIController) GET(path string, needsAuth bool, out interface{}) error {
|
||||||
|
// build request
|
||||||
|
url := "https://apiv3.fansly.com/api/v1/" + path
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
f.logger.Error("[FanslyAPIController] NewRequest GET " + path + ": " + err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set headers
|
||||||
|
req.Header.Set("User-Agent", "FanslySync/3.0 sticks@teamhydra.dev")
|
||||||
|
req.Header.Set("Accept", "application/json")
|
||||||
|
|
||||||
|
// set auth
|
||||||
|
if needsAuth && f.token != "" {
|
||||||
|
req.Header.Set("Authorization", f.token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// send
|
||||||
|
resp, err := f.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
f.logger.Error("[FanslyAPIController] Do GET " + path + ": " + err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// non-200
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
f.logger.Error(fmt.Sprintf("[FanslyAPIController] GET %s failed: %s", path, resp.Status))
|
||||||
|
// read body for logs
|
||||||
|
body, _ := io.ReadAll(resp.Body)
|
||||||
|
f.logger.Info("[FanslyAPIController] Response body: " + string(body))
|
||||||
|
return fmt.Errorf("unexpected status %s", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 200 ok, log
|
||||||
|
f.logger.Debug(fmt.Sprintf("[FanslyAPIController] GET %s succeeded: %s", path, resp.Status))
|
||||||
|
|
||||||
|
// read body and unmarshal
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
f.logger.Error("[FanslyAPIController] Request was OK, but failed to read body: " + err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmarshal into our expected response type
|
||||||
|
err = json.Unmarshal(body, &out)
|
||||||
|
if err != nil {
|
||||||
|
f.logger.Error("[FanslyAPIController] GET " + path + " failed to unmarshal response: " + err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetToken updates the authorization token for subsequent requests.
|
||||||
|
func (f *FanslyAPIController) SetToken(token string) {
|
||||||
|
f.token = token
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the current user's account information from the Fansly API.
|
||||||
|
//
|
||||||
|
// Will error if the token is not set or the request fails.
|
||||||
|
//
|
||||||
|
// Returns a FanslyAccount struct containing the account information.
|
||||||
|
func (f *FanslyAPIController) GetMe() (*structs.FanslyAccount, error) {
|
||||||
|
var response structs.FanslyBaseResponse[structs.FanslyAccountResponse]
|
||||||
|
|
||||||
|
err := f.GET("account/me", true, &response)
|
||||||
|
if err != nil {
|
||||||
|
f.logger.Error("[FanslyAPIController] GetMe failed: " + err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the account info
|
||||||
|
return &response.Response.Account, nil
|
||||||
|
}
|
@ -5,6 +5,10 @@ type FanslyBaseResponse[T any] struct {
|
|||||||
Response T `json:"response"` // The response data, type of T
|
Response T `json:"response"` // The response data, type of T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FanslyAccountResponse struct {
|
||||||
|
Account FanslyAccount `json:"account"`
|
||||||
|
}
|
||||||
|
|
||||||
type FanslyFollowResponse struct {
|
type FanslyFollowResponse struct {
|
||||||
Followers []struct {
|
Followers []struct {
|
||||||
FollowerID string `json:"followerId"` // The ID of the follower
|
FollowerID string `json:"followerId"` // The ID of the follower
|
||||||
@ -50,4 +54,23 @@ type Subscription struct {
|
|||||||
PromoStatus any `json:"promoStatus"`
|
PromoStatus any `json:"promoStatus"`
|
||||||
PromoStartsAt any `json:"promoStartsAt"`
|
PromoStartsAt any `json:"promoStartsAt"`
|
||||||
PromoEndsAt any `json:"promoEndsAt"`
|
PromoEndsAt any `json:"promoEndsAt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FanslyAccount struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
DisplayName string `json:"displayName,omitempty"`
|
||||||
|
Flags int `json:"flags"`
|
||||||
|
Version int `json:"version"`
|
||||||
|
CreatedAt int64 `json:"createdAt"`
|
||||||
|
FollowCount int `json:"followCount"`
|
||||||
|
SubscriberCount int `json:"subscriberCount"`
|
||||||
|
AccountMediaLikes int `json:"accountMediaLikes"`
|
||||||
|
StatusID int `json:"statusId"`
|
||||||
|
LastSeenAt int `json:"lastSeenAt"`
|
||||||
|
About string `json:"about,omitempty"`
|
||||||
|
Location string `json:"location,omitempty"`
|
||||||
|
PostLikes int `json:"postLikes"`
|
||||||
|
ProfileAccess bool `json:"profileAccess"`
|
||||||
|
}
|
||||||
|
35
structs/sync.go
Normal file
35
structs/sync.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package structs
|
||||||
|
|
||||||
|
type SyncProgressEvent struct {
|
||||||
|
// The current step of the sync process.
|
||||||
|
Step string `json:"step"`
|
||||||
|
// The current percent done of the sync process.
|
||||||
|
PercentDone int `json:"percent_done"`
|
||||||
|
// The current count of the current step of the sync process.
|
||||||
|
Count int `json:"current_count"`
|
||||||
|
// The total count of the current step of the sync process.
|
||||||
|
TotalCount int `json:"total_count"`
|
||||||
|
// Are we complete?
|
||||||
|
Complete bool `json:"complete"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PasteDataReply struct {
|
||||||
|
// The ID of the paste.
|
||||||
|
Id string `json:"id"`
|
||||||
|
|
||||||
|
// The content of the paste.
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PastePutResponse struct {
|
||||||
|
// If Error is not empty, the request failed.
|
||||||
|
Error string `json:"error"`
|
||||||
|
|
||||||
|
// If the request was successful, this will of type PasteDataReply.
|
||||||
|
// If the request was not successful, this will be empty.
|
||||||
|
Payload PasteDataReply `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PastePayload struct {
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
@ -64,18 +64,19 @@ func (m *multiLogger) Fatal(message string) {
|
|||||||
|
|
||||||
// NewRuntimeFileLogger returns a logger that writes all output both to
|
// NewRuntimeFileLogger returns a logger that writes all output both to
|
||||||
//
|
//
|
||||||
// $XDG_CONFIG_HOME/FanslySync/logs/runtime_latest.log
|
// $XDG_CONFIG_HOME/FanslySync/logs/runtime_latest.log (or OS equivalent)
|
||||||
//
|
//
|
||||||
// and to a timestamped file
|
// and to a timestamped file
|
||||||
//
|
//
|
||||||
// $XDG_CONFIG_HOME/FanslySync/logs/runtime_YYYY-MM-DD_HH-MM-SS.log
|
// $XDG_CONFIG_HOME/FanslySync/logs/runtime_YYYY-MM-DD_HH-MM-SS.log (or OS equivalent)
|
||||||
//
|
//
|
||||||
// It also deletes any timestamped logs older than 14 days.
|
// It also deletes any timestamped logs older than 14 days.
|
||||||
//
|
//
|
||||||
// The returned logger implements github.com/wailsapp/wails/v2/pkg/logger.Logger
|
// The returned logger implements github.com/wailsapp/wails/v2/pkg/logger.Logger
|
||||||
// and will be used by Wails for all Go-side logging.
|
// and will be used by Wails for all Go-side logging.
|
||||||
func NewRuntimeFileLogger() (logger.Logger, error) {
|
func NewRuntimeFileLogger() (logger.Logger, error) {
|
||||||
// 1) Ensure log directory exists
|
// Make sure the log directory exists
|
||||||
|
// We use $XDG_CONFIG_HOME/FanslySync/logs/runtime_latest.log or OS equivalent for $XDG_CONFIG_HOME
|
||||||
cfgDir, err := os.UserConfigDir()
|
cfgDir, err := os.UserConfigDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot determine user config dir: %w", err)
|
return nil, fmt.Errorf("cannot determine user config dir: %w", err)
|
||||||
@ -85,7 +86,8 @@ func NewRuntimeFileLogger() (logger.Logger, error) {
|
|||||||
return nil, fmt.Errorf("cannot create log directory: %w", err)
|
return nil, fmt.Errorf("cannot create log directory: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Prune old timestamped logs (>14 days)
|
// Prune old logs
|
||||||
|
// We keep logs for 14 days, so delete any logs older than that
|
||||||
cutoff := time.Now().Add(-14 * 24 * time.Hour)
|
cutoff := time.Now().Add(-14 * 24 * time.Hour)
|
||||||
entries, _ := os.ReadDir(logDir)
|
entries, _ := os.ReadDir(logDir)
|
||||||
for _, e := range entries {
|
for _, e := range entries {
|
||||||
@ -101,17 +103,17 @@ func NewRuntimeFileLogger() (logger.Logger, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3) Build paths for timestamped + latest
|
|
||||||
ts := time.Now().Format("2006-01-02_15-04-05")
|
ts := time.Now().Format("2006-01-02_15-04-05")
|
||||||
tsPath := filepath.Join(logDir, fmt.Sprintf("runtime_%s.log", ts))
|
tsPath := filepath.Join(logDir, fmt.Sprintf("runtime_%s.log", ts))
|
||||||
latestPath := filepath.Join(logDir, "runtime_latest.log")
|
latestPath := filepath.Join(logDir, "runtime_latest.log")
|
||||||
|
|
||||||
// 4) Create both loggers
|
// Create loggers to attach to the multiLogger
|
||||||
tsLogger := logger.NewFileLogger(tsPath)
|
tsLogger := logger.NewFileLogger(tsPath)
|
||||||
latestLogger := logger.NewFileLogger(latestPath)
|
latestLogger := logger.NewFileLogger(latestPath)
|
||||||
termLogger := logger.NewDefaultLogger()
|
termLogger := logger.NewDefaultLogger()
|
||||||
|
|
||||||
// 5) Fan-out into a multiLogger
|
// Spread into a multiLogger
|
||||||
|
// This will fan out all log messages to all three loggers
|
||||||
multi := &multiLogger{
|
multi := &multiLogger{
|
||||||
targets: []logger.Logger{tsLogger, latestLogger, termLogger},
|
targets: []logger.Logger{tsLogger, latestLogger, termLogger},
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package utils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user